From 48001a660b335dc0a3c4961da27e97c6f42b6039 Mon Sep 17 00:00:00 2001 From: Bruno Herbelin Date: Fri, 14 Oct 2022 19:05:14 +0200 Subject: [PATCH] Source callbacks for Image Processing color correction Added SourceCallback classes for brightness, contrast, saturation, etc. Added OSC interface to modify color corrections --- ControlManager.cpp | 96 ++++++++++++- ControlManager.h | 9 ++ SessionCreator.cpp | 15 ++ SessionCreator.h | 1 + SessionVisitor.cpp | 7 + SessionVisitor.h | 1 + SourceCallback.cpp | 342 ++++++++++++++++++++++++++++++++++++++++----- SourceCallback.h | 194 +++++++++++++++++++++---- Visitor.h | 2 + 9 files changed, 600 insertions(+), 67 deletions(-) diff --git a/ControlManager.cpp b/ControlManager.cpp index 2726313..6daca29 100644 --- a/ControlManager.cpp +++ b/ControlManager.cpp @@ -32,6 +32,7 @@ #include "Mixer.h" #include "Source.h" #include "SourceCallback.h" +#include "ImageProcessingShader.h" #include "ActionManager.h" #include "SystemToolkit.h" #include "tinyxml2Toolkit.h" @@ -659,7 +660,6 @@ bool Control::receiveSourceAttribute(Source *target, const std::string &attribut /// e.g. '/vimix/current/resize ff 10.0 2.2' else if ( attribute.compare(OSC_SOURCE_RESIZE) == 0) { float x = 0.f, y = 0.f; - try { arguments >> x; } @@ -696,13 +696,13 @@ bool Control::receiveSourceAttribute(Source *target, const std::string &attribut } /// e.g. '/vimix/current/turn f 1.0' else if ( attribute.compare(OSC_SOURCE_TURN) == 0) { - float x = 0.f, y = 0.f; + float x = 0.f, t = 0.f; arguments >> x; if (arguments.Eos()) arguments >> osc::EndMessage; - else // ignore second argument - arguments >> y >> osc::EndMessage; - target->call( new Turn( x, 0.f), true ); + else + arguments >> t >> osc::EndMessage; + target->call( new Turn( x, t), true ); } /// e.g. '/vimix/current/angle f 3.1416' else if ( attribute.compare(OSC_SOURCE_ANGLE) == 0) { @@ -717,7 +717,91 @@ bool Control::receiveSourceAttribute(Source *target, const std::string &attribut else if ( attribute.compare(OSC_SOURCE_RESET) == 0) { target->call( new ResetGeometry(), true ); } - /// e.g. '/vimix/current/seek f 1.25' + /// e.g. '/vimix/current/brightness f 0.0' + else if ( attribute.compare(OSC_SOURCE_BRIGHTNESS) == 0) { + float val = 0.f, t = 0.f; + arguments >> val; + if (arguments.Eos()) + arguments >> osc::EndMessage; + else + arguments >> t >> osc::EndMessage; + target->call( new SetBrightness( val, t ), true ); + } + /// e.g. '/vimix/current/contrast f 0.0' + else if ( attribute.compare(OSC_SOURCE_CONTRAST) == 0) { + float val = 0.f, t = 0.f; + arguments >> val; + if (arguments.Eos()) + arguments >> osc::EndMessage; + else + arguments >> t >> osc::EndMessage; + target->call( new SetContrast( val, t ), true ); + } + /// e.g. '/vimix/current/saturation f 0.0' + else if ( attribute.compare(OSC_SOURCE_SATURATION) == 0) { + float val = 0.f, t = 0.f; + arguments >> val; + if (arguments.Eos()) + arguments >> osc::EndMessage; + else + arguments >> t >> osc::EndMessage; + target->call( new SetSaturation( val, t ), true ); + } + /// e.g. '/vimix/current/hue f 1.0' + else if ( attribute.compare(OSC_SOURCE_HUE) == 0) { + float val = 0.f, t = 0.f; + arguments >> val; + if (arguments.Eos()) + arguments >> osc::EndMessage; + else + arguments >> t >> osc::EndMessage; + target->call( new SetHue( val, t ), true ); + } + /// e.g. '/vimix/current/threshold f 1.0' + else if ( attribute.compare(OSC_SOURCE_THRESHOLD) == 0) { + float val = 0.f, t = 0.f; + arguments >> val; + if (arguments.Eos()) + arguments >> osc::EndMessage; + else + arguments >> t >> osc::EndMessage; + target->call( new SetThreshold( val, t ), true ); + } + /// e.g. '/vimix/current/gamma f 1.0' + else if ( attribute.compare(OSC_SOURCE_GAMMA) == 0) { + glm::vec4 g = target->processingShader()->gamma; + float t = 0.f; + arguments >> g.w; + if (arguments.Eos()) + arguments >> osc::EndMessage; + else + arguments >> t >> osc::EndMessage; + target->call( new SetGamma( g, t ), true ); + } + /// e.g. '/vimix/current/color fff 1.0 0.5 0.9' + else if ( attribute.compare(OSC_SOURCE_COLOR) == 0) { + glm::vec4 g = target->processingShader()->gamma; + float t = 0.f; + arguments >> g.x >> g.y >> g.z; + if (arguments.Eos()) + arguments >> osc::EndMessage; + else + arguments >> t >> osc::EndMessage; + target->call( new SetGamma( g, t ), true ); + } + /// e.g. '/vimix/current/invert f 1' + else if ( attribute.compare(OSC_SOURCE_INVERT) == 0) { + float v = 0.f; + arguments >> v >> osc::EndMessage; + target->call( new SetInvert( v ), true ); + } + /// e.g. '/vimix/current/posterize f 1' + else if ( attribute.compare(OSC_SOURCE_POSTERIZE) == 0) { + float v = 0.f; + arguments >> v >> osc::EndMessage; + target->call( new SetPosterize( v ), true ); + } + /// e.g. '/vimix/current/seek f 0.25' else if ( attribute.compare(OSC_SOURCE_SEEK) == 0) { float t = 0.f; arguments >> t >> osc::EndMessage; diff --git a/ControlManager.h b/ControlManager.h index 29860c4..74d0882 100644 --- a/ControlManager.h +++ b/ControlManager.h @@ -47,6 +47,15 @@ #define OSC_SOURCE_SIZE "/size" #define OSC_SOURCE_ANGLE "/angle" #define OSC_SOURCE_SEEK "/seek" +#define OSC_SOURCE_BRIGHTNESS "/brightness" +#define OSC_SOURCE_CONTRAST "/contrast" +#define OSC_SOURCE_SATURATION "/saturation" +#define OSC_SOURCE_HUE "/hue" +#define OSC_SOURCE_THRESHOLD "/threshold" +#define OSC_SOURCE_GAMMA "/gamma" +#define OSC_SOURCE_COLOR "/color" +#define OSC_SOURCE_POSTERIZE "/posterize" +#define OSC_SOURCE_INVERT "/invert" #define OSC_SESSION "/session" #define OSC_SESSION_VERSION "/version" diff --git a/SessionCreator.cpp b/SessionCreator.cpp index 17dc5c7..ace3125 100644 --- a/SessionCreator.cpp +++ b/SessionCreator.cpp @@ -1347,6 +1347,21 @@ void SessionLoader::visit (SourceCallback &) { } +void SessionLoader::visit (ValueSourceCallback &c) +{ + float v = 0.f; + xmlCurrent_->QueryFloatAttribute("value", &v); + c.setValue(v); + + float d = 0.f; + xmlCurrent_->QueryFloatAttribute("duration", &d); + c.setDuration(d); + + bool b = false; + xmlCurrent_->QueryBoolAttribute("bidirectional", &b); + c.setBidirectional(b); +} + void SessionLoader::visit (Play &c) { bool p = true; diff --git a/SessionCreator.h b/SessionCreator.h index 12bc38f..2e94f9a 100644 --- a/SessionCreator.h +++ b/SessionCreator.h @@ -74,6 +74,7 @@ public: // callbacks void visit (SourceCallback&) override; + void visit (ValueSourceCallback&) override; void visit (SetAlpha&) override; void visit (SetDepth&) override; void visit (SetGeometry&) override; diff --git a/SessionVisitor.cpp b/SessionVisitor.cpp index ce63cd5..8655b7a 100644 --- a/SessionVisitor.cpp +++ b/SessionVisitor.cpp @@ -889,6 +889,13 @@ void SessionVisitor::visit (SourceCallback &c) xmlCurrent_->SetAttribute("type", (uint) c.type()); } +void SessionVisitor::visit (ValueSourceCallback &c) +{ + xmlCurrent_->SetAttribute("value", c.value()); + xmlCurrent_->SetAttribute("duration", c.duration()); + xmlCurrent_->SetAttribute("bidirectional", c.bidirectional()); +} + void SessionVisitor::visit (Play &c) { xmlCurrent_->SetAttribute("play", c.value()); diff --git a/SessionVisitor.h b/SessionVisitor.h index fcd05ce..37698f6 100644 --- a/SessionVisitor.h +++ b/SessionVisitor.h @@ -82,6 +82,7 @@ public: // callbacks void visit (SourceCallback&) override; + void visit (ValueSourceCallback&) override; void visit (SetAlpha&) override; void visit (SetDepth&) override; void visit (SetGeometry&) override; diff --git a/SourceCallback.cpp b/SourceCallback.cpp index 2a53987..c89a210 100644 --- a/SourceCallback.cpp +++ b/SourceCallback.cpp @@ -19,6 +19,7 @@ #include "defines.h" #include "Source.h" +#include "ImageProcessingShader.h" #include "MediaSource.h" #include "MediaPlayer.h" #include "UpdateCallback.h" @@ -66,6 +67,33 @@ SourceCallback *SourceCallback::create(CallbackType type) case SourceCallback::CALLBACK_LOCK: loadedcallback = new Lock; break; + case SourceCallback::CALLBACK_SEEK: + loadedcallback = new Seek; + break; + case SourceCallback::CALLBACK_BRIGHTNESS: + loadedcallback = new SetBrightness; + break; + case SourceCallback::CALLBACK_CONTRAST: + loadedcallback = new SetContrast; + break; + case SourceCallback::CALLBACK_SATURATION: + loadedcallback = new SetBrightness; + break; + case SourceCallback::CALLBACK_HUE: + loadedcallback = new SetContrast; + break; + case SourceCallback::CALLBACK_THRESHOLD: + loadedcallback = new SetThreshold; + break; + case SourceCallback::CALLBACK_GAMMA: + loadedcallback = new SetGamma; + break; + case SourceCallback::CALLBACK_INVERT: + loadedcallback = new SetInvert; + break; + case SourceCallback::CALLBACK_POSTERIZE: + loadedcallback = new SetPosterize; + break; default: break; } @@ -139,6 +167,81 @@ void SourceCallback::update (Source *s, float dt) } + +ValueSourceCallback::ValueSourceCallback(float target, float ms, bool revert) : SourceCallback(), + duration_(ms), start_(0.f), target_(target), bidirectional_(revert) +{ +} + + +void ValueSourceCallback::update(Source *s, float dt) +{ + SourceCallback::update(s, dt); + + // set start on first time it is ready + if ( status_ == READY ){ + start_ = readValue(s); + status_ = ACTIVE; + } + + // update when active + if ( status_ == ACTIVE ) { + + // time passed since start + float progress = elapsed_ - delay_; + + // time-out or instantaneous + if ( !(ABS(duration_) > 0.f) || progress > duration_ ) { + // apply target + writeValue(s, target_); + // done + status_ = FINISHED; + } + // perform iteration of interpolation + else { + // apply calculated intermediate + writeValue(s, glm::mix(start_, target_, progress/duration_) ); + } + } +} + +void ValueSourceCallback::multiply (float factor) +{ + target_ *= factor; +} + +SourceCallback *ValueSourceCallback::clone() const +{ + SourceCallback *ret = SourceCallback::create(type()); + ValueSourceCallback *vsc = static_cast(ret); + vsc->setValue( target_ ); + vsc->setDuration( duration_ ); + vsc->setBidirectional( bidirectional_ ); + + return ret; +} + +SourceCallback *ValueSourceCallback::reverse(Source *s) const +{ + SourceCallback *ret = nullptr; + if (bidirectional_) { + ret = SourceCallback::create(type()); + ValueSourceCallback *vsc = static_cast(ret); + vsc->setValue( readValue(s) ); + vsc->setDuration( duration_ ); + vsc->setBidirectional( true ); + } + + return ret; +} + +void ValueSourceCallback::accept(Visitor& v) +{ + SourceCallback::accept(v); + v.visit(*this); +} + + void ResetGeometry::update(Source *s, float dt) { SourceCallback::update(s, dt); @@ -168,7 +271,7 @@ SourceCallback *ResetGeometry::clone() const SetAlpha::SetAlpha(float alpha, float ms, bool revert) : SourceCallback(), duration_(ms), alpha_(alpha), bidirectional_(revert) { - alpha_ = CLAMP(alpha_, 0.f, 1.f); + alpha_ = glm::clamp(alpha_, 0.f, 1.f); start_ = glm::vec2(); target_ = glm::vec2(); } @@ -221,7 +324,7 @@ void SetAlpha::update(Source *s, float dt) // perform movement if ( ABS(duration_) > 0.f) - s->group(View::MIXING)->translation_ = glm::vec3(start_ + (progress/duration_)*(target_ - start_), s->group(View::MIXING)->translation_.z); + s->group(View::MIXING)->translation_ = glm::vec3(glm::mix(start_, target_, progress/duration_), s->group(View::MIXING)->translation_.z); // time-out if ( progress > duration_ ) { @@ -343,7 +446,7 @@ void Loom::accept(Visitor& v) SetDepth::SetDepth(float target, float ms, bool revert) : SourceCallback(), duration_(ms), start_(0.f), target_(target), bidirectional_(revert) { - target_ = CLAMP(target_, MIN_DEPTH, MAX_DEPTH); + target_ = glm::clamp(target_, MIN_DEPTH, MAX_DEPTH); } void SetDepth::update(Source *s, float dt) @@ -365,7 +468,7 @@ void SetDepth::update(Source *s, float dt) // perform movement if ( ABS(duration_) > 0.f) - s->group(View::LAYER)->translation_.z = start_ + (progress/duration_) * (target_ - start_); + s->group(View::LAYER)->translation_.z = glm::mix(start_, target_, progress/duration_); // time-out if ( progress > duration_ ) { @@ -459,41 +562,40 @@ SourceCallback *RePlay::clone() const return new RePlay; } - -Seek::Seek(float time) : SourceCallback(), target_(time) +Seek::Seek(float v, float ms, bool r) : ValueSourceCallback(v, ms, r) { } -void Seek::update(Source *s, float dt) +float Seek::readValue(Source *s) const { - SourceCallback::update(s, dt); + double ret = 0.f; + // access media player if target source is a media source + MediaSource *ms = dynamic_cast(s); + if (ms != nullptr) { + GstClockTime media_duration = ms->mediaplayer()->timeline()->duration(); + GstClockTime media_position = ms->mediaplayer()->position(); - // perform seek when ready - if ( status_ == READY ){ - - // access media player if target source is a media source - MediaSource *ms = dynamic_cast(s); - if (ms != nullptr) { - GstClockTime duration = ms->mediaplayer()->timeline()->duration(); - ms->mediaplayer()->seek( target_ * duration ); + if (GST_CLOCK_TIME_IS_VALID(media_duration) && media_duration > 0 && + GST_CLOCK_TIME_IS_VALID(media_position) && media_position > 0){ + ret = static_cast(media_position) / static_cast(media_duration); } + } - status_ = FINISHED; + return (float)ret; +} + +void Seek::writeValue(Source *s, float val) +{ + // access media player if target source is a media source + MediaSource *ms = dynamic_cast(s); + if (ms != nullptr) { + GstClockTime media_duration = ms->mediaplayer()->timeline()->duration(); + double media_position = glm::clamp( (double) val, 0.0, 1.0); + if (GST_CLOCK_TIME_IS_VALID(media_duration)) + ms->mediaplayer()->seek( media_position * media_duration ); } } -SourceCallback *Seek::clone() const -{ - return new Seek(target_); -} - -void Seek::accept(Visitor& v) -{ - SourceCallback::accept(v); - v.visit(*this); -} - - SetGeometry::SetGeometry(const Group *g, float ms, bool revert) : SourceCallback(), duration_(ms), bidirectional_(revert) { @@ -531,11 +633,10 @@ void SetGeometry::update(Source *s, float dt) // perform movement if ( ABS(duration_) > 0.f){ - float ratio = progress / duration_; Group intermediate; - intermediate.translation_ = (1.f - ratio) * start_.translation_ + ratio * target_.translation_; - intermediate.scale_ = (1.f - ratio) * start_.scale_ + ratio * target_.scale_; - intermediate.rotation_ = (1.f - ratio) * start_.rotation_ + ratio * target_.rotation_; + intermediate.translation_ = glm::mix(start_.translation_, target_.translation_, progress/duration_); + intermediate.scale_ = glm::mix(start_.scale_, target_.scale_, progress/duration_); + intermediate.rotation_ = glm::mix(start_.rotation_, target_.rotation_, progress/duration_); // apply geometry s->group(View::GEOMETRY)->copyTransform(&intermediate); s->touch(); @@ -727,3 +828,178 @@ void Turn::accept(Visitor& v) SourceCallback::accept(v); v.visit(*this); } + +SetBrightness::SetBrightness(float v, float ms, bool r) : ValueSourceCallback(v, ms, r) +{ + target_ = glm::clamp(target_, -1.f, 1.f); +} + +float SetBrightness::readValue(Source *s) const +{ + return s->processingShader()->brightness; +} + +void SetBrightness::writeValue(Source *s, float val) +{ + if (s->imageProcessingEnabled()) + s->processingShader()->brightness = val; +} + +SetContrast::SetContrast(float v, float ms, bool r) : ValueSourceCallback(v, ms, r) +{ + target_ = glm::clamp(target_, -1.f, 1.f); +} + +float SetContrast::readValue(Source *s) const +{ + return s->processingShader()->contrast; +} + +void SetContrast::writeValue(Source *s, float val) +{ + if (s->imageProcessingEnabled()) + s->processingShader()->contrast = val; +} + +SetSaturation::SetSaturation(float v, float ms, bool r) : ValueSourceCallback(v, ms, r) +{ + target_ = glm::clamp(target_, -1.f, 1.f); +} + +float SetSaturation::readValue(Source *s) const +{ + return s->processingShader()->saturation; +} + +void SetSaturation::writeValue(Source *s, float val) +{ + if (s->imageProcessingEnabled()) + s->processingShader()->saturation = val; +} + +SetHue::SetHue(float v, float ms, bool r) : ValueSourceCallback(v, ms, r) +{ + target_ = glm::clamp(target_, 0.f, 1.f); +} + +float SetHue::readValue(Source *s) const +{ + return s->processingShader()->hueshift; +} + +void SetHue::writeValue(Source *s, float val) +{ + if (s->imageProcessingEnabled()) + s->processingShader()->hueshift = val; +} + +SetThreshold::SetThreshold(float v, float ms, bool r) : ValueSourceCallback(v, ms, r) +{ + target_ = glm::clamp(target_, 0.f, 1.f); +} + +float SetThreshold::readValue(Source *s) const +{ + return s->processingShader()->threshold; +} + +void SetThreshold::writeValue(Source *s, float val) +{ + if (s->imageProcessingEnabled()) + s->processingShader()->threshold = val; +} + + +SetInvert::SetInvert(float v, float ms, bool r) : ValueSourceCallback(v, ms, r) +{ + target_ = glm::clamp(target_, 0.f, 2.f); +} + +float SetInvert::readValue(Source *s) const +{ + return static_cast(s->processingShader()->invert); +} + +void SetInvert::writeValue(Source *s, float val) +{ + if (s->imageProcessingEnabled()) + s->processingShader()->invert = static_cast(val); +} + +SetPosterize::SetPosterize(float v, float ms, bool r) : ValueSourceCallback(v, ms, r) +{ + target_ = glm::clamp(target_, 0.f, 128.f); +} + +float SetPosterize::readValue(Source *s) const +{ + return static_cast(s->processingShader()->nbColors); +} + +void SetPosterize::writeValue(Source *s, float val) +{ + if (s->imageProcessingEnabled()) + s->processingShader()->nbColors = static_cast(val); +} + + +SetGamma::SetGamma(glm::vec4 g, float ms, bool revert) : SourceCallback(), + duration_(ms), start_(glm::vec4()), target_(g), bidirectional_(revert) +{ + start_ = glm::clamp(start_, glm::vec4(0.f), glm::vec4(10.f)); +} + +void SetGamma::update(Source *s, float dt) +{ + SourceCallback::update(s, dt); + + if (!s->imageProcessingEnabled()) + status_ = FINISHED; + + // set start on first time it is ready + if ( status_ == READY ){ + start_ = s->processingShader()->gamma; + status_ = ACTIVE; + } + + // update when active + if ( status_ == ACTIVE ) { + + // time passed since start + float progress = elapsed_ - delay_; + + // time-out or instantaneous + if ( !(ABS(duration_) > 0.f) || progress > duration_ ) { + // apply target + s->processingShader()->gamma = target_; + // done + status_ = FINISHED; + } + // perform iteration of interpolation + else { + // apply calculated intermediate + s->processingShader()->gamma = glm::mix(start_, target_, progress/duration_); + } + } +} + +void SetGamma::multiply (float factor) +{ + target_ *= factor; +} + +SourceCallback *SetGamma::clone() const +{ + return new SetGamma(target_, duration_, bidirectional_); +} + +SourceCallback *SetGamma::reverse(Source *s) const +{ + return bidirectional_ ? new SetGamma(s->processingShader()->gamma, duration_) : nullptr; +} + +void SetGamma::accept(Visitor& v) +{ + SourceCallback::accept(v); + v.visit(*this); +} diff --git a/SourceCallback.h b/SourceCallback.h index 3b8d9f0..12cfb49 100644 --- a/SourceCallback.h +++ b/SourceCallback.h @@ -7,25 +7,46 @@ class Visitor; class Source; +class ImageProcessingShader; +/** + * @brief The SourceCallback class defines operations on Sources that + * are applied at each update of Source. A SourceCallback is added to + * a source with; source->call( new SourceCallback ); + * + * A source contains a list of SourceCallbacks. At each update(dt) + * a source calls the update on all its SourceCallbacks. + * The SourceCallback is created as PENDING, and becomes ACTIVE after + * the first update call. + * The SourceCallback is removed from the list when FINISHED. + * + */ class SourceCallback { public: typedef enum { CALLBACK_GENERIC = 0, - CALLBACK_ALPHA = 1, - CALLBACK_LOOM = 2, - CALLBACK_GEOMETRY = 3, - CALLBACK_GRAB = 4, - CALLBACK_RESIZE = 5, - CALLBACK_TURN = 6, - CALLBACK_DEPTH = 7, - CALLBACK_PLAY = 8, - CALLBACK_REPLAY = 9, - CALLBACK_RESETGEO = 10, - CALLBACK_LOCK = 11, - CALLBACK_SEEK = 12 + CALLBACK_ALPHA, + CALLBACK_LOOM, + CALLBACK_GEOMETRY, + CALLBACK_GRAB, + CALLBACK_RESIZE, + CALLBACK_TURN, + CALLBACK_DEPTH, + CALLBACK_PLAY, + CALLBACK_REPLAY, + CALLBACK_RESETGEO, + CALLBACK_LOCK, + CALLBACK_SEEK, + CALLBACK_BRIGHTNESS, + CALLBACK_CONTRAST, + CALLBACK_SATURATION, + CALLBACK_HUE, + CALLBACK_THRESHOLD, + CALLBACK_GAMMA, + CALLBACK_INVERT, + CALLBACK_POSTERIZE } CallbackType; static SourceCallback *create(CallbackType type); @@ -59,6 +80,43 @@ protected: float elapsed_; }; +/** + * @brief The ValueSourceCallback class is a generic type of + * callback which operates on a single float value of a source. + * + * ValueSourceCallback are practical for creating SourceCallbacks + * that change a single attribute of a source, that can be read + * and write on the source object. + */ +class ValueSourceCallback : public SourceCallback +{ +protected: + float duration_; + float start_; + float target_; + bool bidirectional_; + + virtual float readValue(Source *s) const = 0; + virtual void writeValue(Source *s, float val) = 0; + +public: + ValueSourceCallback (float v = 0.f, float ms = 0.f, bool revert = false); + + float value () const { return target_;} + void setValue (float v) { target_ = v; } + float duration () const { return duration_;} + void setDuration (float ms) { duration_ = ms; } + bool bidirectional () const { return bidirectional_;} + void setBidirectional (bool on) { bidirectional_ = on; } + + void update (Source *s, float) override; + void multiply (float factor) override; + SourceCallback *clone () const override; + SourceCallback *reverse(Source *s) const override; + void accept (Visitor& v) override; +}; + + class SetAlpha : public SourceCallback { float duration_; @@ -178,22 +236,6 @@ public: CallbackType type () const override { return CALLBACK_REPLAY; } }; -class Seek : public SourceCallback -{ - float target_; - -public: - Seek (float time = 0.f); - - float value () const { return target_;} - void setValue (float t) { target_ = t; } - - void update (Source *s, float dt) override; - SourceCallback *clone() const override; - CallbackType type () const override { return CALLBACK_SEEK; } - void accept (Visitor& v) override; -}; - class ResetGeometry : public SourceCallback { public: @@ -293,5 +335,101 @@ public: void accept (Visitor& v) override; }; +class Seek : public ValueSourceCallback +{ + float readValue(Source *s) const override; + void writeValue(Source *s, float val) override; +public: + Seek (float v = 0.f, float ms = 0.f, bool revert = false); + CallbackType type () const override { return CALLBACK_SEEK; } +}; + +class SetBrightness : public ValueSourceCallback +{ + float readValue(Source *s) const override; + void writeValue(Source *s, float val) override; +public: + SetBrightness (float v = 0.f, float ms = 0.f, bool revert = false); + CallbackType type () const override { return CALLBACK_BRIGHTNESS; } +}; + +class SetContrast : public ValueSourceCallback +{ + float readValue(Source *s) const override; + void writeValue(Source *s, float val) override; +public: + SetContrast (float v = 0.f, float ms = 0.f, bool revert = false); + CallbackType type () const override { return CALLBACK_CONTRAST; } +}; + +class SetSaturation : public ValueSourceCallback +{ + float readValue(Source *s) const override; + void writeValue(Source *s, float val) override; +public: + SetSaturation (float v = 0.f, float ms = 0.f, bool revert = false); + CallbackType type () const override { return CALLBACK_SATURATION; } +}; + +class SetHue : public ValueSourceCallback +{ + float readValue(Source *s) const override; + void writeValue(Source *s, float val) override; +public: + SetHue (float v = 0.f, float ms = 0.f, bool revert = false); + CallbackType type () const override { return CALLBACK_HUE; } +}; + +class SetThreshold : public ValueSourceCallback +{ + float readValue(Source *s) const override; + void writeValue(Source *s, float val) override; +public: + SetThreshold (float v = 0.f, float ms = 0.f, bool revert = false); + CallbackType type () const override { return CALLBACK_THRESHOLD; } +}; + +class SetInvert : public ValueSourceCallback +{ + float readValue(Source *s) const override; + void writeValue(Source *s, float val) override; +public: + SetInvert (float v = 0.f, float ms = 0.f, bool revert = false); + CallbackType type () const override { return CALLBACK_INVERT; } +}; + +class SetPosterize : public ValueSourceCallback +{ + float readValue(Source *s) const override; + void writeValue(Source *s, float val) override; +public: + SetPosterize (float v = 0.f, float ms = 0.f, bool revert = false); + CallbackType type () const override { return CALLBACK_POSTERIZE; } +}; + +class SetGamma : public SourceCallback +{ + float duration_; + glm::vec4 start_; + glm::vec4 target_; + bool bidirectional_; + +public: + SetGamma (glm::vec4 g = glm::vec4(), float ms = 0.f, bool revert = false); + + glm::vec4 value () const { return target_; } + void setValue (glm::vec4 g) { target_ = g; } + float duration () const { return duration_; } + void setDuration (float ms) { duration_ = ms; } + bool bidirectional () const { return bidirectional_; } + void setBidirectional (bool on) { bidirectional_ = on; } + + void update (Source *s, float) override; + void multiply (float factor) override; + SourceCallback *clone () const override; + SourceCallback *reverse(Source *s) const override; + CallbackType type () const override { return CALLBACK_GAMMA; } + void accept (Visitor& v) override; +}; #endif // SOURCECALLBACK_H diff --git a/Visitor.h b/Visitor.h index da425b7..8bfa5f7 100644 --- a/Visitor.h +++ b/Visitor.h @@ -53,6 +53,7 @@ class AlphaFilter; class ImageFilter; class SourceCallback; +class ValueSourceCallback; class SetAlpha; class SetDepth; class SetGeometry; @@ -119,6 +120,7 @@ public: virtual void visit (ImageFilter&) {} virtual void visit (SourceCallback&) {} + virtual void visit (ValueSourceCallback&) {} virtual void visit (SetAlpha&) {} virtual void visit (SetDepth&) {} virtual void visit (SetGeometry&) {}