MythTV master
mythimage.cpp
Go to the documentation of this file.
1// Own header
2#include "mythimage.h"
3
4// C++ headers
5#include <cstdint>
6#include <iostream>
7
8// QT headers
9#include <QImageReader>
10#include <QNetworkReply>
11#include <QPainter>
12#include <QRgb>
13
14// libmythbase headers
18
19// MythUI headers
20#include "mythuihelper.h"
21#include "mythmainwindow.h"
22
24
25MythImage::MythImage(MythPainter *parent, const char *name) :
27{
28 if (!parent)
29 LOG(VB_GENERAL, LOG_ERR, "Image created without parent!");
30
31 m_parent = parent;
32 m_fileName = "";
33
34 if (!s_ui)
35 s_ui = GetMythUI();
36}
37
39{
40 if (m_parent)
42}
43
45{
46 int cnt = ReferenceCounter::IncrRef();
47 if ((2 == cnt) && s_ui && m_cached)
49 return cnt;
50}
51
53{
54 bool cached = m_cached;
55 int cnt = ReferenceCounter::DecrRef();
56 if (cached)
57 {
58 if (s_ui && (1 == cnt))
60
61 if (0 == cnt)
62 {
63 LOG(VB_GENERAL, LOG_INFO,
64 "Image should be removed from cache prior to deletion.");
65 }
66 }
67 return cnt;
68}
69
70void MythImage::SetIsInCache(bool bCached)
71{
72 IncrRef();
73 m_cached = bCached;
74 DecrRef();
75}
76
77void MythImage::Assign(const QImage &img)
78{
79 if (m_cached)
80 {
81 SetIsInCache(false);
82 *(static_cast<QImage*>(this)) = img;
84 }
85 else
86 {
87 *(static_cast<QImage*>(this)) = img;
88 }
89 SetChanged();
90}
91
92void MythImage::Assign(const QPixmap &pix)
93{
94 Assign(pix.toImage());
95}
96
97QImage MythImage::ApplyExifOrientation(QImage &image, int orientation)
98{
99 QTransform transform;
100
101 switch (orientation)
102 {
103 case 1: // normal
104 return image;
105 case 2: // mirror horizontal
106#if QT_VERSION < QT_VERSION_CHECK(6,9,0)
107 return image.mirrored(true, false);
108#else
109 return image.flipped(Qt::Horizontal);
110#endif
111 case 3: // rotate 180
112 transform.rotate(180);
113 return image.transformed(transform);
114 case 4: // mirror vertical
115#if QT_VERSION < QT_VERSION_CHECK(6,9,0)
116 return image.mirrored(false, true);
117#else
118 return image.flipped(Qt::Vertical);
119#endif
120 case 5: // mirror horizontal and rotate 270 CCW
121 transform.rotate(270);
122#if QT_VERSION < QT_VERSION_CHECK(6,9,0)
123 return image.mirrored(true, false).transformed(transform);
124#else
125 return image.flipped(Qt::Horizontal).transformed(transform);
126#endif
127 case 6: // rotate 90 CW
128 transform.rotate(90);
129 return image.transformed(transform);
130 case 7: // mirror horizontal and rotate 90 CW
131 transform.rotate(90);
132#if QT_VERSION < QT_VERSION_CHECK(6,9,0)
133 return image.mirrored(true, false).transformed(transform);
134#else
135 return image.flipped(Qt::Horizontal).transformed(transform);
136#endif
137 case 8: // rotate 270 CW
138 transform.rotate(270);
139 return image.transformed(transform);
140 }
141 return image;
142}
143
150void MythImage::Orientation(int orientation)
151{
152 if (!m_isOriented)
153 {
154 Assign(ApplyExifOrientation(*this, orientation));
155 m_isOriented = true;
156 }
157}
158
159void MythImage::Resize(QSize newSize, bool preserveAspect)
160{
161 if ((size() == newSize) && !isNull())
162 return;
163
164 if (m_isGradient)
165 {
166 *(static_cast<QImage *> (this)) = QImage(newSize, QImage::Format_ARGB32);
169 SetChanged();
170 }
171 else
172 {
173 Qt::AspectRatioMode mode = Qt::IgnoreAspectRatio;
174 if (preserveAspect)
175 mode = Qt::KeepAspectRatio;
176
177 Assign(scaled(newSize, mode, Qt::SmoothTransformation));
178 }
179}
180
181void MythImage::Reflect(ReflectAxis axis, int shear, int scale, int length,
182 int spacing)
183{
184 if (m_isReflected)
185 return;
186
187 QImage mirrorImage;
189 if (axis == ReflectAxis::Vertical)
190 {
191#if QT_VERSION < QT_VERSION_CHECK(6,9,0)
192 mirrorImage = mirrored(false,true);
193#else
194 mirrorImage = flipped(Qt::Vertical);
195#endif
196 if (length < 100)
197 {
198 int height = (int)((float)mirrorImage.height() * (float)length/100);
199 mirrorImage = mirrorImage.copy(0,0,mirrorImage.width(),height);
200 }
201 fillDirection = FillDirection::TopToBottom;
202 }
203 else if (axis == ReflectAxis::Horizontal)
204 {
205#if QT_VERSION < QT_VERSION_CHECK(6,9,0)
206 mirrorImage = mirrored(true,false);
207#else
208 mirrorImage = flipped(Qt::Horizontal);
209#endif
210 if (length < 100)
211 {
212 int width = (int)((float)mirrorImage.width() * (float)length/100);
213 mirrorImage = mirrorImage.copy(0,0,width,mirrorImage.height());
214 }
215 fillDirection = FillDirection::LeftToRight;
216 }
217
218 QImage alphaChannel(mirrorImage.size(), QImage::Format_ARGB32);
219 MakeGradient(alphaChannel, QColor(0xAA, 0xAA, 0xAA), QColor(0x00, 0x00, 0x00), 255,
220 BoundaryWanted::No, fillDirection);
221 mirrorImage.setAlphaChannel(alphaChannel);
222
223 QTransform shearTransform;
224 if (axis == ReflectAxis::Vertical)
225 {
226 shearTransform.scale(1,static_cast<qreal>(scale)/100);
227 shearTransform.shear(static_cast<qreal>(shear)/100,0);
228 }
229 else if (axis == ReflectAxis::Horizontal)
230 {
231 shearTransform.scale(static_cast<qreal>(scale)/100,1);
232 shearTransform.shear(0,static_cast<qreal>(shear)/100);
233 }
234
235 mirrorImage = mirrorImage.transformed(shearTransform, Qt::SmoothTransformation);
236
237 QSize newsize;
238 if (axis == ReflectAxis::Vertical)
239 newsize = QSize(mirrorImage.width(), height()+spacing+mirrorImage.height());
240 else if (axis == ReflectAxis::Horizontal)
241 newsize = QSize(width()+spacing+mirrorImage.width(), mirrorImage.height());
242
243 QImage temp(newsize, QImage::Format_ARGB32);
244 temp.fill(Qt::transparent);
245
246 QPainter newpainter(&temp);
247 newpainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
248 if (axis == ReflectAxis::Vertical)
249 {
250 if (shear < 0)
251 {
252 newpainter.drawImage(mirrorImage.width()-width(), 0,
253 *(static_cast<QImage*>(this)));
254 }
255 else
256 {
257 newpainter.drawImage(0, 0, *(static_cast<QImage*>(this)));
258 }
259
260 newpainter.drawImage(0, height()+spacing, mirrorImage);
261 }
262 else if (axis == ReflectAxis::Horizontal)
263 {
264 if (shear < 0)
265 {
266 newpainter.drawImage(0, mirrorImage.height()-height(),
267 *(static_cast<QImage*>(this)));
268 }
269 else
270 {
271 newpainter.drawImage(0, 0, *(static_cast<QImage*>(this)));
272 }
273
274 newpainter.drawImage(width()+spacing, 0, mirrorImage);
275 }
276
277 newpainter.end();
278
279 Assign(temp);
280
281 m_isReflected = true;
282}
283
285{
286 if (isGrayscale())
287 return;
288
289 for (int y = 0; y < height(); ++y)
290 {
291 for (int x = 0; x < width(); ++x)
292 {
293 QRgb oldPixel = pixel(x, y);
294 int greyVal = qGray(oldPixel);
295 setPixel(x, y, qRgba(greyVal, greyVal, greyVal, qAlpha(oldPixel)));
296 }
297 }
298}
299
301{
302 if (!reader || !reader->canRead())
303 return false;
304
305 auto *im = new QImage;
306
307 if (im && reader->read(im))
308 {
309 Assign(*im);
310 delete im;
311 return true;
312 }
313
314 delete im;
315 return false;
316}
317
318bool MythImage::Load(const QString &filename)
319{
320 if (filename.isEmpty())
321 return false;
322
323 QImage *im = nullptr;
324 if (filename.startsWith("myth://"))
325 {
326 // Attempting a file transfer on a file that doesn't exist throws
327 // a lot of noisy warnings on the backend and frontend, so to avoid
328 // that first check it's there
329 QUrl url(filename);
330 QString fname = url.path();
331
332 if (url.hasFragment())
333 fname += '#' + url.fragment();
334
335 QString mythUrl = RemoteFile::FindFile(fname, url.host(), url.userName());
336 if (!mythUrl.isEmpty())
337 {
338 auto *rf = new RemoteFile(mythUrl, false, false, 0ms);
339
340 QByteArray data;
341 bool ret = rf->SaveAs(data);
342
343 delete rf;
344
345 if (ret)
346 {
347 im = new QImage();
348 im->loadFromData(data);
349 }
350 }
351#if 0
352 else
353 LOG(VB_GENERAL, LOG_ERR,
354 QString("MythImage::Load failed to load remote image %1")
355 .arg(filename));
356#endif
357
358 }
359 else if ((filename.startsWith("http://")) ||
360 (filename.startsWith("https://")) ||
361 (filename.startsWith("ftp://")))
362 {
363 QByteArray data;
364 if (GetMythDownloadManager()->download(filename, &data))
365 {
366 im = new QImage();
367 im->loadFromData(data);
368 }
369 }
370 else
371 {
372 QString path = filename;
373 if (path.startsWith('/') ||
374 GetMythUI()->FindThemeFile(path))
375 im = new QImage(path);
376 }
377
378 if (im && im->isNull())
379 {
380 delete im;
381 im = nullptr;
382 }
383
385 if (im)
386 {
387 Assign(*im);
388 delete im;
389 return true;
390 }
391 LOG(VB_GUI, LOG_WARNING, QString("MythImage::Load(%1) failed").arg(filename));
392
393 return false;
394}
395
396void MythImage::MakeGradient(QImage &image, const QColor &begin,
397 const QColor &end, int alpha,
398 BoundaryWanted drawBoundary,
399 FillDirection direction)
400{
401 // Gradient fill colours
402 QColor startColor = begin;
403 QColor endColor = end;
404 startColor.setAlpha(alpha);
405 endColor.setAlpha(alpha);
406
407 // Define Gradient
408 QPoint pointA(0,0);
409 QPoint pointB;
410 if (direction == FillDirection::TopToBottom)
411 {
412 pointB = QPoint(0,image.height());
413 }
414 else if (direction == FillDirection::LeftToRight)
415 {
416 pointB = QPoint(image.width(),0);
417 }
418
419 QLinearGradient gradient(pointA, pointB);
420 gradient.setColorAt(0, startColor);
421 gradient.setColorAt(1, endColor);
422
423 // Draw Gradient
424 QPainter painter(&image);
425 painter.setCompositionMode(QPainter::CompositionMode_Source);
426 painter.fillRect(0, 0, image.width(), image.height(), gradient);
427
428 if (drawBoundary == BoundaryWanted::Yes)
429 {
430 // Draw boundary rect
431 QColor black(0, 0, 0, alpha);
432 painter.setPen(black);
433 QPen pen = painter.pen();
434 pen.setWidth(1);
435 painter.drawRect(image.rect());
436 }
437 painter.end();
438}
439
441 QSize size, const QColor &begin,
442 const QColor &end, uint alpha,
443 FillDirection direction)
444{
445 QImage img(size.width(), size.height(), QImage::Format_ARGB32);
446
447 MakeGradient(img, begin, end, alpha, BoundaryWanted::Yes, direction);
448
449 MythImage *ret = painter->GetFormatImage();
450 ret->Assign(img);
451 ret->m_isGradient = true;
452 ret->m_gradBegin = begin;
453 ret->m_gradEnd = end;
454 ret->m_gradAlpha = alpha;
455 ret->m_gradDirection = direction;
456 return ret;
457}
458
460 : m_fileName(std::move(fileName))
461{
462 if ((m_fileName.startsWith("http://")) ||
463 (m_fileName.startsWith("https://")) ||
464 (m_fileName.startsWith("ftp://")))
465 {
467 if (m_networkReply)
468 setDevice(m_networkReply);
469 }
470 else if (!m_fileName.isEmpty())
471 {
472 if (!m_fileName.startsWith("/") && !QFile::exists(m_fileName))
473 {
474 QString tmpFile = GetMythUI()->GetThemeDir() + '/' + m_fileName;
475 if (QFile::exists(tmpFile))
476 m_fileName = tmpFile;
477 }
478 setFileName(m_fileName);
479 }
480}
481
483{
484 if (m_networkReply)
485 {
486 setDevice(nullptr);
487 m_networkReply->deleteLater();
488 m_networkReply = nullptr;
489 }
490}
bool download(const QString &url, const QString &dest, bool reload=false)
Downloads a URL to a file in blocking mode.
MythImageReader(QString fileName)
Definition: mythimage.cpp:459
QString m_fileName
Definition: mythimage.h:32
QNetworkReply * m_networkReply
Definition: mythimage.h:33
QColor m_gradEnd
Definition: mythimage.h:115
QColor m_gradBegin
Definition: mythimage.h:114
bool m_cached
Definition: mythimage.h:126
QString m_fileName
Definition: mythimage.h:124
FillDirection m_gradDirection
Definition: mythimage.h:117
bool Load(MythImageReader *reader)
Definition: mythimage.cpp:300
~MythImage() override
Definition: mythimage.cpp:38
void SetIsInCache(bool bCached)
Definition: mythimage.cpp:70
bool m_isGradient
Definition: mythimage.h:113
virtual void SetChanged(bool change=true)
Definition: mythimage.h:50
static void MakeGradient(QImage &image, const QColor &begin, const QColor &end, int alpha, BoundaryWanted drawBoundary=BoundaryWanted::Yes, FillDirection direction=FillDirection::TopToBottom)
Definition: mythimage.cpp:396
int m_gradAlpha
Definition: mythimage.h:116
MythPainter * m_parent
Definition: mythimage.h:111
void SetFileName(QString fname)
Definition: mythimage.h:90
bool m_isReflected
Definition: mythimage.h:120
static MythUIHelper * s_ui
Definition: mythimage.h:128
void Orientation(int orientation)
Changes the orientation angle of the image according to the exif rotation values.
Definition: mythimage.cpp:150
void Assign(const QImage &img)
Definition: mythimage.cpp:77
void Reflect(ReflectAxis axis, int shear, int scale, int length, int spacing=0)
Definition: mythimage.cpp:181
MythImage(MythPainter *parent, const char *name="MythImage")
Creates a reference counted image, call DecrRef() to delete.
Definition: mythimage.cpp:25
static MythImage * Gradient(MythPainter *painter, QSize size, const QColor &begin, const QColor &end, uint alpha, FillDirection direction=FillDirection::TopToBottom)
Create a gradient image.
Definition: mythimage.cpp:440
void Resize(QSize newSize, bool preserveAspect=false)
Definition: mythimage.cpp:159
int DecrRef(void) override
Decrements reference count and deletes on 0.
Definition: mythimage.cpp:52
bool m_isOriented
Definition: mythimage.h:119
void ToGreyscale()
Definition: mythimage.cpp:284
int IncrRef(void) override
Increments reference count.
Definition: mythimage.cpp:44
static QImage ApplyExifOrientation(QImage &image, int orientation)
Definition: mythimage.cpp:97
void DeleteFormatImage(MythImage *im)
MythImage * GetFormatImage()
Returns a blank reference counted image in the format required for the Draw functions for this painte...
void ExcludeFromCacheSize(MythImage *Image)
void IncludeInCacheSize(MythImage *Image)
General purpose reference counter.
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
virtual int IncrRef(void)
Increments reference count.
static QString FindFile(const QString &filename, const QString &host, const QString &storageGroup, bool useRegex=false, bool allowFallback=false)
Search all BE's for a file in the give storage group.
unsigned int uint
Definition: compat.h:68
static guint32 * pixel
--------------------------------------------------—**
Definition: goom_core.cpp:24
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
ReflectAxis
Definition: mythimage.h:18
BoundaryWanted
Definition: mythimage.h:20
FillDirection
Definition: mythimage.h:19
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythUIHelper * GetMythUI()
STL namespace.
bool exists(str path)
Definition: xbmcvfs.py:51