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