MythTV  master
galleryslide.cpp
Go to the documentation of this file.
1 #include "galleryslide.h"
2 
3 #include <cmath> // for roundf
4 #include "mythmainwindow.h"
5 #include "mythlogging.h"
6 
7 #include "imagemetadata.h"
8 
9 
10 #define LOC QString("Slide: ")
11 #define SBLOC QString("SlideBuffer: ")
12 
13 
14 // Number of slides to use for buffering image requests.
15 // When browsing quickly the buffer will load consecutive slides until it fills.
16 // If too large, rapid browsing will be stodgy (sequential access) for images that
17 // aren't cached (Cached images are always fast).
18 // If too small, rapid browsing will result in skipping slides rather than flicking
19 // quickly through them.
20 // Minimum is 4: 3 for displaying a transition, 1 to handle load requests
21 #define SLIDE_BUFFER_SIZE 9
22 
23 
29 void AbstractAnimation::Start(bool forwards, float speed)
30 {
31  m_forwards = forwards;
32  m_speed = speed;
33  m_running = true;
34 }
35 
36 
45 void Animation::Set(const QVariant& from, const QVariant& to, int duration,
46  const QEasingCurve& curve, UIEffects::Centre centre)
47 {
48  setStartValue(from);
49  setEndValue(to);
50  m_centre = centre;
51  setDuration(duration);
52  setEasingCurve(curve);
53 }
54 
55 
61 void Animation::Start(bool forwards, float speed)
62 {
63  if (duration() == 0)
64  return;
65 
66  m_elapsed = forwards ? 0 : duration();
67  setCurrentTime(m_elapsed);
68 
69  AbstractAnimation::Start(forwards, speed);
70 }
71 
72 
77 void Animation::Pulse(int interval)
78 {
79  if (!m_running)
80  return;
81 
82  m_elapsed += (m_forwards ? interval : -interval) * m_speed;
83 
84  setCurrentTime(m_elapsed);
85 
86  // Detect completion
87  if ((m_forwards && m_elapsed >= duration())
88  || (!m_forwards && m_elapsed <= 0))
89  Finished();
90 }
91 
92 
97 void Animation::updateCurrentValue(const QVariant &value)
98 {
99  if (m_parent && m_running)
100  {
102 
103  switch (m_type)
104  {
105  case None: break;
106  case Position: m_parent->SetPosition(value.toPoint()); break;
107  case Alpha: m_parent->SetAlpha(value.toInt()); break;
108  case Zoom: m_parent->SetZoom(value.toFloat()); break;
109  case HorizontalZoom: m_parent->SetHorizontalZoom(value.toFloat()); break;
110  case VerticalZoom: m_parent->SetVerticalZoom(value.toFloat()); break;
111  case Angle: m_parent->SetAngle(value.toFloat()); break;
112  }
113  }
114 }
115 
116 
122 {
123  // Signal group when child completes
124  m_group.append(child);
125  connect(child, SIGNAL(finished()), this, SLOT(Finished()));
126 }
127 
128 
133 {
134  qDeleteAll(m_group);
135  m_group.clear();
136 }
137 
138 
143 void SequentialAnimation::Pulse(int interval)
144 {
145  if (!m_running || m_current < 0 || m_current >= m_group.size())
146  return;
147 
148  // Pulse current running child
149  m_group.at(m_current)->Pulse(interval);
150 }
151 
152 
158 void SequentialAnimation::Start(bool forwards, float speed)
159 {
160  if (m_group.empty())
161  return;
162 
163  m_current = forwards ? 0 : m_group.size() - 1;
164 
165  // Start group, then first child
166  GroupAnimation::Start(forwards, speed);
167  m_group.at(m_current)->Start(m_forwards, m_speed);
168 }
169 
170 
176 {
177  // Set group speed for subsequent children
179 
180  // Set active child
181  if (!m_running || m_current < 0 || m_current >= m_group.size())
182  return;
183 
184  m_group.at(m_current)->SetSpeed(speed);
185 }
186 
187 
192 {
193  // Finish group when last child finishes
194  if ((m_forwards && ++m_current == m_group.size())
195  || (!m_forwards && --m_current < 0))
197  else
198  // Start next child
199  m_group.at(m_current)->Start(m_forwards, m_speed);
200 }
201 
202 
207 void ParallelAnimation::Pulse(int interval)
208 {
209  if (m_running)
210  // Pulse all children
211  foreach(AbstractAnimation *animation, m_group)
212  animation->Pulse(interval);
213 }
214 
215 
221 void ParallelAnimation::Start(bool forwards, float speed)
222 {
223  if (m_group.empty())
224  return;
225 
226  m_finished = m_group.size();
227 
228  // Start group, then all children
229  GroupAnimation::Start(forwards, speed);
230  foreach(AbstractAnimation *animation, m_group)
231  animation->Start(m_forwards, m_speed);
232 }
233 
234 
240 {
241  // Set group speed, then all children
243  foreach(AbstractAnimation *animation, m_group)
244  animation->SetSpeed(m_speed);
245 }
246 
247 
252 {
253  // Finish group when last child finishes
254  if (--m_finished == 0)
256 }
257 
258 
263 void PanAnimation::updateCurrentValue(const QVariant &value)
264 {
265  if (m_parent && m_running)
266  {
267  Slide *image = m_parent;
268  image->SetPan(value.toPoint());
269  }
270 }
271 
272 
279 Slide::Slide(MythUIType *parent, const QString& name, MythUIImage *image)
280  : MythUIImage(parent, name)
281 {
282  // Clone from image
283  CopyFrom(image);
284 
285  // Null parent indicates we should become a child of the image (after
286  // copy to avoid recursion)
287  if (!parent)
288  {
289  // Slides sit on top of parent image area
290  SetArea(MythRect(image->GetArea().toQRect()));
291  m_Area.moveTo(0, 0);
292  setParent(image);
293  m_Parent = image;
294  image->AddChild(this);
295  }
296 
297  // Provide animations for pan & zoom
298  if (GetPainter()->SupportsAnimation())
299  {
301  m_panAnimation = new PanAnimation(this);
302  }
303 
304  connect(this, SIGNAL(LoadComplete()), this, SLOT(SlideLoaded()));
305 }
306 
307 
312 {
313  delete m_zoomAnimation;
314  delete m_panAnimation;
315  LOG(VB_GUI, LOG_DEBUG, "Deleted Slide " + objectName());
316 }
317 
318 
323 {
324  m_state = kEmpty;
325  m_data.clear();
326  m_waitingFor.clear();
327  SetCropRect(0, 0, 0, 0);
328  SetVisible(false);
329 }
330 
331 
336 QChar Slide::GetDebugState() const
337 {
338  switch (m_state)
339  {
340  case kEmpty: return 'e';
341  case kFailed: return 'f';
342  case kLoaded: return m_waitingFor ? 'r' : 'a';
343  case kLoading: return m_waitingFor ? 'l' : 'p';
344  }
345  return '?';
346 }
347 
348 
361 bool Slide::LoadSlide(const ImagePtrK& im, int direction, bool notifyCompletion)
362 {
363  m_direction = direction;
364  m_waitingFor = notifyCompletion ? im : ImagePtrK();
365 
366  if (im == m_data)
367  {
368  LOG(VB_FILE, LOG_DEBUG, LOC + QString("Already loading/loaded %1 in %2")
369  .arg(im->m_filePath, objectName()));
370 
371  if (m_state >= kLoaded && notifyCompletion)
372  // Image has been pre-loaded
373  emit ImageLoaded(this);
374 
375  return (m_state >= kLoaded);
376  }
377 
378  // Is a different image loading ?
379  if (m_state == kLoading)
380  {
381  // Can't abort image loads, so must wait for it to finish
382  // before starting new load
383  m_waitingFor = im;
384 
385  LOG(VB_FILE, LOG_DEBUG, LOC + QString("Postponing load of %1 in %2")
386  .arg(im->m_filePath, objectName()));
387 
388  return false;
389  }
390 
391  // Start load
392  m_data = im;
393  m_state = kLoading;
394 
395  if (im->m_type == kVideoFile)
396  {
397  // Use thumbnail, which has already been orientated
398  SetFilename(im->m_thumbNails.at(0).second);
399  SetOrientation(1);
400  }
401  else
402  {
403  // Load image, compensating for any Qt auto-orientation
404  SetFilename(im->m_url);
405  SetOrientation(Orientation(m_data->m_orientation).GetCurrent(true));
406  }
407 
408  // Load in background
409  Load(true);
410  return false;
411 }
412 
413 
421 {
422  m_state = m_Images[0] ? kLoaded : kFailed;
423  if (m_state == kFailed)
424  LOG(VB_GENERAL, LOG_ERR, LOC +
425  QString("Failed to load %1").arg(m_data->m_filePath));
426 
427  // Ignore superseded requests and preloads
428  if (m_data == m_waitingFor)
429  {
430  // Loaded image is the latest requested
431  emit ImageLoaded(this);
432  }
433  else if (m_waitingFor)
434  {
435  LOG(VB_FILE, LOG_DEBUG, LOC + QString("Starting delayed load %1")
436  .arg(m_waitingFor->m_filePath));
437 
438  // Start latest postponed load
440  }
441 }
442 
443 
449 void Slide::Zoom(int percentage)
450 {
451  // Sentinel indicates reset to default zoom
452  float newZoom = (percentage == 0)
453  ? 1.0F
454  : qMax(MIN_ZOOM,
455  qMin(MAX_ZOOM, m_zoom * (1.0F + percentage / 100.0F)));
456  if (newZoom != m_zoom)
457  {
458  if (m_zoomAnimation)
459  {
460  m_zoomAnimation->Set(m_zoom, newZoom, 250, QEasingCurve::OutQuad);
462  }
463  else
464  SetZoom(newZoom);
465  }
466 }
467 
468 
475 void Slide::SetZoom(float zoom)
476 {
477  m_zoom = zoom;
479 
480  // TODO
481  // MythUIImage displaces widget or doesn't centre for some combinations of
482  // zoom centre/cropping so frig centre for now.
484 
485  SetPan(m_pan);
486 }
487 
488 
493 void Slide::Pan(QPoint offset)
494 {
495  // Panning only possible when zoomed in
496  if (m_zoom > 1.0F)
497  {
498  QPoint start = m_pan;
499 
500  // Sentinel indicates reset to centre
501  // Panning is applied to original (unzoomed) image co-ords.
502  // Adjust offset for zoom so that pan moves a constant screen distance rather
503  // than constant image distance
504  QPoint dest = offset.isNull() ? QPoint(0, 0) : start + offset / m_zoom;
505 
506  if (m_panAnimation)
507  {
508  m_panAnimation->Set(start, dest, 250, QEasingCurve::Linear);
510  }
511  else
512  SetPan(dest);
513  }
514 }
515 
516 
523 void Slide::SetPan(QPoint pos)
524 {
525  if (m_state == kFailed)
526  {
527  m_pan = pos;
528  return;
529  }
530 
531  // Determine zoom of largest dimension
532  QRect imageArea = m_Images[m_CurPos]->rect();
533  float hRatio = float(imageArea.height()) / m_Area.height();
534  float wRatio = float(imageArea.width()) / m_Area.width();
535  float ratio = qMax(hRatio, wRatio);
536 
537  if (m_zoom != 0.0F)
538  ratio /= m_zoom;
539 
540  // Determine crop area
541  int h = qMin(int(roundf(m_Area.height() * ratio)), imageArea.height());
542  int w = qMin(int(roundf(m_Area.width() * ratio)), imageArea.width());
543  int x = imageArea.center().x() - w / 2;
544  int y = imageArea.center().y() - h / 2;
545 
546  // Constrain pan to boundaries
547  int limitX = (imageArea.width() - w) / 2;
548  int limitY = (imageArea.height() - h) / 2;
549  m_pan.setX(qMax(qMin(pos.x(), limitX), -limitX));
550  m_pan.setY(qMax(qMin(pos.y(), limitY), -limitY));
551 
552  SetCropRect(x + m_pan.x(), y + m_pan.y(), w, h);
553  SetRedraw();
554 }
555 
556 
561 {
562  // Update zoom/pan animations
563  if (m_zoomAnimation)
564  m_zoomAnimation->Pulse(GetMythMainWindow()->GetDrawInterval());
565 
566  if (m_panAnimation)
567  m_panAnimation->Pulse(GetMythMainWindow()->GetDrawInterval());
568 }
569 
570 
572 {
573  LOG(VB_GUI, LOG_DEBUG, "Deleted Slidebuffer");
574 }
575 
576 
578 {
579  QMutexLocker lock(&m_mutexQ);
580  foreach (Slide *s, m_queue)
581  s->Clear();
582  LOG(VB_GUI, LOG_DEBUG, "Aborted Slidebuffer");
583 }
584 
585 
592 {
593  // Require at least 4 slides: 2 for transitions, 1 to handle further requests
594  // and 1 to prevent solitary slide from being used whilst it is loading
595  int size = qMax(SLIDE_BUFFER_SIZE, 4);
596 
597  // Fill buffer with slides cloned from the XML image widget
598 
599  // Create first as a child of the XML image.
600  auto *slide = new Slide(nullptr, "slide0", &image);
601 
602  // Buffer is notified when it has loaded image
603  connect(slide, SIGNAL(ImageLoaded(Slide *)),
604  this, SLOT(Flush(Slide *)));
605 
606  m_queue.enqueue(slide);
607 
608  // Rest are simple clones of first
609  for (int i = 1; i < size; ++i)
610  {
611  slide = new Slide(&image, QString("slide%1").arg(i), slide);
612 
613  // All slides (except first) start off hidden
614  slide->SetVisible(false);
615 
616  // Buffer is notified when it has loaded image
617  connect(slide, SIGNAL(ImageLoaded(Slide *)),
618  this, SLOT(Flush(Slide *)));
619 
620  m_queue.enqueue(slide);
621  }
622 
623  m_nextLoad = 1;
624 }
625 
626 
632 {
633  QMutexLocker lock(&m_mutexQ);
634 
635  QString state;
636  for (int i = 0; i < m_queue.size(); ++i)
637  {
638  QChar code(m_queue.at(i)->GetDebugState());
639  state += (i == m_nextLoad ? code.toUpper() : code);
640  }
641  return QString("[%1] (%2)").arg(state, m_queue.head()->objectName());
642 }
643 
644 
652 bool SlideBuffer::Load(const ImagePtrK& im, int direction)
653 {
654  if (!im)
655  return false;
656 
657  QMutexLocker lock(&m_mutexQ);
658 
659  // Start loading image in next available slide
660  Slide *slide = m_queue.at(m_nextLoad);
661 
662  // Further load requests will go to same slide if no free ones are available
663  if (m_nextLoad < m_queue.size() - 1)
664  ++m_nextLoad;
665 
666  LOG(VB_FILE, LOG_DEBUG, SBLOC + QString("Loading %1 in %2, %3")
667  .arg(im->m_filePath, slide->objectName()).arg(BufferState()));
668 
669  return slide->LoadSlide(im, direction, true);
670 }
671 
672 
678 {
679  if (!im)
680  return;
681 
682  QMutexLocker lock(&m_mutexQ);
683 
684  // Start loading image in next available slide
685  Slide *slide = m_queue.at(m_nextLoad);
686 
687  LOG(VB_FILE, LOG_DEBUG, SBLOC + QString("Preloading %1 in %2, %3")
688  .arg(im->m_filePath, slide->objectName()).arg(BufferState()));
689 
690  // Load silently
691  slide->LoadSlide(im);
692 }
693 
694 
700 {
701  QMutexLocker lock(&m_mutexQ);
702 
703  // Reset slide & return to buffer for re-use
704  Slide *slide = m_queue.dequeue();
705  slide->Clear();
706  m_queue.enqueue(slide);
707 
708  QString name = slide->objectName();
709 
710  // Free constrained load ptr now a spare slide is available
711  if (!m_queue.at(--m_nextLoad)->IsEmpty())
712  ++m_nextLoad;
713 
714  LOG(VB_FILE, LOG_DEBUG, SBLOC + QString("Released %1").arg(name));
715 
716  // Flush any pending slides that originate from multiple requests (skipping)
717  Flush(m_queue.head(), "Pending");
718 }
719 
720 
727 void SlideBuffer::Flush(Slide *slide, const QString& reason)
728 {
729  QMutexLocker lock(&m_mutexQ);
730 
731  // Determine number of consecutive slides that are now available after head
732  // Include last slide to ensure transition speed is consistent: it will never
733  // be displayed because queue size is always > 2
734  int available = 1;
735  while (available < m_queue.size() && m_queue.at(available)->IsLoaded())
736  ++available;
737 
738  if (available == 1)
739  return;
740 
741  // Notify that more slides are available
742  ImagePtrK im = slide->GetImageData();
743  QString path = im ? im->m_filePath : "Unknown";
744 
745  LOG(VB_FILE, LOG_DEBUG, SBLOC + QString("%1 %2 in %3, %4")
746  .arg(reason, path, slide->objectName()).arg(BufferState()));
747 
748  emit SlideReady(--available);
749 }
void SetSpeed(float speed) override
Change speed of group and all child animations.
A video.
Definition: imagetypes.h:39
QPoint m_pan
Pan position (0,0) = no pan.
Definition: galleryslide.h:200
PanAnimation * m_panAnimation
Dedicated animation for panning, if supported.
Definition: galleryslide.h:199
int m_elapsed
Current millisec position within animation, 0..duration.
Definition: galleryslide.h:86
void Flush(Slide *, const QString &reason="Loaded")
Signal if any slides are waiting to be displayed.
Encapsulates Exif orientation processing.
Definition: imagemetadata.h:58
virtual MythPainter * GetPainter(void)
void finished()
Signals animation has finished.
void SetVerticalZoom(float zoom)
Definition: mythuitype.cpp:955
void Pulse() override
Update pan & zoom animations.
void SlideLoaded()
An image has completed loading.
virtual void Finished()
To be called when animation completes.
Definition: galleryslide.h:40
virtual void Add(AbstractAnimation *child)
Add child animation to group.
Slide(MythUIType *parent, const QString &name, MythUIImage *image)
Clone slide from a theme MythUIImage.
Specialised animation for panning slideshow images (MythUI doesn't support panning)
Definition: galleryslide.h:148
void SetAlpha(int newalpha)
Definition: mythuitype.cpp:924
void SetRedraw(void)
Definition: mythuitype.cpp:295
bool m_forwards
Play direction.
Definition: galleryslide.h:47
Image widget, displays a single image or multiple images in sequence.
Definition: mythuiimage.h:97
void Start(bool forwards, float speed=1.0) override
Start sequential animation.
void Finished() override
A child animation has completed.
virtual void Pulse(int interval)=0
Base animation class that is driven by a Myth pulse and implements variable speed.
Definition: galleryslide.h:27
#define MAX_ZOOM
Definition: galleryslide.h:21
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 SetHorizontalZoom(float zoom)
Definition: mythuitype.cpp:949
int m_nextLoad
Index of first spare slide, (or last slide if none spare)
Definition: galleryslide.h:253
bool Load(const ImagePtrK &im, int direction)
Assign an image to next available slide, start loading and signal when done.
ImagePtrK m_data
The image currently loading/loaded.
Definition: galleryslide.h:192
void updateCurrentValue(const QVariant &value) override
Update pan value.
UIEffects m_Effects
Definition: mythuitype.h:257
The base class on which all widgets and screens are based.
Definition: mythuitype.h:63
bool Load(bool allowLoadInBackground=true, bool forceStat=false)
Load the image(s), wraps ImageLoader::LoadImage()
void ReleaseCurrent()
Move head slide to back of queue and flush waiting slides.
void SetPosition(int x, int y)
Convenience method, calls SetPosition(const MythPoint&) Override that instead to change functionality...
Definition: mythuitype.cpp:519
Handles Exif/FFMpeg metadata tags for images.
QSharedPointer< ImageItemK > ImagePtrK
Definition: imagetypes.h:179
virtual void Start(bool forwards, float speed=1.0)
Initialise & start base animation.
void CopyFrom(MythUIType *base) override
Copy this widgets state from another.
Centre m_centre
void SetOrientation(int orientation)
Saves the exif orientation value of the first image in the widget.
void SetZoom(float)
Sets slide zoom.
float m_zoom
Current zoom, 1.0 = fullsize.
Definition: galleryslide.h:195
ImagePtrK GetImageData() const
Definition: galleryslide.h:166
virtual void SetVisible(bool visible)
void Pulse(int interval) override
Progress sequential animation.
QRect toQRect(void) const
Definition: mythrect.cpp:354
bool m_running
True whilst animation is active.
Definition: galleryslide.h:48
void Finished() override
A child animation has completed.
void Initialise(MythUIImage &image)
Construct buffer.
void updateCurrentValue(const QVariant &value) override
Update animated value.
void Preload(const ImagePtrK &im)
Load an image in next available slide.
int GetCurrent(bool compensate)
Determines orientation required for an image.
virtual MythRect GetArea(void) const
If the object has a minimum area defined, return it, other wise return the default area.
Definition: mythuitype.cpp:863
void SetSpeed(float speed) override
Definition: galleryslide.h:99
QMutex m_mutexQ
Queue protection.
Definition: galleryslide.h:251
void Pan(QPoint offset)
Initiate pan.
Defines specialised images used by the Gallery slideshow and the animation framework used by transfor...
virtual void SetSpeed(float speed)
Definition: galleryslide.h:34
Wrapper around QRect allowing us to handle percentage and other relative values for areas in mythui.
Definition: mythrect.h:17
SlideState m_state
Slide validity.
Definition: galleryslide.h:191
bool LoadSlide(const ImagePtrK &im, int direction=0, bool notifyCompletion=false)
Load slide with an image.
float m_speed
Real-time = 1.0, Double-speed = 2.0.
Definition: galleryslide.h:49
MythRect m_Area
Definition: mythuitype.h:249
QList< AbstractAnimation * > m_group
Definition: galleryslide.h:105
QChar GetDebugState() const
Return debug status.
void SetSpeed(float speed) override
Change speed of current child animation and all subsequent ones.
void Clear()
Reset slide to unused state.
void SetPan(QPoint pos)
Sets slide pan.
void ImageLoaded(Slide *)
Generated when the last requested image has loaded.
#define SBLOC
#define MIN_ZOOM
Definition: galleryslide.h:20
~Slide()
Destructor.
MythMainWindow * GetMythMainWindow(void)
void Set(const QVariant &from, const QVariant &to, int duration=500, const QEasingCurve &curve=QEasingCurve::InOutCubic, UIEffects::Centre=UIEffects::Middle)
Initialises an animation.
Slide * m_parent
Image to be animated.
Definition: galleryslide.h:81
void LoadComplete()
virtual void SetArea(const MythRect &rect)
Definition: mythuitype.cpp:591
void Start(bool forwards, float speed=1.0) override
Initialise & start base animation.
Definition: galleryslide.h:97
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
void Zoom(int percentage)
Initiate slide zoom.
A specialised image for slideshows.
Definition: galleryslide.h:157
int m_direction
Navigation that created this image, -1 = Prev, 0 = Update, 1 = Next.
Definition: galleryslide.h:197
#define LOC
QQueue< Slide * > m_queue
Queue of slides.
Definition: galleryslide.h:252
void SetFilename(const QString &filename)
Must be followed by a call to Load() to load the image.
ImagePtrK m_waitingFor
The most recently requested image. Null for preloads. Differs from m_data when skipping.
Definition: galleryslide.h:194
QHash< int, MythImage * > m_Images
Definition: mythuiimage.h:170
A single animation controlling alpha, zoom, rotation and position.
Definition: galleryslide.h:55
#define SLIDE_BUFFER_SIZE
MythUIType * m_Parent
Definition: mythuitype.h:270
unsigned int m_CurPos
Definition: mythuiimage.h:178
Animation * m_zoomAnimation
Dedicated animation for zoom, if supported.
Definition: galleryslide.h:198
void Start(bool forwards, float speed=1.0) override
Start parallel group. All children play simultaneously.
int m_current
Index of child currently playing.
Definition: galleryslide.h:124
void Pulse(int interval) override
Progress parallel animations.
void Pulse(int interval) override
Progress single animation.
QString BufferState()
Determines buffer state for debug logging.
void SetAngle(float angle)
Definition: mythuitype.cpp:961
void Clear() override
Delete all child animations.
int m_finished
Count of child animations that have finished.
Definition: galleryslide.h:142
UIEffects::Centre m_centre
Definition: galleryslide.h:83
void SetCentre(UIEffects::Centre centre)
Definition: mythuitype.cpp:938
void Start(bool forwards=true, float speed=1.0) override
Start a single animation.
void AddChild(MythUIType *child)
Add a child UIType.
Definition: mythuitype.cpp:85
void SlideReady(int count)
Signals that buffer has (count) loaded slides awaiting display.
Type m_type
Definition: galleryslide.h:82