MythTV  master
mythuiimage.cpp
Go to the documentation of this file.
1 
2 #include "mythuiimage.h"
3 
4 // C++
5 #include <cstdint>
6 #include <cstdlib>
7 #include <ctime>
8 
9 // QT
10 #include <QFile>
11 #include <QDir>
12 #include <QDomDocument>
13 #include <QImageReader>
14 #include <QReadWriteLock>
15 #include <QRunnable>
16 #include <QEvent>
17 #include <QCoreApplication>
18 
19 // libmythbase
20 #include "mythlogging.h"
21 #include "mthreadpool.h"
22 
23 // Mythui
24 #include "mythpainter.h"
25 #include "mythmainwindow.h"
26 #include "mythuihelper.h"
27 #include "mythscreentype.h"
28 
29 #ifdef _MSC_VER
30 # include "compat.h" // random
31 #endif
32 
33 class ImageLoadThread;
34 
35 #define LOC QString("MythUIImage(0x%1): ").arg((uint64_t)this,0,16)
36 
38 
40 {
41  Copy(other);
42 }
43 
45 {
46  Copy(other);
47 
48  return *this;
49 }
50 
52 {
53  if (m_maskImage)
55 }
56 
58 {
59  m_filename = other.m_filename;
60 
61  m_cropRect = other.m_cropRect;
62  m_forceSize = other.m_forceSize;
63 
67  m_isOriented = other.m_isOriented;
68 
75 
77 
79  m_isMasked = other.m_isMasked;
81 }
82 
84 {
85  if (image)
86  image->IncrRef();
87  if (m_maskImage)
89 
90  m_maskImage = image;
92 }
93 
98 {
99  public:
100  ImageLoader() = default;
101  ~ImageLoader() = default;
102 
103  static QHash<QString, const MythUIImage *> m_loadingImages;
104  static QMutex m_loadingImagesLock;
105  static QWaitCondition m_loadingImagesCond;
106 
107  static bool PreLoad(const QString &cacheKey, const MythUIImage *uitype)
108  {
109  m_loadingImagesLock.lock();
110 
111  // Check to see if the image is being loaded by us in another thread
112  if ((m_loadingImages.contains(cacheKey)) &&
113  (m_loadingImages[cacheKey] == uitype))
114  {
115  LOG(VB_GUI | VB_FILE, LOG_DEBUG,
116  QString("ImageLoader::PreLoad(%1), this "
117  "file is already being loaded by this same MythUIImage "
118  "in another thread.").arg(cacheKey));
119  m_loadingImagesLock.unlock();
120  return false;
121  }
122 
123  // Check to see if the exact same image is being loaded anywhere else
124  while (m_loadingImages.contains(cacheKey))
126 
127  m_loadingImages[cacheKey] = uitype;
128  m_loadingImagesLock.unlock();
129 
130  return true;
131  }
132 
133  static void PostLoad(const QString &cacheKey)
134  {
135  m_loadingImagesLock.lock();
136  m_loadingImages.remove(cacheKey);
137  m_loadingImagesCond.wakeAll();
138  m_loadingImagesLock.unlock();
139  }
140 
141  static bool SupportsAnimation(const QString &filename)
142  {
143  QString extension = filename.section('.', -1);
144  return !filename.startsWith("myth://") &&
145  (extension == "gif" ||
146  extension == "apng" ||
147  extension == "mng");
148  }
149 
154  static QString GenImageLabel(const ImageProperties &imProps)
155  {
156  QString imagelabel;
157  QString s_Attrib;
158 
159  if (imProps.m_isMasked)
160  s_Attrib = "masked";
161 
162  if (imProps.m_isReflected)
163  s_Attrib += "reflected";
164 
165  if (imProps.m_isGreyscale)
166  s_Attrib += "greyscale";
167 
168  if (imProps.m_isOriented)
169  {
170  s_Attrib += "orientation";
171  s_Attrib += QString("%1").arg(imProps.m_orientation);
172  }
173 
174  int w = -1;
175  int h = -1;
176  if (!imProps.m_forceSize.isNull())
177  {
178  if (imProps.m_forceSize.width() != -1)
179  w = imProps.m_forceSize.width();
180 
181  if (imProps.m_forceSize.height() != -1)
182  h = imProps.m_forceSize.height();
183  }
184 
185 
186  imagelabel = QString("%1-%2-%3x%4.png")
187  .arg(imProps.m_filename)
188  .arg(s_Attrib)
189  .arg(w)
190  .arg(h);
191  imagelabel.replace('/', '-');
192 #ifdef Q_OS_ANDROID
193  imagelabel.replace(':', '-');
194 #endif
195 
196  return imagelabel;
197  }
198 
199  static MythImage *LoadImage(MythPainter *painter,
200  // Must be a copy for thread safety
201  ImageProperties imProps,
202  ImageCacheMode cacheMode,
203  // Included only to check address, could be
204  // replaced by generating a unique value for
205  // each MythUIImage object?
206  const MythUIImage *parent,
207  bool &aborted,
208  MythImageReader *imageReader = nullptr)
209  {
210  QString cacheKey = GenImageLabel(imProps);
211  if (!PreLoad(cacheKey, parent))
212  {
213  aborted = true;
214  return nullptr;
215  }
216 
217  QString filename = imProps.m_filename;
218  MythImage *image = nullptr;
219 
220  bool bResize = false;
221  bool bFoundInCache = false;
222 
223  int w = -1;
224  int h = -1;
225 
226  if (!imProps.m_forceSize.isNull())
227  {
228  if (imProps.m_forceSize.width() != -1)
229  w = imProps.m_forceSize.width();
230 
231  if (imProps.m_forceSize.height() != -1)
232  h = imProps.m_forceSize.height();
233 
234  bResize = true;
235  }
236 
237  if (!imageReader)
238  {
239  image = GetMythUI()->LoadCacheImage(filename, cacheKey,
240  painter, cacheMode);
241  }
242 
243  if (image)
244  {
245  if (VERBOSE_LEVEL_CHECK(VB_GUI | VB_FILE, LOG_INFO))
246  {
247  image->IncrRef();
248  int cnt = image->DecrRef();
249  LOG(VB_GUI | VB_FILE, LOG_INFO,
250  QString("ImageLoader::LoadImage(%1) Found in cache, "
251  "RefCount = %2")
252  .arg(cacheKey).arg(cnt));
253  }
254 
255  if (imProps.m_isReflected)
256  image->setIsReflected(true);
257 
258  if (imProps.m_isOriented)
259  image->setIsOriented(true);
260 
261  bFoundInCache = true;
262  }
263  else
264  {
265  LOG(VB_GUI | VB_FILE, LOG_INFO,
266  QString("ImageLoader::LoadImage(%1) NOT Found in cache. "
267  "Loading Directly").arg(cacheKey));
268 
269  image = painter->GetFormatImage();
270  bool ok = false;
271 
272  if (imageReader)
273  ok = image->Load(imageReader);
274  else
275  ok = image->Load(filename);
276 
277  if (!ok)
278  {
279  image->DecrRef();
280  image = nullptr;
281  }
282  }
283 
284  if (image && image->isNull())
285  {
286  LOG(VB_GUI | VB_FILE, LOG_INFO,
287  QString("ImageLoader::LoadImage(%1) Image is NULL")
288  .arg(filename));
289 
290  image->DecrRef();
291  image = nullptr;
292  }
293 
294  if (image && !bFoundInCache)
295  {
296  if (imProps.m_isReflected)
297  image->Reflect(imProps.m_reflectAxis, imProps.m_reflectShear,
298  imProps.m_reflectScale, imProps.m_reflectLength,
299  imProps.m_reflectSpacing);
300 
301  if (imProps.m_isGreyscale)
302  image->ToGreyscale();
303 
304  if (imProps.m_isOriented)
305  image->Orientation(imProps.m_orientation);
306 
307  // Even if an explicit size wasn't defined this image may still need
308  // to be scaled because of a difference between the theme resolution
309  // and the screen resolution. We want to avoid scaling twice.
310  if (!bResize && imProps.m_isThemeImage)
311  {
312  float wmult; // Width multipler
313  float hmult; // Height multipler
314  GetMythUI()->GetScreenSettings(wmult, hmult);
315  if (wmult != 1.0F || hmult != 1.0F)
316  {
317  w = image->size().width() * wmult;
318  h = image->size().height() * hmult;
319  bResize = true;
320  }
321  }
322 
323  if (bResize)
324  image->Resize(QSize(w, h), imProps.m_preserveAspect);
325 
326  if (imProps.m_isMasked)
327  {
328  MythImage *newMaskImage = painter->GetFormatImage();
329  if (newMaskImage->Load(imProps.GetMaskImageFilename()))
330  {
331  float wmult; // Width multipler
332  float hmult; // Height multipler
333  GetMythUI()->GetScreenSettings(wmult, hmult);
334  if (wmult != 1.0F || hmult != 1.0F)
335  {
336  int width = newMaskImage->size().width() * wmult;
337  int height = newMaskImage->size().height() * hmult;
338  newMaskImage->Resize(QSize(width, height));
339  }
340 
341  imProps.SetMaskImage(newMaskImage);
342  }
343  else
344  imProps.SetMaskImage(nullptr);
345  newMaskImage->DecrRef();
346 
347  QRect imageArea = image->rect();
348  QRect maskArea = imProps.GetMaskImageRect();
349 
350  // Crop the mask to the image
351  int x = 0;
352  int y = 0;
353 
354  if (maskArea.width() > imageArea.width())
355  x = (maskArea.width() - imageArea.width()) / 2;
356 
357  if (maskArea.height() > imageArea.height())
358  y = (maskArea.height() - imageArea.height()) / 2;
359 
360  if (x > 0 || y > 0)
361  imageArea.translate(x, y);
362 
363  QImage mask = imProps.GetMaskImageSubset(imageArea);
364  image->setAlphaChannel(mask.alphaChannel());
365  }
366 
367  if (!imageReader)
368  GetMythUI()->CacheImage(cacheKey, image);
369  }
370 
371  if (image)
372  image->SetChanged();
373 
374  PostLoad(cacheKey);
375 
376  return image;
377  }
378 
380  // Must be a copy for thread safety
381  const ImageProperties& imProps,
382  ImageCacheMode cacheMode,
383  // Included only to check address, could be
384  // replaced by generating a unique value for
385  // each MythUIImage object?
386  const MythUIImage *parent,
387  bool &aborted)
388  {
389  QString filename = QString("frame-%1-") + imProps.m_filename;
390  QString frameFilename;
391  int imageCount = 1;
392 
393  MythImageReader *imageReader = new MythImageReader(imProps.m_filename);
394 
395  AnimationFrames *images = new AnimationFrames();
396 
397  while (imageReader->canRead() && !aborted)
398  {
399  frameFilename = filename.arg(imageCount);
400 
401  ImageProperties frameProps = imProps;
402  frameProps.m_filename = frameFilename;
403 
404  MythImage *im = LoadImage(painter, frameProps, cacheMode, parent,
405  aborted, imageReader);
406 
407  if (!im)
408  aborted = true;
409 
410  images->append(AnimationFrame(im, imageReader->nextImageDelay()));
411  imageCount++;
412  }
413 
414  delete imageReader;
415 
416  return images;
417  }
418 
419 };
420 
421 QHash<QString, const MythUIImage *> ImageLoader::m_loadingImages;
423 QWaitCondition ImageLoader::m_loadingImagesCond;
424 
428 class ImageLoadEvent : public QEvent
429 {
430  public:
431  ImageLoadEvent(const MythUIImage *parent, MythImage *image,
432  const QString &basefile, const QString &filename,
433  int number, bool aborted)
434  : QEvent(kEventType),
435  m_parent(parent), m_image(image), m_basefile(basefile),
436  m_filename(filename), m_number(number),
437  m_aborted(aborted) { }
438 
440  const QString &basefile,
441  const QString &filename, bool aborted)
442  : QEvent(kEventType),
443  m_parent(parent), m_basefile(basefile),
444  m_filename(filename),
445  m_images(frames), m_aborted(aborted) { }
446 
447  const MythUIImage *GetParent() const { return m_parent; }
448  MythImage *GetImage() const { return m_image; }
449  QString GetBasefile() const { return m_basefile; }
450  QString GetFilename() const { return m_filename; }
451  int GetNumber() const { return m_number; }
453  bool GetAbortState() const { return m_aborted; }
454 
455  static Type kEventType;
456 
457  private:
458  const MythUIImage *m_parent {nullptr};
459  MythImage *m_image {nullptr};
460  QString m_basefile;
461  QString m_filename;
462  int m_number {0};
463 
464  // Animated Images
466 
467  // Image Load
468  bool m_aborted;
469 };
470 
471 QEvent::Type ImageLoadEvent::kEventType =
472  (QEvent::Type) QEvent::registerEventType();
473 
477 class ImageLoadThread : public QRunnable
478 {
479  public:
480  ImageLoadThread(const MythUIImage *parent, MythPainter *painter,
481  const ImageProperties &imProps, const QString &basefile,
482  int number, ImageCacheMode mode) :
483  m_parent(parent), m_painter(painter), m_imageProperties(imProps),
484  m_basefile(basefile), m_number(number), m_cacheMode(mode)
485  {
486  }
487 
488  void run() override // QRunnable
489  {
490  bool aborted = false;
491  QString filename = m_imageProperties.m_filename;
492 
493  // NOTE Do NOT use MythImageReader::supportsAnimation here, it defeats
494  // the point of caching remote images
495  if (ImageLoader::SupportsAnimation(filename))
496  {
498 
502  aborted);
503 
504  if (frames && frames->count() > 1)
505  {
507  m_basefile,
509  aborted);
510  QCoreApplication::postEvent(const_cast<MythUIImage*>(m_parent), le);
511 
512  return;
513  }
514  delete frames;
515  }
516 
520  aborted);
521 
524  m_number, aborted);
525  QCoreApplication::postEvent(const_cast<MythUIImage*>(m_parent), le);
526  }
527 
528 private:
529  const MythUIImage *m_parent {nullptr};
530  MythPainter *m_painter {nullptr};
532  QString m_basefile;
533  int m_number;
535 };
536 
539 {
540 public:
542  : m_parent(p) { }
543  ~MythUIImagePrivate() = default;
544 
545  MythUIImage *m_parent {nullptr};
546 
547  QReadWriteLock m_UpdateLock {QReadWriteLock::Recursive};
548 };
549 
551 
552 MythUIImage::MythUIImage(const QString &filepattern,
553  int low, int high, int delayms,
554  MythUIType *parent, const QString &name)
555  : MythUIType(parent, name)
556 {
557  m_imageProperties.m_filename = filepattern;
558  m_LowNum = low;
559  m_HighNum = high;
560 
561  m_Delay = delayms;
562  m_EnableInitiator = true;
563 
564  d = new MythUIImagePrivate(this);
565  emit DependChanged(false);
566 }
567 
568 MythUIImage::MythUIImage(const QString &filename, MythUIType *parent,
569  const QString &name)
570  : MythUIType(parent, name)
571 {
572  m_imageProperties.m_filename = filename;
573  m_OrigFilename = filename;
574 
575  m_LowNum = 0;
576  m_HighNum = 0;
577  m_Delay = -1;
578  m_EnableInitiator = true;
579 
580  d = new MythUIImagePrivate(this);
581  emit DependChanged(false);
582 }
583 
584 MythUIImage::MythUIImage(MythUIType *parent, const QString &name)
585  : MythUIType(parent, name)
586 {
587  m_LowNum = 0;
588  m_HighNum = 0;
589  m_Delay = -1;
590  m_EnableInitiator = true;
591 
592  d = new MythUIImagePrivate(this);
593 }
594 
596 {
597  // Wait until all image loading threads are complete or bad things
598  // may happen if this MythUIImage disappears when a queued thread
599  // needs it.
600  if (m_runningThreads > 0)
601  {
603  }
604 
605  Clear();
606 
607  delete d;
608 }
609 
614 {
615  QWriteLocker updateLocker(&d->m_UpdateLock);
616  QMutexLocker locker(&m_ImagesLock);
617 
618  while (!m_Images.isEmpty())
619  {
620  QHash<int, MythImage *>::iterator it = m_Images.begin();
621 
622  if (*it)
623  (*it)->DecrRef();
624 
625  m_Images.remove(it.key());
626  }
627 
628  m_Delays.clear();
629 
630  if (m_animatedImage)
631  {
632  m_LowNum = 0;
633  m_HighNum = 0;
634  m_animatedImage = false;
635  }
636 }
637 
642 {
643  d->m_UpdateLock.lockForWrite();
644 
645  SetMinArea(MythRect());
646 
648  {
651 
652  if (m_animatedImage)
653  {
654  m_LowNum = 0;
655  m_HighNum = 0;
656  m_animatedImage = false;
657  }
658  emit DependChanged(true);
659 
660  d->m_UpdateLock.unlock();
661  Load();
662  }
663  else
664  d->m_UpdateLock.unlock();
665 
667 }
668 
672 void MythUIImage::SetFilename(const QString &filename)
673 {
674  QWriteLocker updateLocker(&d->m_UpdateLock);
676  m_imageProperties.m_filename = filename;
677  if (filename == m_OrigFilename)
678  emit DependChanged(true);
679  else
680  emit DependChanged(false);
681 }
682 
687 void MythUIImage::SetFilepattern(const QString &filepattern, int low,
688  int high)
689 {
690  QWriteLocker updateLocker(&d->m_UpdateLock);
692  m_imageProperties.m_filename = filepattern;
693  m_LowNum = low;
694  m_HighNum = high;
695  if (filepattern == m_OrigFilename)
696  emit DependChanged(true);
697  else
698  emit DependChanged(false);
699 }
700 
704 void MythUIImage::SetImageCount(int low, int high)
705 {
706  QWriteLocker updateLocker(&d->m_UpdateLock);
707  m_LowNum = low;
708  m_HighNum = high;
709 }
710 
714 void MythUIImage::SetDelay(int delayms)
715 {
716  QWriteLocker updateLocker(&d->m_UpdateLock);
717  m_Delay = delayms;
718  m_LastDisplay = QTime::currentTime();
719  m_CurPos = 0;
720 }
721 
725 void MythUIImage::SetDelays(QVector<int> delays)
726 {
727  QWriteLocker updateLocker(&d->m_UpdateLock);
728  QMutexLocker imageLocker(&m_ImagesLock);
729  QVector<int>::iterator it;
730 
731  for (it = delays.begin(); it != delays.end(); ++it)
732  m_Delays[m_Delays.size()] = *it;
733 
734  if (m_Delay == -1)
735  m_Delay = m_Delays[0];
736 
737  m_LastDisplay = QTime::currentTime();
738  m_CurPos = 0;
739 }
740 
746 {
747  d->m_UpdateLock.lockForWrite();
748 
749  if (!img)
750  {
751  d->m_UpdateLock.unlock();
752  Reset();
753  return;
754  }
755 
758 
759  img->IncrRef();
760 
761  QSize forceSize = m_imageProperties.m_forceSize;
762  if (!forceSize.isNull())
763  {
764  int w = (forceSize.width() <= 0) ? img->width() : forceSize.width();
765  int h = (forceSize.height() <= 0) ? img->height() : forceSize.height();
766  img->Resize(QSize(w, h), m_imageProperties.m_preserveAspect);
767  }
768 
775 
776  if (m_imageProperties.m_isGreyscale && !img->isGrayscale())
777  img->ToGreyscale();
778 
779  Clear();
780  m_Delay = -1;
781 
784 
785  if (m_imageProperties.m_forceSize.isNull())
786  SetSize(img->size());
787 
788  m_ImagesLock.lock();
789  m_Images[0] = img;
790  m_Delays.clear();
791  m_ImagesLock.unlock();
792 
793  m_CurPos = 0;
795  SetRedraw();
796 
797  d->m_UpdateLock.unlock();
798 }
799 
805 void MythUIImage::SetImages(QVector<MythImage *> *images)
806 {
807  Clear();
808 
809  QWriteLocker updateLocker(&d->m_UpdateLock);
810  QSize aSize = GetFullArea().size();
811 
813 
814  QVector<MythImage *>::iterator it;
815 
816  for (it = images->begin(); it != images->end(); ++it)
817  {
818  MythImage *im = (*it);
819 
820  if (!im)
821  {
822  QMutexLocker locker(&m_ImagesLock);
823  m_Images[m_Images.size()] = im;
824  continue;
825  }
826 
827  im->IncrRef();
828 
829 
830  QSize forceSize = m_imageProperties.m_forceSize;
831  if (!forceSize.isNull())
832  {
833  int w = (forceSize.width() <= 0) ? im->width() : forceSize.width();
834  int h = (forceSize.height() <= 0) ? im->height() : forceSize.height();
835  im->Resize(QSize(w, h), m_imageProperties.m_preserveAspect);
836  }
837 
844 
845  if (m_imageProperties.m_isGreyscale && !im->isGrayscale())
846  im->ToGreyscale();
847 
850 
851  m_ImagesLock.lock();
852  m_Images[m_Images.size()] = im;
853  m_ImagesLock.unlock();
854 
855  aSize = aSize.expandedTo(im->size());
856  }
857 
858  SetImageCount(1, m_Images.size());
859 
860  if (m_imageProperties.m_forceSize.isNull())
861  SetSize(aSize);
862 
863  MythRect rect(GetFullArea());
864  rect.setSize(aSize);
865  SetMinArea(rect);
866 
867  m_CurPos = 0;
868  m_animatedImage = true;
870  SetRedraw();
871 }
872 
874 {
875  QVector<int> delays;
876  QVector<MythImage *> images;
877 
878  AnimationFrames::iterator it;
879 
880  for (it = frames.begin(); it != frames.end(); ++it)
881  {
882  images.append((*it).first);
883  delays.append((*it).second);
884  }
885 
886  if (!images.empty())
887  {
888  SetImages(&images);
889 
890  if (m_Delay < 0 && !delays.empty())
891  SetDelays(delays);
892  }
893  else
894  Reset();
895 }
896 
900 void MythUIImage::ForceSize(const QSize &size)
901 {
902  if (m_imageProperties.m_forceSize == size)
903  return;
904 
905  d->m_UpdateLock.lockForWrite();
907  d->m_UpdateLock.unlock();
908 
909  if (size.isEmpty())
910  return;
911 
913 
914  Load();
915 }
916 
920 void MythUIImage::SetOrientation(int orientation)
921 {
923  m_imageProperties.m_orientation = orientation;
924 }
925 
929 void MythUIImage::SetSize(int width, int height)
930 {
931  SetSize(QSize(width, height));
932 }
933 
937 void MythUIImage::SetSize(const QSize &size)
938 {
939  QWriteLocker updateLocker(&d->m_UpdateLock);
940  MythUIType::SetSize(size);
941  m_NeedLoad = true;
942 }
943 
948 void MythUIImage::SetCropRect(int x, int y, int width, int height)
949 {
950  SetCropRect(MythRect(x, y, width, height));
951 }
952 
958 {
959  QWriteLocker updateLocker(&d->m_UpdateLock);
961  SetRedraw();
962 }
963 
967 bool MythUIImage::Load(bool allowLoadInBackground, bool forceStat)
968 {
969  d->m_UpdateLock.lockForRead();
970 
972 
973  QString bFilename = m_imageProperties.m_filename;
974 
975  d->m_UpdateLock.unlock();
976 
977  QString filename = bFilename;
978 
979  if (bFilename.isEmpty())
980  {
981  Clear();
982  SetMinArea(MythRect());
983  SetRedraw();
984 
985  return false;
986  }
987 
988  if (getenv("DISABLETHREADEDMYTHUIIMAGE"))
989  allowLoadInBackground = false;
990 
991  // Don't clear the widget before we need to, otherwise it causes
992  // unsightly flashing. We exclude animations for now since that requires a
993  // deeper fix
994  bool isAnimation = (m_HighNum != m_LowNum) || m_animatedImage;
995 
996  if (isAnimation)
997  Clear();
998 
999  bool complete = true;
1000 
1001  QString imagelabel;
1002 
1003  int j = 0;
1004 
1005  for (int i = m_LowNum; i <= m_HighNum && !m_animatedImage; i++)
1006  {
1007  if (!m_animatedImage && m_HighNum != m_LowNum &&
1008  bFilename.contains("%1"))
1009  filename = bFilename.arg(i);
1010 
1012  imProps.m_filename = filename;
1013  imagelabel = ImageLoader::GenImageLabel(imProps);
1014 
1015  // Only load in the background if allowed and the image is
1016  // not already in our mem cache
1017  int cacheMode = kCacheIgnoreDisk;
1018 
1019  if (forceStat)
1020  cacheMode |= (int)kCacheForceStat;
1021 
1022  int cacheMode2 = kCacheNormal;
1023 
1024  if (forceStat)
1025  cacheMode2 |= (int)kCacheForceStat;
1026 
1027  bool do_background_load = false;
1028  if (allowLoadInBackground)
1029  {
1031  filename, imagelabel, GetPainter(),
1032  static_cast<ImageCacheMode>(cacheMode));
1033  if (img)
1034  img->DecrRef();
1035  else
1036  do_background_load = true;
1037  }
1038 
1039  if (do_background_load)
1040  {
1041  SetMinArea(MythRect());
1042  LOG(VB_GUI | VB_FILE, LOG_DEBUG, LOC +
1043  QString("Load(), spawning thread to load '%1'").arg(filename));
1044 
1045  m_runningThreads++;
1046  ImageLoadThread *bImgThread;
1047  bImgThread = new ImageLoadThread(this, GetPainter(),
1048  imProps,
1049  bFilename, i,
1050  static_cast<ImageCacheMode>(cacheMode2));
1051  GetMythUI()->GetImageThreadPool()->start(bImgThread, "ImageLoad");
1052  }
1053  else
1054  {
1055  if (!isAnimation && !GetMythUI()->IsImageInCache(imagelabel))
1056  Clear();
1057 
1058  // Perform a blocking load
1059  LOG(VB_GUI | VB_FILE, LOG_DEBUG, LOC +
1060  QString("Load(), loading '%1' in foreground").arg(filename));
1061  bool aborted = false;
1062 
1063  if (ImageLoader::SupportsAnimation(filename))
1064  {
1065  AnimationFrames *myFrames;
1066 
1067  myFrames = ImageLoader::LoadAnimatedImage(GetPainter(), imProps,
1068  static_cast<ImageCacheMode>(cacheMode2),
1069  this, aborted);
1070 
1071  // TODO We might want to handle an abort here more gracefully
1072  if (aborted)
1073  LOG(VB_GUI, LOG_DEBUG, QString("Aborted loading animated"
1074  "image %1 in foreground")
1075  .arg(filename));
1076 
1077  SetAnimationFrames(*myFrames);
1078 
1079  delete myFrames;
1080  }
1081  else
1082  {
1083  MythImage *image = nullptr;
1084 
1086  imProps,
1087  static_cast<ImageCacheMode>(cacheMode2),
1088  this, aborted);
1089 
1090  // TODO We might want to handle an abort here more gracefully
1091  if (aborted)
1092  LOG(VB_GUI, LOG_DEBUG, QString("Aborted loading animated"
1093  "image %1 in foreground")
1094  .arg(filename));
1095 
1096  if (image)
1097  {
1098  if (m_imageProperties.m_forceSize.isNull())
1099  SetSize(image->size());
1100 
1101  MythRect rect(GetFullArea());
1102  rect.setSize(image->size());
1103  SetMinArea(rect);
1104 
1105  m_ImagesLock.lock();
1106  m_Images[j] = image;
1107  m_ImagesLock.unlock();
1108 
1109  SetRedraw();
1110  d->m_UpdateLock.lockForWrite();
1111  m_LastDisplay = QTime::currentTime();
1112  d->m_UpdateLock.unlock();
1113  }
1114  else
1115  {
1116  Reset();
1117 
1118  m_ImagesLock.lock();
1119  m_Images[j] = nullptr;
1120  m_ImagesLock.unlock();
1121  }
1122  }
1123  }
1124 
1125  ++j;
1126 
1127  // Load is complete if no image is loading in background
1128  complete &= !do_background_load;
1129  }
1130 
1131  if (complete)
1132  emit LoadComplete();
1133 
1134  return true;
1135 }
1136 
1141 {
1142  d->m_UpdateLock.lockForWrite();
1143 
1144  int delay = -1;
1145 
1146  if (m_Delays.contains(m_CurPos))
1147  delay = m_Delays[m_CurPos];
1148  else if (m_Delay > 0)
1149  delay = m_Delay;
1150 
1151  if (delay > 0 &&
1152  abs(m_LastDisplay.msecsTo(QTime::currentTime())) > delay)
1153  {
1155  {
1156  FindRandomImage();
1157  d->m_UpdateLock.unlock();
1158  Load();
1159  d->m_UpdateLock.lockForWrite();
1160  }
1161  else
1162  {
1163  m_ImagesLock.lock();
1164 
1166  {
1167  ++m_CurPos;
1168 
1169  if (m_CurPos >= (uint)m_Images.size())
1170  m_CurPos = 0;
1171  }
1172  else if (m_animationCycle == kCycleReverse)
1173  {
1174  if ((m_CurPos + 1) >= (uint)m_Images.size())
1175  {
1176  m_animationReverse = true;
1177  }
1178  else if (m_CurPos == 0)
1179  {
1180  m_animationReverse = false;
1181  }
1182 
1183  if (m_animationReverse)
1184  --m_CurPos;
1185  else
1186  ++m_CurPos;
1187  }
1188 
1189  m_ImagesLock.unlock();
1190 
1191  SetRedraw();
1192  }
1193 
1194  m_LastDisplay = QTime::currentTime();
1195  }
1196 
1198 
1199  d->m_UpdateLock.unlock();
1200 }
1201 
1205 void MythUIImage::DrawSelf(MythPainter *p, int xoffset, int yoffset,
1206  int alphaMod, QRect clipRect)
1207 {
1208  m_ImagesLock.lock();
1209 
1210  if (!m_Images.empty())
1211  {
1212  d->m_UpdateLock.lockForWrite();
1213 
1214  if (m_CurPos >= (uint)m_Images.size())
1215  m_CurPos = 0;
1216 
1217  if (!m_Images[m_CurPos])
1218  {
1219  unsigned int origPos = m_CurPos;
1220  m_CurPos++;
1221 
1222  while (!m_Images[m_CurPos] && m_CurPos != origPos)
1223  {
1224  m_CurPos++;
1225 
1226  if (m_CurPos >= (uint)m_Images.size())
1227  m_CurPos = 0;
1228  }
1229  }
1230 
1231  QRect area = GetArea().toQRect();
1232  area.translate(xoffset, yoffset);
1233 
1234  int alpha = CalcAlpha(alphaMod);
1235 
1236  MythImage *currentImage = m_Images[m_CurPos];
1237 
1238  if (currentImage)
1239  currentImage->IncrRef();
1240 
1241  m_ImagesLock.unlock();
1242  d->m_UpdateLock.unlock();
1243 
1244  if (!currentImage)
1245  return;
1246 
1247  d->m_UpdateLock.lockForRead();
1248 
1249  QRect currentImageArea = currentImage->rect();
1250 
1251  if (!m_imageProperties.m_forceSize.isNull())
1252  area.setSize(area.size().expandedTo(currentImage->size()));
1253 
1254  // Centre image in available space, accounting for zoom
1255  int x = 0, y = 0;
1256  QRect visibleImage = m_Effects.GetExtent(currentImageArea.size());
1257 
1258  if (area.width() > visibleImage.width())
1259  x = area.width() / 2 + visibleImage.topLeft().x();
1260 
1261  if (area.height() > visibleImage.height())
1262  y = area.height() / 2 + visibleImage.topLeft().y();
1263 
1264  if ((x > 0 || y > 0))
1265  area.translate(x, y);
1266 
1267  QRect srcRect;
1269 
1270  if (!m_imageProperties.m_cropRect.isEmpty())
1271  srcRect = m_imageProperties.m_cropRect.toQRect();
1272  else
1273  srcRect = currentImageArea;
1274 
1275  p->SetClipRect(clipRect);
1276  p->DrawImage(area, currentImage, srcRect, alpha);
1277  currentImage->DecrRef();
1278  d->m_UpdateLock.unlock();
1279  }
1280  else
1281  m_ImagesLock.unlock();
1282 }
1283 
1288  const QString &filename, QDomElement &element, bool showWarnings)
1289 {
1290  QWriteLocker updateLocker(&d->m_UpdateLock);
1291 
1292  if (element.tagName() == "filename")
1293  {
1294  m_imageProperties.m_isThemeImage = true; // This is an image distributed with the theme
1296 
1297  if (m_imageProperties.m_filename.endsWith('/'))
1298  {
1299  m_showingRandomImage = true;
1301 
1302  FindRandomImage();
1303  }
1304  }
1305  else if (element.tagName() == "filepattern")
1306  {
1307  m_imageProperties.m_isThemeImage = true; // This is an image distributed with the theme
1309  QString tmp = element.attribute("low");
1310 
1311  if (!tmp.isEmpty())
1312  m_LowNum = tmp.toInt();
1313 
1314  tmp = element.attribute("high");
1315 
1316  if (!tmp.isEmpty())
1317  m_HighNum = tmp.toInt();
1318 
1319  tmp = element.attribute("cycle", "start");
1320 
1321  if (tmp == "reverse")
1323  }
1324  else if (element.tagName() == "area")
1325  {
1326  SetArea(parseRect(element));
1328  }
1329  else if (element.tagName() == "preserveaspect")
1331  else if (element.tagName() == "crop")
1333  else if (element.tagName() == "delay")
1334  {
1335  QString value = getFirstText(element);
1336 
1337  if (value.contains(","))
1338  {
1339  QVector<int> delays;
1340  QStringList tokens = value.split(",");
1341  QStringList::iterator it = tokens.begin();
1342 
1343  for (; it != tokens.end(); ++it)
1344  {
1345  if ((*it).isEmpty())
1346  {
1347  if (!delays.empty())
1348  delays.append(delays[delays.size()-1]);
1349  else
1350  delays.append(0); // Default 0ms delay before first image
1351  }
1352  else
1353  {
1354  delays.append((*it).toInt());
1355  }
1356  }
1357 
1358  if (!delays.empty())
1359  {
1360  m_Delay = delays[0];
1361  SetDelays(delays);
1362  }
1363  }
1364  else
1365  {
1366  m_Delay = value.toInt();
1367  }
1368  }
1369  else if (element.tagName() == "reflection")
1370  {
1372  QString tmp = element.attribute("axis");
1373 
1374  if (!tmp.isEmpty())
1375  {
1376  if (tmp.toLower() == "horizontal")
1378  else
1380  }
1381 
1382  tmp = element.attribute("shear");
1383 
1384  if (!tmp.isEmpty())
1386 
1387  tmp = element.attribute("scale");
1388 
1389  if (!tmp.isEmpty())
1391 
1392  tmp = element.attribute("length");
1393 
1394  if (!tmp.isEmpty())
1396 
1397  tmp = element.attribute("spacing");
1398 
1399  if (!tmp.isEmpty())
1401  }
1402  else if (element.tagName() == "mask")
1403  {
1406  }
1407  else if (element.tagName() == "grayscale" ||
1408  element.tagName() == "greyscale")
1409  {
1411  }
1412  else
1413  {
1414  return MythUIType::ParseElement(filename, element, showWarnings);
1415  }
1416 
1417  m_NeedLoad = true;
1418 
1419  if (m_Parent && m_Parent->IsDeferredLoading(true))
1420  m_NeedLoad = false;
1421 
1422  return true;
1423 }
1424 
1429 {
1430  d->m_UpdateLock.lockForWrite();
1431  MythUIImage *im = dynamic_cast<MythUIImage *>(base);
1432 
1433  if (!im)
1434  {
1435  LOG(VB_GENERAL, LOG_ERR,
1436  QString("'%1' (%2) ERROR, bad parsing '%3' (%4)")
1437  .arg(objectName()).arg(GetXMLLocation())
1438  .arg(base->objectName()).arg(base->GetXMLLocation()));
1439  d->m_UpdateLock.unlock();
1440  return;
1441  }
1442 
1444 
1445  m_Delay = im->m_Delay;
1446  m_LowNum = im->m_LowNum;
1447  m_HighNum = im->m_HighNum;
1448 
1449  m_LastDisplay = QTime::currentTime();
1450  m_CurPos = 0;
1451 
1453 
1456 
1459 
1460  MythUIType::CopyFrom(base);
1461 
1462  // We need to update forceSize in case the parent area has changed
1463  // however we only want to set forceSize if it was previously in use
1464  if (!m_imageProperties.m_forceSize.isNull())
1466 
1467  m_NeedLoad = im->m_NeedLoad;
1468 
1469  d->m_UpdateLock.unlock();
1470 
1471  d->m_UpdateLock.lockForRead();
1472 
1473  if (m_NeedLoad)
1474  {
1475  d->m_UpdateLock.unlock();
1476  Load();
1477  }
1478  else
1479  d->m_UpdateLock.unlock();
1480 }
1481 
1486 {
1487  QReadLocker updateLocker(&d->m_UpdateLock);
1488  MythUIImage *im = new MythUIImage(parent, objectName());
1489  im->CopyFrom(this);
1490 }
1491 
1496 {
1497  d->m_UpdateLock.lockForRead();
1498 
1499  if (m_NeedLoad)
1500  {
1501  d->m_UpdateLock.unlock();
1502  Load();
1503  }
1504  else
1505  d->m_UpdateLock.unlock();
1506 
1508 }
1509 
1514 {
1515  d->m_UpdateLock.lockForWrite();
1516 
1517  if (m_NeedLoad)
1518  {
1519  d->m_UpdateLock.unlock();
1520  return;
1521  }
1522 
1523  m_NeedLoad = true;
1524  d->m_UpdateLock.unlock();
1525 
1526  Load(false);
1527 
1529 }
1530 
1534 void MythUIImage::customEvent(QEvent *event)
1535 {
1536  if (event->type() == ImageLoadEvent::kEventType)
1537  {
1538  MythImage *image = nullptr;
1539  AnimationFrames *animationFrames = nullptr;
1540  int number = 0;
1541  QString filename;
1542  bool aborted;
1543 
1544  ImageLoadEvent *le = static_cast<ImageLoadEvent *>(event);
1545 
1546  if (le->GetParent() != this)
1547  return;
1548 
1549  image = le->GetImage();
1550  number = le->GetNumber();
1551  filename = le->GetFilename();
1552  animationFrames = le->GetAnimationFrames();
1553  aborted = le->GetAbortState();
1554 
1555  m_runningThreads--;
1556 
1557  d->m_UpdateLock.lockForRead();
1558  QString propFilename = m_imageProperties.m_filename;
1559  d->m_UpdateLock.unlock();
1560 
1561  // 1) We aborted loading the image for some reason (e.g. two requests
1562  // for same image)
1563  // 2) Filename changed since we started this image, so abort to avoid
1564  // rendering two different images in quick succession which causes
1565  // unsightly flickering
1566  if (aborted || (le->GetBasefile() != propFilename))
1567  {
1568  if (aborted)
1569  LOG(VB_GUI, LOG_DEBUG, QString("Aborted loading image %1")
1570  .arg(filename));
1571 
1572  if (image)
1573  image->DecrRef();
1574 
1575  if (animationFrames)
1576  {
1577  AnimationFrames::iterator it;
1578 
1579  for (it = animationFrames->begin(); it != animationFrames->end();
1580  ++it)
1581  {
1582  MythImage *im = (*it).first;
1583  if (im)
1584  im->DecrRef();
1585  }
1586 
1587  delete animationFrames;
1588  }
1589  }
1590  else if (animationFrames)
1591  {
1592  SetAnimationFrames(*animationFrames);
1593 
1594  delete animationFrames;
1595  }
1596  else if (image)
1597  {
1598  // We don't clear until we have the new image ready to display to
1599  // avoid unsightly flashing. This isn't currently supported for
1600  // animations.
1601  if ((m_HighNum == m_LowNum) && !m_animatedImage)
1602  Clear();
1603 
1604  d->m_UpdateLock.lockForWrite();
1605 
1606  if (m_imageProperties.m_forceSize.isNull())
1607  SetSize(image->size());
1608 
1609  MythRect rect(GetFullArea());
1610  rect.setSize(image->size());
1611  SetMinArea(rect);
1612 
1613  d->m_UpdateLock.unlock();
1614 
1615  m_ImagesLock.lock();
1616 
1617  if (m_Images[number])
1618  {
1619  // If we got to this point, it means this same MythUIImage
1620  // was told to reload the same image, so we use the newest
1621  // copy of the image.
1622  m_Images[number]->DecrRef(); // delete the original
1623  }
1624 
1625  m_Images[number] = image;
1626  m_ImagesLock.unlock();
1627 
1628  SetRedraw();
1629 
1630  d->m_UpdateLock.lockForWrite();
1631  m_LastDisplay = QTime::currentTime();
1632  d->m_UpdateLock.unlock();
1633  }
1634  else
1635  {
1636  // No Images were loaded, so trigger Reset to default
1637  Reset();
1638  }
1639 
1640  emit LoadComplete();
1641  }
1642 }
1643 
1645 {
1646  QDir imageDir(m_imageDirectory);
1647 
1648  if (!imageDir.exists())
1649  {
1650  QString themeDir = GetMythUI()->GetThemeDir() + '/';
1651  imageDir = themeDir + m_imageDirectory;
1652  }
1653 
1654  QStringList imageTypes;
1655 
1656  QList< QByteArray > exts = QImageReader::supportedImageFormats();
1657  QList< QByteArray >::Iterator it = exts.begin();
1658 
1659  for (; it != exts.end(); ++it)
1660  {
1661  imageTypes.append(QString("*.").append(*it));
1662  }
1663 
1664  imageDir.setNameFilters(imageTypes);
1665 
1666  QStringList imageList = imageDir.entryList();
1667  QString randFile;
1668 
1669  if (!imageList.empty())
1670  {
1671  // try to find a different image
1672  do
1673  {
1674  randFile = QString("%1%2").arg(m_imageDirectory)
1675  .arg(imageList.takeAt(random() % imageList.size()));
1676 
1677  } while (imageList.size() > 1 && randFile == m_OrigFilename);
1678  }
1679 
1681 }
void SetFilepattern(const QString &filepattern, int low, int high)
Must be followed by a call to Load() to load the image.
MythImage * CacheImage(const QString &url, MythImage *im, bool nodisk=false)
void SetAnimationFrames(AnimationFrames frames)
void Clear(void)
Remove all images from the widget.
MythImage * m_image
~ImageLoader()=default
void CalculateArea(const MythRect &parentArea)
Definition: mythrect.cpp:32
AnimationFrames * GetAnimationFrames() const
void SetImage(MythImage *img)
Should not be used unless absolutely necessary since it bypasses the image caching and threaded loade...
virtual MythPainter * GetPainter(void)
QString m_OrigFilename
Definition: mythuiimage.h:168
bool Load(MythImageReader *reader)
Definition: mythimage.cpp:271
ImageCacheMode
Definition: mythuihelper.h:24
ImageProperties m_imageProperties
const MythUIImage * m_parent
int GetNumber() const
static void PostLoad(const QString &cacheKey)
MythUIImage * m_parent
static QMutex m_loadingImagesLock
void Finalize(void) override
Perform any post-xml parsing initialisation tasks.
ImageProperties m_imageProperties
Definition: mythuiimage.h:183
virtual void SetChanged(bool change=true)
Definition: mythimage.h:45
void SetRedraw(void)
Definition: mythuitype.cpp:295
int DecrRef(void) override
Decrements reference count and deletes on 0.
Definition: mythimage.cpp:55
Image widget, displays a single image or multiple images in sequence.
Definition: mythuiimage.h:97
AnimationCycle m_animationCycle
Definition: mythuiimage.h:193
QString m_filename
Definition: mythuiimage.h:58
QTime m_LastDisplay
Definition: mythuiimage.h:179
void SetCropRect(int x, int y, int width, int height)
Crop the image using the given rectangle, useful for removing unsightly edges from imported images or...
ImageProperties & operator=(const ImageProperties &other)
Definition: mythuiimage.cpp:44
static QHash< QString, const MythUIImage * > m_loadingImages
ImageLoadEvent(const MythUIImage *parent, MythImage *image, const QString &basefile, const QString &filename, int number, bool aborted)
QString GetMaskImageFilename()
Definition: mythuiimage.h:37
int CalcAlpha(int alphamod)
Definition: mythuitype.cpp:460
ImageLoadEvent(const MythUIImage *parent, AnimationFrames *frames, const QString &basefile, const QString &filename, bool aborted)
unsigned int uint
Definition: compat.h:140
UIEffects m_Effects
Definition: mythuitype.h:257
void SetSize(int width, int height)
Set the size of the widget.
static QString GenImageLabel(const ImageProperties &imProps)
Generates a unique identifying string for this image which is used as a key in the image cache.
friend class MythUIImagePrivate
Definition: mythuiimage.h:197
void waitForDone(void)
QString GetThemeDir(void)
void SetMaskImageFilename(const QString &filename)
Definition: mythuiimage.h:33
void setIsOriented(bool oriented)
Definition: mythimage.h:89
static guint32 * tmp
Definition: goom_core.c:35
QString m_basefile
The base class on which all widgets and screens are based.
Definition: mythuitype.h:63
bool Load(bool allowLoadInBackground=true, bool forceStat=false)
Load the image(s), wraps ImageLoader::LoadImage()
ReflectAxis m_reflectAxis
Definition: mythuiimage.h:69
QString GetXMLLocation(void) const
Definition: mythuitype.h:155
QString GetBasefile() const
bool ParseElement(const QString &filename, QDomElement &element, bool showWarnings) override
Parse the xml definition of this widget setting the state of the object accordingly.
static bool PreLoad(const QString &cacheKey, const MythUIImage *uitype)
QString GetFilename() const
void CopyFrom(MythUIType *base) override
Copy this widgets state from another.
static QWaitCondition m_loadingImagesCond
bool IsReflected() const
Definition: mythimage.h:49
void SetOrientation(int orientation)
Saves the exif orientation value of the first image in the widget.
void Copy(const ImageProperties &other)
Definition: mythuiimage.cpp:57
QReadWriteLock m_UpdateLock
bool m_NeedLoad
Definition: mythuiimage.h:181
QRect toQRect(void) const
Definition: mythrect.cpp:354
QString m_maskImageFilename
Definition: mythuiimage.h:84
void Orientation(int orientation)
Changes the orientation angle of the image according to the exif rotation values.
Definition: mythimage.cpp:137
QString m_filename
bool m_animatedImage
Definition: mythuiimage.h:195
#define VERBOSE_LEVEL_CHECK(_MASK_, _LEVEL_)
Definition: mythlogging.h:24
bool m_showingRandomImage
Definition: mythuiimage.h:187
virtual void SetMinArea(const MythRect &rect)
Set the minimum area based on the given size.
Definition: mythuitype.cpp:798
void customEvent(QEvent *event) override
virtual MythRect GetArea(void) const
If the object has a minimum area defined, return it, other wise return the default area.
Definition: mythuitype.cpp:863
void DependChanged(bool isDefault)
void SetMaskImage(MythImage *image)
Definition: mythuiimage.cpp:83
static QString getFirstText(QDomElement &element)
void SetImageCount(int low, int high)
Set the integer range for an animated image pattern.
ImageLoadThread(const MythUIImage *parent, MythPainter *painter, const ImageProperties &imProps, const QString &basefile, int number, ImageCacheMode mode)
static Type kEventType
virtual void SetSize(const QSize &size)
Definition: mythuitype.cpp:550
int IncrRef(void) override
Increments reference count.
Definition: mythimage.cpp:47
QHash< int, int > m_Delays
Definition: mythuiimage.h:171
bool IsOriented() const
Definition: mythimage.h:50
virtual void Pulse(void)
Pulse is called 70 times a second to trigger a single frame of an animation.
Definition: mythuitype.cpp:442
bool m_Initiator
Definition: mythuitype.h:241
Wrapper around QRect allowing us to handle percentage and other relative values for areas in mythui.
Definition: mythrect.h:17
static bool SupportsAnimation(const QString &filename)
void Reset(void) override
Reset the image back to the default defined in the theme.
MythRect m_Area
Definition: mythuitype.h:249
AnimationFrames * m_images
ImageProperties()=default
ImageCacheMode m_cacheMode
static AnimationFrames * LoadAnimatedImage(MythPainter *painter, const ImageProperties &imProps, ImageCacheMode cacheMode, const MythUIImage *parent, bool &aborted)
const char * name
Definition: ParseText.cpp:328
void ToGreyscale()
Definition: mythimage.cpp:255
MythUIHelper * GetMythUI()
#define LOC
Definition: mythuiimage.cpp:35
void SetDelay(int delayms)
Set the delay between each image in an animation.
QRect GetExtent(const QSize &size)
MythUIImagePrivate * d
Definition: mythuiimage.h:190
virtual void LoadNow(void)
Cause images in this and child widgets to be loaded.
virtual void DrawImage(const QRect &dest, MythImage *im, const QRect &src, int alpha)=0
MythRect m_cropRect
Definition: mythuiimage.h:60
void FindRandomImage(void)
MythImage * LoadCacheImage(QString srcfile, const QString &label, MythPainter *painter, ImageCacheMode cacheMode=kCacheNormal)
Returns a reference counted image from the cache.
MythImage * GetImage() const
void LoadComplete()
friend class ImageLoadThread
Definition: mythuiimage.h:203
virtual void SetArea(const MythRect &rect)
Definition: mythuitype.cpp:591
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
virtual void Reset(void)
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuitype.cpp:69
int m_runningThreads
Definition: mythuiimage.h:185
MythUIImagePrivate(MythUIImage *p)
QString m_imageDirectory
Definition: mythuiimage.h:188
void start(QRunnable *runnable, const QString &debugName, int priority=0)
bool GetAbortState() const
const MythUIImage * m_parent
void CreateCopy(MythUIType *parent) override
Copy the state of this widget to the one given, it must be of the same type.
bool IsDeferredLoading(bool recurse=false) const
MythPainter * m_painter
QImage GetMaskImageSubset(const QRect &imageArea)
Definition: mythuiimage.h:48
void Reflect(ReflectAxis axis, int shear, int scale, int length, int spacing=0)
Definition: mythimage.cpp:167
virtual MythRect GetFullArea(void) const
Definition: mythuitype.cpp:871
void SetFilename(const QString &filename)
Must be followed by a call to Load() to load the image.
bool m_preserveAspect
Definition: mythuiimage.h:63
bool m_animationReverse
Definition: mythuiimage.h:194
virtual void CopyFrom(MythUIType *base)
Copy this widgets state from another.
QHash< int, MythImage * > m_Images
Definition: mythuiimage.h:170
MythUIType * m_Parent
Definition: mythuitype.h:270
~MythUIImagePrivate()=default
const char * frames[3]
Definition: element.c:46
static MythRect parseRect(const QString &text, bool normalize=true)
QString GetFileName(void) const
Definition: mythimage.h:86
unsigned int m_CurPos
Definition: mythuiimage.h:178
void DrawSelf(MythPainter *p, int xoffset, int yoffset, int alphaMod, QRect clipRect) override
void Resize(const QSize &newSize, bool preserveAspect=false)
Definition: mythimage.cpp:146
QMutex m_ImagesLock
Definition: mythuiimage.h:172
MythUIImage(const QString &filepattern, int low, int high, int delayms, MythUIType *parent, const QString &name)
void SetDelays(QVector< int > delays)
Sets the delays between each image in an animation.
static long int random(void)
Definition: compat.h:149
bool m_EnableInitiator
Definition: mythuitype.h:240
void ForceSize(const QSize &size)
Force the dimensions of the widget and image to the given size.
static MythImage * LoadImage(MythPainter *painter, ImageProperties imProps, ImageCacheMode cacheMode, const MythUIImage *parent, bool &aborted, MythImageReader *imageReader=nullptr)
MThreadPool * GetImageThreadPool(void)
QRect GetMaskImageRect(void)
Definition: mythuiimage.h:41
MythImage * GetFormatImage()
Returns a blank reference counted image in the format required for the Draw functions for this painte...
const MythUIImage * GetParent() const
MythImage * m_maskImage
Definition: mythuiimage.h:83
void run() override
ImageLoader()=default
static bool parseBool(const QString &text)
void setIsReflected(bool reflected)
Definition: mythimage.h:88
void SetImages(QVector< MythImage * > *images)
Should not be used unless absolutely necessary since it bypasses the image caching and threaded loade...
virtual void SetClipRect(const QRect &clipRect)
Definition: mythpainter.cpp:48
virtual bool ParseElement(const QString &filename, QDomElement &element, bool showWarnings)
Parse the xml definition of this widget setting the state of the object accordingly.
void Pulse(void) override
Pulse is called 70 times a second to trigger a single frame of an animation.
void GetScreenSettings(float &wmult, float &hmult)
QVector< AnimationFrame > AnimationFrames
Definition: mythuiimage.h:88
QPair< MythImage *, int > AnimationFrame
Definition: mythuiimage.h:87
virtual void Finalize(void)
Perform any post-xml parsing initialisation tasks.
void LoadNow(void) override
Cause images in this and child widgets to be loaded.