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