MythTV  master
imageview.cpp
Go to the documentation of this file.
1 // -*- Mode: c++ -*-
2 /* ============================================================
3  * File : imageview.cpp
4  * Description :
5  *
6  * Copyright 2004-2006 Renchi Raju, Daniel Kristjansson
7  *
8  * This program is free software; you can redistribute it
9  * and/or modify it under the terms of the GNU General
10  * Public License as published bythe Free Software Foundation;
11  * either version 2, or (at your option)
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * ============================================================ */
20 
21 // STL headers
22 #include <algorithm>
23 #include <cmath>
24 #include <cstdlib>
25 
26 // Qt headers
27 #include <QDateTime>
28 #include <QDir>
29 #include <QMutex>
30 #include <QMutexLocker>
31 #include <QRunnable>
32 #include <QWaitCondition>
33 #include <random>
34 
35 // MythTV plugin headers
36 #include <mythcontext.h>
37 #include <lcddevice.h>
38 #include <mthread.h>
39 #include <mythuihelper.h>
40 
41 // MythGallery headers
42 #include "imageview.h"
43 #include "galleryutil.h"
44 #include "thumbgenerator.h"
45 
46 // Tuning parameter for seasonal weights, between 0 and 1, where lower numbers
47 // give greater weight to seasonal photos. The leading beta shape controls
48 // dates that are approaching and the trailing beta shape controls dates that
49 // just passed. When these are set to 0.175 and 0.31, respectively, about one
50 // quarter of the photos are from the upcoming week in prior years and about one
51 // quarter of the photos are from the preceding month in prior years.
52 const double LEADING_BETA_SHAPE = 0.175;
53 const double TRAILING_BETA_SHAPE = 0.31;
54 // Photos without a timestamp will default to the mode of the beta distribution.
55 const double DEFAULT_WEIGHT = std::pow(0.5, TRAILING_BETA_SHAPE - 1) *
56  std::pow(0.5, LEADING_BETA_SHAPE - 1);
57 // The edges of the distribution get clipped to avoid a singularity.
58 const qint64 BETA_CLIP = 60 * 60 * 24;
59 
60 class ImageView::LoadAlbumRunnable : public QRunnable {
61  ImageView *m_parent {nullptr};
65  QMutex m_isAliveLock;
66  bool m_isAlive {true};
67 
68 public:
69  LoadAlbumRunnable(ImageView *parent, const ThumbList &roots, int sortorder,
70  int slideshow_sequencing);
71 
72  void abort();
73  void run() override; // QRunnable
74 
76  static void filterDirectories(const ThumbList &input,
77  ThumbList &fileList, ThumbList &dirList);
78 };
79 
81  int *pos, int slideShow, int sortorder)
82  : m_pos(*pos),
83  m_savedPos(pos),
84 
85  // Common slideshow variables
86  m_slideshow_sequencing(slideShow),
87 
88  m_listener(this),
89  m_slideshow_sequence(ComposeSlideshowSequence(slideShow))
90 {
91 
92  int xbase, ybase, screenwidth, screenheight;
93  GetMythUI()->GetScreenSettings(xbase, screenwidth, m_wmult,
94  ybase, screenheight, m_hmult);
95  m_screenSize = QSize(screenwidth, screenheight);
96 
97  // --------------------------------------------------------------------
98 
99  bool recurse = gCoreContext->GetBoolSetting("GalleryRecursiveSlideshow", false);
100 
101  ThumbItem *origItem = nullptr;
102  if (m_pos < itemList.size())
103  origItem = itemList.at(m_pos);
104 
105  ThumbList fileList, dirList;
106  LoadAlbumRunnable::filterDirectories(itemList, fileList, dirList);
107  ImageView::AddItems(fileList);
108 
109  if (recurse)
110  {
111  // Load pictures from all directories on a different thread.
112  m_loaderRunnable = new LoadAlbumRunnable(this, dirList, sortorder,
114  m_loaderThread = new MThread("LoadAlbum", m_loaderRunnable);
115  QObject::connect(m_loaderThread->qthread(), SIGNAL(finished()),
116  &m_listener, SLOT(FinishLoading()));
118 
119  // Wait for at least one image to be loaded.
120  {
121  QMutexLocker guard(&m_itemListLock);
122  while (m_itemList.empty() && !m_finishedLoading)
123  {
125  }
126  }
127  }
128 
129  // --------------------------------------------------------------------
130 
131  // since we remove dirs item position might have changed
132  if (origItem)
133  m_pos = m_itemList.indexOf(origItem);
134 
135  m_pos = (!origItem || (m_pos == -1)) ? 0 : m_pos;
137 
138  // --------------------------------------------------------------------
139 
140  m_slideshow_frame_delay = gCoreContext->GetNumSetting("SlideshowDelay", 0);
144 
145  // --------------------------------------------------------------------
146 
147  if (slideShow == 2)
148  {
149  m_slideshow_mode = QT_TR_NOOP("Random Slideshow");
150  }
151  else if (slideShow == 3)
152  {
153  m_slideshow_mode = QT_TR_NOOP("Seasonal Slideshow");
154  }
155  else
156  {
157  m_slideshow_mode = QT_TR_NOOP("Slideshow");
158  }
159 }
160 
162 {
163  UpdateLCD(nullptr);
165  {
167  m_loaderThread->wait();
168  }
169 
171  {
172  delete m_slideshow_sequence;
173  m_slideshow_sequence = nullptr;
174  }
175 
176  if (m_loaderRunnable)
177  {
178  delete m_loaderRunnable;
179  m_loaderRunnable = nullptr;
180  }
181 
182  if (m_loaderThread)
183  {
184  delete m_loaderThread;
185  m_loaderThread = nullptr;
186  }
187 
188  *m_savedPos = m_pos;
189 }
190 
192  switch (slideshow_sequencing)
193  {
194  case 2:
195  return new SequenceShuffle();
196  case 3:
197  return new SequenceWeighted();
198  default:
199  return new SequenceInc();
200  }
201 }
202 
203 QString ImageView::GetRandomEffect(void) const
204 {
205  QMap<QString,QString> tmpMap = m_effect_map;
206  tmpMap.remove("none");
207  tmpMap.remove("Ken Burns (gl)");
208  QStringList t = tmpMap.keys();
209  int i = (int) ( (float)(t.count()) * random() / (RAND_MAX + 1.0F) );
210  return tmpMap[t[i]];
211 }
212 
214 {
215  LCD *lcd = LCD::Get();
216  if (!lcd)
217  return;
218 
219  if (!item)
220  {
221  lcd->setFunctionLEDs(FUNC_PHOTO, false);
222  lcd->switchToTime();
223  return;
224  }
225  lcd->setFunctionLEDs(FUNC_PHOTO, true);
226 
227  QString name = item->GetName();
228  QString desc = QString::number(m_pos + 1) + " / " +
229  QString::number(m_itemList.size());
230 
231  QList<LCDTextItem> textItems;
232  textItems.append(LCDTextItem(
233  1, ALIGN_CENTERED, name, "Generic", true));
234  textItems.append(LCDTextItem(
235  2, ALIGN_CENTERED, desc, "Generic", false));
236 
237  lcd->switchToGeneric(textItems);
238 }
239 
241 {
243  return " [" + tr(m_slideshow_mode) + "]";
244 
245  return "";
246 }
247 
248 void ImageView::GetScreenShot(QImage& image, const ThumbItem *item)
249 {
250  QFileInfo fi(item->GetPath());
251  QString screenshot = QString("%1%2-screenshot.jpg")
252  .arg(ThumbGenerator::getThumbcacheDir(fi.path()))
253  .arg(item->GetName());
254 
255  if (QFile::exists(screenshot))
256  {
257  QImage img(screenshot);
258  image = img;
259  }
260  else
261  {
262  QString movie("gallery-moviethumb.png");
263  if (GetMythUI()->FindThemeFile(movie))
264  image.load(movie);
265  }
266 }
267 
268 void ImageView::AddItems(const ThumbList &itemList)
269 {
270  QMutexLocker guard(&m_itemListLock);
271 
272  m_itemList.append(itemList);
273 
274  m_slideshow_sequence->extend(itemList.size());
275 
276  if (m_slideshow_sequencing == 3)
277  {
278  for (int i = 0; i < itemList.size(); ++i)
279  {
280  ThumbItem *item = itemList.at(i);
281  double weight = GetSeasonalWeight(item);
282  static_cast<SequenceWeighted *>(m_slideshow_sequence)->add(weight);
283  }
284  }
285 
286  if (!m_itemList.empty())
287  {
288  m_imagesLoaded.wakeAll();
289  }
290 }
291 
293 {
294  QMutexLocker guard(&m_itemListLock);
295  return m_itemList.at(m_pos);
296 }
297 
299 {
300  QMutexLocker guard(&m_itemListLock);
302  return m_itemList.at(m_pos);
303 }
304 
306 {
307  QMutexLocker guard(&m_itemListLock);
309  return m_itemList.at(m_pos);
310 }
311 
313 {
314  QMutexLocker guard(&m_itemListLock);
315  m_finishedLoading = true;
316  m_imagesLoaded.wakeAll();
317 }
318 
327  item->InitTimestamp();
328  if (item->HasTimestamp())
329  {
330  QDateTime timestamp = item->GetTimestamp();
331  QDateTime now = QDateTime::currentDateTime();
332  QDateTime curYearAnniversary = QDateTime(QDate(
333  now.date().year(),
334  timestamp.date().month(),
335  timestamp.date().day()),
336  timestamp.time());
337  bool isAnniversaryPast = curYearAnniversary < now;
338  QDateTime adjacentYearAnniversary = QDateTime(QDate(
339  now.date().year() + (isAnniversaryPast ? 1 : -1),
340  timestamp.date().month(),
341  timestamp.date().day()),
342  timestamp.time());
343  double range = std::abs(
344  curYearAnniversary.secsTo(adjacentYearAnniversary)) + BETA_CLIP;
345  // This calculation is not normalized, because that would require the
346  // beta function, which isn't part of the C++98 libraries. Weights
347  // that aren't normalized work just as well relative to each other.
348  double weight = std::pow(abs(now.secsTo(
349  isAnniversaryPast ? curYearAnniversary : adjacentYearAnniversary
350  ) + BETA_CLIP) / range,
351  TRAILING_BETA_SHAPE - 1) *
352  std::pow(abs(now.secsTo(
353  isAnniversaryPast ? adjacentYearAnniversary : curYearAnniversary
354  ) + BETA_CLIP) / range,
355  LEADING_BETA_SHAPE - 1);
356  return weight;
357  }
358  return DEFAULT_WEIGHT;
359 }
360 
362  ImageView *parent, const ThumbList &roots, int sortorder,
363  int slideshow_sequencing)
364  : m_parent(parent),
365  m_dirList(roots),
366  m_sortorder(sortorder),
367  m_slideshow_sequencing(slideshow_sequencing)
368 {
369 }
370 
373 {
374  QMutexLocker guard(&m_isAliveLock);
375  m_isAlive = false;
376 }
377 
379 {
380  while (!m_dirList.empty())
381  {
382  ThumbItem *dir = m_dirList.takeFirst();
383  ThumbList children;
384  GalleryUtil::LoadDirectory(children, dir->GetPath(),
385  GalleryFilter(m_sortorder != 0),
386  false, nullptr, nullptr);
387 
388  {
389  QMutexLocker guard(&m_isAliveLock);
390  if (!m_isAlive)
391  {
392  break;
393  }
394  }
395 
396  // The first images should not always come from the first directory.
397  if (m_slideshow_sequencing > 1)
398  {
399  std::shuffle(children.begin(), children.end(),
400  std::mt19937(std::random_device()()));
401  }
402 
403  ThumbList fileList;
404  filterDirectories(children, fileList, m_dirList);
405  if (!fileList.empty())
406  {
407  m_parent->AddItems(fileList);
408  }
409  }
410 }
411 
414  ThumbList &fileList, ThumbList &dirList)
415 {
416  for (int i = 0; i < input.size(); ++i)
417  {
418  ThumbItem *item = input.at(i);
419  ThumbList &targetList = item->IsDir() ? dirList : fileList;
420  targetList.append(item);
421  }
422 }
423 
425 {
426  m_parent->FinishLoading();
427 }
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
void GetScreenShot(QImage &image, const ThumbItem *item)
Definition: imageview.cpp:248
int m_pos
Definition: imageview.h:96
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
static SequenceBase * ComposeSlideshowSequence(int slideshow_sequencing)
Definition: imageview.cpp:191
int * m_savedPos
Definition: imageview.h:97
virtual void AddItems(const ThumbList &itemList)
Definition: imageview.cpp:268
float m_hmult
Definition: imageview.h:95
ImageView(const ThumbList &itemList, int *pos, int slideShow, int sortorder)
Definition: imageview.cpp:80
bool m_finishedLoading
Definition: imageview.h:131
void switchToGeneric(QList< LCDTextItem > &textItems)
Definition: lcddevice.cpp:630
int m_slideshow_frame_delay_state
Definition: imageview.h:108
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:311
LoadAlbumRunnable * m_loaderRunnable
Definition: imageview.h:123
ThumbItem * getCurrentItem() const
Definition: imageview.cpp:292
int m_slideshow_frame_delay
Definition: imageview.h:107
static bool LoadDirectory(ThumbList &itemList, const QString &dir, const GalleryFilter &flt, bool recurse, ThumbHash *itemHash, ThumbGenerator *thumbGen)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
QList< ThumbItem * > ThumbList
Definition: thumbview.h:78
size_t prev()
Definition: sequence.h:63
const double TRAILING_BETA_SHAPE
Definition: imageview.cpp:53
void UpdateLCD(const ThumbItem *item)
Definition: imageview.cpp:213
size_t next()
Definition: sequence.h:53
QDateTime GetTimestamp(void) const
Definition: thumbview.h:57
QString GetDescriptionStatus(void) const
Definition: imageview.cpp:240
QMap< QString, QString > m_effect_map
Definition: imageview.h:116
static LCD * Get(void)
Definition: lcddevice.cpp:65
ThumbList m_itemList
Definition: imageview.h:129
ThumbItem * retreatItem()
Definition: imageview.cpp:305
QString GetName(void) const
Definition: thumbview.h:53
bool IsDir(void) const
Definition: thumbview.h:60
virtual QString GetRandomEffect(void) const
Definition: imageview.cpp:203
SequenceBase * m_slideshow_sequence
Definition: imageview.h:130
const qint64 BETA_CLIP
Definition: imageview.cpp:58
const int m_slideshow_sequencing
Definition: imageview.h:106
unsigned char t
Definition: ParseText.cpp:329
static QString getThumbcacheDir(const QString &inDir)
virtual ~ImageView()
Definition: imageview.cpp:161
void FinishLoading()
Definition: imageview.cpp:312
bool HasTimestamp(void) const
Definition: thumbview.h:56
QSize m_screenSize
Definition: imageview.h:93
virtual void extend(size_t items)
Definition: sequence.h:48
const double DEFAULT_WEIGHT
Definition: imageview.cpp:55
const char * name
Definition: ParseText.cpp:328
MythUIHelper * GetMythUI()
void switchToTime()
Definition: lcddevice.cpp:554
float m_wmult
Definition: imageview.h:94
const char * m_slideshow_mode
Definition: imageview.h:110
QString GetPath(void) const
Definition: thumbview.h:59
int GetNumSetting(const QString &key, int defaultval=0)
void FinishLoading() const
Definition: imageview.cpp:424
QWaitCondition m_imagesLoaded
Definition: imageview.h:126
const double LEADING_BETA_SHAPE
Definition: imageview.cpp:52
bool GetBoolSetting(const QString &key, bool defaultval=false)
LoadAlbumListener m_listener
Definition: imageview.h:124
virtual void set(size_t _idx)=0
MThread * m_loaderThread
Definition: imageview.h:125
QThread * qthread(void)
Returns the thread, this will always return the same pointer no matter how often you restart the thre...
Definition: mthread.cpp:244
Definition: lcddevice.h:165
double GetSeasonalWeight(ThumbItem *item)
This method calculates a weight for the item based on how closely it was taken to the current time of...
Definition: imageview.cpp:326
QMutex m_itemListLock
Definition: imageview.h:128
static long int random(void)
Definition: compat.h:149
static void filterDirectories(const ThumbList &input, ThumbList &fileList, ThumbList &dirList)
Separate the input into files and directories.
Definition: imageview.cpp:413
void abort()
Request that all processing stop.
Definition: imageview.cpp:372
LoadAlbumRunnable(ImageView *parent, const ThumbList &roots, int sortorder, int slideshow_sequencing)
Definition: imageview.cpp:361
ThumbItem * advanceItem()
Definition: imageview.cpp:298
void setFunctionLEDs(enum LCDFunctionSet func, bool on)
Definition: lcddevice.cpp:427
bool m_slideshow_running
Definition: imageview.h:105
void GetScreenSettings(float &wmult, float &hmult)
void InitTimestamp()
Definition: thumbview.cpp:52