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