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