Go to the documentation of this file.
13 #include <QCoreApplication>
15 #include <QDomDocument>
18 #include <QImageReader>
19 #include <QReadWriteLock>
38 #define LOC QString("MythUIImage(0x%1): ").arg((uint64_t)this,0,16)
124 LOG(VB_GUI | VB_FILE, LOG_DEBUG,
125 QString(
"ImageLoader::PreLoad(%1), this "
126 "file is already being loaded by this same MythUIImage "
127 "in another thread.").arg(cacheKey));
152 QString extension =
filename.section(
'.', -1);
153 return !
filename.startsWith(
"myth://") &&
154 (extension ==
"gif" ||
155 extension ==
"apng" ||
172 s_Attrib +=
"reflected";
175 s_Attrib +=
"greyscale";
179 s_Attrib +=
"orientation";
195 imagelabel = QString(
"%1-%2-%3x%4.png")
200 imagelabel.replace(
'/',
'-');
202 imagelabel.replace(
':',
'-');
220 if (!
PreLoad(cacheKey, parent))
229 bool bResize =
false;
230 bool bFoundInCache =
false;
258 LOG(VB_GUI | VB_FILE, LOG_INFO,
259 QString(
"ImageLoader::LoadImage(%1) Found in cache, "
261 .arg(cacheKey).arg(cnt));
270 bFoundInCache =
true;
274 LOG(VB_GUI | VB_FILE, LOG_INFO,
275 QString(
"ImageLoader::LoadImage(%1) NOT Found in cache. "
276 "Loading Directly").arg(cacheKey));
282 ok = image->
Load(imageReader);
293 if (image && image->isNull())
295 LOG(VB_GUI | VB_FILE, LOG_INFO,
296 QString(
"ImageLoader::LoadImage(%1) Image is NULL")
303 if (image && !bFoundInCache)
326 if (wmult != 1.0F || hmult != 1.0F)
328 w = image->size().width() * wmult;
329 h = image->size().height() * hmult;
345 if (wmult != 1.0F || hmult != 1.0F)
347 int width = newMaskImage->size().width() * wmult;
348 int height = newMaskImage->size().height() * hmult;
349 newMaskImage->
Resize(QSize(width, height));
358 QRect imageArea = image->rect();
365 if (maskArea.width() > imageArea.width())
366 x = (maskArea.width() - imageArea.width()) / 2;
368 if (maskArea.height() > imageArea.height())
369 y = (maskArea.height() - imageArea.height()) / 2;
372 imageArea.translate(x, y);
375 image->setAlphaChannel(mask.convertToFormat(QImage::Format_Alpha8));
401 QString frameFilename;
407 while (imageReader->canRead() && !aborted)
409 frameFilename =
filename.arg(imageCount);
415 aborted, imageReader);
420 images->append(
AnimationFrame(im, std::chrono::milliseconds(imageReader->nextImageDelay())));
443 int number,
bool aborted)
482 (QEvent::Type) QEvent::registerEventType();
500 bool aborted =
false;
513 if (frames && frames->count() > 1)
518 QCoreApplication::postEvent(
m_parent, le);
533 QCoreApplication::postEvent(
m_parent, le);
561 int low,
int high, std::chrono::milliseconds delay,
732 for (std::chrono::milliseconds delay : qAsConst(delays))
763 if (!forceSize.isNull())
765 int w = (forceSize.width() <= 0) ? img->width() : forceSize.width();
766 int h = (forceSize.height() <= 0) ? img->height() : forceSize.height();
817 for (
auto *im : qAsConst(*images))
830 if (!forceSize.isNull())
832 int w = (forceSize.width() <= 0) ? im->width() : forceSize.width();
833 int h = (forceSize.height() <= 0) ? im->height() : forceSize.height();
856 aSize = aSize.expandedTo(im->size());
876 QVector<std::chrono::milliseconds> delays;
877 QVector<MythImage *> images;
879 for (
const auto & frame : qAsConst(frames))
881 images.append(frame.first);
882 delays.append(frame.second);
889 if (
m_delay < 0ms && !delays.empty())
978 if (bFilename.isEmpty())
987 if (qEnvironmentVariableIsSet(
"DISABLETHREADEDMYTHUIIMAGE"))
988 allowLoadInBackground =
false;
998 bool complete =
true;
1007 bFilename.contains(
"%1"))
1026 bool do_background_load =
false;
1027 if (allowLoadInBackground)
1035 do_background_load =
true;
1038 if (do_background_load)
1041 LOG(VB_GUI | VB_FILE, LOG_DEBUG,
LOC +
1042 QString(
"Load(), spawning thread to load '%1'").arg(
filename));
1046 imProps, bFilename, i,
1052 if (!isAnimation && !
GetMythUI()->IsImageInCache(imagelabel))
1056 LOG(VB_GUI | VB_FILE, LOG_DEBUG,
LOC +
1057 QString(
"Load(), loading '%1' in foreground").arg(
filename));
1058 bool aborted =
false;
1070 LOG(VB_GUI, LOG_DEBUG, QString(
"Aborted loading animated"
1071 "image %1 in foreground")
1091 LOG(VB_GUI, LOG_DEBUG, QString(
"Aborted loading animated"
1092 "image %1 in foreground")
1102 rect.setSize(image->size());
1128 complete &= !do_background_load;
1152 abs(
m_lastDisplay.msecsTo(QTime::currentTime())) > delay.count())
1206 int alphaMod, QRect clipRect)
1232 area.translate(xoffset, yoffset);
1249 QRect currentImageArea = currentImage->rect();
1252 area.setSize(area.size().expandedTo(currentImage->size()));
1259 if (area.width() > visibleImage.width())
1260 x = area.width() / 2 + visibleImage.topLeft().x();
1262 if (area.height() > visibleImage.height())
1263 y = area.height() / 2 + visibleImage.topLeft().y();
1265 if ((x > 0 || y > 0))
1266 area.translate(x, y);
1274 srcRect = currentImageArea;
1276 p->SetClipRect(clipRect);
1277 p->DrawImage(area, currentImage, srcRect, alpha);
1289 const QString &
filename, QDomElement &element,
bool showWarnings)
1293 if (element.tagName() ==
"filename")
1306 else if (element.tagName() ==
"filepattern")
1310 QString
tmp = element.attribute(
"low");
1315 tmp = element.attribute(
"high");
1320 tmp = element.attribute(
"cycle",
"start");
1322 if (
tmp ==
"reverse")
1325 else if (element.tagName() ==
"area")
1330 else if (element.tagName() ==
"preserveaspect")
1332 else if (element.tagName() ==
"crop")
1334 else if (element.tagName() ==
"delay")
1338 if (value.contains(
","))
1340 QVector<std::chrono::milliseconds> delays;
1341 QStringList tokens = value.split(
",");
1342 for (
const auto & token : qAsConst(tokens))
1344 if (token.isEmpty())
1346 if (!delays.empty())
1347 delays.append(delays[delays.size()-1]);
1353 delays.append(std::chrono::milliseconds(token.toInt()));
1357 if (!delays.empty())
1365 m_delay = std::chrono::milliseconds(value.toInt());
1368 else if (element.tagName() ==
"reflection")
1371 QString
tmp = element.attribute(
"axis");
1375 if (
tmp.toLower() ==
"horizontal")
1381 tmp = element.attribute(
"shear");
1386 tmp = element.attribute(
"scale");
1391 tmp = element.attribute(
"length");
1396 tmp = element.attribute(
"spacing");
1401 else if (element.tagName() ==
"mask")
1406 else if (element.tagName() ==
"grayscale" ||
1407 element.tagName() ==
"greyscale")
1433 LOG(VB_GENERAL, LOG_ERR,
1434 QString(
"'%1' (%2) ERROR, bad parsing '%3' (%4)")
1537 if (!le || le->GetParent() !=
this)
1541 int number = le->GetNumber();
1542 QString
filename = le->GetFilename();
1544 bool aborted = le->GetAbortState();
1557 if (aborted || (le->GetBasefile() != propFilename))
1560 LOG(VB_GUI, LOG_DEBUG, QString(
"Aborted loading image %1")
1566 if (animationFrames)
1568 for (
const auto & frame : qAsConst(*animationFrames))
1575 delete animationFrames;
1578 else if (animationFrames)
1582 delete animationFrames;
1598 rect.setSize(image->size());
1642 if (!imageDir.exists())
1648 QStringList imageTypes;
1650 QList< QByteArray > exts = QImageReader::supportedImageFormats();
1651 for (
const auto & ext : qAsConst(exts))
1653 imageTypes.append(QString(
"*.").append(ext));
1656 imageDir.setNameFilters(imageTypes);
1667 std::random_device rd;
1668 std::mt19937 g(rd());
1682 std::random_device rd;
1683 std::mt19937 g(rd());
friend class MythUIImagePrivate
void SetDelays(const QVector< std::chrono::milliseconds > &delays)
Sets the delays between each image in an animation.
ImageCacheMode m_cacheMode
void SetMaskImage(MythImage *image)
bool GetAbortState() const
Image widget, displays a single image or multiple images in sequence.
ImageLoadThread(MythUIImage *parent, MythPainter *painter, const ImageProperties &imProps, QString basefile, int number, ImageCacheMode mode)
bool Load(MythImageReader *reader)
QHash< int, MythImage * > m_images
QReadWriteLock m_updateLock
MythImage * GetImage() const
virtual MythRect GetFullArea(void) const
int CalcAlpha(int alphamod) const
QRect toQRect(void) const
void Reflect(ReflectAxis axis, int shear, int scale, int length, int spacing=0)
MythUIImage(const QString &filepattern, int low, int high, std::chrono::milliseconds delay, MythUIType *parent, const QString &name)
bool Load(bool allowLoadInBackground=true, bool forceStat=false)
Load the image(s), wraps ImageLoader::LoadImage()
MythImage * GetFormatImage()
Returns a blank reference counted image in the format required for the Draw functions for this painte...
static QHash< QString, const MythUIImage * > m_loadingImages
static void PostLoad(const QString &cacheKey)
QHash< int, std::chrono::milliseconds > m_delays
static QMutex m_loadingImagesLock
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
virtual MythPainter * GetPainter(void)
ReflectAxis m_reflectAxis
static QString GenImageLabel(const ImageProperties &imProps)
Generates a unique identifying string for this image which is used as a key in the image cache.
ImageLoadEvent(const MythUIImage *parent, AnimationFrames *frames, QString basefile, QString filename, bool aborted)
void customEvent(QEvent *event) override
void SetOrientation(int orientation)
Saves the exif orientation value of the first image in the widget.
virtual void SetArea(const MythRect &rect)
QString GetBasefile() const
QImage GetMaskImageSubset(QRect imageArea)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
AnimationFrames * GetAnimationFrames() const
void LoadNow(void) override
Cause images in this and child widgets to be loaded.
void SetFilepattern(const QString &filepattern, int low, int high)
Must be followed by a call to Load() to load the image.
void Clear(void)
Remove all images from the widget.
void Reset(void) override
Reset the image back to the default defined in the theme.
Wrapper around QRect allowing us to handle percentage and other relative values for areas in mythui.
virtual void Pulse(void)
Pulse is called 70 times a second to trigger a single frame of an animation.
QVector< AnimationFrame > AnimationFrames
static QWaitCondition m_loadingImagesCond
bool m_showingRandomImage
virtual MythRect GetArea(void) const
If the object has a minimum area defined, return it, other wise return the default area.
void setIsOriented(bool oriented)
std::chrono::milliseconds m_delay
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_maskImageFilename
static bool PreLoad(const QString &cacheKey, const MythUIImage *uitype)
ImageProperties & operator=(const ImageProperties &other)
void SetSize(int width, int height)
Set the size of the widget.
QString GetXMLLocation(void) const
virtual void SetMinArea(const MythRect &rect)
Set the minimum area based on the given size.
void DependChanged(bool isDefault)
~MythUIImagePrivate()=default
void SetAnimationFrames(const AnimationFrames &frames)
AnimationFrames * m_images
static QString getFirstText(QDomElement &element)
int DecrRef(void) override
Decrements reference count and deletes on 0.
void CalculateArea(QRect parentArea)
friend class ImageLoadThread
void setIsReflected(bool reflected)
virtual void CopyFrom(MythUIType *base)
Copy this widgets state from another.
void Copy(const ImageProperties &other)
void SetDelay(std::chrono::milliseconds delayms)
Set the delay between each image in an animation.
MythImage * LoadCacheImage(QString File, const QString &Label, MythPainter *Painter, ImageCacheMode cacheMode=kCacheNormal)
static MythRect parseRect(const QString &text, bool normalize=true)
virtual void SetChanged(bool change=true)
void CreateCopy(MythUIType *parent) override
Copy the state of this widget to the one given, it must be of the same type.
void SetMaskImageFilename(const QString &filename)
static bool SupportsAnimation(const QString &filename)
bool ParseElement(const QString &filename, QDomElement &element, bool showWarnings) override
Parse the xml definition of this widget setting the state of the object accordingly.
void CopyFrom(MythUIType *base) override
Copy this widgets state from another.
const MythUIImage * m_parent
virtual void Reset(void)
Reset the widget to it's original state, should not reset changes made by the theme.
static AnimationFrames * LoadAnimatedImage(MythPainter *painter, const ImageProperties &imProps, ImageCacheMode cacheMode, const MythUIImage *parent, bool &aborted)
The base class on which all widgets and screens are based.
static const Type kEventType
void Orientation(int orientation)
Changes the orientation angle of the image according to the exif rotation values.
bool IsDeferredLoading(bool recurse=false) const
void SetImageCount(int low, int high)
Set the integer range for an animated image pattern.
ImageProperties m_imageProperties
QString GetFilename() const
int IncrRef(void) override
Increments reference count.
void SetImages(QVector< MythImage * > *images)
Should not be used unless absolutely necessary since it bypasses the image caching and threaded loade...
QRect GetMaskImageRect(void)
virtual void LoadNow(void)
Cause images in this and child widgets to be loaded.
ImageProperties()=default
virtual void SetSize(QSize size)
void ForceSize(QSize size)
Force the dimensions of the widget and image to the given size.
MythUIImagePrivate(MythUIImage *p)
MythMainWindow * GetMythMainWindow(void)
void FindRandomImage(void)
QString GetMaskImageFilename()
void Pulse(void) override
Pulse is called 70 times a second to trigger a single frame of an animation.
ImageLoadEvent(const MythUIImage *parent, MythImage *image, QString basefile, QString filename, int number, bool aborted)
void Resize(QSize newSize, bool preserveAspect=false)
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.
ImageProperties m_imageProperties
void SetFilename(const QString &filename)
Must be followed by a call to Load() to load the image.
virtual bool ParseElement(const QString &filename, QDomElement &element, bool showWarnings)
Parse the xml definition of this widget setting the state of the object accordingly.
MythUIHelper * GetMythUI()
void GetScalingFactors(float &Horizontal, float &Vertical) const
AnimationCycle m_animationCycle
static bool parseBool(const QString &text)
QRect GetExtent(QSize size) const
static MythImage * LoadImage(MythPainter *painter, ImageProperties imProps, ImageCacheMode cacheMode, const MythUIImage *parent, bool &aborted, MythImageReader *imageReader=nullptr)
void start(QRunnable *runnable, const QString &debugName, int priority=0)
virtual void Finalize(void)
Perform any post-xml parsing initialisation tasks.
MThreadPool * GetImageThreadPool()
const MythUIImage * GetParent() const
MythImage * CacheImage(const QString &URL, MythImage *Image, bool NoDisk=false)
QString GetFileName(void) const
QPair< MythImage *, std::chrono::milliseconds > AnimationFrame
void DrawSelf(MythPainter *p, int xoffset, int yoffset, int alphaMod, QRect clipRect) override