MythTV  master
mythuiimage.cpp
Go to the documentation of this file.
1 
2 #include "mythuiimage.h"
3 
4 // C++
5 #include <cmath>
6 #include <cstdint>
7 #include <cstdlib>
8 #include <ctime>
9 
10 // QT
11 #include <QCoreApplication>
12 #include <QDir>
13 #include <QDomDocument>
14 #include <QEvent>
15 #include <QFile>
16 #include <QImageReader>
17 #include <QReadWriteLock>
18 #include <QRunnable>
19 
20 // libmythbase
21 #include "mythlogging.h"
22 #include "mthreadpool.h"
23 
24 // Mythui
25 #include "mythpainter.h"
26 #include "mythmainwindow.h"
27 #include "mythuihelper.h"
28 #include "mythscreentype.h"
29 
30 #ifdef _MSC_VER
31 # include "compat.h" // random
32 #endif
33 
34 class ImageLoadThread;
35 
36 #define LOC QString("MythUIImage(0x%1): ").arg((uint64_t)this,0,16)
37 
39 
41 {
42  Copy(other);
43 }
44 
46 {
47  Copy(other);
48 
49  return *this;
50 }
51 
53 {
54  if (m_maskImage)
56 }
57 
59 {
60  m_filename = other.m_filename;
61 
62  m_cropRect = other.m_cropRect;
63  m_forceSize = other.m_forceSize;
64 
68  m_isOriented = other.m_isOriented;
69 
76 
78 
80  m_isMasked = other.m_isMasked;
82 }
83 
85 {
86  if (image)
87  image->IncrRef();
88  if (m_maskImage)
90 
91  m_maskImage = image;
93 }
94 
99 {
100  public:
101  ImageLoader() = default;
102  ~ImageLoader() = default;
103 
104  static QHash<QString, const MythUIImage *> m_loadingImages;
105  static QMutex m_loadingImagesLock;
106  static QWaitCondition m_loadingImagesCond;
107 
108  static bool PreLoad(const QString &cacheKey, const MythUIImage *uitype)
109  {
110  m_loadingImagesLock.lock();
111 
112  // Check to see if the image is being loaded by us in another thread
113  if ((m_loadingImages.contains(cacheKey)) &&
114  (m_loadingImages[cacheKey] == uitype))
115  {
116  LOG(VB_GUI | VB_FILE, LOG_DEBUG,
117  QString("ImageLoader::PreLoad(%1), this "
118  "file is already being loaded by this same MythUIImage "
119  "in another thread.").arg(cacheKey));
120  m_loadingImagesLock.unlock();
121  return false;
122  }
123 
124  // Check to see if the exact same image is being loaded anywhere else
125  while (m_loadingImages.contains(cacheKey))
127 
128  m_loadingImages[cacheKey] = uitype;
129  m_loadingImagesLock.unlock();
130 
131  return true;
132  }
133 
134  static void PostLoad(const QString &cacheKey)
135  {
136  m_loadingImagesLock.lock();
137  m_loadingImages.remove(cacheKey);
138  m_loadingImagesCond.wakeAll();
139  m_loadingImagesLock.unlock();
140  }
141 
142  static bool SupportsAnimation(const QString &filename)
143  {
144  QString extension = filename.section('.', -1);
145  return !filename.startsWith("myth://") &&
146  (extension == "gif" ||
147  extension == "apng" ||
148  extension == "mng");
149  }
150 
155  static QString GenImageLabel(const ImageProperties &imProps)
156  {
157  QString imagelabel;
158  QString s_Attrib;
159 
160  if (imProps.m_isMasked)
161  s_Attrib = "masked";
162 
163  if (imProps.m_isReflected)
164  s_Attrib += "reflected";
165 
166  if (imProps.m_isGreyscale)
167  s_Attrib += "greyscale";
168 
169  if (imProps.m_isOriented)
170  {
171  s_Attrib += "orientation";
172  s_Attrib += QString("%1").arg(imProps.m_orientation);
173  }
174 
175  int w = -1;
176  int h = -1;
177  if (!imProps.m_forceSize.isNull())
178  {
179  if (imProps.m_forceSize.width() != -1)
180  w = imProps.m_forceSize.width();
181 
182  if (imProps.m_forceSize.height() != -1)
183  h = imProps.m_forceSize.height();
184  }
185 
186 
187  imagelabel = QString("%1-%2-%3x%4.png")
188  .arg(imProps.m_filename)
189  .arg(s_Attrib)
190  .arg(w)
191  .arg(h);
192  imagelabel.replace('/', '-');
193 #ifdef Q_OS_ANDROID
194  imagelabel.replace(':', '-');
195 #endif
196 
197  return imagelabel;
198  }
199 
200  static MythImage *LoadImage(MythPainter *painter,
201  // Must be a copy for thread safety
202  ImageProperties imProps,
203  ImageCacheMode cacheMode,
204  // Included only to check address, could be
205  // replaced by generating a unique value for
206  // each MythUIImage object?
207  const MythUIImage *parent,
208  bool &aborted,
209  MythImageReader *imageReader = nullptr)
210  {
211  QString cacheKey = GenImageLabel(imProps);
212  if (!PreLoad(cacheKey, parent))
213  {
214  aborted = true;
215  return nullptr;
216  }
217 
218  QString filename = imProps.m_filename;
219  MythImage *image = nullptr;
220 
221  bool bResize = false;
222  bool bFoundInCache = false;
223 
224  int w = -1;
225  int h = -1;
226 
227  if (!imProps.m_forceSize.isNull())
228  {
229  if (imProps.m_forceSize.width() != -1)
230  w = imProps.m_forceSize.width();
231 
232  if (imProps.m_forceSize.height() != -1)
233  h = imProps.m_forceSize.height();
234 
235  bResize = true;
236  }
237 
238  if (!imageReader)
239  {
240  image = GetMythUI()->LoadCacheImage(filename, cacheKey,
241  painter, cacheMode);
242  }
243 
244  if (image)
245  {
246  if (VERBOSE_LEVEL_CHECK(VB_GUI | VB_FILE, LOG_INFO))
247  {
248  image->IncrRef();
249  int cnt = image->DecrRef();
250  LOG(VB_GUI | VB_FILE, LOG_INFO,
251  QString("ImageLoader::LoadImage(%1) Found in cache, "
252  "RefCount = %2")
253  .arg(cacheKey).arg(cnt));
254  }
255 
256  if (imProps.m_isReflected)
257  image->setIsReflected(true);
258 
259  if (imProps.m_isOriented)
260  image->setIsOriented(true);
261 
262  bFoundInCache = true;
263  }
264  else
265  {
266  LOG(VB_GUI | VB_FILE, LOG_INFO,
267  QString("ImageLoader::LoadImage(%1) NOT Found in cache. "
268  "Loading Directly").arg(cacheKey));
269 
270  image = painter->GetFormatImage();
271  bool ok = false;
272 
273  if (imageReader)
274  ok = image->Load(imageReader);
275  else
276  ok = image->Load(filename);
277 
278  if (!ok)
279  {
280  image->DecrRef();
281  image = nullptr;
282  }
283  }
284 
285  if (image && image->isNull())
286  {
287  LOG(VB_GUI | VB_FILE, LOG_INFO,
288  QString("ImageLoader::LoadImage(%1) Image is NULL")
289  .arg(filename));
290 
291  image->DecrRef();
292  image = nullptr;
293  }
294 
295  if (image && !bFoundInCache)
296  {
297  if (imProps.m_isReflected)
298  image->Reflect(imProps.m_reflectAxis, imProps.m_reflectShear,
299  imProps.m_reflectScale, imProps.m_reflectLength,
300  imProps.m_reflectSpacing);
301 
302  if (imProps.m_isGreyscale)
303  image->ToGreyscale();
304 
305  if (imProps.m_isOriented)
306  image->Orientation(imProps.m_orientation);
307 
308  // Even if an explicit size wasn't defined this image may still need
309  // to be scaled because of a difference between the theme resolution
310  // and the screen resolution. We want to avoid scaling twice.
311  if (!bResize && imProps.m_isThemeImage)
312  {
313  float wmult = NAN; // Width multipler
314  float hmult = NAN; // Height multipler
315  GetMythUI()->GetScreenSettings(wmult, hmult);
316  if (wmult != 1.0F || hmult != 1.0F)
317  {
318  w = image->size().width() * wmult;
319  h = image->size().height() * hmult;
320  bResize = true;
321  }
322  }
323 
324  if (bResize)
325  image->Resize(QSize(w, h), imProps.m_preserveAspect);
326 
327  if (imProps.m_isMasked)
328  {
329  MythImage *newMaskImage = painter->GetFormatImage();
330  if (newMaskImage->Load(imProps.GetMaskImageFilename()))
331  {
332  float wmult = NAN; // Width multipler
333  float hmult = NAN; // Height multipler
334  GetMythUI()->GetScreenSettings(wmult, hmult);
335  if (wmult != 1.0F || hmult != 1.0F)
336  {
337  int width = newMaskImage->size().width() * wmult;
338  int height = newMaskImage->size().height() * hmult;
339  newMaskImage->Resize(QSize(width, height));
340  }
341 
342  imProps.SetMaskImage(newMaskImage);
343  }
344  else
345  imProps.SetMaskImage(nullptr);
346  newMaskImage->DecrRef();
347 
348  QRect imageArea = image->rect();
349  QRect maskArea = imProps.GetMaskImageRect();
350 
351  // Crop the mask to the image
352  int x = 0;
353  int y = 0;
354 
355  if (maskArea.width() > imageArea.width())
356  x = (maskArea.width() - imageArea.width()) / 2;
357 
358  if (maskArea.height() > imageArea.height())
359  y = (maskArea.height() - imageArea.height()) / 2;
360 
361  if (x > 0 || y > 0)
362  imageArea.translate(x, y);
363 
364  QImage mask = imProps.GetMaskImageSubset(imageArea);
365  image->setAlphaChannel(mask.alphaChannel());
366  }
367 
368  if (!imageReader)
369  GetMythUI()->CacheImage(cacheKey, image);
370  }
371 
372  if (image)
373  image->SetChanged();
374 
375  PostLoad(cacheKey);
376 
377  return image;
378  }
379 
381  // Must be a copy for thread safety
382  const ImageProperties& imProps,
383  ImageCacheMode cacheMode,
384  // Included only to check address, could be
385  // replaced by generating a unique value for
386  // each MythUIImage object?
387  const MythUIImage *parent,
388  bool &aborted)
389  {
390  QString filename = QString("frame-%1-") + imProps.m_filename;
391  QString frameFilename;
392  int imageCount = 1;
393 
394  auto *imageReader = new MythImageReader(imProps.m_filename);
395  auto *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  QString basefile, QString filename,
433  int number, bool aborted)
434  : QEvent(kEventType),
435  m_parent(parent), m_image(image), m_basefile(std::move(basefile)),
436  m_filename(std::move(filename)), m_number(number),
437  m_aborted(aborted) { }
438 
440  QString basefile,
441  QString filename, bool aborted)
442  : QEvent(kEventType),
443  m_parent(parent), m_basefile(std::move(basefile)),
444  m_filename(std::move(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:
481  const ImageProperties &imProps, QString basefile,
482  int number, ImageCacheMode mode) :
483  m_parent(parent), m_painter(painter), m_imageProperties(imProps),
484  m_basefile(std::move(basefile)), m_number(number), m_cacheMode(mode)
485  {
486  }
487 
488  void run() override // QRunnable
489  {
490  bool aborted = false;
492 
493  // NOTE Do NOT use MythImageReader::supportsAnimation here, it defeats
494  // the point of caching remote images
496  {
501  aborted);
502 
503  if (frames && frames->count() > 1)
504  {
505  auto *le = new ImageLoadEvent(m_parent, frames, m_basefile,
507  aborted);
508  QCoreApplication::postEvent(m_parent, le);
509 
510  return;
511  }
512  delete frames;
513  }
514 
518  aborted);
519 
520  auto *le = new ImageLoadEvent(m_parent, image, m_basefile,
522  m_number, aborted);
523  QCoreApplication::postEvent(m_parent, le);
524  }
525 
526 private:
527  MythUIImage *m_parent {nullptr};
528  MythPainter *m_painter {nullptr};
530  QString m_basefile;
531  int m_number;
533 };
534 
537 {
538 public:
540  : m_parent(p) { }
541  ~MythUIImagePrivate() = default;
542 
543  MythUIImage *m_parent {nullptr};
544 
545  QReadWriteLock m_updateLock {QReadWriteLock::Recursive};
546 };
547 
549 
550 MythUIImage::MythUIImage(const QString &filepattern,
551  int low, int high, int delayms,
552  MythUIType *parent, const QString &name)
553  : MythUIType(parent, name)
554 {
555  m_imageProperties.m_filename = filepattern;
556  m_LowNum = low;
557  m_HighNum = high;
558 
559  m_Delay = delayms;
560  m_EnableInitiator = true;
561 
562  d = new MythUIImagePrivate(this);
563  emit DependChanged(false);
564 }
565 
567  const QString &name)
568  : MythUIType(parent, name)
569 {
572 
573  m_LowNum = 0;
574  m_HighNum = 0;
575  m_Delay = -1;
576  m_EnableInitiator = true;
577 
578  d = new MythUIImagePrivate(this);
579  emit DependChanged(false);
580 }
581 
582 MythUIImage::MythUIImage(MythUIType *parent, const QString &name)
583  : MythUIType(parent, name)
584 {
585  m_LowNum = 0;
586  m_HighNum = 0;
587  m_Delay = -1;
588  m_EnableInitiator = true;
589 
590  d = new MythUIImagePrivate(this);
591 }
592 
594 {
595  // Wait until all image loading threads are complete or bad things
596  // may happen if this MythUIImage disappears when a queued thread
597  // needs it.
598  if (m_runningThreads > 0)
599  {
601  }
602 
603  Clear();
604 
605  delete d;
606 }
607 
612 {
613  QWriteLocker updateLocker(&d->m_updateLock);
614  QMutexLocker locker(&m_ImagesLock);
615 
616  while (!m_Images.isEmpty())
617  {
618  QHash<int, MythImage *>::iterator it = m_Images.begin();
619 
620  if (*it)
621  (*it)->DecrRef();
622 
623  m_Images.remove(it.key());
624  }
625 
626  m_Delays.clear();
627 
628  if (m_animatedImage)
629  {
630  m_LowNum = 0;
631  m_HighNum = 0;
632  m_animatedImage = false;
633  }
634 }
635 
640 {
641  d->m_updateLock.lockForWrite();
642 
643  SetMinArea(MythRect());
644 
646  {
649 
650  if (m_animatedImage)
651  {
652  m_LowNum = 0;
653  m_HighNum = 0;
654  m_animatedImage = false;
655  }
656  emit DependChanged(true);
657 
658  d->m_updateLock.unlock();
659  Load();
660  }
661  else
662  d->m_updateLock.unlock();
663 
665 }
666 
670 void MythUIImage::SetFilename(const QString &filename)
671 {
672  QWriteLocker updateLocker(&d->m_updateLock);
675  if (filename == m_OrigFilename)
676  emit DependChanged(true);
677  else
678  emit DependChanged(false);
679 }
680 
685 void MythUIImage::SetFilepattern(const QString &filepattern, int low,
686  int high)
687 {
688  QWriteLocker updateLocker(&d->m_updateLock);
690  m_imageProperties.m_filename = filepattern;
691  m_LowNum = low;
692  m_HighNum = high;
693  if (filepattern == m_OrigFilename)
694  emit DependChanged(true);
695  else
696  emit DependChanged(false);
697 }
698 
702 void MythUIImage::SetImageCount(int low, int high)
703 {
704  QWriteLocker updateLocker(&d->m_updateLock);
705  m_LowNum = low;
706  m_HighNum = high;
707 }
708 
712 void MythUIImage::SetDelay(int delayms)
713 {
714  QWriteLocker updateLocker(&d->m_updateLock);
715  m_Delay = delayms;
716  m_LastDisplay = QTime::currentTime();
717  m_CurPos = 0;
718 }
719 
723 void MythUIImage::SetDelays(QVector<int> delays)
724 {
725  QWriteLocker updateLocker(&d->m_updateLock);
726  QMutexLocker imageLocker(&m_ImagesLock);
727 
728  for (auto it = delays.begin(); it != delays.end(); ++it)
729  m_Delays[m_Delays.size()] = *it;
730 
731  if (m_Delay == -1)
732  m_Delay = m_Delays[0];
733 
734  m_LastDisplay = QTime::currentTime();
735  m_CurPos = 0;
736 }
737 
743 {
744  d->m_updateLock.lockForWrite();
745 
746  if (!img)
747  {
748  d->m_updateLock.unlock();
749  Reset();
750  return;
751  }
752 
755 
756  img->IncrRef();
757 
758  QSize forceSize = m_imageProperties.m_forceSize;
759  if (!forceSize.isNull())
760  {
761  int w = (forceSize.width() <= 0) ? img->width() : forceSize.width();
762  int h = (forceSize.height() <= 0) ? img->height() : forceSize.height();
763  img->Resize(QSize(w, h), m_imageProperties.m_preserveAspect);
764  }
765 
772 
773  if (m_imageProperties.m_isGreyscale && !img->isGrayscale())
774  img->ToGreyscale();
775 
776  Clear();
777  m_Delay = -1;
778 
781 
782  if (m_imageProperties.m_forceSize.isNull())
783  SetSize(img->size());
784 
785  m_ImagesLock.lock();
786  m_Images[0] = img;
787  m_Delays.clear();
788  m_ImagesLock.unlock();
789 
790  m_CurPos = 0;
792  SetRedraw();
793 
794  d->m_updateLock.unlock();
795 }
796 
802 void MythUIImage::SetImages(QVector<MythImage *> *images)
803 {
804  Clear();
805 
806  QWriteLocker updateLocker(&d->m_updateLock);
807  QSize aSize = GetFullArea().size();
808 
810 
811  for (auto it = images->begin(); it != images->end(); ++it)
812  {
813  MythImage *im = (*it);
814 
815  if (!im)
816  {
817  QMutexLocker locker(&m_ImagesLock);
818  m_Images[m_Images.size()] = im;
819  continue;
820  }
821 
822  im->IncrRef();
823 
824 
825  QSize forceSize = m_imageProperties.m_forceSize;
826  if (!forceSize.isNull())
827  {
828  int w = (forceSize.width() <= 0) ? im->width() : forceSize.width();
829  int h = (forceSize.height() <= 0) ? im->height() : forceSize.height();
830  im->Resize(QSize(w, h), m_imageProperties.m_preserveAspect);
831  }
832 
839 
840  if (m_imageProperties.m_isGreyscale && !im->isGrayscale())
841  im->ToGreyscale();
842 
845 
846  m_ImagesLock.lock();
847  m_Images[m_Images.size()] = im;
848  m_ImagesLock.unlock();
849 
850  aSize = aSize.expandedTo(im->size());
851  }
852 
853  SetImageCount(1, m_Images.size());
854 
855  if (m_imageProperties.m_forceSize.isNull())
856  SetSize(aSize);
857 
858  MythRect rect(GetFullArea());
859  rect.setSize(aSize);
860  SetMinArea(rect);
861 
862  m_CurPos = 0;
863  m_animatedImage = true;
865  SetRedraw();
866 }
867 
869 {
870  QVector<int> delays;
871  QVector<MythImage *> images;
872 
873  for (auto it = frames.begin(); it != frames.end(); ++it)
874  {
875  images.append((*it).first);
876  delays.append((*it).second);
877  }
878 
879  if (!images.empty())
880  {
881  SetImages(&images);
882 
883  if (m_Delay < 0 && !delays.empty())
884  SetDelays(delays);
885  }
886  else
887  Reset();
888 }
889 
893 void MythUIImage::ForceSize(const QSize &size)
894 {
895  if (m_imageProperties.m_forceSize == size)
896  return;
897 
898  d->m_updateLock.lockForWrite();
900  d->m_updateLock.unlock();
901 
902  if (size.isEmpty())
903  return;
904 
906 
907  Load();
908 }
909 
913 void MythUIImage::SetOrientation(int orientation)
914 {
916  m_imageProperties.m_orientation = orientation;
917 }
918 
922 void MythUIImage::SetSize(int width, int height)
923 {
924  SetSize(QSize(width, height));
925 }
926 
930 void MythUIImage::SetSize(const QSize &size)
931 {
932  QWriteLocker updateLocker(&d->m_updateLock);
933  MythUIType::SetSize(size);
934  m_NeedLoad = true;
935 }
936 
941 void MythUIImage::SetCropRect(int x, int y, int width, int height)
942 {
943  SetCropRect(MythRect(x, y, width, height));
944 }
945 
951 {
952  QWriteLocker updateLocker(&d->m_updateLock);
954  SetRedraw();
955 }
956 
960 bool MythUIImage::Load(bool allowLoadInBackground, bool forceStat)
961 {
962  d->m_updateLock.lockForRead();
963 
965 
966  QString bFilename = m_imageProperties.m_filename;
967 
968  d->m_updateLock.unlock();
969 
970  QString filename = bFilename;
971 
972  if (bFilename.isEmpty())
973  {
974  Clear();
975  SetMinArea(MythRect());
976  SetRedraw();
977 
978  return false;
979  }
980 
981  if (getenv("DISABLETHREADEDMYTHUIIMAGE"))
982  allowLoadInBackground = false;
983 
984  // Don't clear the widget before we need to, otherwise it causes
985  // unsightly flashing. We exclude animations for now since that requires a
986  // deeper fix
987  bool isAnimation = (m_HighNum != m_LowNum) || m_animatedImage;
988 
989  if (isAnimation)
990  Clear();
991 
992  bool complete = true;
993 
994  QString imagelabel;
995 
996  int j = 0;
997 
998  for (int i = m_LowNum; i <= m_HighNum && !m_animatedImage; i++)
999  {
1000  if (!m_animatedImage && m_HighNum != m_LowNum &&
1001  bFilename.contains("%1"))
1002  filename = bFilename.arg(i);
1003 
1005  imProps.m_filename = filename;
1006  imagelabel = ImageLoader::GenImageLabel(imProps);
1007 
1008  // Only load in the background if allowed and the image is
1009  // not already in our mem cache
1010  int cacheMode = kCacheIgnoreDisk;
1011 
1012  if (forceStat)
1013  cacheMode |= (int)kCacheForceStat;
1014 
1015  int cacheMode2 = kCacheNormal;
1016 
1017  if (forceStat)
1018  cacheMode2 |= (int)kCacheForceStat;
1019 
1020  bool do_background_load = false;
1021  if (allowLoadInBackground)
1022  {
1024  filename, imagelabel, GetPainter(),
1025  static_cast<ImageCacheMode>(cacheMode));
1026  if (img)
1027  img->DecrRef();
1028  else
1029  do_background_load = true;
1030  }
1031 
1032  if (do_background_load)
1033  {
1034  SetMinArea(MythRect());
1035  LOG(VB_GUI | VB_FILE, LOG_DEBUG, LOC +
1036  QString("Load(), spawning thread to load '%1'").arg(filename));
1037 
1038  m_runningThreads++;
1039  auto *bImgThread = new ImageLoadThread(this, GetPainter(),
1040  imProps, bFilename, i,
1041  static_cast<ImageCacheMode>(cacheMode2));
1042  GetMythUI()->GetImageThreadPool()->start(bImgThread, "ImageLoad");
1043  }
1044  else
1045  {
1046  if (!isAnimation && !GetMythUI()->IsImageInCache(imagelabel))
1047  Clear();
1048 
1049  // Perform a blocking load
1050  LOG(VB_GUI | VB_FILE, LOG_DEBUG, LOC +
1051  QString("Load(), loading '%1' in foreground").arg(filename));
1052  bool aborted = false;
1053 
1055  {
1056  AnimationFrames *myFrames =
1058  static_cast<ImageCacheMode>(cacheMode2),
1059  this, aborted);
1060 
1061  // TODO We might want to handle an abort here more gracefully
1062  if (aborted)
1063  LOG(VB_GUI, LOG_DEBUG, QString("Aborted loading animated"
1064  "image %1 in foreground")
1065  .arg(filename));
1066 
1067  SetAnimationFrames(*myFrames);
1068 
1069  delete myFrames;
1070  }
1071  else
1072  {
1073  MythImage *image = nullptr;
1074 
1076  imProps,
1077  static_cast<ImageCacheMode>(cacheMode2),
1078  this, aborted);
1079 
1080  // TODO We might want to handle an abort here more gracefully
1081  if (aborted)
1082  LOG(VB_GUI, LOG_DEBUG, QString("Aborted loading animated"
1083  "image %1 in foreground")
1084  .arg(filename));
1085 
1086  if (image)
1087  {
1088  if (m_imageProperties.m_forceSize.isNull())
1089  SetSize(image->size());
1090 
1091  MythRect rect(GetFullArea());
1092  rect.setSize(image->size());
1093  SetMinArea(rect);
1094 
1095  m_ImagesLock.lock();
1096  m_Images[j] = image;
1097  m_ImagesLock.unlock();
1098 
1099  SetRedraw();
1100  d->m_updateLock.lockForWrite();
1101  m_LastDisplay = QTime::currentTime();
1102  d->m_updateLock.unlock();
1103  }
1104  else
1105  {
1106  Reset();
1107 
1108  m_ImagesLock.lock();
1109  m_Images[j] = nullptr;
1110  m_ImagesLock.unlock();
1111  }
1112  }
1113  }
1114 
1115  ++j;
1116 
1117  // Load is complete if no image is loading in background
1118  complete &= !do_background_load;
1119  }
1120 
1121  if (complete)
1122  emit LoadComplete();
1123 
1124  return true;
1125 }
1126 
1131 {
1132  d->m_updateLock.lockForWrite();
1133 
1134  int delay = -1;
1135 
1136  if (m_Delays.contains(m_CurPos))
1137  delay = m_Delays[m_CurPos];
1138  else if (m_Delay > 0)
1139  delay = m_Delay;
1140 
1141  if (delay > 0 &&
1142  abs(m_LastDisplay.msecsTo(QTime::currentTime())) > delay)
1143  {
1145  {
1146  FindRandomImage();
1147  d->m_updateLock.unlock();
1148  Load();
1149  d->m_updateLock.lockForWrite();
1150  }
1151  else
1152  {
1153  m_ImagesLock.lock();
1154 
1156  {
1157  ++m_CurPos;
1158 
1159  if (m_CurPos >= (uint)m_Images.size())
1160  m_CurPos = 0;
1161  }
1162  else if (m_animationCycle == kCycleReverse)
1163  {
1164  if ((m_CurPos + 1) >= (uint)m_Images.size())
1165  {
1166  m_animationReverse = true;
1167  }
1168  else if (m_CurPos == 0)
1169  {
1170  m_animationReverse = false;
1171  }
1172 
1173  if (m_animationReverse)
1174  --m_CurPos;
1175  else
1176  ++m_CurPos;
1177  }
1178 
1179  m_ImagesLock.unlock();
1180 
1181  SetRedraw();
1182  }
1183 
1184  m_LastDisplay = QTime::currentTime();
1185  }
1186 
1188 
1189  d->m_updateLock.unlock();
1190 }
1191 
1195 void MythUIImage::DrawSelf(MythPainter *p, int xoffset, int yoffset,
1196  int alphaMod, QRect clipRect)
1197 {
1198  m_ImagesLock.lock();
1199 
1200  if (!m_Images.empty())
1201  {
1202  d->m_updateLock.lockForWrite();
1203 
1204  if (m_CurPos >= (uint)m_Images.size())
1205  m_CurPos = 0;
1206 
1207  if (!m_Images[m_CurPos])
1208  {
1209  unsigned int origPos = m_CurPos;
1210  m_CurPos++;
1211 
1212  while (!m_Images[m_CurPos] && m_CurPos != origPos)
1213  {
1214  m_CurPos++;
1215 
1216  if (m_CurPos >= (uint)m_Images.size())
1217  m_CurPos = 0;
1218  }
1219  }
1220 
1221  QRect area = GetArea().toQRect();
1222  area.translate(xoffset, yoffset);
1223 
1224  int alpha = CalcAlpha(alphaMod);
1225 
1226  MythImage *currentImage = m_Images[m_CurPos];
1227 
1228  if (currentImage)
1229  currentImage->IncrRef();
1230 
1231  m_ImagesLock.unlock();
1232  d->m_updateLock.unlock();
1233 
1234  if (!currentImage)
1235  return;
1236 
1237  d->m_updateLock.lockForRead();
1238 
1239  QRect currentImageArea = currentImage->rect();
1240 
1241  if (!m_imageProperties.m_forceSize.isNull())
1242  area.setSize(area.size().expandedTo(currentImage->size()));
1243 
1244  // Centre image in available space, accounting for zoom
1245  int x = 0, y = 0;
1246  QRect visibleImage = m_Effects.GetExtent(currentImageArea.size());
1247 
1248  if (area.width() > visibleImage.width())
1249  x = area.width() / 2 + visibleImage.topLeft().x();
1250 
1251  if (area.height() > visibleImage.height())
1252  y = area.height() / 2 + visibleImage.topLeft().y();
1253 
1254  if ((x > 0 || y > 0))
1255  area.translate(x, y);
1256 
1257  QRect srcRect;
1259 
1260  if (!m_imageProperties.m_cropRect.isEmpty())
1261  srcRect = m_imageProperties.m_cropRect.toQRect();
1262  else
1263  srcRect = currentImageArea;
1264 
1265  p->SetClipRect(clipRect);
1266  p->DrawImage(area, currentImage, srcRect, alpha);
1267  currentImage->DecrRef();
1268  d->m_updateLock.unlock();
1269  }
1270  else
1271  m_ImagesLock.unlock();
1272 }
1273 
1278  const QString &filename, QDomElement &element, bool showWarnings)
1279 {
1280  QWriteLocker updateLocker(&d->m_updateLock);
1281 
1282  if (element.tagName() == "filename")
1283  {
1284  m_imageProperties.m_isThemeImage = true; // This is an image distributed with the theme
1286 
1287  if (m_imageProperties.m_filename.endsWith('/'))
1288  {
1289  m_showingRandomImage = true;
1291 
1292  FindRandomImage();
1293  }
1294  }
1295  else if (element.tagName() == "filepattern")
1296  {
1297  m_imageProperties.m_isThemeImage = true; // This is an image distributed with the theme
1299  QString tmp = element.attribute("low");
1300 
1301  if (!tmp.isEmpty())
1302  m_LowNum = tmp.toInt();
1303 
1304  tmp = element.attribute("high");
1305 
1306  if (!tmp.isEmpty())
1307  m_HighNum = tmp.toInt();
1308 
1309  tmp = element.attribute("cycle", "start");
1310 
1311  if (tmp == "reverse")
1313  }
1314  else if (element.tagName() == "area")
1315  {
1316  SetArea(parseRect(element));
1318  }
1319  else if (element.tagName() == "preserveaspect")
1321  else if (element.tagName() == "crop")
1323  else if (element.tagName() == "delay")
1324  {
1325  QString value = getFirstText(element);
1326 
1327  if (value.contains(","))
1328  {
1329  QVector<int> delays;
1330  QStringList tokens = value.split(",");
1331  QStringList::iterator it = tokens.begin();
1332 
1333  for (; it != tokens.end(); ++it)
1334  {
1335  if ((*it).isEmpty())
1336  {
1337  if (!delays.empty())
1338  delays.append(delays[delays.size()-1]);
1339  else
1340  delays.append(0); // Default 0ms delay before first image
1341  }
1342  else
1343  {
1344  delays.append((*it).toInt());
1345  }
1346  }
1347 
1348  if (!delays.empty())
1349  {
1350  m_Delay = delays[0];
1351  SetDelays(delays);
1352  }
1353  }
1354  else
1355  {
1356  m_Delay = value.toInt();
1357  }
1358  }
1359  else if (element.tagName() == "reflection")
1360  {
1362  QString tmp = element.attribute("axis");
1363 
1364  if (!tmp.isEmpty())
1365  {
1366  if (tmp.toLower() == "horizontal")
1368  else
1370  }
1371 
1372  tmp = element.attribute("shear");
1373 
1374  if (!tmp.isEmpty())
1376 
1377  tmp = element.attribute("scale");
1378 
1379  if (!tmp.isEmpty())
1381 
1382  tmp = element.attribute("length");
1383 
1384  if (!tmp.isEmpty())
1386 
1387  tmp = element.attribute("spacing");
1388 
1389  if (!tmp.isEmpty())
1391  }
1392  else if (element.tagName() == "mask")
1393  {
1396  }
1397  else if (element.tagName() == "grayscale" ||
1398  element.tagName() == "greyscale")
1399  {
1401  }
1402  else
1403  {
1404  return MythUIType::ParseElement(filename, element, showWarnings);
1405  }
1406 
1407  m_NeedLoad = true;
1408 
1409  if (m_Parent && m_Parent->IsDeferredLoading(true))
1410  m_NeedLoad = false;
1411 
1412  return true;
1413 }
1414 
1419 {
1420  d->m_updateLock.lockForWrite();
1421  auto *im = dynamic_cast<MythUIImage *>(base);
1422  if (!im)
1423  {
1424  LOG(VB_GENERAL, LOG_ERR,
1425  QString("'%1' (%2) ERROR, bad parsing '%3' (%4)")
1426  .arg(objectName()).arg(GetXMLLocation())
1427  .arg(base->objectName()).arg(base->GetXMLLocation()));
1428  d->m_updateLock.unlock();
1429  return;
1430  }
1431 
1432  m_OrigFilename = im->m_OrigFilename;
1433 
1434  m_Delay = im->m_Delay;
1435  m_LowNum = im->m_LowNum;
1436  m_HighNum = im->m_HighNum;
1437 
1438  m_LastDisplay = QTime::currentTime();
1439  m_CurPos = 0;
1440 
1441  m_imageProperties = im->m_imageProperties;
1442 
1443  m_animationCycle = im->m_animationCycle;
1444  m_animatedImage = im->m_animatedImage;
1445 
1446  m_showingRandomImage = im->m_showingRandomImage;
1447  m_imageDirectory = im->m_imageDirectory;
1448 
1449  MythUIType::CopyFrom(base);
1450 
1451  // We need to update forceSize in case the parent area has changed
1452  // however we only want to set forceSize if it was previously in use
1453  if (!m_imageProperties.m_forceSize.isNull())
1455 
1456  m_NeedLoad = im->m_NeedLoad;
1457 
1458  d->m_updateLock.unlock();
1459 
1460  d->m_updateLock.lockForRead();
1461 
1462  if (m_NeedLoad)
1463  {
1464  d->m_updateLock.unlock();
1465  Load();
1466  }
1467  else
1468  d->m_updateLock.unlock();
1469 }
1470 
1475 {
1476  QReadLocker updateLocker(&d->m_updateLock);
1477  auto *im = new MythUIImage(parent, objectName());
1478  im->CopyFrom(this);
1479 }
1480 
1485 {
1486  d->m_updateLock.lockForRead();
1487 
1488  if (m_NeedLoad)
1489  {
1490  d->m_updateLock.unlock();
1491  Load();
1492  }
1493  else
1494  d->m_updateLock.unlock();
1495 
1497 }
1498 
1503 {
1504  d->m_updateLock.lockForWrite();
1505 
1506  if (m_NeedLoad)
1507  {
1508  d->m_updateLock.unlock();
1509  return;
1510  }
1511 
1512  m_NeedLoad = true;
1513  d->m_updateLock.unlock();
1514 
1515  Load(false);
1516 
1518 }
1519 
1523 void MythUIImage::customEvent(QEvent *event)
1524 {
1525  if (event->type() == ImageLoadEvent::kEventType)
1526  {
1527  auto *le = static_cast<ImageLoadEvent *>(event);
1528  if (le->GetParent() != this)
1529  return;
1530 
1531  MythImage *image = le->GetImage();
1532  int number = le->GetNumber();
1533  QString filename = le->GetFilename();
1534  AnimationFrames *animationFrames = le->GetAnimationFrames();
1535  bool aborted = le->GetAbortState();
1536 
1537  m_runningThreads--;
1538 
1539  d->m_updateLock.lockForRead();
1540  QString propFilename = m_imageProperties.m_filename;
1541  d->m_updateLock.unlock();
1542 
1543  // 1) We aborted loading the image for some reason (e.g. two requests
1544  // for same image)
1545  // 2) Filename changed since we started this image, so abort to avoid
1546  // rendering two different images in quick succession which causes
1547  // unsightly flickering
1548  if (aborted || (le->GetBasefile() != propFilename))
1549  {
1550  if (aborted)
1551  LOG(VB_GUI, LOG_DEBUG, QString("Aborted loading image %1")
1552  .arg(filename));
1553 
1554  if (image)
1555  image->DecrRef();
1556 
1557  if (animationFrames)
1558  {
1559  for (auto it = animationFrames->begin();
1560  it != animationFrames->end();
1561  ++it)
1562  {
1563  MythImage *im = (*it).first;
1564  if (im)
1565  im->DecrRef();
1566  }
1567 
1568  delete animationFrames;
1569  }
1570  }
1571  else if (animationFrames)
1572  {
1573  SetAnimationFrames(*animationFrames);
1574 
1575  delete animationFrames;
1576  }
1577  else if (image)
1578  {
1579  // We don't clear until we have the new image ready to display to
1580  // avoid unsightly flashing. This isn't currently supported for
1581  // animations.
1582  if ((m_HighNum == m_LowNum) && !m_animatedImage)
1583  Clear();
1584 
1585  d->m_updateLock.lockForWrite();
1586 
1587  if (m_imageProperties.m_forceSize.isNull())
1588  SetSize(image->size());
1589 
1590  MythRect rect(GetFullArea());
1591  rect.setSize(image->size());
1592  SetMinArea(rect);
1593 
1594  d->m_updateLock.unlock();
1595 
1596  m_ImagesLock.lock();
1597 
1598  if (m_Images[number])
1599  {
1600  // If we got to this point, it means this same MythUIImage
1601  // was told to reload the same image, so we use the newest
1602  // copy of the image.
1603  m_Images[number]->DecrRef(); // delete the original
1604  }
1605 
1606  m_Images[number] = image;
1607  m_ImagesLock.unlock();
1608 
1609  SetRedraw();
1610 
1611  d->m_updateLock.lockForWrite();
1612  m_LastDisplay = QTime::currentTime();
1613  d->m_updateLock.unlock();
1614  }
1615  else
1616  {
1617  // No Images were loaded, so trigger Reset to default
1618  Reset();
1619  }
1620 
1621  // NOLINTNEXTLINE(readability-misleading-indentation)
1622  emit LoadComplete();
1623  }
1624 }
1625 
1627 {
1628  QDir imageDir(m_imageDirectory);
1629 
1630  if (!imageDir.exists())
1631  {
1632  QString themeDir = GetMythUI()->GetThemeDir() + '/';
1633  imageDir = themeDir + m_imageDirectory;
1634  }
1635 
1636  QStringList imageTypes;
1637 
1638  QList< QByteArray > exts = QImageReader::supportedImageFormats();
1639  QList< QByteArray >::Iterator it = exts.begin();
1640 
1641  for (; it != exts.end(); ++it)
1642  {
1643  imageTypes.append(QString("*.").append(*it));
1644  }
1645 
1646  imageDir.setNameFilters(imageTypes);
1647 
1648  QStringList imageList = imageDir.entryList();
1649  QString randFile;
1650 
1651  if (!imageList.empty())
1652  {
1653  // try to find a different image
1654  do
1655  {
1656  randFile = QString("%1%2").arg(m_imageDirectory)
1657  .arg(imageList.takeAt(random() % imageList.size()));
1658 
1659  } while (imageList.size() > 1 && randFile == m_OrigFilename);
1660  }
1661 
1663 }
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
QReadWriteLock m_updateLock
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:45
static QHash< QString, const MythUIImage * > m_loadingImages
QString GetMaskImageFilename()
Definition: mythuiimage.h:37
int CalcAlpha(int alphamod)
Definition: mythuitype.cpp:460
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
ImageLoadThread(MythUIImage *parent, MythPainter *painter, const ImageProperties &imProps, QString basefile, int number, ImageCacheMode mode)
void SetOrientation(int orientation)
Saves the exif orientation value of the first image in the widget.
void Copy(const ImageProperties &other)
Definition: mythuiimage.cpp:58
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:84
static QString getFirstText(QDomElement &element)
void SetImageCount(int low, int high)
Set the integer range for an animated image pattern.
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
ImageLoadEvent(const MythUIImage *parent, AnimationFrames *frames, QString basefile, QString filename, bool aborted)
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
unsigned int uint
Definition: compat.h:140
AnimationFrames * m_images
ImageProperties()=default
ImageCacheMode m_cacheMode
QPair< MythImage *, int > AnimationFrame
Definition: mythuiimage.h:87
static AnimationFrames * LoadAnimatedImage(MythPainter *painter, const ImageProperties &imProps, ImageCacheMode cacheMode, const MythUIImage *parent, bool &aborted)
void ToGreyscale()
Definition: mythimage.cpp:255
MythUIHelper * GetMythUI()
#define LOC
Definition: mythuiimage.cpp:36
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.
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
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
MythUIImage * m_parent
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
QVector< AnimationFrame > AnimationFrames
Definition: mythuiimage.h:88
MythUIType * m_Parent
Definition: mythuitype.h:270
~MythUIImagePrivate()=default
const char * frames[3]
Definition: element.c:46
ImageLoadEvent(const MythUIImage *parent, MythImage *image, QString basefile, QString filename, int number, bool aborted)
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 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)
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.