diff --git a/app/codec/decoder.cpp b/app/codec/decoder.cpp index 336aaa6f1..6fb2a2676 100644 --- a/app/codec/decoder.cpp +++ b/app/codec/decoder.cpp @@ -293,7 +293,7 @@ bool Decoder::RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVecto const qint64 buffer_length_in_bytes = sample_buffer.sample_count() * input_params.bytes_per_sample_per_channel(); while (write_index < buffer_length_in_bytes) { - if (loop_mode == kLoopModeLoop) { + if (loop_mode == LoopMode::kLoopModeLoop) { while (read_index >= input.size()) { read_index -= input.size(); } diff --git a/app/codec/decoder.h b/app/codec/decoder.h index 029cefa25..471958bc9 100644 --- a/app/codec/decoder.h +++ b/app/codec/decoder.h @@ -31,12 +31,11 @@ extern "C" { #include #include -#include "codec/frame.h" #include "codec/samplebuffer.h" #include "common/rational.h" #include "node/block/block.h" #include "node/project/footage/footagedescription.h" -#include "task/task.h" +#include "render/cancelatom.h" namespace olive { @@ -71,12 +70,6 @@ public: kIndexUnavailable }; - enum LoopMode { - kLoopModeOff, - kLoopModeLoop, - kLoopModeClamp - }; - Decoder(); /** diff --git a/app/dialog/speedduration/speeddurationdialog.cpp b/app/dialog/speedduration/speeddurationdialog.cpp index 7214ca27c..8f186ed20 100644 --- a/app/dialog/speedduration/speeddurationdialog.cpp +++ b/app/dialog/speedduration/speeddurationdialog.cpp @@ -100,9 +100,9 @@ SpeedDurationDialog::SpeedDurationDialog(const QVector &clips, cons loop_layout->addWidget(new QLabel(tr("Loop:")), row, 0); loop_combo_ = new QComboBox(); - loop_combo_->addItem(tr("None"), Decoder::kLoopModeOff); - loop_combo_->addItem(tr("Loop"), Decoder::kLoopModeLoop); - loop_combo_->addItem(tr("Clamp"), Decoder::kLoopModeClamp); + loop_combo_->addItem(tr("None"), int(LoopMode::kLoopModeOff)); + loop_combo_->addItem(tr("Loop"), int(LoopMode::kLoopModeLoop)); + loop_combo_->addItem(tr("Clamp"), int(LoopMode::kLoopModeClamp)); loop_layout->addWidget(loop_combo_, row, 1); } @@ -117,7 +117,7 @@ SpeedDurationDialog::SpeedDurationDialog(const QVector &clips, cons start_duration_ = clips.first()->length(); start_reverse_ = clips.first()->reverse(); start_maintain_audio_pitch_ = clips.first()->maintain_audio_pitch(); - start_loop_ = clips.first()->loop_mode(); + start_loop_ = int(clips.first()->loop_mode()); for (int i=1; i &clips, cons start_maintain_audio_pitch_ = -1; } - if (start_loop_ != -1 && c->loop_mode() != start_loop_) { + if (start_loop_ != -1 && int(c->loop_mode()) != start_loop_) { start_loop_ = -1; } } diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index 110a26ee4..4d4bf1ebd 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -453,6 +453,8 @@ void ClipBlock::InputValueChangedEvent(const QString &input, int element) } } } + } else if (input == kLoopModeInput) { + emit PreviewChanged(); } } diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index 15cadcfb7..ddbbd881a 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -188,12 +188,12 @@ public: /** * @brief Get currently set loop mode */ - Decoder::LoopMode loop_mode() const + LoopMode loop_mode() const { - return static_cast(GetStandardValue(kLoopModeInput).toInt()); + return static_cast(GetStandardValue(kLoopModeInput).toInt()); } - void set_loop_mode(Decoder::LoopMode l) + void set_loop_mode(LoopMode l) { SetStandardValue(kLoopModeInput, int(l)); } diff --git a/app/node/globals.h b/app/node/globals.h index c7ec1ce59..9c8eb26a9 100644 --- a/app/node/globals.h +++ b/app/node/globals.h @@ -25,6 +25,7 @@ #include "common/timerange.h" #include "render/audioparams.h" +#include "render/loopmode.h" #include "render/videoparams.h" namespace olive { @@ -34,10 +35,16 @@ class NodeGlobals public: NodeGlobals(){} - NodeGlobals(const VideoParams &vparam, const AudioParams &aparam, const TimeRange &time) : + NodeGlobals(const VideoParams &vparam, const AudioParams &aparam, const TimeRange &time, LoopMode loop_mode) : video_params_(vparam), audio_params_(aparam), - time_(time) + time_(time), + loop_mode_(loop_mode) + { + } + + NodeGlobals(const VideoParams &vparam, const AudioParams &aparam, const rational &time, LoopMode loop_mode) : + NodeGlobals(vparam, aparam, TimeRange(time, time + vparam.frame_rate_as_time_base()), loop_mode) { } @@ -46,11 +53,13 @@ public: const AudioParams &aparams() const { return audio_params_; } const VideoParams &vparams() const { return video_params_; } const TimeRange &time() const { return time_; } + LoopMode loop_mode() const { return loop_mode_; } private: VideoParams video_params_; AudioParams audio_params_; TimeRange time_; + LoopMode loop_mode_; }; diff --git a/app/node/project/footage/footage.cpp b/app/node/project/footage/footage.cpp index 210d448f5..26e41a5de 100644 --- a/app/node/project/footage/footage.cpp +++ b/app/node/project/footage/footage.cpp @@ -265,7 +265,7 @@ void Footage::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeV // Push each stream as a footage job for (int i=0; i= length; } -rational Footage::AdjustTimeByLoopMode(rational time, Decoder::LoopMode loop_mode, const rational &length, VideoParams::Type type, const rational& timebase) +rational Footage::AdjustTimeByLoopMode(rational time, LoopMode loop_mode, const rational &length, VideoParams::Type type, const rational& timebase) { if (type == VideoParams::kVideoTypeStill) { // No looping for still images @@ -346,15 +346,15 @@ rational Footage::AdjustTimeByLoopMode(rational time, Decoder::LoopMode loop_mod if (TimeIsOutOfBounds(time, length)) { switch (loop_mode) { - case Decoder::kLoopModeOff: + case LoopMode::kLoopModeOff: // Return no time to indicate no frame should be shown here time = rational::NaN; break; - case Decoder::kLoopModeClamp: + case LoopMode::kLoopModeClamp: // Clamp footage time to length time = clamp(time, rational(0), length - timebase); break; - case Decoder::kLoopModeLoop: + case LoopMode::kLoopModeLoop: // Loop footage time around job length do { if (time >= length) { diff --git a/app/node/project/footage/footage.h b/app/node/project/footage/footage.h index 7eb38f3c5..321afa829 100644 --- a/app/node/project/footage/footage.h +++ b/app/node/project/footage/footage.h @@ -173,7 +173,7 @@ public: virtual Node *GetConnectedSampleOutput() override; - static rational AdjustTimeByLoopMode(rational time, Decoder::LoopMode loop_mode, const rational& length, VideoParams::Type type, const rational &timebase); + static rational AdjustTimeByLoopMode(rational time, LoopMode loop_mode, const rational& length, VideoParams::Type type, const rational &timebase); virtual void LoadFinishedEvent() override; diff --git a/app/node/traverser.cpp b/app/node/traverser.cpp index ee1fa0fbd..e99be2d4f 100644 --- a/app/node/traverser.cpp +++ b/app/node/traverser.cpp @@ -32,7 +32,7 @@ NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const TimeRa NodeValueDatabase database; // HACK: Pick up loop mode from clips - Decoder::LoopMode old_loop_mode = loop_mode_; + LoopMode old_loop_mode = loop_mode_; if (const ClipBlock *clip = dynamic_cast(node)) { loop_mode_ = clip->loop_mode(); } @@ -184,11 +184,6 @@ void NodeTraverser::Transform(QTransform *transform, const Node *start, const No transform_ = nullptr; } -NodeGlobals NodeTraverser::GenerateGlobals(const VideoParams &vparams, const AudioParams &aparams, const TimeRange &time) -{ - return NodeGlobals(vparams, aparams, time); -} - NodeValueTable NodeTraverser::ProcessInput(const Node* node, const QString& input, const TimeRange& range) { // If input is connected, retrieve value directly @@ -259,7 +254,7 @@ void NodeTraverser::ProcessInputElement(NodeValueTableArray &array_tbl, const No NodeTraverser::NodeTraverser() : cancel_(nullptr), transform_(nullptr), - loop_mode_(Decoder::kLoopModeOff) + loop_mode_(LoopMode::kLoopModeOff) { } @@ -309,7 +304,7 @@ NodeValueTable NodeTraverser::GenerateTable(const Node *n, const TimeRange& rang table = database.Merge(); // By this point, the node should have all the inputs it needs to render correctly - NodeGlobals globals = GenerateGlobals(video_params_, audio_params_, range); + NodeGlobals globals(video_params_, audio_params_, range, loop_mode_); n->Value(row, globals, &table); // `transform_now_` is the next node in the path that needs to be traversed. It only ever goes @@ -430,7 +425,7 @@ void NodeTraverser::ResolveJobs(NodeValue &val) } else if (FootageJob *fj = dynamic_cast(base_job)) { - rational footage_time = Footage::AdjustTimeByLoopMode(fj->time().in(), loop_mode_, fj->length(), fj->video_params().video_type(), fj->video_params().frame_rate_as_time_base()); + rational footage_time = Footage::AdjustTimeByLoopMode(fj->time().in(), fj->loop_mode(), fj->length(), fj->video_params().video_type(), fj->video_params().frame_rate_as_time_base()); TexturePtr tex; diff --git a/app/node/traverser.h b/app/node/traverser.h index 4f2041d57..84284a0d8 100644 --- a/app/node/traverser.h +++ b/app/node/traverser.h @@ -54,12 +54,6 @@ public: void Transform(QTransform *transform, const Node *start, const Node *end, const TimeRange &range); - static NodeGlobals GenerateGlobals(const VideoParams &vparams, const AudioParams &aparams, const TimeRange &time); - static NodeGlobals GenerateGlobals(const VideoParams &vparams, const AudioParams &aparams, const rational &time) - { - return GenerateGlobals(vparams, aparams, TimeRange(time, time + vparams.frame_rate_as_time_base())); - } - const VideoParams& GetCacheVideoParams() const { return video_params_; @@ -144,7 +138,7 @@ protected: return block_stack_.empty() ? nullptr : block_stack_.back(); } - Decoder::LoopMode loop_mode() const { return loop_mode_; } + LoopMode loop_mode() const { return loop_mode_; } virtual bool UseCache() const { return false; } @@ -163,7 +157,7 @@ private: std::list block_stack_; - Decoder::LoopMode loop_mode_; + LoopMode loop_mode_; QHash > value_cache_; QHash resolved_texture_cache_; diff --git a/app/render/CMakeLists.txt b/app/render/CMakeLists.txt index 2c779e2e8..068552763 100644 --- a/app/render/CMakeLists.txt +++ b/app/render/CMakeLists.txt @@ -38,6 +38,7 @@ set(OLIVE_SOURCES render/framehashcache.h render/framemanager.cpp render/framemanager.h + render/loopmode.h render/managedcolor.cpp render/managedcolor.h render/playbackcache.cpp diff --git a/app/render/job/footagejob.h b/app/render/job/footagejob.h index dcafee554..3fbd7e267 100644 --- a/app/render/job/footagejob.h +++ b/app/render/job/footagejob.h @@ -33,12 +33,13 @@ public: { } - FootageJob(const TimeRange &time, const QString& decoder, const QString& filename, Track::Type type, const rational& length) : + FootageJob(const TimeRange &time, const QString& decoder, const QString& filename, Track::Type type, const rational& length, LoopMode loop_mode) : time_(time), decoder_(decoder), filename_(filename), type_(type), - length_(length) + length_(length), + loop_mode_(loop_mode) { } @@ -99,6 +100,9 @@ public: const TimeRange &time() const { return time_; } + LoopMode loop_mode() const { return loop_mode_; } + void set_loop_mode(LoopMode m) { loop_mode_ = m; } + private: TimeRange time_; @@ -116,6 +120,8 @@ private: rational length_; + LoopMode loop_mode_; + }; } diff --git a/app/render/loopmode.h b/app/render/loopmode.h new file mode 100644 index 000000000..9e4927727 --- /dev/null +++ b/app/render/loopmode.h @@ -0,0 +1,14 @@ +#ifndef LOOPMODE_H +#define LOOPMODE_H + +namespace olive { + +enum class LoopMode { + kLoopModeOff, + kLoopModeLoop, + kLoopModeClamp +}; + +} + +#endif // LOOPMODE_H diff --git a/app/widget/timelinewidget/view/timelineview.cpp b/app/widget/timelinewidget/view/timelineview.cpp index 2ba27f240..a8198029d 100644 --- a/app/widget/timelinewidget/view/timelineview.cpp +++ b/app/widget/timelinewidget/view/timelineview.cpp @@ -532,18 +532,18 @@ void TimelineView::DrawBlock(QPainter *painter, bool foreground, Block *block, q qreal zebra_right = TimeToScene(clip->in() - clip->media_in()); switch (clip->loop_mode()) { - case Decoder::kLoopModeOff: + case LoopMode::kLoopModeOff: // Draw stripes for sections of clip < 0 if (zebra_right > GetTimelineLeftBound()) { DrawZebraStripes(painter, QRectF(block_left, block_top, zebra_right - block_left, block_height)); } break; - case Decoder::kLoopModeLoop: + case LoopMode::kLoopModeLoop: for (qreal i=zebra_right; i>block_left; i-=TimeToScene(clip->connected_viewer()->GetLength())) { painter->drawLine(i, block_top, i, block_top + block_height); } break; - case Decoder::kLoopModeClamp: + case LoopMode::kLoopModeClamp: painter->drawLine(zebra_right, block_top, zebra_right, block_top + block_height); break; } @@ -552,18 +552,18 @@ void TimelineView::DrawBlock(QPainter *painter, bool foreground, Block *block, q if (clip->length() + clip->media_in() > clip->connected_viewer()->GetLength()) { qreal zebra_left = TimeToScene(clip->out() - (clip->media_in() + clip->length() - clip->connected_viewer()->GetLength())); switch (clip->loop_mode()) { - case Decoder::kLoopModeOff: + case LoopMode::kLoopModeOff: // Draw stripes for sections for clip > clip length if (zebra_left < GetTimelineRightBound()) { DrawZebraStripes(painter, QRectF(zebra_left, block_top, block_right - zebra_left, block_height)); } break; - case Decoder::kLoopModeLoop: + case LoopMode::kLoopModeLoop: for (qreal i=zebra_left; iconnected_viewer()->GetLength())) { painter->drawLine(i, block_top, i, block_top + block_height); } break; - case Decoder::kLoopModeClamp: + case LoopMode::kLoopModeClamp: painter->drawLine(zebra_left, block_top, zebra_left, block_top + block_height); break; } diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index cd75d91e2..a2f036cd8 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -380,15 +380,7 @@ void ViewerDisplayWidget::OnPaint() VideoParams device_params = GetViewportParams(); if (push_mode_ == kPushBlank) { - if (blank_shader_.isNull()) { - blank_shader_ = renderer()->CreateNativeShader(ShaderCode()); - } - - ShaderJob job; - job.Insert(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, combined_matrix_flipped_)); - job.Insert(QStringLiteral("ove_cropmatrix"), NodeValue(NodeValue::kMatrix, crop_matrix_)); - - renderer()->Blit(blank_shader_, job, device_params, false); + DrawBlank(device_params); } else if (color_service()) { if (FramePtr frame = load_frame_.value()) { // This is a CPU frame, upload it now @@ -415,35 +407,39 @@ void ViewerDisplayWidget::OnPaint() TexturePtr texture_to_draw = texture_; - if (deinterlace_) { - if (deinterlace_shader_.isNull()) { - deinterlace_shader_ = renderer()->CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/deinterlace.frag")))); + if (!texture_to_draw || texture_to_draw->IsDummy()) { + DrawBlank(device_params); + } else { + if (deinterlace_) { + if (deinterlace_shader_.isNull()) { + deinterlace_shader_ = renderer()->CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/deinterlace.frag")))); + } + + if (!deinterlace_texture_ + || deinterlace_texture_->params() != texture_to_draw->params()) { + // (Re)create texture + deinterlace_texture_ = renderer()->CreateTexture(texture_to_draw->params()); + } + + ShaderJob job; + job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, QVector2D(texture_to_draw->width(), texture_to_draw->height()))); + job.Insert(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture_to_draw))); + + renderer()->BlitToTexture(deinterlace_shader_, job, deinterlace_texture_.get()); + + texture_to_draw = deinterlace_texture_; } - if (!deinterlace_texture_ - || deinterlace_texture_->params() != texture_to_draw->params()) { - // (Re)create texture - deinterlace_texture_ = renderer()->CreateTexture(texture_to_draw->params()); - } + ColorTransformJob ctj; + ctj.SetColorProcessor(color_service()); + ctj.SetInputTexture(texture_to_draw); + ctj.SetInputAlphaAssociation(OLIVE_CONFIG("ReassocLinToNonLin").toBool() ? kAlphaAssociated : kAlphaNone); + ctj.SetClearDestinationEnabled(false); + ctj.SetTransformMatrix(combined_matrix_flipped_); + ctj.SetCropMatrix(crop_matrix_); - ShaderJob job; - job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, QVector2D(texture_to_draw->width(), texture_to_draw->height()))); - job.Insert(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture_to_draw))); - - renderer()->BlitToTexture(deinterlace_shader_, job, deinterlace_texture_.get()); - - texture_to_draw = deinterlace_texture_; + renderer()->BlitColorManaged(ctj, device_params); } - - ColorTransformJob ctj; - ctj.SetColorProcessor(color_service()); - ctj.SetInputTexture(texture_to_draw); - ctj.SetInputAlphaAssociation(OLIVE_CONFIG("ReassocLinToNonLin").toBool() ? kAlphaAssociated : kAlphaNone); - ctj.SetClearDestinationEnabled(false); - ctj.SetTransformMatrix(combined_matrix_flipped_); - ctj.SetCropMatrix(crop_matrix_); - - renderer()->BlitColorManaged(ctj, device_params); } } @@ -455,7 +451,7 @@ void ViewerDisplayWidget::OnPaint() p.setWorldTransform(gizmo_last_draw_transform_); - gizmos_->UpdateGizmoPositions(gizmo_db_, NodeTraverser::GenerateGlobals(gizmo_params_, gizmo_audio_params_, gizmo_draw_time_)); + gizmos_->UpdateGizmoPositions(gizmo_db_, NodeGlobals(gizmo_params_, gizmo_audio_params_, gizmo_draw_time_, LoopMode::kLoopModeOff)); foreach (NodeGizmo *gizmo, gizmos_->GetGizmos()) { if (gizmo->IsVisible()) { gizmo->Draw(&p); @@ -831,7 +827,7 @@ bool ViewerDisplayWidget::OnMousePress(QMouseEvent *event) // Handle gizmo click gizmo_start_drag_ = event->pos(); gizmo_last_drag_ = gizmo_start_drag_; - current_gizmo_->SetGlobals(NodeTraverser::GenerateGlobals(gizmo_params_, gizmo_audio_params_, GenerateGizmoTime())); + current_gizmo_->SetGlobals(NodeGlobals(gizmo_params_, gizmo_audio_params_, GenerateGizmoTime(), LoopMode::kLoopModeOff)); } else { @@ -1213,6 +1209,19 @@ void ViewerDisplayWidget::GenerateGizmoTransforms() gizmo_last_draw_transform_inverted_ = gizmo_last_draw_transform_.inverted(); } +void ViewerDisplayWidget::DrawBlank(const VideoParams &device_params) +{ + if (blank_shader_.isNull()) { + blank_shader_ = renderer()->CreateNativeShader(ShaderCode()); + } + + ShaderJob job; + job.Insert(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, combined_matrix_flipped_)); + job.Insert(QStringLiteral("ove_cropmatrix"), NodeValue(NodeValue::kMatrix, crop_matrix_)); + + renderer()->Blit(blank_shader_, job, device_params, false); +} + void ViewerDisplayWidget::SetShowFPS(bool e) { show_fps_ = e; diff --git a/app/widget/viewer/viewerdisplay.h b/app/widget/viewer/viewerdisplay.h index a82bad747..913d5d93e 100644 --- a/app/widget/viewer/viewerdisplay.h +++ b/app/widget/viewer/viewerdisplay.h @@ -306,6 +306,8 @@ private: void GenerateGizmoTransforms(); + void DrawBlank(const VideoParams &device_params); + /** * @brief Internal reference to the OpenGL texture to draw. Set in SetTexture() and used in paintGL(). */