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 <random>
9#include <algorithm>
10
11// QT
12#include <QCoreApplication>
13#include <QDir>
14#include <QDomDocument>
15#include <QEvent>
16#include <QFile>
17#include <QImageReader>
18#include <QReadWriteLock>
19#include <QRunnable>
20
21// libmythbase
25
26// Mythui
27#include "mythpainter.h"
28#include "mythmainwindow.h"
29#include "mythuihelper.h"
30#include "mythscreentype.h"
31
32class ImageLoadThread;
33
34#define LOC QString("MythUIImage(0x%1): ").arg((uint64_t)this,0,16)
35
37
39{
40 Copy(other);
41}
42
44{
45 if (this == &other)
46 return *this;
47
48 Copy(other);
49
50 return *this;
51}
52
54{
55 if (m_maskImage)
57}
58
59// The m_maskImage field is assigned in the call to SetMaskImage().
60//
61// cppcheck-suppress operatorEqVarError
63{
64 m_filename = other.m_filename;
65
66 m_cropRect = other.m_cropRect;
68
73
80
82
84 m_isMasked = other.m_isMasked;
86}
87
89{
90 if (image)
91 image->IncrRef();
92 if (m_maskImage)
94
95 m_maskImage = image;
97}
98
103{
104 public:
105 ImageLoader() = default;
106 ~ImageLoader() = default;
107
108 static QHash<QString, const MythUIImage *> m_loadingImages;
109 static QMutex m_loadingImagesLock;
110 static QWaitCondition m_loadingImagesCond;
111
112 static bool PreLoad(const QString &cacheKey, const MythUIImage *uitype)
113 {
114 m_loadingImagesLock.lock();
115
116 // Check to see if the image is being loaded by us in another thread
117 if ((m_loadingImages.contains(cacheKey)) &&
118 (m_loadingImages[cacheKey] == uitype))
119 {
120 LOG(VB_GUI | VB_FILE, LOG_DEBUG,
121 QString("ImageLoader::PreLoad(%1), this "
122 "file is already being loaded by this same MythUIImage "
123 "in another thread.").arg(cacheKey));
124 m_loadingImagesLock.unlock();
125 return false;
126 }
127
128 // Check to see if the exact same image is being loaded anywhere else
129 while (m_loadingImages.contains(cacheKey))
131
132 m_loadingImages[cacheKey] = uitype;
133 m_loadingImagesLock.unlock();
134
135 return true;
136 }
137
138 static void PostLoad(const QString &cacheKey)
139 {
140 m_loadingImagesLock.lock();
141 m_loadingImages.remove(cacheKey);
142 m_loadingImagesCond.wakeAll();
143 m_loadingImagesLock.unlock();
144 }
145
146 static bool SupportsAnimation(const QString &filename)
147 {
148 QString extension = filename.section('.', -1);
149 return !filename.startsWith("myth://") &&
150 (extension == "gif" ||
151 extension == "apng" ||
152 extension == "mng");
153 }
154
159 static QString GenImageLabel(const ImageProperties &imProps)
160 {
161 QString imagelabel;
162 QString s_Attrib;
163
164 if (imProps.m_isMasked)
165 s_Attrib = "masked";
166
167 if (imProps.m_isReflected)
168 s_Attrib += "reflected";
169
170 if (imProps.m_isGreyscale)
171 s_Attrib += "greyscale";
172
173 if (imProps.m_isOriented)
174 {
175 s_Attrib += "orientation";
176 s_Attrib += QString("%1").arg(imProps.m_orientation);
177 }
178
179 int w = -1;
180 int h = -1;
181 if (!imProps.m_forceSize.isNull())
182 {
183 if (imProps.m_forceSize.width() != -1)
184 w = imProps.m_forceSize.width();
185
186 if (imProps.m_forceSize.height() != -1)
187 h = imProps.m_forceSize.height();
188 }
189
190
191 imagelabel = QString("%1-%2-%3x%4.png")
192 .arg(imProps.m_filename,
193 s_Attrib)
194 .arg(w)
195 .arg(h);
196 imagelabel.replace('/', '-');
197#ifdef Q_OS_ANDROID
198 imagelabel.replace(':', '-');
199#endif
200
201 return imagelabel;
202 }
203
205 // Must be a copy for thread safety
206 ImageProperties imProps,
207 ImageCacheMode cacheMode,
208 // Included only to check address, could be
209 // replaced by generating a unique value for
210 // each MythUIImage object?
211 const MythUIImage *parent,
212 bool &aborted,
213 MythImageReader *imageReader = nullptr)
214 {
215 QString cacheKey = GenImageLabel(imProps);
216 if (!PreLoad(cacheKey, parent))
217 {
218 aborted = true;
219 return nullptr;
220 }
221
222 QString filename = imProps.m_filename;
223 MythImage *image = nullptr;
224
225 bool bResize = false;
226 bool bFoundInCache = false;
227
228 int w = -1;
229 int h = -1;
230
231 if (!imProps.m_forceSize.isNull())
232 {
233 if (imProps.m_forceSize.width() != -1)
234 w = imProps.m_forceSize.width();
235
236 if (imProps.m_forceSize.height() != -1)
237 h = imProps.m_forceSize.height();
238
239 bResize = true;
240 }
241
242 if (!imageReader)
243 {
244 image = GetMythUI()->LoadCacheImage(filename, cacheKey,
245 painter, cacheMode);
246 }
247
248 if (image)
249 {
250 if (VERBOSE_LEVEL_CHECK(VB_GUI | VB_FILE, LOG_INFO))
251 {
252 image->IncrRef();
253 int cnt = image->DecrRef();
254 LOG(VB_GUI | VB_FILE, LOG_INFO,
255 QString("ImageLoader::LoadImage(%1) Found in cache, "
256 "RefCount = %2")
257 .arg(cacheKey).arg(cnt));
258 }
259
260 if (imProps.m_isReflected)
261 image->setIsReflected(true);
262
263 if (imProps.m_isOriented)
264 image->setIsOriented(true);
265
266 bFoundInCache = true;
267 }
268 else
269 {
270 LOG(VB_GUI | VB_FILE, LOG_INFO,
271 QString("ImageLoader::LoadImage(%1) NOT Found in cache. "
272 "Loading Directly").arg(cacheKey));
273
274 image = painter->GetFormatImage();
275 bool ok = false;
276
277 if (imageReader)
278 ok = image->Load(imageReader);
279 else
280 ok = image->Load(filename);
281
282 if (!ok)
283 {
284 image->DecrRef();
285 image = nullptr;
286 }
287 }
288
289 if (image && image->isNull())
290 {
291 LOG(VB_GUI | VB_FILE, LOG_INFO,
292 QString("ImageLoader::LoadImage(%1) Image is NULL")
293 .arg(filename));
294
295 image->DecrRef();
296 image = nullptr;
297 }
298
299 if (image && !bFoundInCache)
300 {
301 if (imProps.m_isReflected)
302 {
303 image->Reflect(imProps.m_reflectAxis, imProps.m_reflectShear,
304 imProps.m_reflectScale, imProps.m_reflectLength,
305 imProps.m_reflectSpacing);
306 }
307
308 if (imProps.m_isGreyscale)
309 image->ToGreyscale();
310
311 if (imProps.m_isOriented)
312 image->Orientation(imProps.m_orientation);
313
314 // Even if an explicit size wasn't defined this image may still need
315 // to be scaled because of a difference between the theme resolution
316 // and the screen resolution. We want to avoid scaling twice.
317 if (!bResize && imProps.m_isThemeImage)
318 {
319 float wmult = NAN; // Width multipler
320 float hmult = NAN; // Height multipler
321 GetMythMainWindow()->GetScalingFactors(wmult, hmult);
322 if (wmult != 1.0F || hmult != 1.0F)
323 {
324 w = image->size().width() * wmult;
325 h = image->size().height() * hmult;
326 bResize = true;
327 }
328 }
329
330 if (bResize)
331 image->Resize(QSize(w, h), imProps.m_preserveAspect);
332
333 if (imProps.m_isMasked)
334 {
335 MythImage *newMaskImage = painter->GetFormatImage();
336 if (newMaskImage->Load(imProps.GetMaskImageFilename()))
337 {
338 float wmult = NAN; // Width multipler
339 float hmult = NAN; // Height multipler
340 GetMythMainWindow()->GetScalingFactors(wmult, hmult);
341 if (wmult != 1.0F || hmult != 1.0F)
342 {
343 int width = newMaskImage->size().width() * wmult;
344 int height = newMaskImage->size().height() * hmult;
345 newMaskImage->Resize(QSize(width, height));
346 }
347
348 imProps.SetMaskImage(newMaskImage);
349 }
350 else
351 {
352 imProps.SetMaskImage(nullptr);
353 }
354 newMaskImage->DecrRef();
355
356 QRect imageArea = image->rect();
357 QRect maskArea = imProps.GetMaskImageRect();
358
359 // Crop the mask to the image
360 int x = 0;
361 int y = 0;
362
363 if (maskArea.width() > imageArea.width())
364 x = (maskArea.width() - imageArea.width()) / 2;
365
366 if (maskArea.height() > imageArea.height())
367 y = (maskArea.height() - imageArea.height()) / 2;
368
369 if (x > 0 || y > 0)
370 imageArea.translate(x, y);
371
372 QImage mask = imProps.GetMaskImageSubset(imageArea);
373 image->setAlphaChannel(mask.convertToFormat(QImage::Format_Alpha8));
374 }
375
376 if (!imageReader)
377 GetMythUI()->CacheImage(cacheKey, image);
378 }
379
380 if (image)
381 image->SetChanged();
382
383 PostLoad(cacheKey);
384
385 return image;
386 }
387
389 // Must be a copy for thread safety
390 const ImageProperties& imProps,
391 ImageCacheMode cacheMode,
392 // Included only to check address, could be
393 // replaced by generating a unique value for
394 // each MythUIImage object?
395 const MythUIImage *parent,
396 bool &aborted)
397 {
398 QString filename = QString("frame-%1-") + imProps.m_filename;
399 QString frameFilename;
400 int imageCount = 1;
401
402 auto *imageReader = new MythImageReader(imProps.m_filename);
403 auto *images = new AnimationFrames();
404
405 while (imageReader->canRead() && !aborted)
406 {
407 frameFilename = filename.arg(imageCount);
408
409 ImageProperties frameProps = imProps;
410 frameProps.m_filename = frameFilename;
411
412 MythImage *im = LoadImage(painter, frameProps, cacheMode, parent,
413 aborted, imageReader);
414
415 if (!im)
416 aborted = true;
417
418 images->append(AnimationFrame(im, std::chrono::milliseconds(imageReader->nextImageDelay())));
419 imageCount++;
420 }
421
422 delete imageReader;
423
424 return images;
425 }
426
427};
428
429QHash<QString, const MythUIImage *> ImageLoader::m_loadingImages;
432
436class ImageLoadEvent : public QEvent
437{
438 public:
439 ImageLoadEvent(const MythUIImage *parent, MythImage *image,
440 QString basefile, QString filename,
441 int number, bool aborted)
442 : QEvent(kEventType),
443 m_parent(parent), m_image(image), m_basefile(std::move(basefile)),
444 m_filename(std::move(filename)), m_number(number),
445 m_aborted(aborted) { }
446
448 QString basefile,
449 QString filename, bool aborted)
450 : QEvent(kEventType),
451 m_parent(parent), m_basefile(std::move(basefile)),
452 m_filename(std::move(filename)),
453 m_images(frames), m_aborted(aborted) { }
454
455 const MythUIImage *GetParent() const { return m_parent; }
456 MythImage *GetImage() const { return m_image; }
457 QString GetBasefile() const { return m_basefile; }
458 QString GetFilename() const { return m_filename; }
459 int GetNumber() const { return m_number; }
461 bool GetAbortState() const { return m_aborted; }
462
463 static const Type kEventType;
464
465 private:
466 const MythUIImage *m_parent {nullptr};
467 MythImage *m_image {nullptr};
468 QString m_basefile;
469 QString m_filename;
470 int m_number {0};
471
472 // Animated Images
474
475 // Image Load
477};
478
479const QEvent::Type ImageLoadEvent::kEventType =
480 (QEvent::Type) QEvent::registerEventType();
481
485class ImageLoadThread : public QRunnable
486{
487 public:
489 const ImageProperties &imProps, QString basefile,
490 int number, ImageCacheMode mode) :
491 m_parent(parent), m_painter(painter), m_imageProperties(imProps),
492 m_basefile(std::move(basefile)), m_number(number), m_cacheMode(mode)
493 {
494 }
495
496 void run() override // QRunnable
497 {
498 bool aborted = false;
500
501 // NOTE Do NOT use MythImageReader::supportsAnimation here, it defeats
502 // the point of caching remote images
504 {
505 AnimationFrames *frames =
509 aborted);
510
511 if (frames && frames->count() > 1)
512 {
513 auto *le = new ImageLoadEvent(m_parent, frames, m_basefile,
515 aborted);
516 QCoreApplication::postEvent(m_parent, le);
517
518 return;
519 }
520 delete frames;
521 }
522
526 aborted);
527
528 auto *le = new ImageLoadEvent(m_parent, image, m_basefile,
530 m_number, aborted);
531 QCoreApplication::postEvent(m_parent, le);
532 }
533
534private:
538 QString m_basefile;
541};
542
545{
546public:
548 : m_parent(p) { }
550
552
553 QReadWriteLock m_updateLock {QReadWriteLock::Recursive};
554};
555
557
558MythUIImage::MythUIImage(const QString &filepattern,
559 int low, int high, std::chrono::milliseconds delay,
560 MythUIType *parent, const QString &name)
561 : MythUIType(parent, name),
562 m_delay(delay),
563 m_lowNum(low),
564 m_highNum(high),
565 d(new MythUIImagePrivate(this))
566{
567 m_imageProperties.m_filename = filepattern;
568
569 m_enableInitiator = true;
570
571 emit DependChanged(false);
572}
573
575 const QString &name)
576 : MythUIType(parent, name),
577 m_origFilename(filename),
578 m_delay(-1ms),
579 d(new MythUIImagePrivate(this))
580{
582
583 m_enableInitiator = true;
584
585 emit DependChanged(false);
586}
587
588MythUIImage::MythUIImage(MythUIType *parent, const QString &name)
589 : MythUIType(parent, name),
590 m_delay(-1ms),
591 d(new MythUIImagePrivate(this))
592{
593 m_enableInitiator = true;
594}
595
597{
598 // Wait until all image loading threads are complete or bad things
599 // may happen if this MythUIImage disappears when a queued thread
600 // needs it.
601 if (m_runningThreads > 0)
602 {
604 }
605
606 Clear();
607
608 delete d;
609}
610
615{
616 QWriteLocker updateLocker(&d->m_updateLock);
617 QMutexLocker locker(&m_imagesLock);
618
619 for (auto it = m_images.begin();
620 it != m_images.end();
621 it = m_images.erase(it))
622 {
623 if (*it)
624 (*it)->DecrRef();
625 }
626
627 m_delays.clear();
628
629 if (m_animatedImage)
630 {
631 m_lowNum = 0;
632 m_highNum = 0;
633 m_animatedImage = false;
634 }
635}
636
641{
642 d->m_updateLock.lockForWrite();
643
645
647 {
650
651 if (m_animatedImage)
652 {
653 m_lowNum = 0;
654 m_highNum = 0;
655 m_animatedImage = false;
656 }
657 emit DependChanged(true);
658
659 d->m_updateLock.unlock();
660 Load();
661 }
662 else
663 {
664 d->m_updateLock.unlock();
665 }
666
668}
669
674{
675 QWriteLocker updateLocker(&d->m_updateLock);
679 emit DependChanged(true);
680 else
681 emit DependChanged(false);
682}
683
688void MythUIImage::SetFilepattern(const QString &filepattern, int low,
689 int high)
690{
691 QWriteLocker updateLocker(&d->m_updateLock);
693 m_imageProperties.m_filename = filepattern;
694 m_lowNum = low;
695 m_highNum = high;
696 if (filepattern == m_origFilename)
697 emit DependChanged(true);
698 else
699 emit DependChanged(false);
700}
701
705void MythUIImage::SetImageCount(int low, int high)
706{
707 QWriteLocker updateLocker(&d->m_updateLock);
708 m_lowNum = low;
709 m_highNum = high;
710}
711
715void MythUIImage::SetDelay(std::chrono::milliseconds delay)
716{
717 QWriteLocker updateLocker(&d->m_updateLock);
718 m_delay = delay;
719 m_lastDisplay = QTime::currentTime();
720 m_curPos = 0;
721}
722
726void MythUIImage::SetDelays(const QVector<std::chrono::milliseconds>& delays)
727{
728 QWriteLocker updateLocker(&d->m_updateLock);
729 QMutexLocker imageLocker(&m_imagesLock);
730
731 for (std::chrono::milliseconds delay : std::as_const(delays))
732 m_delays[m_delays.size()] = delay;
733
734 if (m_delay == -1ms)
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
770 {
776 }
777
778 if (m_imageProperties.m_isGreyscale && !img->isGrayscale())
779 img->ToGreyscale();
780
781 Clear();
782 m_delay = -1ms;
783
786
787 if (m_imageProperties.m_forceSize.isNull())
788 SetSize(img->size());
789
790 m_imagesLock.lock();
791 m_images[0] = img;
792 m_delays.clear();
793 m_imagesLock.unlock();
794
795 m_curPos = 0;
797 SetRedraw();
798
799 d->m_updateLock.unlock();
800}
801
807void MythUIImage::SetImages(QVector<MythImage *> *images)
808{
809 Clear();
810
811 QWriteLocker updateLocker(&d->m_updateLock);
812 QSize aSize = GetFullArea().size();
813
815
816 for (auto *im : std::as_const(*images))
817 {
818 if (!im)
819 {
820 QMutexLocker locker(&m_imagesLock);
821 m_images[m_images.size()] = im;
822 continue;
823 }
824
825 im->IncrRef();
826
827
828 QSize forceSize = m_imageProperties.m_forceSize;
829 if (!forceSize.isNull())
830 {
831 int w = (forceSize.width() <= 0) ? im->width() : forceSize.width();
832 int h = (forceSize.height() <= 0) ? im->height() : forceSize.height();
833 im->Resize(QSize(w, h), m_imageProperties.m_preserveAspect);
834 }
835
836 if (m_imageProperties.m_isReflected && !im->IsReflected())
837 {
843 }
844
845 if (m_imageProperties.m_isGreyscale && !im->isGrayscale())
846 im->ToGreyscale();
847
848 if (m_imageProperties.m_isOriented && !im->IsOriented())
849 im->Orientation(m_imageProperties.m_orientation);
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<std::chrono::milliseconds> delays;
876 QVector<MythImage *> images;
877
878 for (const auto & frame : std::as_const(frames))
879 {
880 images.append(frame.first);
881 delays.append(frame.second);
882 }
883
884 if (!images.empty())
885 {
886 SetImages(&images);
887
888 if (m_delay < 0ms && !delays.empty())
889 SetDelays(delays);
890 }
891 else
892 {
893 Reset();
894 }
895}
896
900void 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
920void MythUIImage::SetOrientation(int orientation)
921{
923 m_imageProperties.m_orientation = orientation;
924}
925
929void MythUIImage::SetSize(int width, int height)
930{
931 SetSize(QSize(width, height));
932}
933
937void MythUIImage::SetSize(const QSize size)
938{
939 QWriteLocker updateLocker(&d->m_updateLock);
941 m_needLoad = true;
942}
943
948void 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
967bool 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();
983 SetRedraw();
984
985 return false;
986 }
987
988 if (qEnvironmentVariableIsSet("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 {
1042 LOG(VB_GUI | VB_FILE, LOG_DEBUG, LOC +
1043 QString("Load(), spawning thread to load '%1'").arg(filename));
1044
1046 auto *bImgThread = new ImageLoadThread(this, GetPainter(),
1047 imProps, bFilename, i,
1048 static_cast<ImageCacheMode>(cacheMode2));
1049 GetMythUI()->GetImageThreadPool()->start(bImgThread, "ImageLoad");
1050 }
1051 else
1052 {
1053 if (!isAnimation && !GetMythUI()->IsImageInCache(imagelabel))
1054 Clear();
1055
1056 // Perform a blocking load
1057 LOG(VB_GUI | VB_FILE, LOG_DEBUG, LOC +
1058 QString("Load(), loading '%1' in foreground").arg(filename));
1059 bool aborted = false;
1060
1062 {
1063 AnimationFrames *myFrames =
1065 static_cast<ImageCacheMode>(cacheMode2),
1066 this, aborted);
1067
1068 // TODO We might want to handle an abort here more gracefully
1069 if (aborted)
1070 {
1071 LOG(VB_GUI, LOG_DEBUG, QString("Aborted loading animated"
1072 "image %1 in foreground")
1073 .arg(filename));
1074 }
1075
1076 SetAnimationFrames(*myFrames);
1077
1078 delete myFrames;
1079 }
1080 else
1081 {
1082 MythImage *image = nullptr;
1083
1085 imProps,
1086 static_cast<ImageCacheMode>(cacheMode2),
1087 this, aborted);
1088
1089 // TODO We might want to handle an abort here more gracefully
1090 if (aborted)
1091 {
1092 LOG(VB_GUI, LOG_DEBUG, QString("Aborted loading animated"
1093 "image %1 in foreground")
1094 .arg(filename));
1095 }
1096
1097 if (image)
1098 {
1099 if (m_imageProperties.m_forceSize.isNull())
1100 SetSize(image->size());
1101
1102 MythRect rect(GetFullArea());
1103 rect.setSize(image->size());
1104 SetMinArea(rect);
1105
1106 m_imagesLock.lock();
1107 m_images[j] = image;
1108 m_imagesLock.unlock();
1109
1110 SetRedraw();
1111 d->m_updateLock.lockForWrite();
1112 m_lastDisplay = QTime::currentTime();
1113 d->m_updateLock.unlock();
1114 }
1115 else
1116 {
1117 Reset();
1118
1119 m_imagesLock.lock();
1120 m_images[j] = nullptr;
1121 m_imagesLock.unlock();
1122 }
1123 }
1124 }
1125
1126 ++j;
1127
1128 // Load is complete if no image is loading in background
1129 complete &= !do_background_load;
1130 }
1131
1132 if (complete)
1133 emit LoadComplete();
1134
1135 return true;
1136}
1137
1142{
1143 d->m_updateLock.lockForWrite();
1144
1145 auto delay = -1ms;
1146
1147 if (m_delays.contains(m_curPos))
1148 delay = m_delays[m_curPos];
1149 else if (m_delay > 0ms)
1150 delay = m_delay;
1151
1152 if (delay > 0ms &&
1153 abs(m_lastDisplay.msecsTo(QTime::currentTime())) > delay.count())
1154 {
1156 {
1158 d->m_updateLock.unlock();
1159 Load();
1160 d->m_updateLock.lockForWrite();
1161 }
1162 else
1163 {
1164 m_imagesLock.lock();
1165
1167 {
1168 ++m_curPos;
1169
1170 if (m_curPos >= (uint)m_images.size())
1171 m_curPos = 0;
1172 }
1173 else if (m_animationCycle == kCycleReverse)
1174 {
1175 if ((m_curPos + 1) >= (uint)m_images.size())
1176 {
1177 m_animationReverse = true;
1178 }
1179 else if (m_curPos == 0)
1180 {
1181 m_animationReverse = false;
1182 }
1183
1185 --m_curPos;
1186 else
1187 ++m_curPos;
1188 }
1189
1190 m_imagesLock.unlock();
1191
1192 SetRedraw();
1193 }
1194
1195 m_lastDisplay = QTime::currentTime();
1196 }
1197
1199
1200 d->m_updateLock.unlock();
1201}
1202
1206void MythUIImage::DrawSelf(MythPainter *p, int xoffset, int yoffset,
1207 int alphaMod, QRect clipRect)
1208{
1209 m_imagesLock.lock();
1210
1211 if (!m_images.empty())
1212 {
1213 d->m_updateLock.lockForWrite();
1214
1215 if (m_curPos >= (uint)m_images.size())
1216 m_curPos = 0;
1217
1218 if (!m_images[m_curPos])
1219 {
1220 unsigned int origPos = m_curPos;
1221 m_curPos++;
1222
1223 while (!m_images[m_curPos] && m_curPos != origPos)
1224 {
1225 m_curPos++;
1226
1227 if (m_curPos >= (uint)m_images.size())
1228 m_curPos = 0;
1229 }
1230 }
1231
1232 QRect area = GetArea().toQRect();
1233 area.translate(xoffset, yoffset);
1234
1235 int alpha = CalcAlpha(alphaMod);
1236
1237 MythImage *currentImage = m_images[m_curPos];
1238
1239 if (currentImage)
1240 currentImage->IncrRef();
1241
1242 m_imagesLock.unlock();
1243 d->m_updateLock.unlock();
1244
1245 if (!currentImage)
1246 return;
1247
1248 d->m_updateLock.lockForRead();
1249
1250 QRect currentImageArea = currentImage->rect();
1251
1252 if (!m_imageProperties.m_forceSize.isNull())
1253 area.setSize(area.size().expandedTo(currentImage->size()));
1254
1255 // Centre image in available space, accounting for zoom
1256 int x = 0;
1257 int y = 0;
1258 QRect visibleImage = m_effects.GetExtent(currentImageArea.size());
1259
1260 if (area.width() > visibleImage.width())
1261 x = (area.width() / 2) + visibleImage.topLeft().x();
1262
1263 if (area.height() > visibleImage.height())
1264 y = (area.height() / 2) + visibleImage.topLeft().y();
1265
1266 if ((x > 0 || y > 0))
1267 area.translate(x, y);
1268
1269 QRect srcRect;
1271
1272 if (!m_imageProperties.m_cropRect.isEmpty())
1274 else
1275 srcRect = currentImageArea;
1276
1277 p->SetClipRect(clipRect);
1278 p->DrawImage(area, currentImage, srcRect, alpha);
1279 currentImage->DecrRef();
1280 d->m_updateLock.unlock();
1281 }
1282 else
1283 {
1284 m_imagesLock.unlock();
1285 }
1286}
1287
1292 const QString &filename, QDomElement &element, bool showWarnings)
1293{
1294 QWriteLocker updateLocker(&d->m_updateLock);
1295
1296 if (element.tagName() == "filename")
1297 {
1298 m_imageProperties.m_isThemeImage = true; // This is an image distributed with the theme
1300
1301 if (m_imageProperties.m_filename.endsWith('/'))
1302 {
1303 m_showingRandomImage = true;
1305
1307 }
1308 }
1309 else if (element.tagName() == "filepattern")
1310 {
1311 m_imageProperties.m_isThemeImage = true; // This is an image distributed with the theme
1313 QString tmp = element.attribute("low");
1314
1315 if (!tmp.isEmpty())
1316 m_lowNum = tmp.toInt();
1317
1318 tmp = element.attribute("high");
1319
1320 if (!tmp.isEmpty())
1321 m_highNum = tmp.toInt();
1322
1323 tmp = element.attribute("cycle", "start");
1324
1325 if (tmp == "reverse")
1327 }
1328 else if (element.tagName() == "area")
1329 {
1330 SetArea(parseRect(element));
1332 }
1333 else if (element.tagName() == "preserveaspect")
1334 {
1336 }
1337 else if (element.tagName() == "crop")
1338 {
1340 }
1341 else if (element.tagName() == "delay")
1342 {
1343 QString value = getFirstText(element);
1344
1345 if (value.contains(","))
1346 {
1347 QVector<std::chrono::milliseconds> delays;
1348 QStringList tokens = value.split(",");
1349 for (const auto & token : std::as_const(tokens))
1350 {
1351 if (token.isEmpty())
1352 {
1353 if (!delays.empty())
1354 delays.append(delays[delays.size()-1]);
1355 else
1356 delays.append(0ms); // Default delay before first image
1357 }
1358 else
1359 {
1360 delays.append(std::chrono::milliseconds(token.toInt()));
1361 }
1362 }
1363
1364 if (!delays.empty())
1365 {
1366 m_delay = delays[0];
1367 SetDelays(delays);
1368 }
1369 }
1370 else
1371 {
1372 m_delay = std::chrono::milliseconds(value.toInt());
1373 }
1374 }
1375 else if (element.tagName() == "reflection")
1376 {
1378 QString tmp = element.attribute("axis");
1379
1380 if (!tmp.isEmpty())
1381 {
1382 if (tmp.toLower() == "horizontal")
1384 else
1386 }
1387
1388 tmp = element.attribute("shear");
1389
1390 if (!tmp.isEmpty())
1392
1393 tmp = element.attribute("scale");
1394
1395 if (!tmp.isEmpty())
1397
1398 tmp = element.attribute("length");
1399
1400 if (!tmp.isEmpty())
1402
1403 tmp = element.attribute("spacing");
1404
1405 if (!tmp.isEmpty())
1407 }
1408 else if (element.tagName() == "mask")
1409 {
1412 }
1413 else if (element.tagName() == "grayscale" ||
1414 element.tagName() == "greyscale")
1415 {
1417 }
1418 else
1419 {
1420 return MythUIType::ParseElement(filename, element, showWarnings);
1421 }
1422
1423 m_needLoad = true;
1424
1425 if (m_parent && m_parent->IsDeferredLoading(true))
1426 m_needLoad = false;
1427
1428 return true;
1429}
1430
1435{
1436 d->m_updateLock.lockForWrite();
1437 auto *im = dynamic_cast<MythUIImage *>(base);
1438 if (!im)
1439 {
1440 LOG(VB_GENERAL, LOG_ERR,
1441 QString("'%1' (%2) ERROR, bad parsing '%3' (%4)")
1442 .arg(objectName(), GetXMLLocation(),
1443 base->objectName(), base->GetXMLLocation()));
1444 d->m_updateLock.unlock();
1445 return;
1446 }
1447
1448 m_origFilename = im->m_origFilename;
1449
1450 m_delay = im->m_delay;
1451 m_lowNum = im->m_lowNum;
1452 m_highNum = im->m_highNum;
1453
1454 m_lastDisplay = QTime::currentTime();
1455 m_curPos = 0;
1456
1457 m_imageProperties = im->m_imageProperties;
1458
1459 m_animationCycle = im->m_animationCycle;
1460 m_animatedImage = im->m_animatedImage;
1461
1462 m_showingRandomImage = im->m_showingRandomImage;
1463 m_imageDirectory = im->m_imageDirectory;
1464
1466
1467 // We need to update forceSize in case the parent area has changed
1468 // however we only want to set forceSize if it was previously in use
1469 if (!m_imageProperties.m_forceSize.isNull())
1471
1472 m_needLoad = im->m_needLoad;
1473
1474 d->m_updateLock.unlock();
1475
1476 d->m_updateLock.lockForRead();
1477
1478 if (m_needLoad)
1479 {
1480 d->m_updateLock.unlock();
1481 Load();
1482 }
1483 else
1484 {
1485 d->m_updateLock.unlock();
1486 }
1487}
1488
1493{
1494 QReadLocker updateLocker(&d->m_updateLock);
1495 auto *im = new MythUIImage(parent, objectName());
1496 im->CopyFrom(this);
1497}
1498
1503{
1504 d->m_updateLock.lockForRead();
1505
1506 if (m_needLoad)
1507 {
1508 d->m_updateLock.unlock();
1509 Load();
1510 }
1511 else
1512 {
1513 d->m_updateLock.unlock();
1514 }
1515
1517}
1518
1523{
1524 d->m_updateLock.lockForWrite();
1525
1526 if (m_needLoad)
1527 {
1528 d->m_updateLock.unlock();
1529 return;
1530 }
1531
1532 m_needLoad = true;
1533 d->m_updateLock.unlock();
1534
1535 Load(false);
1536
1538}
1539
1543void MythUIImage::customEvent(QEvent *event)
1544{
1545 if (event->type() == ImageLoadEvent::kEventType)
1546 {
1547 auto * le = dynamic_cast<ImageLoadEvent *>(event);
1548 if (!le || le->GetParent() != this)
1549 return;
1550
1551 MythImage *image = le->GetImage();
1552 int number = le->GetNumber();
1553 QString filename = le->GetFilename();
1554 AnimationFrames *animationFrames = le->GetAnimationFrames();
1555 bool aborted = le->GetAbortState();
1556
1558
1559 d->m_updateLock.lockForRead();
1560 QString propFilename = m_imageProperties.m_filename;
1561 d->m_updateLock.unlock();
1562
1563 // 1) We aborted loading the image for some reason (e.g. two requests
1564 // for same image)
1565 // 2) Filename changed since we started this image, so abort to avoid
1566 // rendering two different images in quick succession which causes
1567 // unsightly flickering
1568 if (aborted || (le->GetBasefile() != propFilename))
1569 {
1570 if (aborted)
1571 LOG(VB_GUI, LOG_DEBUG, QString("Aborted loading image %1")
1572 .arg(filename));
1573
1574 if (image)
1575 image->DecrRef();
1576
1577 if (animationFrames)
1578 {
1579 for (const auto & frame : std::as_const(*animationFrames))
1580 {
1581 MythImage *im = frame.first;
1582 if (im)
1583 im->DecrRef();
1584 }
1585
1586 delete animationFrames;
1587 }
1588 }
1589 else if (animationFrames)
1590 {
1591 SetAnimationFrames(*animationFrames);
1592
1593 delete animationFrames;
1594 }
1595 else if (image)
1596 {
1597 // We don't clear until we have the new image ready to display to
1598 // avoid unsightly flashing. This isn't currently supported for
1599 // animations.
1600 if ((m_highNum == m_lowNum) && !m_animatedImage)
1601 Clear();
1602
1603 d->m_updateLock.lockForWrite();
1604
1605 if (m_imageProperties.m_forceSize.isNull())
1606 SetSize(image->size());
1607
1608 MythRect rect(GetFullArea());
1609 rect.setSize(image->size());
1610 SetMinArea(rect);
1611
1612 d->m_updateLock.unlock();
1613
1614 m_imagesLock.lock();
1615
1616 if (m_images[number])
1617 {
1618 // If we got to this point, it means this same MythUIImage
1619 // was told to reload the same image, so we use the newest
1620 // copy of the image.
1621 m_images[number]->DecrRef(); // delete the original
1622 }
1623
1624 m_images[number] = image;
1625 m_imagesLock.unlock();
1626
1627 SetRedraw();
1628
1629 d->m_updateLock.lockForWrite();
1630 m_lastDisplay = QTime::currentTime();
1631 d->m_updateLock.unlock();
1632 }
1633 else
1634 {
1635 // No Images were loaded, so trigger Reset to default
1636 Reset();
1637 }
1638
1639 // NOLINTNEXTLINE(readability-misleading-indentation)
1640 emit LoadComplete();
1641 }
1642}
1643
1645{
1646 QString randFile;
1647
1648 // find and save the list of available images
1649 if (m_imageList.isEmpty())
1650 {
1651 QDir imageDir(m_imageDirectory);
1652
1653 if (!imageDir.exists())
1654 {
1655 QString themeDir = GetMythUI()->GetThemeDir() + '/';
1656 imageDir.setPath(themeDir + m_imageDirectory);
1657 }
1658
1659 QStringList imageTypes;
1660
1661 QList< QByteArray > exts = QImageReader::supportedImageFormats();
1662 for (const auto & ext : std::as_const(exts))
1663 {
1664 imageTypes.append(QString("*.").append(ext));
1665 }
1666
1667 imageDir.setNameFilters(imageTypes);
1668
1669 m_imageList = imageDir.entryList();
1670
1671 if (m_imageList.empty())
1672 {
1674 return;
1675 }
1676
1677 // randomly shuffle the images
1678 std::random_device rd;
1679 std::mt19937 g(rd());
1680 std::shuffle(m_imageList.begin(), m_imageList.end(), g);
1681 m_imageListIndex = 0;
1682 randFile = QString("%1%2").arg(m_imageDirectory, m_imageList.at(m_imageListIndex));
1683 }
1684 else
1685 {
1686 if (!m_imageList.empty())
1687 {
1689
1690 // if we are at the last image in the list re-shuffle the list and start from the beginning
1691 if (m_imageListIndex == m_imageList.size())
1692 {
1693 std::random_device rd;
1694 std::mt19937 g(rd());
1695 std::shuffle(m_imageList.begin(), m_imageList.end(), g);
1696 m_imageListIndex = 0;
1697 }
1698
1699 // make sure we don't show the same image again in the unlikely event the re-shuffle shows the same image again
1700 if (m_imageList.at(m_imageListIndex) == m_origFilename && m_imageList.size() > 1)
1702
1703 randFile = QString("%1%2").arg(m_imageDirectory, m_imageList.at(m_imageListIndex));
1704 }
1705 }
1706
1708}
ImageLoadEvent(const MythUIImage *parent, MythImage *image, QString basefile, QString filename, int number, bool aborted)
const MythUIImage * GetParent() const
QString GetFilename() const
ImageLoadEvent(const MythUIImage *parent, AnimationFrames *frames, QString basefile, QString filename, bool aborted)
bool GetAbortState() const
AnimationFrames * m_images
QString m_filename
QString m_basefile
int GetNumber() const
static const Type kEventType
AnimationFrames * GetAnimationFrames() const
MythImage * m_image
const MythUIImage * m_parent
MythImage * GetImage() const
QString GetBasefile() const
MythUIImage * m_parent
ImageCacheMode m_cacheMode
ImageLoadThread(MythUIImage *parent, MythPainter *painter, const ImageProperties &imProps, QString basefile, int number, ImageCacheMode mode)
ImageProperties m_imageProperties
MythPainter * m_painter
void run() override
static QHash< QString, const MythUIImage * > m_loadingImages
static MythImage * LoadImage(MythPainter *painter, ImageProperties imProps, ImageCacheMode cacheMode, const MythUIImage *parent, bool &aborted, MythImageReader *imageReader=nullptr)
static QMutex m_loadingImagesLock
static QString GenImageLabel(const ImageProperties &imProps)
Generates a unique identifying string for this image which is used as a key in the image cache.
static void PostLoad(const QString &cacheKey)
static bool PreLoad(const QString &cacheKey, const MythUIImage *uitype)
ImageLoader()=default
static AnimationFrames * LoadAnimatedImage(MythPainter *painter, const ImageProperties &imProps, ImageCacheMode cacheMode, const MythUIImage *parent, bool &aborted)
~ImageLoader()=default
static QWaitCondition m_loadingImagesCond
static bool SupportsAnimation(const QString &filename)
QRect GetMaskImageRect(void)
Definition: mythuiimage.h:41
ImageProperties()=default
void SetMaskImageFilename(const QString &filename)
Definition: mythuiimage.h:33
MythRect m_cropRect
Definition: mythuiimage.h:60
ReflectAxis m_reflectAxis
Definition: mythuiimage.h:69
MythImage * m_maskImage
Definition: mythuiimage.h:83
void SetMaskImage(MythImage *image)
Definition: mythuiimage.cpp:88
void Copy(const ImageProperties &other)
Definition: mythuiimage.cpp:62
ImageProperties & operator=(const ImageProperties &other)
Definition: mythuiimage.cpp:43
QString GetMaskImageFilename()
Definition: mythuiimage.h:37
QString m_maskImageFilename
Definition: mythuiimage.h:84
QImage GetMaskImageSubset(QRect imageArea)
Definition: mythuiimage.h:48
QString m_filename
Definition: mythuiimage.h:58
bool m_preserveAspect
Definition: mythuiimage.h:63
void start(QRunnable *runnable, const QString &debugName, int priority=0)
void waitForDone(void)
bool Load(MythImageReader *reader)
Definition: mythimage.cpp:300
virtual void SetChanged(bool change=true)
Definition: mythimage.h:50
void setIsOriented(bool oriented)
Definition: mythimage.h:94
bool IsReflected() const
Definition: mythimage.h:54
void Orientation(int orientation)
Changes the orientation angle of the image according to the exif rotation values.
Definition: mythimage.cpp:150
bool IsOriented() const
Definition: mythimage.h:55
void Reflect(ReflectAxis axis, int shear, int scale, int length, int spacing=0)
Definition: mythimage.cpp:181
void Resize(QSize newSize, bool preserveAspect=false)
Definition: mythimage.cpp:159
void setIsReflected(bool reflected)
Definition: mythimage.h:93
int DecrRef(void) override
Decrements reference count and deletes on 0.
Definition: mythimage.cpp:52
QString GetFileName(void) const
Definition: mythimage.h:91
void ToGreyscale()
Definition: mythimage.cpp:284
int IncrRef(void) override
Increments reference count.
Definition: mythimage.cpp:44
MythImage * GetFormatImage()
Returns a blank reference counted image in the format required for the Draw functions for this painte...
Wrapper around QRect allowing us to handle percentage and other relative values for areas in mythui.
Definition: mythrect.h:18
void CalculateArea(QRect parentArea)
Definition: mythrect.cpp:64
QRect toQRect(void) const
Definition: mythrect.cpp:405
~MythUIImagePrivate()=default
MythUIImagePrivate(MythUIImage *p)
QReadWriteLock m_updateLock
MythUIImage * m_parent
Image widget, displays a single image or multiple images in sequence.
Definition: mythuiimage.h:98
MythUIImagePrivate * d
Definition: mythuiimage.h:191
void LoadNow(void) override
Cause images in this and child widgets to be loaded.
bool Load(bool allowLoadInBackground=true, bool forceStat=false)
Load the image(s), wraps ImageLoader::LoadImage()
MythUIImage(const QString &filepattern, int low, int high, std::chrono::milliseconds delay, MythUIType *parent, const QString &name)
void ForceSize(QSize size)
Force the dimensions of the widget and image to the given size.
QHash< int, MythImage * > m_images
Definition: mythuiimage.h:169
void CopyFrom(MythUIType *base) override
Copy this widgets state from another.
void SetFilename(const QString &filename)
Must be followed by a call to Load() to load the image.
QMutex m_imagesLock
Definition: mythuiimage.h:171
unsigned int m_curPos
Definition: mythuiimage.h:177
QString m_imageDirectory
Definition: mythuiimage.h:187
std::chrono::milliseconds m_delay
Definition: mythuiimage.h:173
friend class ImageLoadThread
Definition: mythuiimage.h:204
void SetFilepattern(const QString &filepattern, int low, int high)
Must be followed by a call to Load() to load the image.
void DrawSelf(MythPainter *p, int xoffset, int yoffset, int alphaMod, QRect clipRect) override
void CreateCopy(MythUIType *parent) override
Copy the state of this widget to the one given, it must be of the same type.
AnimationCycle m_animationCycle
Definition: mythuiimage.h:194
bool m_animatedImage
Definition: mythuiimage.h:196
void FindRandomImage(void)
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...
QString m_origFilename
Definition: mythuiimage.h:167
void LoadComplete()
void SetDelays(const QVector< std::chrono::milliseconds > &delays)
Sets the delays between each image in an animation.
~MythUIImage() override
void customEvent(QEvent *event) override
void SetImage(MythImage *img)
Should not be used unless absolutely necessary since it bypasses the image caching and threaded loade...
void Finalize(void) override
Perform any post-xml parsing initialisation tasks.
bool ParseElement(const QString &filename, QDomElement &element, bool showWarnings) override
Parse the xml definition of this widget setting the state of the object accordingly.
QTime m_lastDisplay
Definition: mythuiimage.h:178
int m_runningThreads
Definition: mythuiimage.h:184
bool m_showingRandomImage
Definition: mythuiimage.h:186
void Clear(void)
Remove all images from the widget.
bool m_animationReverse
Definition: mythuiimage.h:195
void SetDelay(std::chrono::milliseconds delayms)
Set the delay between each image in an animation.
ImageProperties m_imageProperties
Definition: mythuiimage.h:182
void SetImages(QVector< MythImage * > *images)
Should not be used unless absolutely necessary since it bypasses the image caching and threaded loade...
QHash< int, std::chrono::milliseconds > m_delays
Definition: mythuiimage.h:170
void Pulse(void) override
Pulse is called 70 times a second to trigger a single frame of an animation.
void SetSize(int width, int height)
Set the size of the widget.
QStringList m_imageList
Definition: mythuiimage.h:188
bool m_needLoad
Definition: mythuiimage.h:180
int m_imageListIndex
Definition: mythuiimage.h:189
void Reset(void) override
Reset the image back to the default defined in the theme.
void SetAnimationFrames(const AnimationFrames &frames)
void SetOrientation(int orientation)
Saves the exif orientation value of the first image in the widget.
void SetImageCount(int low, int high)
Set the integer range for an animated image pattern.
void GetScalingFactors(float &Horizontal, float &Vertical) const
MThreadPool * GetImageThreadPool()
MythImage * CacheImage(const QString &URL, MythImage *Image, bool NoDisk=false)
MythImage * LoadCacheImage(QString File, const QString &Label, MythPainter *Painter, ImageCacheMode cacheMode=kCacheNormal)
The base class on which all widgets and screens are based.
Definition: mythuitype.h:86
bool m_enableInitiator
Definition: mythuitype.h:267
virtual void SetSize(QSize size)
Definition: mythuitype.cpp:554
bool m_initiator
Definition: mythuitype.h:268
UIEffects m_effects
Definition: mythuitype.h:284
virtual MythPainter * GetPainter(void)
QString GetXMLLocation(void) const
Definition: mythuitype.h:182
virtual void SetArea(const MythRect &rect)
Definition: mythuitype.cpp:595
virtual void LoadNow(void)
Cause images in this and child widgets to be loaded.
virtual void CopyFrom(MythUIType *base)
Copy this widgets state from another.
virtual void Finalize(void)
Perform any post-xml parsing initialisation tasks.
void SetRedraw(void)
Definition: mythuitype.cpp:298
virtual void SetMinArea(const MythRect &rect)
Set the minimum area based on the given size.
Definition: mythuitype.cpp:805
virtual void Pulse(void)
Pulse is called 70 times a second to trigger a single frame of an animation.
Definition: mythuitype.cpp:441
virtual MythRect GetArea(void) const
If the object has a minimum area defined, return it, other wise return the default area.
Definition: mythuitype.cpp:870
bool IsDeferredLoading(bool recurse=false) const
MythUIType * m_parent
Definition: mythuitype.h:297
virtual MythRect GetFullArea(void) const
Definition: mythuitype.cpp:878
int CalcAlpha(int alphamod) const
Definition: mythuitype.cpp:459
void DependChanged(bool isDefault)
virtual void Reset(void)
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuitype.cpp:70
virtual bool ParseElement(const QString &filename, QDomElement &element, bool showWarnings)
Parse the xml definition of this widget setting the state of the object accordingly.
MythRect m_area
Definition: mythuitype.h:277
QRect GetExtent(QSize size) const
static MythRect parseRect(const QString &text, bool normalize=true)
static QString getFirstText(QDomElement &element)
static bool parseBool(const QString &text)
unsigned int uint
Definition: compat.h:60
static uint32_t * tmp
Definition: goom_core.cpp:28
static const iso6937table * d
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
Definition: mythlogging.h:29
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)
MythUIHelper * GetMythUI()
#define LOC
Definition: mythuiimage.cpp:34
QPair< MythImage *, std::chrono::milliseconds > AnimationFrame
Definition: mythuiimage.h:87
QVector< AnimationFrame > AnimationFrames
Definition: mythuiimage.h:88
ImageCacheMode
@ kCacheNormal
@ kCacheForceStat
@ kCacheIgnoreDisk
STL namespace.