9 changed files with 1174 additions and 18 deletions
@ -0,0 +1,201 @@ |
|||||
|
//
|
||||
|
// magSlide.cpp
|
||||
|
// Copyright (c) 2017 Cristobal Mendoza
|
||||
|
// http://cuppetellimendoza.com
|
||||
|
//
|
||||
|
|
||||
|
#include "magSlide.h" |
||||
|
#include "magSlideTransition.h" |
||||
|
|
||||
|
#pragma mark magSlide |
||||
|
|
||||
|
int magSlide::idCount = 0; |
||||
|
|
||||
|
magSlide::magSlide(std::string type) |
||||
|
{ |
||||
|
this->type = type; |
||||
|
position = ofPoint(0, 0); |
||||
|
id = magSlide::idCount; |
||||
|
magSlide::idCount++; |
||||
|
} |
||||
|
|
||||
|
void magSlide::update(u_int64_t deltaTime) |
||||
|
{ |
||||
|
runningTime += deltaTime; |
||||
|
|
||||
|
switch (slideState) |
||||
|
{ |
||||
|
case SlideState::BuildIn: |
||||
|
if (runningTime >= buildInDuration) |
||||
|
{ |
||||
|
setState(Normal); |
||||
|
activeTransition = nullptr; |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case SlideState::Normal: |
||||
|
if (runningTime >= buildOutStartTime) |
||||
|
{ |
||||
|
setState(BuildOut); |
||||
|
if (buildOut != nullptr) |
||||
|
{ |
||||
|
activeTransition = buildOut; |
||||
|
activeTransition->start(); |
||||
|
} |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case SlideState::BuildOut: |
||||
|
if (runningTime >= endTime) |
||||
|
{ |
||||
|
setState(Complete); |
||||
|
activeTransition = nullptr; |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
void magSlide::setSize(float w, float h) |
||||
|
{ |
||||
|
width = w; |
||||
|
height = h; |
||||
|
} |
||||
|
|
||||
|
void magSlide::setResizeOption(magSlide::ResizeOptions resizeOption) |
||||
|
{ |
||||
|
this->resizeOption = resizeOption; |
||||
|
} |
||||
|
|
||||
|
magSlide::ResizeOptions magSlide::getResizeOption() const |
||||
|
{ |
||||
|
return resizeOption; |
||||
|
} |
||||
|
|
||||
|
void magSlide::setDuration(u_int64_t duration) |
||||
|
{ |
||||
|
this->duration = duration; |
||||
|
} |
||||
|
|
||||
|
void magSlide::setState(SlideState state) |
||||
|
{ |
||||
|
// Don't do anything if the new state is the same
|
||||
|
// as the current one:
|
||||
|
if (slideState == state) return; |
||||
|
|
||||
|
slideState = state; |
||||
|
ofEventArgs args; |
||||
|
ofNotifyEvent(slideStateChangedEvent, args, this); |
||||
|
|
||||
|
if (slideState == Complete) |
||||
|
{ |
||||
|
ofNotifyEvent(slideCompleteEvent, args, this); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void magSlide::setTransitionDuration(u_int64_t tDuration) |
||||
|
{ |
||||
|
buildInDuration = buildOutDuration = tDuration; |
||||
|
} |
||||
|
|
||||
|
const std::string magSlide::getSlideStateName() |
||||
|
{ |
||||
|
switch (slideState) |
||||
|
{ |
||||
|
case SlideState::BuildIn: |
||||
|
return "BuildIn"; |
||||
|
case SlideState::BuildOut: |
||||
|
return "BuildOut"; |
||||
|
case Normal: |
||||
|
return "Normal"; |
||||
|
case SlideState::Complete: |
||||
|
return "Complete"; |
||||
|
case SlideState::Off: |
||||
|
return "Off"; |
||||
|
} |
||||
|
|
||||
|
return "unknown"; |
||||
|
} |
||||
|
|
||||
|
void magSlide::start(u_int64_t startTime) |
||||
|
{ |
||||
|
this->startTime = startTime; |
||||
|
runningTime = 0; |
||||
|
endTime = duration + buildInDuration + buildOutDuration; |
||||
|
buildOutStartTime = endTime - buildOutDuration; |
||||
|
slideState = magSlide::SlideState::BuildIn; |
||||
|
if (buildIn != nullptr) |
||||
|
{ |
||||
|
activeTransition = buildIn; |
||||
|
activeTransition->start(); |
||||
|
} |
||||
|
isComplete = false; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
////////////////////////////////////////////////////////
|
||||
|
#pragma mark MAG_IMAGE_SLIDE |
||||
|
////////////////////////////////////////////////////////
|
||||
|
|
||||
|
magImageSlide::magImageSlide() : magSlide("magImageSlide") {} |
||||
|
|
||||
|
magImageSlide::~magImageSlide() {} |
||||
|
|
||||
|
void magImageSlide::setup(ofImage &image) |
||||
|
{ |
||||
|
// Make a copy of the image:
|
||||
|
this->image = ofImage(image); |
||||
|
image.setAnchorPercent(0.5, 0.5); |
||||
|
width = image.getWidth(); |
||||
|
height = image.getHeight(); |
||||
|
} |
||||
|
|
||||
|
void magImageSlide::draw() |
||||
|
{ |
||||
|
image.draw(position, width, height); |
||||
|
} |
||||
|
|
||||
|
////////////////////////////////////////////////////////
|
||||
|
#pragma mark MAG_VIDEO_SLIDE |
||||
|
////////////////////////////////////////////////////////
|
||||
|
|
||||
|
magVideoSlide::magVideoSlide() : magSlide("magVideoSlide") |
||||
|
{} |
||||
|
|
||||
|
magVideoSlide::~magVideoSlide() |
||||
|
{ |
||||
|
videoPlayer.stop(); |
||||
|
} |
||||
|
|
||||
|
bool magVideoSlide::setup(ofFile &file, bool useVideoDuration) |
||||
|
{ |
||||
|
|
||||
|
bool success = videoPlayer.load(file.getAbsolutePath()); |
||||
|
|
||||
|
if (success) |
||||
|
{ |
||||
|
videoPlayer.setAnchorPercent(0.5, 0.5); |
||||
|
if (useVideoDuration) |
||||
|
{ |
||||
|
useVideoForDuration(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return success; |
||||
|
} |
||||
|
|
||||
|
void magVideoSlide::update() |
||||
|
{ |
||||
|
videoPlayer.update(); |
||||
|
} |
||||
|
|
||||
|
void magVideoSlide::draw() |
||||
|
{ |
||||
|
videoPlayer.draw(position.x, position.y, width, height); |
||||
|
} |
||||
|
|
||||
|
void magVideoSlide::useVideoForDuration() |
||||
|
{ |
||||
|
duration = u_int64_t((videoPlayer.getDuration()*1000)) - buildInDuration - buildOutDuration; |
||||
|
} |
||||
|
|
@ -0,0 +1,227 @@ |
|||||
|
//
|
||||
|
// Created by Cristobal Mendoza on 11/9/17.
|
||||
|
//
|
||||
|
|
||||
|
#ifndef MAGSLIDE_H |
||||
|
#define MAGSLIDE_H |
||||
|
|
||||
|
#include "ofMain.h" |
||||
|
|
||||
|
class magSlideTransition; |
||||
|
|
||||
|
class magSlide |
||||
|
{ |
||||
|
public: |
||||
|
magSlide(std::string type); |
||||
|
// ~magSlide();
|
||||
|
virtual void update(u_int64_t deltaTime); |
||||
|
virtual void draw() = 0; |
||||
|
|
||||
|
/**
|
||||
|
* Sets the slide up for playback. This method must be |
||||
|
* called before displaying the slide. |
||||
|
* @param startTime |
||||
|
*/ |
||||
|
void start(u_int64_t startTime); |
||||
|
|
||||
|
enum ResizeOptions : short |
||||
|
{ |
||||
|
/**
|
||||
|
* No resizing applied, displays the slide in its native pixel dimensions. |
||||
|
* This is the default behavior. |
||||
|
*/ |
||||
|
None = 0, |
||||
|
/**
|
||||
|
* Explicitly set a slide to display in its native dimension. |
||||
|
* None and NoResize result in the same output, but if you specify |
||||
|
* a default resizing option in your slideshow and you want a particular |
||||
|
* slide not to resize, you must specify this option. Otherwise the |
||||
|
* slide show option will apply. |
||||
|
*/ |
||||
|
NoResize, |
||||
|
|
||||
|
/**
|
||||
|
* Sets width and height to match the source's. |
||||
|
* This will distort the slide if the aspect ratios |
||||
|
* don't match. |
||||
|
*/ |
||||
|
Fit, |
||||
|
|
||||
|
/**
|
||||
|
* Resizes the largest dimension to the source's max, |
||||
|
* the other dimension is resized proportionally. |
||||
|
* This is the default behavior. |
||||
|
*/ |
||||
|
FitProportionally, |
||||
|
|
||||
|
/**
|
||||
|
* Resizes the shortest dimensions to the source's max, |
||||
|
*/ |
||||
|
FillProportionally, |
||||
|
}; |
||||
|
|
||||
|
enum SlideState : short |
||||
|
{ |
||||
|
Off = 0, |
||||
|
BuildIn, |
||||
|
Normal, |
||||
|
BuildOut, |
||||
|
Complete |
||||
|
}; |
||||
|
|
||||
|
const std::string &getType() |
||||
|
{ return type; } |
||||
|
|
||||
|
float getWidth() |
||||
|
{ return width; } |
||||
|
|
||||
|
float getHeight() |
||||
|
{ return height; } |
||||
|
|
||||
|
/**
|
||||
|
* Change the display size of a slide. This will implicitly |
||||
|
* set resizeOptions to ResizeOption.NoResize. |
||||
|
* This method does not resize the pixel source of the slide. |
||||
|
* @param w The new width |
||||
|
* @param h The new height |
||||
|
*/ |
||||
|
virtual void setSize(float w, float h); |
||||
|
|
||||
|
u_int64_t getDuration() |
||||
|
{ |
||||
|
return duration; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* Sets the display time of the slide, excluding |
||||
|
* builds (in or out) |
||||
|
* @param duration in milliseconds. |
||||
|
*/ |
||||
|
void setDuration(u_int64_t duration); |
||||
|
|
||||
|
/**
|
||||
|
* Sets the duration of the buildIn and buildOut |
||||
|
* transitions. Transition times are added to the |
||||
|
* duration of the slide. |
||||
|
* @param tDuration in milliseconds. |
||||
|
*/ |
||||
|
void setTransitionDuration(u_int64_t tDuration); |
||||
|
ResizeOptions getResizeOption() const; |
||||
|
void setResizeOption(ResizeOptions resizeOption); |
||||
|
|
||||
|
SlideState getSlideState() const |
||||
|
{ |
||||
|
return slideState; |
||||
|
} |
||||
|
|
||||
|
bool isSlideComplete() |
||||
|
{ return isComplete; } |
||||
|
|
||||
|
int getId() |
||||
|
{ return id; } |
||||
|
|
||||
|
const std::string getSlideStateName(); |
||||
|
|
||||
|
//////////////////////////////
|
||||
|
/// Events
|
||||
|
//////////////////////////////
|
||||
|
ofEvent<ofEventArgs> slideCompleteEvent; |
||||
|
ofEvent<ofEventArgs> slideStateChangedEvent; |
||||
|
|
||||
|
friend class magSlideShowSource; |
||||
|
|
||||
|
protected: |
||||
|
int id; |
||||
|
std::string type; |
||||
|
float width; |
||||
|
float height; |
||||
|
ofPoint position; |
||||
|
ResizeOptions resizeOption = None; |
||||
|
SlideState slideState = Off; |
||||
|
bool isComplete; |
||||
|
|
||||
|
std::shared_ptr<magSlideTransition> buildIn = nullptr; |
||||
|
std::shared_ptr<magSlideTransition> buildOut = nullptr; |
||||
|
std::shared_ptr<magSlideTransition> activeTransition = nullptr; |
||||
|
/**
|
||||
|
* The duration of the slide in millis, not counting builds |
||||
|
*/ |
||||
|
u_int64_t duration; |
||||
|
|
||||
|
/**
|
||||
|
* The start time of the slide in milliseconds, in "local time". |
||||
|
* In practice, this means that the start time is always 0. |
||||
|
*/ |
||||
|
u_int64_t startTime; |
||||
|
|
||||
|
/**
|
||||
|
* The end time of the slide in milliseconds, in "local time". |
||||
|
* The endTime is startTime + buildInDuration + duration + buildOutDuration. |
||||
|
*/ |
||||
|
u_int64_t endTime; |
||||
|
|
||||
|
/**
|
||||
|
* The duration of the build in transition or effect, in milliseconds. |
||||
|
*/ |
||||
|
u_int64_t buildInDuration; |
||||
|
|
||||
|
/**
|
||||
|
* The duration of the build out transition or effect, in milliseconds. |
||||
|
*/ |
||||
|
u_int64_t buildOutDuration; |
||||
|
|
||||
|
/**
|
||||
|
* The "local time" start of the build out transition. This time should also |
||||
|
* signal the enqueuing of the next slide, if applicable. |
||||
|
*/ |
||||
|
u_int64_t buildOutStartTime; |
||||
|
|
||||
|
/**
|
||||
|
* The amount of time the slide has been displayed, in milliseconds. |
||||
|
* This constitutes the "local time" of the slide. |
||||
|
*/ |
||||
|
u_int64_t runningTime; |
||||
|
|
||||
|
void setState(SlideState state); |
||||
|
|
||||
|
static int idCount; |
||||
|
}; |
||||
|
|
||||
|
class magImageSlide : public magSlide |
||||
|
{ |
||||
|
public: |
||||
|
magImageSlide(); |
||||
|
~ magImageSlide(); |
||||
|
/**
|
||||
|
* Sets up an image slide. |
||||
|
* @param image is copied into the member magImageSlide::image and is |
||||
|
* not modified in any way. |
||||
|
*/ |
||||
|
void setup(ofImage &image); |
||||
|
|
||||
|
void draw(); |
||||
|
|
||||
|
protected: |
||||
|
ofImage image; |
||||
|
}; |
||||
|
|
||||
|
class magVideoSlide : public magSlide |
||||
|
{ |
||||
|
public: |
||||
|
magVideoSlide(); |
||||
|
~magVideoSlide(); |
||||
|
bool setup(ofFile &file, bool useVideoDuration = false); |
||||
|
void update(); |
||||
|
void draw(); |
||||
|
|
||||
|
/**
|
||||
|
* Sets the slide duration to the duration of the video. |
||||
|
* magSlide::duration will be changed by this call, so if you later |
||||
|
* change your mind you'll have to use magSlide::setDuration. |
||||
|
*/ |
||||
|
void useVideoForDuration(); |
||||
|
protected: |
||||
|
ofVideoPlayer videoPlayer; |
||||
|
}; |
||||
|
|
||||
|
#endif |
@ -0,0 +1,415 @@ |
|||||
|
//
|
||||
|
// magSlideShowSource.cpp
|
||||
|
// Copyright (c) 2017 Cristobal Mendoza
|
||||
|
// http://cuppetellimendoza.com
|
||||
|
//
|
||||
|
|
||||
|
|
||||
|
#include "magSlideShowSource.h" |
||||
|
#include "magSlide.h" |
||||
|
#include "magSlideTransition.h" |
||||
|
|
||||
|
magSlideShowSource::magSlideShowSource() |
||||
|
{ |
||||
|
name = "Slide Show Source"; |
||||
|
currentSlideIndex = 0; |
||||
|
isPlaying = false; |
||||
|
} |
||||
|
|
||||
|
bool magSlideShowSource::initialize(magSlideShowSource::Settings settings) |
||||
|
{ |
||||
|
this->settings = settings; |
||||
|
bool success = true; |
||||
|
|
||||
|
if (settings.width <= 0 || settings.height <= 0) |
||||
|
{ |
||||
|
ofLogError("magSlideShowSource::initialize") << "Invalid value for width or height. Width and height " |
||||
|
"must be assigned in your Settings struct!"; |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// Allocate the FBO:
|
||||
|
allocate(settings.width, settings.height); |
||||
|
|
||||
|
// If there is a path in slidesFolderPath, attempt
|
||||
|
// to load the folder and any files in it:
|
||||
|
if (!settings.slidesFolderPath.empty()) |
||||
|
{ |
||||
|
// ofDirectory dir = ofDirectory(settings.slidesFolderPath);
|
||||
|
success = createFromFolderContents(settings.slidesFolderPath); |
||||
|
|
||||
|
if (!success) |
||||
|
{ |
||||
|
ofLogError("magSlideShowSource::initialize") << "Failed to create slide show from folder " |
||||
|
<< settings.slidesFolderPath; |
||||
|
return success; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
else if (!settings.slideshowFilePath.empty()) |
||||
|
{ |
||||
|
// try to load slide show from xml
|
||||
|
success = false; |
||||
|
} |
||||
|
return success; |
||||
|
} |
||||
|
|
||||
|
void magSlideShowSource::setup() |
||||
|
{ |
||||
|
ofx::piMapper::FboSource::setup(); |
||||
|
} |
||||
|
|
||||
|
void magSlideShowSource::update() |
||||
|
{ |
||||
|
if (!isPlaying) return; |
||||
|
|
||||
|
auto nowTime = ofGetElapsedTimeMillis(); |
||||
|
deltaTime = nowTime-lastTime; |
||||
|
runningTime += deltaTime; |
||||
|
lastTime = nowTime; |
||||
|
|
||||
|
// ofLogVerbose() << "Delta: " << deltaTime << " running: " << runningTime;
|
||||
|
|
||||
|
for (auto &slide : activeSlides) |
||||
|
{ |
||||
|
slide->update(deltaTime); |
||||
|
} |
||||
|
|
||||
|
// Erase any complete slides:
|
||||
|
auto iter = activeSlides.begin(); |
||||
|
for (; iter < activeSlides.end(); iter++) |
||||
|
{ |
||||
|
if ((*iter)->isSlideComplete()) |
||||
|
{ |
||||
|
// ofLogVerbose() << "Removing from active slides id: " << (*iter)->getId();
|
||||
|
activeSlides.erase(iter); |
||||
|
--iter; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (activeSlides.size() == 0 && isPlaying) |
||||
|
{ |
||||
|
ofEventArgs args; |
||||
|
isPlaying = false; |
||||
|
ofNotifyEvent(slideshowCompleteEvent, args, this); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void magSlideShowSource::draw() |
||||
|
{ |
||||
|
ofBackground(0, 0); |
||||
|
ofPushMatrix(); |
||||
|
ofTranslate(getWidth()/2.0f, getHeight()/2.0f); |
||||
|
ofSetRectMode(OF_RECTMODE_CENTER); |
||||
|
for (auto &slide : activeSlides) |
||||
|
{ |
||||
|
ofSetColor(255); |
||||
|
ofFill(); |
||||
|
slide->draw(); |
||||
|
} |
||||
|
ofPopMatrix(); |
||||
|
} |
||||
|
|
||||
|
bool magSlideShowSource::createFromFolderContents(std::string path) |
||||
|
{ |
||||
|
ofDirectory dir = ofDirectory(path); |
||||
|
slides.clear(); |
||||
|
|
||||
|
if (!dir.isDirectory()) |
||||
|
{ |
||||
|
ofLogError("magSlideShowSource::createFromFolderContents") << "Folder path " << dir.getAbsolutePath() |
||||
|
<< " is not a directory"; |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
auto sortedDir = dir.getSorted(); |
||||
|
auto files = sortedDir.getFiles(); |
||||
|
|
||||
|
if (files.size() < 1) |
||||
|
{ |
||||
|
ofLogError("magSlideShowSource::createFromFolderContents") << "Folder " << dir.getAbsolutePath() << " is empty"; |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
ofImage tempImage; |
||||
|
for (auto &file : files) |
||||
|
{ |
||||
|
if (tempImage.load(file)) |
||||
|
{ |
||||
|
// make a new image slide
|
||||
|
auto slide = std::make_shared<magImageSlide>(); |
||||
|
slide->setup(tempImage); |
||||
|
slide->setDuration(static_cast<u_int64_t>(settings.slideDuration*1000)); |
||||
|
slide->setTransitionDuration(static_cast<u_int64_t>(settings.transitionDuration*1000)); |
||||
|
// if (settings.transitionName == "")
|
||||
|
addSlide(slide); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
auto ext = ofToLower(file.getExtension()); |
||||
|
|
||||
|
static std::vector<std::string> movieExtensions = { |
||||
|
"mov", "qt", // Mac
|
||||
|
"mp4", "m4p", "m4v", // MPEG
|
||||
|
"mpg", "mp2", "mpeg", "mpe", "mpv", "m2v", // MPEG
|
||||
|
"3gp", // Phones
|
||||
|
"avi", "wmv", "asf", // Windows
|
||||
|
"webm", "mkv", "flv", "vob", // Other containers
|
||||
|
"ogv", "ogg", |
||||
|
"drc", "mxf" |
||||
|
}; |
||||
|
|
||||
|
// Check if the extension matches known movie formats:
|
||||
|
if (ofContains(movieExtensions, ext)) |
||||
|
{ |
||||
|
// Make a new video slide
|
||||
|
auto slide = std::make_shared<magVideoSlide>(); |
||||
|
if (slide->setup(file)) |
||||
|
{ |
||||
|
slide->setDuration(settings.slideDuration*1000.0); |
||||
|
slide->setTransitionDuration(settings.transitionDuration*1000.0); |
||||
|
addSlide(slide); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
ofLogError("magSlideShowSource") << "Failed loading video: " << file.getAbsolutePath(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (slides.size() > 0) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
bool magSlideShowSource::loadSlideShow(std::string slideShowXmlPath) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
void magSlideShowSource::addSlide(std::shared_ptr<magSlide> slide) |
||||
|
{ |
||||
|
// ofLogVerbose("addSlide") << slide->getId();
|
||||
|
slides.push_back(slide); |
||||
|
auto rOption = slide->getResizeOption(); |
||||
|
|
||||
|
// If the slide does not have a resize option assign
|
||||
|
// the slide show's option
|
||||
|
if (rOption == magSlide::ResizeOptions::None) |
||||
|
{ |
||||
|
rOption = settings.resizeOption; |
||||
|
} |
||||
|
|
||||
|
// Resize the slide according to the resize option:
|
||||
|
switch (rOption) |
||||
|
{ |
||||
|
float sw, sh, ratio; |
||||
|
|
||||
|
case magSlide::ResizeOptions::FitProportionally: |
||||
|
sw = slide->getWidth(); |
||||
|
sh = slide->getHeight(); |
||||
|
|
||||
|
if (sw > sh) |
||||
|
{ |
||||
|
ratio = (float) getWidth()/sw; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
ratio = (float) getHeight()/sh; |
||||
|
} |
||||
|
|
||||
|
slide->setSize(sw*ratio, sh*ratio); |
||||
|
break; |
||||
|
|
||||
|
case magSlide::ResizeOptions::FillProportionally: |
||||
|
sw = slide->getWidth(); |
||||
|
sh = slide->getHeight(); |
||||
|
|
||||
|
if (sw > sh) |
||||
|
{ |
||||
|
ratio = (float) getHeight()/sh; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
ratio = (float) getWidth()/sw; |
||||
|
} |
||||
|
|
||||
|
slide->setSize(sw*ratio, sh*ratio); |
||||
|
break; |
||||
|
|
||||
|
case magSlide::Fit: |
||||
|
slide->setSize(getWidth(), getHeight()); |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
// Add transitions:
|
||||
|
|
||||
|
if (!settings.transitionName.empty()) |
||||
|
{ |
||||
|
static ofParameterGroup bogusParamGroup; // This is temporary so that things compile
|
||||
|
slide->buildIn = magSlideTransition::createTransition(settings.transitionName, |
||||
|
slide, |
||||
|
bogusParamGroup, |
||||
|
slide->buildInDuration); |
||||
|
slide->buildOut = magSlideTransition::createTransition(settings.transitionName, |
||||
|
slide, |
||||
|
bogusParamGroup, |
||||
|
slide->buildOutDuration); |
||||
|
} |
||||
|
//// void method(const void * sender, ArgumentsType &args)
|
||||
|
ofAddListener(slide->slideStateChangedEvent, this, &magSlideShowSource::slideStateChanged); |
||||
|
ofAddListener(slide->slideCompleteEvent, this, &magSlideShowSource::slideComplete); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
void magSlideShowSource::play() |
||||
|
{ |
||||
|
if (!isPlaying) |
||||
|
{ |
||||
|
runningTime = 0; |
||||
|
lastTime = ofGetElapsedTimeMillis(); |
||||
|
isPlaying = true; |
||||
|
auto currentSlide = slides[currentSlideIndex]; |
||||
|
enqueueSlide(currentSlide, ofGetElapsedTimeMillis()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void magSlideShowSource::pause() |
||||
|
{ |
||||
|
isPlaying = false; |
||||
|
} |
||||
|
|
||||
|
void magSlideShowSource::playNextSlide() |
||||
|
{ |
||||
|
//TODO
|
||||
|
// I should check here to see if there are less than two slides.
|
||||
|
// If so, we should probably return
|
||||
|
|
||||
|
currentSlideIndex += direction; |
||||
|
ofEventArgs args; |
||||
|
|
||||
|
// This makes sure that we are doing a signed integer comparison,
|
||||
|
// otherwise things get weird
|
||||
|
int num = slides.size(); |
||||
|
switch (settings.loopType) |
||||
|
{ |
||||
|
case LoopType::NONE: |
||||
|
if (currentSlideIndex >= slides.size() || currentSlideIndex < 0) |
||||
|
{ |
||||
|
// If we are not looping and we are out of bounds, return
|
||||
|
// without enqueueing a slide. This will cause the slide show
|
||||
|
// to end once the last slide builds out.
|
||||
|
return; |
||||
|
} |
||||
|
break; |
||||
|
case LoopType::NORMAL: |
||||
|
if (currentSlideIndex >= num) |
||||
|
{ |
||||
|
loopCount++; |
||||
|
if (loopCount == settings.numLoops) |
||||
|
{ |
||||
|
// Return without enqueueing a new slide if we have
|
||||
|
// reached the max number of loops.
|
||||
|
return; |
||||
|
} |
||||
|
currentSlideIndex = 0; |
||||
|
ofNotifyEvent(slideshowWillLoopEvent, args, this); |
||||
|
} |
||||
|
else if (currentSlideIndex < 0) |
||||
|
{ |
||||
|
loopCount++; |
||||
|
if (loopCount == settings.numLoops) |
||||
|
{ |
||||
|
// Return without enqueueing a new slide if we have
|
||||
|
// reached the max number of loops.
|
||||
|
return; |
||||
|
} |
||||
|
currentSlideIndex = slides.size()-1; |
||||
|
ofNotifyEvent(slideshowWillLoopEvent, args, this); |
||||
|
} |
||||
|
break; |
||||
|
case LoopType::PING_PONG: |
||||
|
|
||||
|
int num = slides.size(); |
||||
|
if (currentSlideIndex >= num) |
||||
|
{ |
||||
|
loopCount++; |
||||
|
if (loopCount == settings.numLoops) |
||||
|
{ |
||||
|
// Return without enqueueing a new slide if we have
|
||||
|
// reached the max number of loops.
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
direction = -1; |
||||
|
currentSlideIndex = slides.size()-2; |
||||
|
ofNotifyEvent(slideshowWillLoopEvent, args, this); |
||||
|
} |
||||
|
else if (currentSlideIndex < 0) |
||||
|
{ |
||||
|
loopCount++; |
||||
|
if (loopCount == settings.numLoops) |
||||
|
{ |
||||
|
// Return without enqueueing a new slide if we have
|
||||
|
// reached the max number of loops.
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
direction = 1; |
||||
|
currentSlideIndex = 1; |
||||
|
ofNotifyEvent(slideshowWillLoopEvent, args, this); |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
enqueueSlide(slides[currentSlideIndex], ofGetElapsedTimeMillis()); |
||||
|
} |
||||
|
|
||||
|
void magSlideShowSource::playPrevSlide() |
||||
|
{ |
||||
|
currentSlideIndex -= (direction*2); |
||||
|
playNextSlide(); |
||||
|
} |
||||
|
|
||||
|
void magSlideShowSource::playSlide(int slideIndex) |
||||
|
{ |
||||
|
currentSlideIndex = slideIndex-direction; |
||||
|
playNextSlide(); |
||||
|
} |
||||
|
|
||||
|
void magSlideShowSource::enqueueSlide(std::shared_ptr<magSlide> slide, u_int64_t startTime) |
||||
|
{ |
||||
|
// ofLogVerbose() << "Enqueuing slide " << currentSlideIndex << " slide id: " << slide->getId();
|
||||
|
slide->start(startTime); |
||||
|
activeSlides.push_back(slide); |
||||
|
} |
||||
|
|
||||
|
void magSlideShowSource::slideStateChanged(const void *sender, ofEventArgs &args) |
||||
|
{ |
||||
|
magSlide *slide = (magSlide *) sender; |
||||
|
|
||||
|
// ofLogVerbose("slideStateChanged") << "Slide id: " << slide->getId() << " Slide state: "
|
||||
|
// << slide->getSlideStateName();
|
||||
|
if (slide->getSlideState() == magSlide::SlideState::BuildOut) |
||||
|
{ |
||||
|
playNextSlide(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
void magSlideShowSource::slideComplete(const void *sender, ofEventArgs &args) |
||||
|
{ |
||||
|
magSlide *slide = (magSlide *) sender; |
||||
|
// ofLogVerbose() << "Slide Complete. id: " << slide->getId();
|
||||
|
slide->isComplete = true; |
||||
|
} |
||||
|
|
||||
|
|
@ -0,0 +1,160 @@ |
|||||
|
//
|
||||
|
// magSlideShowSource.h
|
||||
|
// Copyright (c) 2017 Cristobal Mendoza
|
||||
|
// http://cuppetellimendoza.com
|
||||
|
//
|
||||
|
|
||||
|
#ifndef MAGSLIDESHOWSOURCE_H |
||||
|
#define MAGSLIDESHOWSOURCE_H |
||||
|
|
||||
|
#include "FboSource.h" |
||||
|
#include "magSlide.h" |
||||
|
|
||||
|
class magSlide; |
||||
|
|
||||
|
|
||||
|
class magSlideShowSource : public ofx::piMapper::FboSource |
||||
|
{ |
||||
|
public: |
||||
|
magSlideShowSource(); |
||||
|
struct Settings; // forward declaration
|
||||
|
bool initialize(magSlideShowSource::Settings settings); |
||||
|
void setup() override; |
||||
|
void update() override; |
||||
|
void draw() override; |
||||
|
|
||||
|
/**
|
||||
|
* Removes all slides and then attempts to create a new slide show |
||||
|
* based on the contents of the ofDirectory specified. The files may |
||||
|
* be images or videos, which will be transformed into the appropriate slide type. |
||||
|
* Any other file type in the directory is ignored (including other directories). |
||||
|
* The slide order is alphabetical according to filename. |
||||
|
* |
||||
|
* @param dir The ofDirectory to use as a source for a slide show. |
||||
|
* @return true if at least one slide was created. false is returned |
||||
|
* otherwise. Check the console for the specific error. |
||||
|
*/ |
||||
|
bool createFromFolderContents(std::string path); |
||||
|
bool loadSlideShow(std::string slideShowXmlPath); |
||||
|
void addSlide(std::shared_ptr<magSlide> slide); |
||||
|
void play(); |
||||
|
void pause(); |
||||
|
void playNextSlide(); |
||||
|
void playPrevSlide(); |
||||
|
void playSlide(int slideIndex); |
||||
|
|
||||
|
enum LoopType |
||||
|
{ |
||||
|
NONE = 0, |
||||
|
NORMAL, |
||||
|
PING_PONG |
||||
|
}; |
||||
|
|
||||
|
struct Settings |
||||
|
{ |
||||
|
/**
|
||||
|
* The pixel width of the FBO. This value must be provided. |
||||
|
*/ |
||||
|
float width = 0; |
||||
|
|
||||
|
/**
|
||||
|
* The pixel height of the FBO. This value must be provided. |
||||
|
*/ |
||||
|
float height = 0; |
||||
|
/**
|
||||
|
* An optional default slide duration, in seconds. |
||||
|
* If a slide specifies a duration this value is ignored. |
||||
|
*/ |
||||
|
float slideDuration = 5; |
||||
|
|
||||
|
/**
|
||||
|
* An optional default transition for the slide show. |
||||
|
*/ |
||||
|
std::string transitionName = ""; |
||||
|
|
||||
|
/**
|
||||
|
* An optional default transition duration. If no transition |
||||
|
* is specified, this value is ignored; |
||||
|
*/ |
||||
|
float transitionDuration = 1; |
||||
|
|
||||
|
/**
|
||||
|
* If specified, all applicable files in the folder will |
||||
|
* be used as slides in the slide show. They will be ordered |
||||
|
* alphabetically according to their file names. |
||||
|
* |
||||
|
* If path is relative, the root will likely be the Data folder. |
||||
|
*/ |
||||
|
std::string slidesFolderPath; |
||||
|
|
||||
|
/**
|
||||
|
* If specified, |
||||
|
*/ |
||||
|
std::string slideshowFilePath; |
||||
|
|
||||
|
|
||||
|
/**
|
||||
|
* Loop type for the slide show. See @code LoopType for options. |
||||
|
* The default is @code LoopType:None. |
||||
|
*/ |
||||
|
LoopType loopType = LoopType::NONE; |
||||
|
|
||||
|
/**
|
||||
|
* The number of loops to perform, if the loopType is not NONE. |
||||
|
* If the value is 0 or less than 0, the slide show loops forever. |
||||
|
*/ |
||||
|
int numLoops = 0; |
||||
|
|
||||
|
/**
|
||||
|
* The resizing option for the slide show. The default is None. If a slide |
||||
|
* already has a resizing option applied, that option will be respected and |
||||
|
* this resizeOption will not be used. |
||||
|
*/ |
||||
|
magSlide::ResizeOptions resizeOption = magSlide::ResizeOptions::None; |
||||
|
}; |
||||
|
|
||||
|
////////////////////////////////////////////
|
||||
|
//// Event Listeners
|
||||
|
////////////////////////////////////////////
|
||||
|
void slideStateChanged(const void* sender, ofEventArgs &args); |
||||
|
void slideComplete(const void* sender, ofEventArgs &args); |
||||
|
|
||||
|
|
||||
|
/**
|
||||
|
* Fires when the slide show is done, which happens when |
||||
|
* the loop count is equal to Settings::numLoops, or when |
||||
|
* the last slide is played when @code LoopType::NONE is specified. |
||||
|
* Sender is this slide show. |
||||
|
*/ |
||||
|
ofEvent<ofEventArgs> slideshowCompleteEvent; |
||||
|
|
||||
|
|
||||
|
/**
|
||||
|
* Fires when the slide show reaches the last slide |
||||
|
* and will perform a loop in the next call. |
||||
|
* Sender is this slide show. |
||||
|
*/ |
||||
|
ofEvent<ofEventArgs> slideshowWillLoopEvent; |
||||
|
|
||||
|
protected: |
||||
|
Settings settings; |
||||
|
std::vector<std::shared_ptr<magSlide>> slides; |
||||
|
|
||||
|
private: |
||||
|
// std::shared_ptr<magSlide> currentSlide;
|
||||
|
std::vector<std::shared_ptr<magSlide>> activeSlides; |
||||
|
void enqueueSlide(std::shared_ptr<magSlide> slide, u_int64_t startTime); |
||||
|
|
||||
|
u_int64_t lastTime; |
||||
|
u_int64_t deltaTime; |
||||
|
u_int64_t runningTime; |
||||
|
|
||||
|
bool isInitialized = false; |
||||
|
bool isPlaying = false; |
||||
|
int currentSlideIndex = 0; |
||||
|
int direction = 1; |
||||
|
int loopCount = 0; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
#endif |
@ -0,0 +1,60 @@ |
|||||
|
//
|
||||
|
// magSlideTransition.cpp
|
||||
|
// Copyright (c) 2017 Cristobal Mendoza
|
||||
|
// http://cuppetellimendoza.com
|
||||
|
//
|
||||
|
|
||||
|
#include "magSlideTransition.h" |
||||
|
|
||||
|
std::shared_ptr<magSlideTransition> |
||||
|
magSlideTransition::createTransition(std::string transitionName, std::shared_ptr<magSlide> slide, |
||||
|
ofParameterGroup &settings, u_int64_t duration) |
||||
|
{ |
||||
|
auto transition = magSlideTransition::instantiateTransition(transitionName); |
||||
|
transition->slide = slide; |
||||
|
transition->duration = duration; |
||||
|
transition->loadSettings(settings); |
||||
|
return transition; |
||||
|
} |
||||
|
|
||||
|
void magSlideTransition::start() |
||||
|
{ |
||||
|
runningTime = 0; |
||||
|
isActive = true; |
||||
|
} |
||||
|
|
||||
|
void magSlideTransition::update(u_int64_t timeDelta) |
||||
|
{ |
||||
|
if (!isActive) return; |
||||
|
|
||||
|
runningTime += timeDelta; |
||||
|
if (runningTime >= duration) |
||||
|
{ |
||||
|
ofEventArgs arghh; // arghhhh...
|
||||
|
transitionCompleteEvent.notify(this, arghh); |
||||
|
isActive = false; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
u_int64_t magSlideTransition::getRunningTime() |
||||
|
{ |
||||
|
return runningTime; |
||||
|
} |
||||
|
|
||||
|
float magSlideTransition::getNormalizedTime() |
||||
|
{ |
||||
|
return (double)runningTime / (double)duration; |
||||
|
} |
||||
|
|
||||
|
std::shared_ptr<magSlideTransition> magSlideTransition::instantiateTransition(string transitionName) |
||||
|
{ |
||||
|
return std::make_shared<magVoidTransition>(); |
||||
|
} |
||||
|
|
||||
|
//magDissolveTransition::magDissolveTransition()
|
||||
|
//{}
|
||||
|
void magVoidTransition::loadSettings(ofParameterGroup &settings) |
||||
|
{ |
||||
|
ofLogNotice("magVoidTransition") << "Void Transition is loading nothing"; |
||||
|
} |
@ -0,0 +1,67 @@ |
|||||
|
//
|
||||
|
// magSlideTransition.h
|
||||
|
// Copyright (c) 2017 Cristobal Mendoza
|
||||
|
// http://cuppetellimendoza.com
|
||||
|
//
|
||||
|
|
||||
|
#ifndef MAGSLIDETRANSITION_H |
||||
|
#define MAGSLIDETRANSITION_H |
||||
|
|
||||
|
|
||||
|
#include "magSlide.h" |
||||
|
|
||||
|
class magSlideTransition |
||||
|
{ |
||||
|
public: |
||||
|
static std::shared_ptr<magSlideTransition> createTransition(string transitionName, |
||||
|
shared_ptr<magSlide> ptr, |
||||
|
ofParameterGroup &group, |
||||
|
u_int64_t i); |
||||
|
/**
|
||||
|
* Begins the transition. This must be called in order for the |
||||
|
* transition to actually do anything! |
||||
|
*/ |
||||
|
void start(); |
||||
|
virtual void loadSettings(ofParameterGroup &settings) = 0; |
||||
|
virtual void setup(){} |
||||
|
virtual void update(u_int64_t timeDelta); |
||||
|
virtual void draw(){} |
||||
|
|
||||
|
/**
|
||||
|
* Current running time in milliseconds. |
||||
|
* @return u_int64_t |
||||
|
*/ |
||||
|
u_int64_t getRunningTime(); |
||||
|
|
||||
|
/**
|
||||
|
* Returns the current time in normalized form. |
||||
|
* 0 = start, 1 = end. |
||||
|
* @return Float between 0 and 1. |
||||
|
*/ |
||||
|
float getNormalizedTime(); |
||||
|
|
||||
|
ofEvent<ofEventArgs> transitionCompleteEvent; |
||||
|
|
||||
|
protected: |
||||
|
magSlideTransition(){} |
||||
|
std::shared_ptr<magSlide> slide; |
||||
|
u_int64_t runningTime; |
||||
|
u_int64_t duration; |
||||
|
u_int64_t endTime; |
||||
|
bool isActive = false; |
||||
|
static shared_ptr<magSlideTransition> instantiateTransition(string transitionName); |
||||
|
}; |
||||
|
|
||||
|
class magVoidTransition : public magSlideTransition |
||||
|
{ |
||||
|
public: |
||||
|
void loadSettings(ofParameterGroup &settings) override; |
||||
|
}; |
||||
|
|
||||
|
class magDissolveTransition : public magSlideTransition |
||||
|
{ |
||||
|
public: |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
#endif |
Loading…
Reference in new issue