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 delays.reserve(frames.size());
879 images.reserve(frames.size());
880 for (const auto & frame : std::as_const(frames))
881 {
882 images.append(frame.first);
883 delays.append(frame.second);
884 }
885
886 if (!images.empty())
887 {
888 SetImages(&images);
889
890 if (m_delay < 0ms && !delays.empty())
891 SetDelays(delays);
892 }
893 else
894 {
895 Reset();
896 }
897}
898
902void MythUIImage::ForceSize(const QSize size)
903{
904 if (m_imageProperties.m_forceSize == size)
905 return;
906
907 d->m_updateLock.lockForWrite();
909 d->m_updateLock.unlock();
910
911 if (size.isEmpty())
912 return;
913
915
916 Load();
917}
918
922void MythUIImage::SetOrientation(int orientation)
923{
925 m_imageProperties.m_orientation = orientation;
926}
927
931void MythUIImage::SetSize(int width, int height)
932{
933 SetSize(QSize(width, height));
934}
935
939void MythUIImage::SetSize(const QSize size)
940{
941 QWriteLocker updateLocker(&d->m_updateLock);
943 m_needLoad = true;
944}
945
950void MythUIImage::SetCropRect(int x, int y, int width, int height)
951{
952 SetCropRect(MythRect(x, y, width, height));
953}
954
960{
961 QWriteLocker updateLocker(&d->m_updateLock);
963 SetRedraw();
964}
965
969bool MythUIImage::Load(bool allowLoadInBackground, bool forceStat)
970{
971 d->m_updateLock.lockForRead();
972
974
975 QString bFilename = m_imageProperties.m_filename;
976
977 d->m_updateLock.unlock();
978
979 QString filename = bFilename;
980
981 if (bFilename.isEmpty())
982 {
983 Clear();
985 SetRedraw();
986
987 return false;
988 }
989
990 if (qEnvironmentVariableIsSet("DISABLETHREADEDMYTHUIIMAGE"))
991 allowLoadInBackground = false;
992
993 // Don't clear the widget before we need to, otherwise it causes
994 // unsightly flashing. We exclude animations for now since that requires a
995 // deeper fix
996 bool isAnimation = (m_highNum != m_lowNum) || m_animatedImage;
997
998 if (isAnimation)
999 Clear();
1000
1001 bool complete = true;
1002
1003 QString imagelabel;
1004
1005 int j = 0;
1006
1007 for (int i = m_lowNum; i <= m_highNum && !m_animatedImage; i++)
1008 {
1009 if (!m_animatedImage && m_highNum != m_lowNum &&
1010 bFilename.contains("%1"))
1011 filename = bFilename.arg(i);
1012
1014 imProps.m_filename = filename;
1015 imagelabel = ImageLoader::GenImageLabel(imProps);
1016
1017 // Only load in the background if allowed and the image is
1018 // not already in our mem cache
1019 int cacheMode = kCacheIgnoreDisk;
1020
1021 if (forceStat)
1022 cacheMode |= (int)kCacheForceStat;
1023
1024 int cacheMode2 = kCacheNormal;
1025
1026 if (forceStat)
1027 cacheMode2 |= (int)kCacheForceStat;
1028
1029 bool do_background_load = false;
1030 if (allowLoadInBackground)
1031 {
1033 filename, imagelabel, GetPainter(),
1034 static_cast<ImageCacheMode>(cacheMode));
1035 if (img)
1036 img->DecrRef();
1037 else
1038 do_background_load = true;
1039 }
1040
1041 if (do_background_load)
1042 {
1044 LOG(VB_GUI | VB_FILE, LOG_DEBUG, LOC +
1045 QString("Load(), spawning thread to load '%1'").arg(filename));
1046
1048 auto *bImgThread = new ImageLoadThread(this, GetPainter(),
1049 imProps, 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
1064 {
1065 AnimationFrames *myFrames =
1067 static_cast<ImageCacheMode>(cacheMode2),
1068 this, aborted);
1069
1070 // TODO We might want to handle an abort here more gracefully
1071 if (aborted)
1072 {
1073 LOG(VB_GUI, LOG_DEBUG, QString("Aborted loading animated"
1074 "image %1 in foreground")
1075 .arg(filename));
1076 }
1077
1078 SetAnimationFrames(*myFrames);
1079
1080 delete myFrames;
1081 }
1082 else
1083 {
1084 MythImage *image = nullptr;
1085
1087 imProps,
1088 static_cast<ImageCacheMode>(cacheMode2),
1089 this, aborted);
1090
1091 // TODO We might want to handle an abort here more gracefully
1092 if (aborted)
1093 {
1094 LOG(VB_GUI, LOG_DEBUG, QString("Aborted loading animated"
1095 "image %1 in foreground")
1096 .arg(filename));
1097 }
1098
1099 if (image)
1100 {
1101 if (m_imageProperties.m_forceSize.isNull())
1102 SetSize(image->size());
1103
1104 MythRect rect(GetFullArea());
1105 rect.setSize(image->size());
1106 SetMinArea(rect);
1107
1108 m_imagesLock.lock();
1109 m_images[j] = image;
1110 m_imagesLock.unlock();
1111
1112 SetRedraw();
1113 d->m_updateLock.lockForWrite();
1114 m_lastDisplay = QTime::currentTime();
1115 d->m_updateLock.unlock();
1116 }
1117 else
1118 {
1119 Reset();
1120
1121 m_imagesLock.lock();
1122 m_images[j] = nullptr;
1123 m_imagesLock.unlock();
1124 }
1125 }
1126 }
1127
1128 ++j;
1129
1130 // Load is complete if no image is loading in background
1131 complete &= !do_background_load;
1132 }
1133
1134 if (complete)
1135 emit LoadComplete();
1136
1137 return true;
1138}
1139
1144{
1145 d->m_updateLock.lockForWrite();
1146
1147 auto delay = -1ms;
1148
1149 if (m_delays.contains(m_curPos))
1150 delay = m_delays[m_curPos];
1151 else if (m_delay > 0ms)
1152 delay = m_delay;
1153
1154 if (delay > 0ms &&
1155 abs(m_lastDisplay.msecsTo(QTime::currentTime())) > delay.count())
1156 {
1158 {
1160 d->m_updateLock.unlock();
1161 Load();
1162 d->m_updateLock.lockForWrite();
1163 }
1164 else
1165 {
1166 m_imagesLock.lock();
1167
1169 {
1170 ++m_curPos;
1171
1172 if (m_curPos >= (uint)m_images.size())
1173 m_curPos = 0;
1174 }
1175 else if (m_animationCycle == kCycleReverse)
1176 {
1177 if ((m_curPos + 1) >= (uint)m_images.size())
1178 {
1179 m_animationReverse = true;
1180 }
1181 else if (m_curPos == 0)
1182 {
1183 m_animationReverse = false;
1184 }
1185
1187 --m_curPos;
1188 else
1189 ++m_curPos;
1190 }
1191
1192 m_imagesLock.unlock();
1193
1194 SetRedraw();
1195 }
1196
1197 m_lastDisplay = QTime::currentTime();
1198 }
1199
1201
1202 d->m_updateLock.unlock();
1203}
1204
1208void MythUIImage::DrawSelf(MythPainter *p, int xoffset, int yoffset,
1209 int alphaMod, QRect clipRect)
1210{
1211 m_imagesLock.lock();
1212
1213 if (!m_images.empty())
1214 {
1215 d->m_updateLock.lockForWrite();
1216
1217 if (m_curPos >= (uint)m_images.size())
1218 m_curPos = 0;
1219
1220 if (!m_images[m_curPos])
1221 {
1222 unsigned int origPos = m_curPos;
1223 m_curPos++;
1224
1225 while (!m_images[m_curPos] && m_curPos != origPos)
1226 {
1227 m_curPos++;
1228
1229 if (m_curPos >= (uint)m_images.size())
1230 m_curPos = 0;
1231 }
1232 }
1233
1234 QRect area = GetArea().toQRect();
1235 area.translate(xoffset, yoffset);
1236
1237 int alpha = CalcAlpha(alphaMod);
1238
1239 MythImage *currentImage = m_images[m_curPos];
1240
1241 if (currentImage)
1242 currentImage->IncrRef();
1243
1244 m_imagesLock.unlock();
1245 d->m_updateLock.unlock();
1246
1247 if (!currentImage)
1248 return;
1249
1250 d->m_updateLock.lockForRead();
1251
1252 QRect currentImageArea = currentImage->rect();
1253
1254 if (!m_imageProperties.m_forceSize.isNull())
1255 area.setSize(area.size().expandedTo(currentImage->size()));
1256
1257 // Centre image in available space, accounting for zoom
1258 int x = 0;
1259 int y = 0;
1260 QRect visibleImage = m_effects.GetExtent(currentImageArea.size());
1261
1262 if (area.width() > visibleImage.width())
1263 x = (area.width() / 2) + visibleImage.topLeft().x();
1264
1265 if (area.height() > visibleImage.height())
1266 y = (area.height() / 2) + visibleImage.topLeft().y();
1267
1268 if ((x > 0 || y > 0))
1269 area.translate(x, y);
1270
1271 QRect srcRect;
1273
1274 if (!m_imageProperties.m_cropRect.isEmpty())
1276 else
1277 srcRect = currentImageArea;
1278
1279 p->SetClipRect(clipRect);
1280 p->DrawImage(area, currentImage, srcRect, alpha);
1281 currentImage->DecrRef();
1282 d->m_updateLock.unlock();
1283 }
1284 else
1285 {
1286 m_imagesLock.unlock();
1287 }
1288}
1289
1294 const QString &filename, QDomElement &element, bool showWarnings)
1295{
1296 QWriteLocker updateLocker(&d->m_updateLock);
1297
1298 if (element.tagName() == "filename")
1299 {
1300 m_imageProperties.m_isThemeImage = true; // This is an image distributed with the theme
1302
1303 if (m_imageProperties.m_filename.endsWith('/'))
1304 {
1305 m_showingRandomImage = true;
1307
1309 }
1310 }
1311 else if (element.tagName() == "filepattern")
1312 {
1313 m_imageProperties.m_isThemeImage = true; // This is an image distributed with the theme
1315 QString tmp = element.attribute("low");
1316
1317 if (!tmp.isEmpty())
1318 m_lowNum = tmp.toInt();
1319
1320 tmp = element.attribute("high");
1321
1322 if (!tmp.isEmpty())
1323 m_highNum = tmp.toInt();
1324
1325 tmp = element.attribute("cycle", "start");
1326
1327 if (tmp == "reverse")
1329 }
1330 else if (element.tagName() == "area")
1331 {
1332 SetArea(parseRect(element));
1334 }
1335 else if (element.tagName() == "preserveaspect")
1336 {
1338 }
1339 else if (element.tagName() == "crop")
1340 {
1342 }
1343 else if (element.tagName() == "delay")
1344 {
1345 QString value = getFirstText(element);
1346
1347 if (value.contains(","))
1348 {
1349 QVector<std::chrono::milliseconds> delays;
1350 QStringList tokens = value.split(",");
1351 for (const auto & token : std::as_const(tokens))
1352 {
1353 if (token.isEmpty())
1354 {
1355 if (!delays.empty())
1356 delays.append(delays[delays.size()-1]);
1357 else
1358 delays.append(0ms); // Default delay before first image
1359 }
1360 else
1361 {
1362 delays.append(std::chrono::milliseconds(token.toInt()));
1363 }
1364 }
1365
1366 if (!delays.empty())
1367 {
1368 m_delay = delays[0];
1369 SetDelays(delays);
1370 }
1371 }
1372 else
1373 {
1374 m_delay = std::chrono::milliseconds(value.toInt());
1375 }
1376 }
1377 else if (element.tagName() == "reflection")
1378 {
1380 QString tmp = element.attribute("axis");
1381
1382 if (!tmp.isEmpty())
1383 {
1384 if (tmp.toLower() == "horizontal")
1386 else
1388 }
1389
1390 tmp = element.attribute("shear");
1391
1392 if (!tmp.isEmpty())
1393 m_imageProperties.m_reflectShear = tmp.toInt();
1394
1395 tmp = element.attribute("scale");
1396
1397 if (!tmp.isEmpty())
1398 m_imageProperties.m_reflectScale = tmp.toInt();
1399
1400 tmp = element.attribute("length");
1401
1402 if (!tmp.isEmpty())
1403 m_imageProperties.m_reflectLength = tmp.toInt();
1404
1405 tmp = element.attribute("spacing");
1406
1407 if (!tmp.isEmpty())
1408 m_imageProperties.m_reflectSpacing = tmp.toInt();
1409 }
1410 else if (element.tagName() == "mask")
1411 {
1414 }
1415 else if (element.tagName() == "grayscale" ||
1416 element.tagName() == "greyscale")
1417 {
1419 }
1420 else
1421 {
1422 return MythUIType::ParseElement(filename, element, showWarnings);
1423 }
1424
1425 m_needLoad = true;
1426
1427 if (m_parent && m_parent->IsDeferredLoading(true))
1428 m_needLoad = false;
1429
1430 return true;
1431}
1432
1437{
1438 d->m_updateLock.lockForWrite();
1439 auto *im = dynamic_cast<MythUIImage *>(base);
1440 if (!im)
1441 {
1442 LOG(VB_GENERAL, LOG_ERR,
1443 QString("'%1' (%2) ERROR, bad parsing '%3' (%4)")
1444 .arg(objectName(), GetXMLLocation(),
1445 base->objectName(), base->GetXMLLocation()));
1446 d->m_updateLock.unlock();
1447 return;
1448 }
1449
1450 m_origFilename = im->m_origFilename;
1451
1452 m_delay = im->m_delay;
1453 m_lowNum = im->m_lowNum;
1454 m_highNum = im->m_highNum;
1455
1456 m_lastDisplay = QTime::currentTime();
1457 m_curPos = 0;
1458
1459 m_imageProperties = im->m_imageProperties;
1460
1461 m_animationCycle = im->m_animationCycle;
1462 m_animatedImage = im->m_animatedImage;
1463
1464 m_showingRandomImage = im->m_showingRandomImage;
1465 m_imageDirectory = im->m_imageDirectory;
1466
1468
1469 // We need to update forceSize in case the parent area has changed
1470 // however we only want to set forceSize if it was previously in use
1471 if (!m_imageProperties.m_forceSize.isNull())
1473
1474 m_needLoad = im->m_needLoad;
1475
1476 d->m_updateLock.unlock();
1477
1478 d->m_updateLock.lockForRead();
1479
1480 if (m_needLoad)
1481 {
1482 d->m_updateLock.unlock();
1483 Load();
1484 }
1485 else
1486 {
1487 d->m_updateLock.unlock();
1488 }
1489}
1490
1495{
1496 QReadLocker updateLocker(&d->m_updateLock);
1497 auto *im = new MythUIImage(parent, objectName());
1498 im->CopyFrom(this);
1499}
1500
1505{
1506 d->m_updateLock.lockForRead();
1507
1508 if (m_needLoad)
1509 {
1510 d->m_updateLock.unlock();
1511 Load();
1512 }
1513 else
1514 {
1515 d->m_updateLock.unlock();
1516 }
1517
1519}
1520
1525{
1526 d->m_updateLock.lockForWrite();
1527
1528 if (m_needLoad)
1529 {
1530 d->m_updateLock.unlock();
1531 return;
1532 }
1533
1534 m_needLoad = true;
1535 d->m_updateLock.unlock();
1536
1537 Load(false);
1538
1540}
1541
1545void MythUIImage::customEvent(QEvent *event)
1546{
1547 if (event->type() == ImageLoadEvent::kEventType)
1548 {
1549 auto * le = dynamic_cast<ImageLoadEvent *>(event);
1550 if (!le || le->GetParent() != this)
1551 return;
1552
1553 MythImage *image = le->GetImage();
1554 int number = le->GetNumber();
1555 QString filename = le->GetFilename();
1556 AnimationFrames *animationFrames = le->GetAnimationFrames();
1557 bool aborted = le->GetAbortState();
1558
1560
1561 d->m_updateLock.lockForRead();
1562 QString propFilename = m_imageProperties.m_filename;
1563 d->m_updateLock.unlock();
1564
1565 // 1) We aborted loading the image for some reason (e.g. two requests
1566 // for same image)
1567 // 2) Filename changed since we started this image, so abort to avoid
1568 // rendering two different images in quick succession which causes
1569 // unsightly flickering
1570 if (aborted || (le->GetBasefile() != propFilename))
1571 {
1572 if (aborted)
1573 LOG(VB_GUI, LOG_DEBUG, QString("Aborted loading image %1")
1574 .arg(filename));
1575
1576 if (image)
1577 image->DecrRef();
1578
1579 if (animationFrames)
1580 {
1581 for (const auto & frame : std::as_const(*animationFrames))
1582 {
1583 MythImage *im = frame.first;
1584 if (im)
1585 im->DecrRef();
1586 }
1587
1588 delete animationFrames;
1589 }
1590 }
1591 else if (animationFrames)
1592 {
1593 SetAnimationFrames(*animationFrames);
1594
1595 delete animationFrames;
1596 }
1597 else if (image)
1598 {
1599 // We don't clear until we have the new image ready to display to
1600 // avoid unsightly flashing. This isn't currently supported for
1601 // animations.
1602 if ((m_highNum == m_lowNum) && !m_animatedImage)
1603 Clear();
1604
1605 d->m_updateLock.lockForWrite();
1606
1607 if (m_imageProperties.m_forceSize.isNull())
1608 SetSize(image->size());
1609
1610 MythRect rect(GetFullArea());
1611 rect.setSize(image->size());
1612 SetMinArea(rect);
1613
1614 d->m_updateLock.unlock();
1615
1616 m_imagesLock.lock();
1617
1618 if (m_images[number])
1619 {
1620 // If we got to this point, it means this same MythUIImage
1621 // was told to reload the same image, so we use the newest
1622 // copy of the image.
1623 m_images[number]->DecrRef(); // delete the original
1624 }
1625
1626 m_images[number] = image;
1627 m_imagesLock.unlock();
1628
1629 SetRedraw();
1630
1631 d->m_updateLock.lockForWrite();
1632 m_lastDisplay = QTime::currentTime();
1633 d->m_updateLock.unlock();
1634 }
1635 else
1636 {
1637 // No Images were loaded, so trigger Reset to default
1638 Reset();
1639 }
1640
1641 // NOLINTNEXTLINE(readability-misleading-indentation)
1642 emit LoadComplete();
1643 }
1644}
1645
1647{
1648 QString randFile;
1649
1650 // find and save the list of available images
1651 if (m_imageList.isEmpty())
1652 {
1653 QDir imageDir(m_imageDirectory);
1654
1655 if (!imageDir.exists())
1656 {
1657 QString themeDir = GetMythUI()->GetThemeDir() + '/';
1658 imageDir.setPath(themeDir + m_imageDirectory);
1659 }
1660
1661 QStringList imageTypes;
1662
1663 QList< QByteArray > exts = QImageReader::supportedImageFormats();
1664 imageTypes.reserve(exts.size());
1665 for (const auto & ext : std::as_const(exts))
1666 {
1667 imageTypes.append(QString("*.").append(ext));
1668 }
1669
1670 imageDir.setNameFilters(imageTypes);
1671
1672 m_imageList = imageDir.entryList();
1673
1674 if (m_imageList.empty())
1675 {
1677 return;
1678 }
1679
1680 // randomly shuffle the images
1681 std::random_device rd;
1682 std::mt19937 g(rd());
1683 std::shuffle(m_imageList.begin(), m_imageList.end(), g);
1684 m_imageListIndex = 0;
1685 randFile = QString("%1%2").arg(m_imageDirectory, m_imageList.at(m_imageListIndex));
1686 }
1687 else
1688 {
1689 if (!m_imageList.empty())
1690 {
1692
1693 // if we are at the last image in the list re-shuffle the list and start from the beginning
1694 if (m_imageListIndex == m_imageList.size())
1695 {
1696 std::random_device rd;
1697 std::mt19937 g(rd());
1698 std::shuffle(m_imageList.begin(), m_imageList.end(), g);
1699 m_imageListIndex = 0;
1700 }
1701
1702 // make sure we don't show the same image again in the unlikely event the re-shuffle shows the same image again
1703 if (m_imageList.at(m_imageListIndex) == m_origFilename && m_imageList.size() > 1)
1705
1706 randFile = QString("%1%2").arg(m_imageDirectory, m_imageList.at(m_imageListIndex));
1707 }
1708 }
1709
1711}
1712
1713#include "moc_mythuiimage.cpp"
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:42
ImageProperties()=default
void SetMaskImageFilename(const QString &filename)
Definition: mythuiimage.h:34
MythRect m_cropRect
Definition: mythuiimage.h:61
ReflectAxis m_reflectAxis
Definition: mythuiimage.h:70
MythImage * m_maskImage
Definition: mythuiimage.h:84
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:38
QString m_maskImageFilename
Definition: mythuiimage.h:85
QImage GetMaskImageSubset(QRect imageArea)
Definition: mythuiimage.h:49
QString m_filename
Definition: mythuiimage.h:59
bool m_preserveAspect
Definition: mythuiimage.h:64
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:99
MythUIImagePrivate * d
Definition: mythuiimage.h:192
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 SetSize(QSize size) override
Set the size of the widget.
void ForceSize(QSize size)
Force the dimensions of the widget and image to the given size.
QHash< int, MythImage * > m_images
Definition: mythuiimage.h:170
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:172
unsigned int m_curPos
Definition: mythuiimage.h:178
QString m_imageDirectory
Definition: mythuiimage.h:188
std::chrono::milliseconds m_delay
Definition: mythuiimage.h:174
friend class ImageLoadThread
Definition: mythuiimage.h:205
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:195
bool m_animatedImage
Definition: mythuiimage.h:197
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:168
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:179
int m_runningThreads
Definition: mythuiimage.h:185
bool m_showingRandomImage
Definition: mythuiimage.h:187
void Clear(void)
Remove all images from the widget.
bool m_animationReverse
Definition: mythuiimage.h:196
void SetDelay(std::chrono::milliseconds delayms)
Set the delay between each image in an animation.
ImageProperties m_imageProperties
Definition: mythuiimage.h:183
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:171
void Pulse(void) override
Pulse is called 70 times a second to trigger a single frame of an animation.
QStringList m_imageList
Definition: mythuiimage.h:189
bool m_needLoad
Definition: mythuiimage.h:181
int m_imageListIndex
Definition: mythuiimage.h:190
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:97
bool m_enableInitiator
Definition: mythuitype.h:278
virtual void SetSize(QSize size)
Definition: mythuitype.cpp:555
bool m_initiator
Definition: mythuitype.h:279
UIEffects m_effects
Definition: mythuitype.h:295
virtual MythPainter * GetPainter(void)
QString GetXMLLocation(void) const
Definition: mythuitype.h:193
virtual void SetArea(const MythRect &rect)
Definition: mythuitype.cpp:596
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:299
virtual void SetMinArea(const MythRect &rect)
Set the minimum area based on the given size.
Definition: mythuitype.cpp:806
virtual void Pulse(void)
Pulse is called 70 times a second to trigger a single frame of an animation.
Definition: mythuitype.cpp:442
virtual MythRect GetArea(void) const
If the object has a minimum area defined, return it, other wise return the default area.
Definition: mythuitype.cpp:871
bool IsDeferredLoading(bool recurse=false) const
MythUIType * m_parent
Definition: mythuitype.h:308
virtual MythRect GetFullArea(void) const
Definition: mythuitype.cpp:879
int CalcAlpha(int alphamod) const
Definition: mythuitype.cpp:460
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:71
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:288
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 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:88
QVector< AnimationFrame > AnimationFrames
Definition: mythuiimage.h:89
ImageCacheMode
@ kCacheNormal
@ kCacheForceStat
@ kCacheIgnoreDisk