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#ifdef _MSC_VER
26# include "libmythbase/compat.h" // random
27#endif
28
29// Mythui
30#include "mythpainter.h"
31#include "mythmainwindow.h"
32#include "mythuihelper.h"
33#include "mythscreentype.h"
34
35class ImageLoadThread;
36
37#define LOC QString("MythUIImage(0x%1): ").arg((uint64_t)this,0,16)
38
40
42{
43 Copy(other);
44}
45
47{
48 if (this == &other)
49 return *this;
50
51 Copy(other);
52
53 return *this;
54}
55
57{
58 if (m_maskImage)
60}
61
62// The m_maskImage field is assigned in the call to SetMaskImage().
63//
64// cppcheck-suppress operatorEqVarError
66{
67 m_filename = other.m_filename;
68
69 m_cropRect = other.m_cropRect;
71
76
83
85
87 m_isMasked = other.m_isMasked;
89}
90
92{
93 if (image)
94 image->IncrRef();
95 if (m_maskImage)
97
98 m_maskImage = image;
100}
101
106{
107 public:
108 ImageLoader() = default;
109 ~ImageLoader() = default;
110
111 static QHash<QString, const MythUIImage *> m_loadingImages;
112 static QMutex m_loadingImagesLock;
113 static QWaitCondition m_loadingImagesCond;
114
115 static bool PreLoad(const QString &cacheKey, const MythUIImage *uitype)
116 {
117 m_loadingImagesLock.lock();
118
119 // Check to see if the image is being loaded by us in another thread
120 if ((m_loadingImages.contains(cacheKey)) &&
121 (m_loadingImages[cacheKey] == uitype))
122 {
123 LOG(VB_GUI | VB_FILE, LOG_DEBUG,
124 QString("ImageLoader::PreLoad(%1), this "
125 "file is already being loaded by this same MythUIImage "
126 "in another thread.").arg(cacheKey));
127 m_loadingImagesLock.unlock();
128 return false;
129 }
130
131 // Check to see if the exact same image is being loaded anywhere else
132 while (m_loadingImages.contains(cacheKey))
134
135 m_loadingImages[cacheKey] = uitype;
136 m_loadingImagesLock.unlock();
137
138 return true;
139 }
140
141 static void PostLoad(const QString &cacheKey)
142 {
143 m_loadingImagesLock.lock();
144 m_loadingImages.remove(cacheKey);
145 m_loadingImagesCond.wakeAll();
146 m_loadingImagesLock.unlock();
147 }
148
149 static bool SupportsAnimation(const QString &filename)
150 {
151 QString extension = filename.section('.', -1);
152 return !filename.startsWith("myth://") &&
153 (extension == "gif" ||
154 extension == "apng" ||
155 extension == "mng");
156 }
157
162 static QString GenImageLabel(const ImageProperties &imProps)
163 {
164 QString imagelabel;
165 QString s_Attrib;
166
167 if (imProps.m_isMasked)
168 s_Attrib = "masked";
169
170 if (imProps.m_isReflected)
171 s_Attrib += "reflected";
172
173 if (imProps.m_isGreyscale)
174 s_Attrib += "greyscale";
175
176 if (imProps.m_isOriented)
177 {
178 s_Attrib += "orientation";
179 s_Attrib += QString("%1").arg(imProps.m_orientation);
180 }
181
182 int w = -1;
183 int h = -1;
184 if (!imProps.m_forceSize.isNull())
185 {
186 if (imProps.m_forceSize.width() != -1)
187 w = imProps.m_forceSize.width();
188
189 if (imProps.m_forceSize.height() != -1)
190 h = imProps.m_forceSize.height();
191 }
192
193
194 imagelabel = QString("%1-%2-%3x%4.png")
195 .arg(imProps.m_filename,
196 s_Attrib)
197 .arg(w)
198 .arg(h);
199 imagelabel.replace('/', '-');
200#ifdef Q_OS_ANDROID
201 imagelabel.replace(':', '-');
202#endif
203
204 return imagelabel;
205 }
206
208 // Must be a copy for thread safety
209 ImageProperties imProps,
210 ImageCacheMode cacheMode,
211 // Included only to check address, could be
212 // replaced by generating a unique value for
213 // each MythUIImage object?
214 const MythUIImage *parent,
215 bool &aborted,
216 MythImageReader *imageReader = nullptr)
217 {
218 QString cacheKey = GenImageLabel(imProps);
219 if (!PreLoad(cacheKey, parent))
220 {
221 aborted = true;
222 return nullptr;
223 }
224
225 QString filename = imProps.m_filename;
226 MythImage *image = nullptr;
227
228 bool bResize = false;
229 bool bFoundInCache = false;
230
231 int w = -1;
232 int h = -1;
233
234 if (!imProps.m_forceSize.isNull())
235 {
236 if (imProps.m_forceSize.width() != -1)
237 w = imProps.m_forceSize.width();
238
239 if (imProps.m_forceSize.height() != -1)
240 h = imProps.m_forceSize.height();
241
242 bResize = true;
243 }
244
245 if (!imageReader)
246 {
247 image = GetMythUI()->LoadCacheImage(filename, cacheKey,
248 painter, cacheMode);
249 }
250
251 if (image)
252 {
253 if (VERBOSE_LEVEL_CHECK(VB_GUI | VB_FILE, LOG_INFO))
254 {
255 image->IncrRef();
256 int cnt = image->DecrRef();
257 LOG(VB_GUI | VB_FILE, LOG_INFO,
258 QString("ImageLoader::LoadImage(%1) Found in cache, "
259 "RefCount = %2")
260 .arg(cacheKey).arg(cnt));
261 }
262
263 if (imProps.m_isReflected)
264 image->setIsReflected(true);
265
266 if (imProps.m_isOriented)
267 image->setIsOriented(true);
268
269 bFoundInCache = true;
270 }
271 else
272 {
273 LOG(VB_GUI | VB_FILE, LOG_INFO,
274 QString("ImageLoader::LoadImage(%1) NOT Found in cache. "
275 "Loading Directly").arg(cacheKey));
276
277 image = painter->GetFormatImage();
278 bool ok = false;
279
280 if (imageReader)
281 ok = image->Load(imageReader);
282 else
283 ok = image->Load(filename);
284
285 if (!ok)
286 {
287 image->DecrRef();
288 image = nullptr;
289 }
290 }
291
292 if (image && image->isNull())
293 {
294 LOG(VB_GUI | VB_FILE, LOG_INFO,
295 QString("ImageLoader::LoadImage(%1) Image is NULL")
296 .arg(filename));
297
298 image->DecrRef();
299 image = nullptr;
300 }
301
302 if (image && !bFoundInCache)
303 {
304 if (imProps.m_isReflected)
305 {
306 image->Reflect(imProps.m_reflectAxis, imProps.m_reflectShear,
307 imProps.m_reflectScale, imProps.m_reflectLength,
308 imProps.m_reflectSpacing);
309 }
310
311 if (imProps.m_isGreyscale)
312 image->ToGreyscale();
313
314 if (imProps.m_isOriented)
315 image->Orientation(imProps.m_orientation);
316
317 // Even if an explicit size wasn't defined this image may still need
318 // to be scaled because of a difference between the theme resolution
319 // and the screen resolution. We want to avoid scaling twice.
320 if (!bResize && imProps.m_isThemeImage)
321 {
322 float wmult = NAN; // Width multipler
323 float hmult = NAN; // Height multipler
324 GetMythMainWindow()->GetScalingFactors(wmult, hmult);
325 if (wmult != 1.0F || hmult != 1.0F)
326 {
327 w = image->size().width() * wmult;
328 h = image->size().height() * hmult;
329 bResize = true;
330 }
331 }
332
333 if (bResize)
334 image->Resize(QSize(w, h), imProps.m_preserveAspect);
335
336 if (imProps.m_isMasked)
337 {
338 MythImage *newMaskImage = painter->GetFormatImage();
339 if (newMaskImage->Load(imProps.GetMaskImageFilename()))
340 {
341 float wmult = NAN; // Width multipler
342 float hmult = NAN; // Height multipler
343 GetMythMainWindow()->GetScalingFactors(wmult, hmult);
344 if (wmult != 1.0F || hmult != 1.0F)
345 {
346 int width = newMaskImage->size().width() * wmult;
347 int height = newMaskImage->size().height() * hmult;
348 newMaskImage->Resize(QSize(width, height));
349 }
350
351 imProps.SetMaskImage(newMaskImage);
352 }
353 else
354 {
355 imProps.SetMaskImage(nullptr);
356 }
357 newMaskImage->DecrRef();
358
359 QRect imageArea = image->rect();
360 QRect maskArea = imProps.GetMaskImageRect();
361
362 // Crop the mask to the image
363 int x = 0;
364 int y = 0;
365
366 if (maskArea.width() > imageArea.width())
367 x = (maskArea.width() - imageArea.width()) / 2;
368
369 if (maskArea.height() > imageArea.height())
370 y = (maskArea.height() - imageArea.height()) / 2;
371
372 if (x > 0 || y > 0)
373 imageArea.translate(x, y);
374
375 QImage mask = imProps.GetMaskImageSubset(imageArea);
376 image->setAlphaChannel(mask.convertToFormat(QImage::Format_Alpha8));
377 }
378
379 if (!imageReader)
380 GetMythUI()->CacheImage(cacheKey, image);
381 }
382
383 if (image)
384 image->SetChanged();
385
386 PostLoad(cacheKey);
387
388 return image;
389 }
390
392 // Must be a copy for thread safety
393 const ImageProperties& imProps,
394 ImageCacheMode cacheMode,
395 // Included only to check address, could be
396 // replaced by generating a unique value for
397 // each MythUIImage object?
398 const MythUIImage *parent,
399 bool &aborted)
400 {
401 QString filename = QString("frame-%1-") + imProps.m_filename;
402 QString frameFilename;
403 int imageCount = 1;
404
405 auto *imageReader = new MythImageReader(imProps.m_filename);
406 auto *images = new AnimationFrames();
407
408 while (imageReader->canRead() && !aborted)
409 {
410 frameFilename = filename.arg(imageCount);
411
412 ImageProperties frameProps = imProps;
413 frameProps.m_filename = frameFilename;
414
415 MythImage *im = LoadImage(painter, frameProps, cacheMode, parent,
416 aborted, imageReader);
417
418 if (!im)
419 aborted = true;
420
421 images->append(AnimationFrame(im, std::chrono::milliseconds(imageReader->nextImageDelay())));
422 imageCount++;
423 }
424
425 delete imageReader;
426
427 return images;
428 }
429
430};
431
432QHash<QString, const MythUIImage *> ImageLoader::m_loadingImages;
435
439class ImageLoadEvent : public QEvent
440{
441 public:
442 ImageLoadEvent(const MythUIImage *parent, MythImage *image,
443 QString basefile, QString filename,
444 int number, bool aborted)
445 : QEvent(kEventType),
446 m_parent(parent), m_image(image), m_basefile(std::move(basefile)),
447 m_filename(std::move(filename)), m_number(number),
448 m_aborted(aborted) { }
449
451 QString basefile,
452 QString filename, bool aborted)
453 : QEvent(kEventType),
454 m_parent(parent), m_basefile(std::move(basefile)),
455 m_filename(std::move(filename)),
456 m_images(frames), m_aborted(aborted) { }
457
458 const MythUIImage *GetParent() const { return m_parent; }
459 MythImage *GetImage() const { return m_image; }
460 QString GetBasefile() const { return m_basefile; }
461 QString GetFilename() const { return m_filename; }
462 int GetNumber() const { return m_number; }
464 bool GetAbortState() const { return m_aborted; }
465
466 static const Type kEventType;
467
468 private:
469 const MythUIImage *m_parent {nullptr};
470 MythImage *m_image {nullptr};
471 QString m_basefile;
472 QString m_filename;
473 int m_number {0};
474
475 // Animated Images
477
478 // Image Load
480};
481
482const QEvent::Type ImageLoadEvent::kEventType =
483 (QEvent::Type) QEvent::registerEventType();
484
488class ImageLoadThread : public QRunnable
489{
490 public:
492 const ImageProperties &imProps, QString basefile,
493 int number, ImageCacheMode mode) :
494 m_parent(parent), m_painter(painter), m_imageProperties(imProps),
495 m_basefile(std::move(basefile)), m_number(number), m_cacheMode(mode)
496 {
497 }
498
499 void run() override // QRunnable
500 {
501 bool aborted = false;
503
504 // NOTE Do NOT use MythImageReader::supportsAnimation here, it defeats
505 // the point of caching remote images
507 {
508 AnimationFrames *frames =
512 aborted);
513
514 if (frames && frames->count() > 1)
515 {
516 auto *le = new ImageLoadEvent(m_parent, frames, m_basefile,
518 aborted);
519 QCoreApplication::postEvent(m_parent, le);
520
521 return;
522 }
523 delete frames;
524 }
525
529 aborted);
530
531 auto *le = new ImageLoadEvent(m_parent, image, m_basefile,
533 m_number, aborted);
534 QCoreApplication::postEvent(m_parent, le);
535 }
536
537private:
541 QString m_basefile;
544};
545
548{
549public:
551 : m_parent(p) { }
553
555
556 QReadWriteLock m_updateLock {QReadWriteLock::Recursive};
557};
558
560
561MythUIImage::MythUIImage(const QString &filepattern,
562 int low, int high, std::chrono::milliseconds delay,
563 MythUIType *parent, const QString &name)
564 : MythUIType(parent, name),
565 m_delay(delay),
566 m_lowNum(low),
567 m_highNum(high),
568 d(new MythUIImagePrivate(this))
569{
570 m_imageProperties.m_filename = filepattern;
571
572 m_enableInitiator = true;
573
574 emit DependChanged(false);
575}
576
578 const QString &name)
579 : MythUIType(parent, name),
580 m_origFilename(filename),
581 m_delay(-1ms),
582 d(new MythUIImagePrivate(this))
583{
585
586 m_enableInitiator = true;
587
588 emit DependChanged(false);
589}
590
591MythUIImage::MythUIImage(MythUIType *parent, const QString &name)
592 : MythUIType(parent, name),
593 m_delay(-1ms),
594 d(new MythUIImagePrivate(this))
595{
596 m_enableInitiator = true;
597}
598
600{
601 // Wait until all image loading threads are complete or bad things
602 // may happen if this MythUIImage disappears when a queued thread
603 // needs it.
604 if (m_runningThreads > 0)
605 {
607 }
608
609 Clear();
610
611 delete d;
612}
613
618{
619 QWriteLocker updateLocker(&d->m_updateLock);
620 QMutexLocker locker(&m_imagesLock);
621
622 for (auto it = m_images.begin();
623 it != m_images.end();
624 it = m_images.erase(it))
625 {
626 if (*it)
627 (*it)->DecrRef();
628 }
629
630 m_delays.clear();
631
632 if (m_animatedImage)
633 {
634 m_lowNum = 0;
635 m_highNum = 0;
636 m_animatedImage = false;
637 }
638}
639
644{
645 d->m_updateLock.lockForWrite();
646
648
650 {
653
654 if (m_animatedImage)
655 {
656 m_lowNum = 0;
657 m_highNum = 0;
658 m_animatedImage = false;
659 }
660 emit DependChanged(true);
661
662 d->m_updateLock.unlock();
663 Load();
664 }
665 else
666 {
667 d->m_updateLock.unlock();
668 }
669
671}
672
677{
678 QWriteLocker updateLocker(&d->m_updateLock);
682 emit DependChanged(true);
683 else
684 emit DependChanged(false);
685}
686
691void MythUIImage::SetFilepattern(const QString &filepattern, int low,
692 int high)
693{
694 QWriteLocker updateLocker(&d->m_updateLock);
696 m_imageProperties.m_filename = filepattern;
697 m_lowNum = low;
698 m_highNum = high;
699 if (filepattern == m_origFilename)
700 emit DependChanged(true);
701 else
702 emit DependChanged(false);
703}
704
708void MythUIImage::SetImageCount(int low, int high)
709{
710 QWriteLocker updateLocker(&d->m_updateLock);
711 m_lowNum = low;
712 m_highNum = high;
713}
714
718void MythUIImage::SetDelay(std::chrono::milliseconds delay)
719{
720 QWriteLocker updateLocker(&d->m_updateLock);
721 m_delay = delay;
722 m_lastDisplay = QTime::currentTime();
723 m_curPos = 0;
724}
725
729void MythUIImage::SetDelays(const QVector<std::chrono::milliseconds>& delays)
730{
731 QWriteLocker updateLocker(&d->m_updateLock);
732 QMutexLocker imageLocker(&m_imagesLock);
733
734 for (std::chrono::milliseconds delay : std::as_const(delays))
735 m_delays[m_delays.size()] = delay;
736
737 if (m_delay == -1ms)
738 m_delay = m_delays[0];
739
740 m_lastDisplay = QTime::currentTime();
741 m_curPos = 0;
742}
743
749{
750 d->m_updateLock.lockForWrite();
751
752 if (!img)
753 {
754 d->m_updateLock.unlock();
755 Reset();
756 return;
757 }
758
761
762 img->IncrRef();
763
764 QSize forceSize = m_imageProperties.m_forceSize;
765 if (!forceSize.isNull())
766 {
767 int w = (forceSize.width() <= 0) ? img->width() : forceSize.width();
768 int h = (forceSize.height() <= 0) ? img->height() : forceSize.height();
769 img->Resize(QSize(w, h), m_imageProperties.m_preserveAspect);
770 }
771
773 {
779 }
780
781 if (m_imageProperties.m_isGreyscale && !img->isGrayscale())
782 img->ToGreyscale();
783
784 Clear();
785 m_delay = -1ms;
786
789
790 if (m_imageProperties.m_forceSize.isNull())
791 SetSize(img->size());
792
793 m_imagesLock.lock();
794 m_images[0] = img;
795 m_delays.clear();
796 m_imagesLock.unlock();
797
798 m_curPos = 0;
800 SetRedraw();
801
802 d->m_updateLock.unlock();
803}
804
810void MythUIImage::SetImages(QVector<MythImage *> *images)
811{
812 Clear();
813
814 QWriteLocker updateLocker(&d->m_updateLock);
815 QSize aSize = GetFullArea().size();
816
818
819 for (auto *im : std::as_const(*images))
820 {
821 if (!im)
822 {
823 QMutexLocker locker(&m_imagesLock);
824 m_images[m_images.size()] = im;
825 continue;
826 }
827
828 im->IncrRef();
829
830
831 QSize forceSize = m_imageProperties.m_forceSize;
832 if (!forceSize.isNull())
833 {
834 int w = (forceSize.width() <= 0) ? im->width() : forceSize.width();
835 int h = (forceSize.height() <= 0) ? im->height() : forceSize.height();
836 im->Resize(QSize(w, h), m_imageProperties.m_preserveAspect);
837 }
838
839 if (m_imageProperties.m_isReflected && !im->IsReflected())
840 {
846 }
847
848 if (m_imageProperties.m_isGreyscale && !im->isGrayscale())
849 im->ToGreyscale();
850
851 if (m_imageProperties.m_isOriented && !im->IsOriented())
852 im->Orientation(m_imageProperties.m_orientation);
853
854 m_imagesLock.lock();
855 m_images[m_images.size()] = im;
856 m_imagesLock.unlock();
857
858 aSize = aSize.expandedTo(im->size());
859 }
860
861 SetImageCount(1, m_images.size());
862
863 if (m_imageProperties.m_forceSize.isNull())
864 SetSize(aSize);
865
866 MythRect rect(GetFullArea());
867 rect.setSize(aSize);
868 SetMinArea(rect);
869
870 m_curPos = 0;
871 m_animatedImage = true;
873 SetRedraw();
874}
875
877{
878 QVector<std::chrono::milliseconds> delays;
879 QVector<MythImage *> images;
880
881 for (const auto & frame : std::as_const(frames))
882 {
883 images.append(frame.first);
884 delays.append(frame.second);
885 }
886
887 if (!images.empty())
888 {
889 SetImages(&images);
890
891 if (m_delay < 0ms && !delays.empty())
892 SetDelays(delays);
893 }
894 else
895 {
896 Reset();
897 }
898}
899
903void MythUIImage::ForceSize(const QSize size)
904{
905 if (m_imageProperties.m_forceSize == size)
906 return;
907
908 d->m_updateLock.lockForWrite();
910 d->m_updateLock.unlock();
911
912 if (size.isEmpty())
913 return;
914
916
917 Load();
918}
919
923void MythUIImage::SetOrientation(int orientation)
924{
926 m_imageProperties.m_orientation = orientation;
927}
928
932void MythUIImage::SetSize(int width, int height)
933{
934 SetSize(QSize(width, height));
935}
936
940void MythUIImage::SetSize(const QSize size)
941{
942 QWriteLocker updateLocker(&d->m_updateLock);
944 m_needLoad = true;
945}
946
951void MythUIImage::SetCropRect(int x, int y, int width, int height)
952{
953 SetCropRect(MythRect(x, y, width, height));
954}
955
961{
962 QWriteLocker updateLocker(&d->m_updateLock);
964 SetRedraw();
965}
966
970bool MythUIImage::Load(bool allowLoadInBackground, bool forceStat)
971{
972 d->m_updateLock.lockForRead();
973
975
976 QString bFilename = m_imageProperties.m_filename;
977
978 d->m_updateLock.unlock();
979
980 QString filename = bFilename;
981
982 if (bFilename.isEmpty())
983 {
984 Clear();
986 SetRedraw();
987
988 return false;
989 }
990
991 if (qEnvironmentVariableIsSet("DISABLETHREADEDMYTHUIIMAGE"))
992 allowLoadInBackground = false;
993
994 // Don't clear the widget before we need to, otherwise it causes
995 // unsightly flashing. We exclude animations for now since that requires a
996 // deeper fix
997 bool isAnimation = (m_highNum != m_lowNum) || m_animatedImage;
998
999 if (isAnimation)
1000 Clear();
1001
1002 bool complete = true;
1003
1004 QString imagelabel;
1005
1006 int j = 0;
1007
1008 for (int i = m_lowNum; i <= m_highNum && !m_animatedImage; i++)
1009 {
1010 if (!m_animatedImage && m_highNum != m_lowNum &&
1011 bFilename.contains("%1"))
1012 filename = bFilename.arg(i);
1013
1015 imProps.m_filename = filename;
1016 imagelabel = ImageLoader::GenImageLabel(imProps);
1017
1018 // Only load in the background if allowed and the image is
1019 // not already in our mem cache
1020 int cacheMode = kCacheIgnoreDisk;
1021
1022 if (forceStat)
1023 cacheMode |= (int)kCacheForceStat;
1024
1025 int cacheMode2 = kCacheNormal;
1026
1027 if (forceStat)
1028 cacheMode2 |= (int)kCacheForceStat;
1029
1030 bool do_background_load = false;
1031 if (allowLoadInBackground)
1032 {
1034 filename, imagelabel, GetPainter(),
1035 static_cast<ImageCacheMode>(cacheMode));
1036 if (img)
1037 img->DecrRef();
1038 else
1039 do_background_load = true;
1040 }
1041
1042 if (do_background_load)
1043 {
1045 LOG(VB_GUI | VB_FILE, LOG_DEBUG, LOC +
1046 QString("Load(), spawning thread to load '%1'").arg(filename));
1047
1049 auto *bImgThread = new ImageLoadThread(this, GetPainter(),
1050 imProps, bFilename, i,
1051 static_cast<ImageCacheMode>(cacheMode2));
1052 GetMythUI()->GetImageThreadPool()->start(bImgThread, "ImageLoad");
1053 }
1054 else
1055 {
1056 if (!isAnimation && !GetMythUI()->IsImageInCache(imagelabel))
1057 Clear();
1058
1059 // Perform a blocking load
1060 LOG(VB_GUI | VB_FILE, LOG_DEBUG, LOC +
1061 QString("Load(), loading '%1' in foreground").arg(filename));
1062 bool aborted = false;
1063
1065 {
1066 AnimationFrames *myFrames =
1068 static_cast<ImageCacheMode>(cacheMode2),
1069 this, aborted);
1070
1071 // TODO We might want to handle an abort here more gracefully
1072 if (aborted)
1073 {
1074 LOG(VB_GUI, LOG_DEBUG, QString("Aborted loading animated"
1075 "image %1 in foreground")
1076 .arg(filename));
1077 }
1078
1079 SetAnimationFrames(*myFrames);
1080
1081 delete myFrames;
1082 }
1083 else
1084 {
1085 MythImage *image = nullptr;
1086
1088 imProps,
1089 static_cast<ImageCacheMode>(cacheMode2),
1090 this, aborted);
1091
1092 // TODO We might want to handle an abort here more gracefully
1093 if (aborted)
1094 {
1095 LOG(VB_GUI, LOG_DEBUG, QString("Aborted loading animated"
1096 "image %1 in foreground")
1097 .arg(filename));
1098 }
1099
1100 if (image)
1101 {
1102 if (m_imageProperties.m_forceSize.isNull())
1103 SetSize(image->size());
1104
1105 MythRect rect(GetFullArea());
1106 rect.setSize(image->size());
1107 SetMinArea(rect);
1108
1109 m_imagesLock.lock();
1110 m_images[j] = image;
1111 m_imagesLock.unlock();
1112
1113 SetRedraw();
1114 d->m_updateLock.lockForWrite();
1115 m_lastDisplay = QTime::currentTime();
1116 d->m_updateLock.unlock();
1117 }
1118 else
1119 {
1120 Reset();
1121
1122 m_imagesLock.lock();
1123 m_images[j] = nullptr;
1124 m_imagesLock.unlock();
1125 }
1126 }
1127 }
1128
1129 ++j;
1130
1131 // Load is complete if no image is loading in background
1132 complete &= !do_background_load;
1133 }
1134
1135 if (complete)
1136 emit LoadComplete();
1137
1138 return true;
1139}
1140
1145{
1146 d->m_updateLock.lockForWrite();
1147
1148 auto delay = -1ms;
1149
1150 if (m_delays.contains(m_curPos))
1151 delay = m_delays[m_curPos];
1152 else if (m_delay > 0ms)
1153 delay = m_delay;
1154
1155 if (delay > 0ms &&
1156 abs(m_lastDisplay.msecsTo(QTime::currentTime())) > delay.count())
1157 {
1159 {
1161 d->m_updateLock.unlock();
1162 Load();
1163 d->m_updateLock.lockForWrite();
1164 }
1165 else
1166 {
1167 m_imagesLock.lock();
1168
1170 {
1171 ++m_curPos;
1172
1173 if (m_curPos >= (uint)m_images.size())
1174 m_curPos = 0;
1175 }
1176 else if (m_animationCycle == kCycleReverse)
1177 {
1178 if ((m_curPos + 1) >= (uint)m_images.size())
1179 {
1180 m_animationReverse = true;
1181 }
1182 else if (m_curPos == 0)
1183 {
1184 m_animationReverse = false;
1185 }
1186
1188 --m_curPos;
1189 else
1190 ++m_curPos;
1191 }
1192
1193 m_imagesLock.unlock();
1194
1195 SetRedraw();
1196 }
1197
1198 m_lastDisplay = QTime::currentTime();
1199 }
1200
1202
1203 d->m_updateLock.unlock();
1204}
1205
1209void MythUIImage::DrawSelf(MythPainter *p, int xoffset, int yoffset,
1210 int alphaMod, QRect clipRect)
1211{
1212 m_imagesLock.lock();
1213
1214 if (!m_images.empty())
1215 {
1216 d->m_updateLock.lockForWrite();
1217
1218 if (m_curPos >= (uint)m_images.size())
1219 m_curPos = 0;
1220
1221 if (!m_images[m_curPos])
1222 {
1223 unsigned int origPos = m_curPos;
1224 m_curPos++;
1225
1226 while (!m_images[m_curPos] && m_curPos != origPos)
1227 {
1228 m_curPos++;
1229
1230 if (m_curPos >= (uint)m_images.size())
1231 m_curPos = 0;
1232 }
1233 }
1234
1235 QRect area = GetArea().toQRect();
1236 area.translate(xoffset, yoffset);
1237
1238 int alpha = CalcAlpha(alphaMod);
1239
1240 MythImage *currentImage = m_images[m_curPos];
1241
1242 if (currentImage)
1243 currentImage->IncrRef();
1244
1245 m_imagesLock.unlock();
1246 d->m_updateLock.unlock();
1247
1248 if (!currentImage)
1249 return;
1250
1251 d->m_updateLock.lockForRead();
1252
1253 QRect currentImageArea = currentImage->rect();
1254
1255 if (!m_imageProperties.m_forceSize.isNull())
1256 area.setSize(area.size().expandedTo(currentImage->size()));
1257
1258 // Centre image in available space, accounting for zoom
1259 int x = 0;
1260 int y = 0;
1261 QRect visibleImage = m_effects.GetExtent(currentImageArea.size());
1262
1263 if (area.width() > visibleImage.width())
1264 x = (area.width() / 2) + visibleImage.topLeft().x();
1265
1266 if (area.height() > visibleImage.height())
1267 y = (area.height() / 2) + visibleImage.topLeft().y();
1268
1269 if ((x > 0 || y > 0))
1270 area.translate(x, y);
1271
1272 QRect srcRect;
1274
1275 if (!m_imageProperties.m_cropRect.isEmpty())
1277 else
1278 srcRect = currentImageArea;
1279
1280 p->SetClipRect(clipRect);
1281 p->DrawImage(area, currentImage, srcRect, alpha);
1282 currentImage->DecrRef();
1283 d->m_updateLock.unlock();
1284 }
1285 else
1286 {
1287 m_imagesLock.unlock();
1288 }
1289}
1290
1295 const QString &filename, QDomElement &element, bool showWarnings)
1296{
1297 QWriteLocker updateLocker(&d->m_updateLock);
1298
1299 if (element.tagName() == "filename")
1300 {
1301 m_imageProperties.m_isThemeImage = true; // This is an image distributed with the theme
1303
1304 if (m_imageProperties.m_filename.endsWith('/'))
1305 {
1306 m_showingRandomImage = true;
1308
1310 }
1311 }
1312 else if (element.tagName() == "filepattern")
1313 {
1314 m_imageProperties.m_isThemeImage = true; // This is an image distributed with the theme
1316 QString tmp = element.attribute("low");
1317
1318 if (!tmp.isEmpty())
1319 m_lowNum = tmp.toInt();
1320
1321 tmp = element.attribute("high");
1322
1323 if (!tmp.isEmpty())
1324 m_highNum = tmp.toInt();
1325
1326 tmp = element.attribute("cycle", "start");
1327
1328 if (tmp == "reverse")
1330 }
1331 else if (element.tagName() == "area")
1332 {
1333 SetArea(parseRect(element));
1335 }
1336 else if (element.tagName() == "preserveaspect")
1337 {
1339 }
1340 else if (element.tagName() == "crop")
1341 {
1343 }
1344 else if (element.tagName() == "delay")
1345 {
1346 QString value = getFirstText(element);
1347
1348 if (value.contains(","))
1349 {
1350 QVector<std::chrono::milliseconds> delays;
1351 QStringList tokens = value.split(",");
1352 for (const auto & token : std::as_const(tokens))
1353 {
1354 if (token.isEmpty())
1355 {
1356 if (!delays.empty())
1357 delays.append(delays[delays.size()-1]);
1358 else
1359 delays.append(0ms); // Default delay before first image
1360 }
1361 else
1362 {
1363 delays.append(std::chrono::milliseconds(token.toInt()));
1364 }
1365 }
1366
1367 if (!delays.empty())
1368 {
1369 m_delay = delays[0];
1370 SetDelays(delays);
1371 }
1372 }
1373 else
1374 {
1375 m_delay = std::chrono::milliseconds(value.toInt());
1376 }
1377 }
1378 else if (element.tagName() == "reflection")
1379 {
1381 QString tmp = element.attribute("axis");
1382
1383 if (!tmp.isEmpty())
1384 {
1385 if (tmp.toLower() == "horizontal")
1387 else
1389 }
1390
1391 tmp = element.attribute("shear");
1392
1393 if (!tmp.isEmpty())
1395
1396 tmp = element.attribute("scale");
1397
1398 if (!tmp.isEmpty())
1400
1401 tmp = element.attribute("length");
1402
1403 if (!tmp.isEmpty())
1405
1406 tmp = element.attribute("spacing");
1407
1408 if (!tmp.isEmpty())
1410 }
1411 else if (element.tagName() == "mask")
1412 {
1415 }
1416 else if (element.tagName() == "grayscale" ||
1417 element.tagName() == "greyscale")
1418 {
1420 }
1421 else
1422 {
1423 return MythUIType::ParseElement(filename, element, showWarnings);
1424 }
1425
1426 m_needLoad = true;
1427
1428 if (m_parent && m_parent->IsDeferredLoading(true))
1429 m_needLoad = false;
1430
1431 return true;
1432}
1433
1438{
1439 d->m_updateLock.lockForWrite();
1440 auto *im = dynamic_cast<MythUIImage *>(base);
1441 if (!im)
1442 {
1443 LOG(VB_GENERAL, LOG_ERR,
1444 QString("'%1' (%2) ERROR, bad parsing '%3' (%4)")
1445 .arg(objectName(), GetXMLLocation(),
1446 base->objectName(), base->GetXMLLocation()));
1447 d->m_updateLock.unlock();
1448 return;
1449 }
1450
1451 m_origFilename = im->m_origFilename;
1452
1453 m_delay = im->m_delay;
1454 m_lowNum = im->m_lowNum;
1455 m_highNum = im->m_highNum;
1456
1457 m_lastDisplay = QTime::currentTime();
1458 m_curPos = 0;
1459
1460 m_imageProperties = im->m_imageProperties;
1461
1462 m_animationCycle = im->m_animationCycle;
1463 m_animatedImage = im->m_animatedImage;
1464
1465 m_showingRandomImage = im->m_showingRandomImage;
1466 m_imageDirectory = im->m_imageDirectory;
1467
1469
1470 // We need to update forceSize in case the parent area has changed
1471 // however we only want to set forceSize if it was previously in use
1472 if (!m_imageProperties.m_forceSize.isNull())
1474
1475 m_needLoad = im->m_needLoad;
1476
1477 d->m_updateLock.unlock();
1478
1479 d->m_updateLock.lockForRead();
1480
1481 if (m_needLoad)
1482 {
1483 d->m_updateLock.unlock();
1484 Load();
1485 }
1486 else
1487 {
1488 d->m_updateLock.unlock();
1489 }
1490}
1491
1496{
1497 QReadLocker updateLocker(&d->m_updateLock);
1498 auto *im = new MythUIImage(parent, objectName());
1499 im->CopyFrom(this);
1500}
1501
1506{
1507 d->m_updateLock.lockForRead();
1508
1509 if (m_needLoad)
1510 {
1511 d->m_updateLock.unlock();
1512 Load();
1513 }
1514 else
1515 {
1516 d->m_updateLock.unlock();
1517 }
1518
1520}
1521
1526{
1527 d->m_updateLock.lockForWrite();
1528
1529 if (m_needLoad)
1530 {
1531 d->m_updateLock.unlock();
1532 return;
1533 }
1534
1535 m_needLoad = true;
1536 d->m_updateLock.unlock();
1537
1538 Load(false);
1539
1541}
1542
1546void MythUIImage::customEvent(QEvent *event)
1547{
1548 if (event->type() == ImageLoadEvent::kEventType)
1549 {
1550 auto * le = dynamic_cast<ImageLoadEvent *>(event);
1551 if (!le || le->GetParent() != this)
1552 return;
1553
1554 MythImage *image = le->GetImage();
1555 int number = le->GetNumber();
1556 QString filename = le->GetFilename();
1557 AnimationFrames *animationFrames = le->GetAnimationFrames();
1558 bool aborted = le->GetAbortState();
1559
1561
1562 d->m_updateLock.lockForRead();
1563 QString propFilename = m_imageProperties.m_filename;
1564 d->m_updateLock.unlock();
1565
1566 // 1) We aborted loading the image for some reason (e.g. two requests
1567 // for same image)
1568 // 2) Filename changed since we started this image, so abort to avoid
1569 // rendering two different images in quick succession which causes
1570 // unsightly flickering
1571 if (aborted || (le->GetBasefile() != propFilename))
1572 {
1573 if (aborted)
1574 LOG(VB_GUI, LOG_DEBUG, QString("Aborted loading image %1")
1575 .arg(filename));
1576
1577 if (image)
1578 image->DecrRef();
1579
1580 if (animationFrames)
1581 {
1582 for (const auto & frame : std::as_const(*animationFrames))
1583 {
1584 MythImage *im = frame.first;
1585 if (im)
1586 im->DecrRef();
1587 }
1588
1589 delete animationFrames;
1590 }
1591 }
1592 else if (animationFrames)
1593 {
1594 SetAnimationFrames(*animationFrames);
1595
1596 delete animationFrames;
1597 }
1598 else if (image)
1599 {
1600 // We don't clear until we have the new image ready to display to
1601 // avoid unsightly flashing. This isn't currently supported for
1602 // animations.
1603 if ((m_highNum == m_lowNum) && !m_animatedImage)
1604 Clear();
1605
1606 d->m_updateLock.lockForWrite();
1607
1608 if (m_imageProperties.m_forceSize.isNull())
1609 SetSize(image->size());
1610
1611 MythRect rect(GetFullArea());
1612 rect.setSize(image->size());
1613 SetMinArea(rect);
1614
1615 d->m_updateLock.unlock();
1616
1617 m_imagesLock.lock();
1618
1619 if (m_images[number])
1620 {
1621 // If we got to this point, it means this same MythUIImage
1622 // was told to reload the same image, so we use the newest
1623 // copy of the image.
1624 m_images[number]->DecrRef(); // delete the original
1625 }
1626
1627 m_images[number] = image;
1628 m_imagesLock.unlock();
1629
1630 SetRedraw();
1631
1632 d->m_updateLock.lockForWrite();
1633 m_lastDisplay = QTime::currentTime();
1634 d->m_updateLock.unlock();
1635 }
1636 else
1637 {
1638 // No Images were loaded, so trigger Reset to default
1639 Reset();
1640 }
1641
1642 // NOLINTNEXTLINE(readability-misleading-indentation)
1643 emit LoadComplete();
1644 }
1645}
1646
1648{
1649 QString randFile;
1650
1651 // find and save the list of available images
1652 if (m_imageList.isEmpty())
1653 {
1654 QDir imageDir(m_imageDirectory);
1655
1656 if (!imageDir.exists())
1657 {
1658 QString themeDir = GetMythUI()->GetThemeDir() + '/';
1659 imageDir.setPath(themeDir + m_imageDirectory);
1660 }
1661
1662 QStringList imageTypes;
1663
1664 QList< QByteArray > exts = QImageReader::supportedImageFormats();
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}
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:91
void Copy(const ImageProperties &other)
Definition: mythuiimage.cpp:65
ImageProperties & operator=(const ImageProperties &other)
Definition: mythuiimage.cpp:46
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: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 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.
void SetSize(int width, int height)
Set the size of the widget.
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:86
bool m_enableInitiator
Definition: mythuitype.h:267
virtual void SetSize(QSize size)
Definition: mythuitype.cpp:569
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:610
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:313
virtual void SetMinArea(const MythRect &rect)
Set the minimum area based on the given size.
Definition: mythuitype.cpp:820
virtual void Pulse(void)
Pulse is called 70 times a second to trigger a single frame of an animation.
Definition: mythuitype.cpp:456
virtual MythRect GetArea(void) const
If the object has a minimum area defined, return it, other wise return the default area.
Definition: mythuitype.cpp:885
bool IsDeferredLoading(bool recurse=false) const
MythUIType * m_parent
Definition: mythuitype.h:297
virtual MythRect GetFullArea(void) const
Definition: mythuitype.cpp:893
int CalcAlpha(int alphamod) const
Definition: mythuitype.cpp:474
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:73
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:68
static guint32 * tmp
Definition: goom_core.cpp:26
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:37
QPair< MythImage *, std::chrono::milliseconds > AnimationFrame
Definition: mythuiimage.h:87
QVector< AnimationFrame > AnimationFrames
Definition: mythuiimage.h:88
ImageCacheMode
@ kCacheNormal
@ kCacheForceStat
@ kCacheIgnoreDisk
STL namespace.