12#include <QCoreApplication>
14#include <QDomDocument>
17#include <QImageReader>
18#include <QReadWriteLock>
34#define LOC QString("MythUIImage(0x%1): ").arg((uint64_t)this,0,16)
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));
148 QString extension =
filename.section(
'.', -1);
149 return !
filename.startsWith(
"myth://") &&
150 (extension ==
"gif" ||
151 extension ==
"apng" ||
168 s_Attrib +=
"reflected";
171 s_Attrib +=
"greyscale";
175 s_Attrib +=
"orientation";
191 imagelabel = QString(
"%1-%2-%3x%4.png")
196 imagelabel.replace(
'/',
'-');
198 imagelabel.replace(
':',
'-');
216 if (!
PreLoad(cacheKey, parent))
225 bool bResize =
false;
226 bool bFoundInCache =
false;
254 LOG(VB_GUI | VB_FILE, LOG_INFO,
255 QString(
"ImageLoader::LoadImage(%1) Found in cache, "
257 .arg(cacheKey).arg(cnt));
266 bFoundInCache =
true;
270 LOG(VB_GUI | VB_FILE, LOG_INFO,
271 QString(
"ImageLoader::LoadImage(%1) NOT Found in cache. "
272 "Loading Directly").arg(cacheKey));
278 ok = image->
Load(imageReader);
289 if (image && image->isNull())
291 LOG(VB_GUI | VB_FILE, LOG_INFO,
292 QString(
"ImageLoader::LoadImage(%1) Image is NULL")
299 if (image && !bFoundInCache)
322 if (wmult != 1.0F || hmult != 1.0F)
324 w = image->size().width() * wmult;
325 h = image->size().height() * hmult;
341 if (wmult != 1.0F || hmult != 1.0F)
343 int width = newMaskImage->size().width() * wmult;
344 int height = newMaskImage->size().height() * hmult;
345 newMaskImage->
Resize(QSize(width, height));
356 QRect imageArea = image->rect();
363 if (maskArea.width() > imageArea.width())
364 x = (maskArea.width() - imageArea.width()) / 2;
366 if (maskArea.height() > imageArea.height())
367 y = (maskArea.height() - imageArea.height()) / 2;
370 imageArea.translate(x, y);
373 image->setAlphaChannel(mask.convertToFormat(QImage::Format_Alpha8));
399 QString frameFilename;
405 while (imageReader->canRead() && !aborted)
407 frameFilename =
filename.arg(imageCount);
413 aborted, imageReader);
418 images->append(
AnimationFrame(im, std::chrono::milliseconds(imageReader->nextImageDelay())));
441 int number,
bool aborted)
480 (QEvent::Type) QEvent::registerEventType();
498 bool aborted =
false;
511 if (frames && frames->count() > 1)
516 QCoreApplication::postEvent(
m_parent, le);
531 QCoreApplication::postEvent(
m_parent, le);
559 int low,
int high, std::chrono::milliseconds delay,
731 for (std::chrono::milliseconds delay : std::as_const(delays))
762 if (!forceSize.isNull())
764 int w = (forceSize.width() <= 0) ? img->width() : forceSize.width();
765 int h = (forceSize.height() <= 0) ? img->height() : forceSize.height();
816 for (
auto *im : std::as_const(*images))
829 if (!forceSize.isNull())
831 int w = (forceSize.width() <= 0) ? im->width() : forceSize.width();
832 int h = (forceSize.height() <= 0) ? im->height() : forceSize.height();
855 aSize = aSize.expandedTo(im->size());
875 QVector<std::chrono::milliseconds> delays;
876 QVector<MythImage *> images;
878 delays.reserve(frames.size());
879 images.reserve(frames.size());
880 for (
const auto & frame : std::as_const(frames))
882 images.append(frame.first);
883 delays.append(frame.second);
890 if (
m_delay < 0ms && !delays.empty())
981 if (bFilename.isEmpty())
990 if (qEnvironmentVariableIsSet(
"DISABLETHREADEDMYTHUIIMAGE"))
991 allowLoadInBackground =
false;
1001 bool complete =
true;
1010 bFilename.contains(
"%1"))
1029 bool do_background_load =
false;
1030 if (allowLoadInBackground)
1038 do_background_load =
true;
1041 if (do_background_load)
1044 LOG(VB_GUI | VB_FILE, LOG_DEBUG,
LOC +
1045 QString(
"Load(), spawning thread to load '%1'").arg(
filename));
1049 imProps, bFilename, i,
1055 if (!isAnimation && !
GetMythUI()->IsImageInCache(imagelabel))
1059 LOG(VB_GUI | VB_FILE, LOG_DEBUG,
LOC +
1060 QString(
"Load(), loading '%1' in foreground").arg(
filename));
1061 bool aborted =
false;
1073 LOG(VB_GUI, LOG_DEBUG, QString(
"Aborted loading animated"
1074 "image %1 in foreground")
1094 LOG(VB_GUI, LOG_DEBUG, QString(
"Aborted loading animated"
1095 "image %1 in foreground")
1105 rect.setSize(image->size());
1131 complete &= !do_background_load;
1155 abs(
m_lastDisplay.msecsTo(QTime::currentTime())) > delay.count())
1209 int alphaMod, QRect clipRect)
1235 area.translate(xoffset, yoffset);
1252 QRect currentImageArea = currentImage->rect();
1255 area.setSize(area.size().expandedTo(currentImage->size()));
1262 if (area.width() > visibleImage.width())
1263 x = (area.width() / 2) + visibleImage.topLeft().x();
1265 if (area.height() > visibleImage.height())
1266 y = (area.height() / 2) + visibleImage.topLeft().y();
1268 if ((x > 0 || y > 0))
1269 area.translate(x, y);
1277 srcRect = currentImageArea;
1279 p->SetClipRect(clipRect);
1280 p->DrawImage(area, currentImage, srcRect, alpha);
1294 const QString &
filename, QDomElement &element,
bool showWarnings)
1298 if (element.tagName() ==
"filename")
1311 else if (element.tagName() ==
"filepattern")
1315 QString tmp = element.attribute(
"low");
1320 tmp = element.attribute(
"high");
1325 tmp = element.attribute(
"cycle",
"start");
1327 if (tmp ==
"reverse")
1330 else if (element.tagName() ==
"area")
1335 else if (element.tagName() ==
"preserveaspect")
1339 else if (element.tagName() ==
"crop")
1343 else if (element.tagName() ==
"delay")
1347 if (value.contains(
","))
1349 QVector<std::chrono::milliseconds> delays;
1350 QStringList tokens = value.split(
",");
1351 for (
const auto & token : std::as_const(tokens))
1353 if (token.isEmpty())
1355 if (!delays.empty())
1356 delays.append(delays[delays.size()-1]);
1362 delays.append(std::chrono::milliseconds(token.toInt()));
1366 if (!delays.empty())
1374 m_delay = std::chrono::milliseconds(value.toInt());
1377 else if (element.tagName() ==
"reflection")
1380 QString tmp = element.attribute(
"axis");
1384 if (tmp.toLower() ==
"horizontal")
1390 tmp = element.attribute(
"shear");
1395 tmp = element.attribute(
"scale");
1400 tmp = element.attribute(
"length");
1405 tmp = element.attribute(
"spacing");
1410 else if (element.tagName() ==
"mask")
1415 else if (element.tagName() ==
"grayscale" ||
1416 element.tagName() ==
"greyscale")
1442 LOG(VB_GENERAL, LOG_ERR,
1443 QString(
"'%1' (%2) ERROR, bad parsing '%3' (%4)")
1550 if (!le || le->GetParent() !=
this)
1554 int number = le->GetNumber();
1555 QString
filename = le->GetFilename();
1557 bool aborted = le->GetAbortState();
1570 if (aborted || (le->GetBasefile() != propFilename))
1573 LOG(VB_GUI, LOG_DEBUG, QString(
"Aborted loading image %1")
1579 if (animationFrames)
1581 for (
const auto & frame : std::as_const(*animationFrames))
1588 delete animationFrames;
1591 else if (animationFrames)
1595 delete animationFrames;
1611 rect.setSize(image->size());
1655 if (!imageDir.exists())
1661 QStringList imageTypes;
1663 QList< QByteArray > exts = QImageReader::supportedImageFormats();
1664 imageTypes.reserve(exts.size());
1665 for (
const auto & ext : std::as_const(exts))
1667 imageTypes.append(QString(
"*.").append(ext));
1670 imageDir.setNameFilters(imageTypes);
1681 std::random_device rd;
1682 std::mt19937 g(rd());
1696 std::random_device rd;
1697 std::mt19937 g(rd());
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
static const Type kEventType
AnimationFrames * GetAnimationFrames() const
const MythUIImage * m_parent
MythImage * GetImage() const
QString GetBasefile() const
ImageCacheMode m_cacheMode
ImageLoadThread(MythUIImage *parent, MythPainter *painter, const ImageProperties &imProps, QString basefile, int number, ImageCacheMode mode)
ImageProperties m_imageProperties
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)
static AnimationFrames * LoadAnimatedImage(MythPainter *painter, const ImageProperties &imProps, ImageCacheMode cacheMode, const MythUIImage *parent, bool &aborted)
static QWaitCondition m_loadingImagesCond
static bool SupportsAnimation(const QString &filename)
QRect GetMaskImageRect(void)
ImageProperties()=default
void SetMaskImageFilename(const QString &filename)
ReflectAxis m_reflectAxis
void SetMaskImage(MythImage *image)
void Copy(const ImageProperties &other)
ImageProperties & operator=(const ImageProperties &other)
QString GetMaskImageFilename()
QString m_maskImageFilename
QImage GetMaskImageSubset(QRect imageArea)
void start(QRunnable *runnable, const QString &debugName, int priority=0)
bool Load(MythImageReader *reader)
virtual void SetChanged(bool change=true)
void setIsOriented(bool oriented)
void Orientation(int orientation)
Changes the orientation angle of the image according to the exif rotation values.
void Reflect(ReflectAxis axis, int shear, int scale, int length, int spacing=0)
void Resize(QSize newSize, bool preserveAspect=false)
void setIsReflected(bool reflected)
int DecrRef(void) override
Decrements reference count and deletes on 0.
QString GetFileName(void) const
int IncrRef(void) override
Increments reference count.
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.
void CalculateArea(QRect parentArea)
QRect toQRect(void) const
~MythUIImagePrivate()=default
MythUIImagePrivate(MythUIImage *p)
QReadWriteLock m_updateLock
Image widget, displays a single image or multiple images in sequence.
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
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.
std::chrono::milliseconds m_delay
friend class ImageLoadThread
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
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...
void SetDelays(const QVector< std::chrono::milliseconds > &delays)
Sets the delays between each image in an animation.
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.
bool m_showingRandomImage
void Clear(void)
Remove all images from the widget.
void SetDelay(std::chrono::milliseconds delayms)
Set the delay between each image in an animation.
ImageProperties m_imageProperties
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
void Pulse(void) override
Pulse is called 70 times a second to trigger a single frame of an animation.
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.
virtual void SetSize(QSize size)
virtual MythPainter * GetPainter(void)
QString GetXMLLocation(void) const
virtual void SetArea(const MythRect &rect)
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.
virtual void SetMinArea(const MythRect &rect)
Set the minimum area based on the given size.
virtual void Pulse(void)
Pulse is called 70 times a second to trigger a single frame of an animation.
virtual MythRect GetArea(void) const
If the object has a minimum area defined, return it, other wise return the default area.
bool IsDeferredLoading(bool recurse=false) const
virtual MythRect GetFullArea(void) const
int CalcAlpha(int alphamod) const
void DependChanged(bool isDefault)
virtual void Reset(void)
Reset the widget to it's original state, should not reset changes made by the theme.
virtual bool ParseElement(const QString &filename, QDomElement &element, bool showWarnings)
Parse the xml definition of this widget setting the state of the object accordingly.
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)
static const iso6937table * d
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
MythMainWindow * GetMythMainWindow(void)
MythUIHelper * GetMythUI()
QPair< MythImage *, std::chrono::milliseconds > AnimationFrame
QVector< AnimationFrame > AnimationFrames