began support of timeline tracks/channels
This commit is contained in:
+27
-3
@@ -219,8 +219,10 @@ void Core::CreateNewFolder()
|
||||
|
||||
// FIXME: Test code
|
||||
#include "node/block/clip/clip.h"
|
||||
#include "node/block/gap/gap.h"
|
||||
#include "node/input/media/media.h"
|
||||
#include "node/output/timeline/timeline.h"
|
||||
#include "node/output/track/track.h"
|
||||
#include "node/output/viewer/viewer.h"
|
||||
#include "node/generator/solid/solid.h"
|
||||
#include "panel/panelmanager.h"
|
||||
@@ -271,16 +273,38 @@ void Core::CreateNewSequence()
|
||||
new_sequence);
|
||||
|
||||
// FIXME: Test code
|
||||
GapBlock* gb = new GapBlock();
|
||||
gb->set_length(1);
|
||||
new_sequence->AddNode(gb);
|
||||
|
||||
ClipBlock* cb = new ClipBlock();
|
||||
cb->set_length(2);
|
||||
new_sequence->AddNode(cb);
|
||||
|
||||
TrackOutput* to = new TrackOutput();
|
||||
new_sequence->AddNode(to);
|
||||
|
||||
ClipBlock* cb2 = new ClipBlock();
|
||||
cb2->set_length(2);
|
||||
new_sequence->AddNode(cb2);
|
||||
|
||||
TrackOutput* to2 = new TrackOutput();
|
||||
new_sequence->AddNode(to2);
|
||||
|
||||
TimelineOutput* tb = new TimelineOutput();
|
||||
new_sequence->AddNode(tb);
|
||||
|
||||
ViewerOutput* vo = new ViewerOutput();
|
||||
vo->AttachViewer(olive::panel_focus_manager->MostRecentlyFocused<ViewerPanel>());
|
||||
new_sequence->AddNode(vo);
|
||||
NodeParam::ConnectEdge(tb->texture_output(), vo->texture_input());
|
||||
|
||||
Block::ConnectBlocks(gb, cb);
|
||||
Block::ConnectBlocks(cb, to);
|
||||
Block::ConnectBlocks(cb2, to2);
|
||||
NodeParam::ConnectEdge(to->texture_output(), vo->texture_input());
|
||||
NodeParam::ConnectEdge(to->track_output(), tb->track_input());
|
||||
|
||||
vo->AttachViewer(olive::panel_focus_manager->MostRecentlyFocused<ViewerPanel>());
|
||||
tb->AttachTimeline(olive::panel_focus_manager->MostRecentlyFocused<TimelinePanel>());
|
||||
|
||||
olive::panel_focus_manager->MostRecentlyFocused<NodePanel>()->SetGraph(new_sequence.get());
|
||||
// End test code
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(blend)
|
||||
add_subdirectory(block)
|
||||
add_subdirectory(generator)
|
||||
add_subdirectory(input)
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2019 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(alphaover)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/blend/blend.h
|
||||
node/blend/blend.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2019 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/blend/alphaover/alphaover.h
|
||||
node/blend/alphaover/alphaover.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "alphaover.h"
|
||||
|
||||
AlphaOverBlend::AlphaOverBlend()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AlphaOverBlend::Process(const rational &time)
|
||||
{
|
||||
Q_UNUSED(time)
|
||||
|
||||
// FIXME: Write Alpha Over Formula
|
||||
|
||||
// Note that alpha will always be premultiplied by this point
|
||||
|
||||
//GLuint base_tex = base_input_->get_value(time).value<GLuint>();
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef ALPHAOVER_H
|
||||
#define ALPHAOVER_H
|
||||
|
||||
#include "node/blend/blend.h"
|
||||
|
||||
class AlphaOverBlend : public BlendNode
|
||||
{
|
||||
public:
|
||||
AlphaOverBlend();
|
||||
|
||||
public slots:
|
||||
virtual void Process(const rational &time) override;
|
||||
};
|
||||
|
||||
#endif // ALPHAOVER_H
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "blend.h"
|
||||
|
||||
BlendNode::BlendNode()
|
||||
{
|
||||
base_input_ = new NodeInput("base_in");
|
||||
base_input_->add_data_input(NodeParam::kTexture);
|
||||
AddParameter(base_input_);
|
||||
|
||||
blend_input_ = new NodeInput("blend_in");
|
||||
blend_input_->add_data_input(NodeParam::kTexture);
|
||||
AddParameter(blend_input_);
|
||||
}
|
||||
|
||||
NodeInput *BlendNode::base_input()
|
||||
{
|
||||
return base_input_;
|
||||
}
|
||||
|
||||
NodeInput *BlendNode::blend_input()
|
||||
{
|
||||
return blend_input_;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef BLEND_H
|
||||
#define BLEND_H
|
||||
|
||||
#include "node/node.h"
|
||||
|
||||
class BlendNode : public Node
|
||||
{
|
||||
public:
|
||||
BlendNode();
|
||||
|
||||
NodeInput* base_input();
|
||||
|
||||
NodeInput* blend_input();
|
||||
|
||||
private:
|
||||
NodeInput* base_input_;
|
||||
|
||||
NodeInput* blend_input_;
|
||||
};
|
||||
|
||||
#endif // BLEND_H
|
||||
@@ -28,14 +28,14 @@ Block::Block()
|
||||
previous_input_->add_data_input(NodeParam::kBlock);
|
||||
AddParameter(previous_input_);
|
||||
|
||||
block_output_ = new NodeOutput("block_out");
|
||||
block_output_->set_data_type(NodeParam::kBlock);
|
||||
AddParameter(block_output_);
|
||||
|
||||
next_input_ = new NodeInput("next_block");
|
||||
next_input_->add_data_input(NodeParam::kBlock);
|
||||
AddParameter(next_input_);
|
||||
|
||||
block_output_ = new NodeOutput("block_out");
|
||||
block_output_->set_data_type(NodeParam::kBlock);
|
||||
AddParameter(block_output_);
|
||||
|
||||
texture_output_ = new NodeOutput("tex_out");
|
||||
texture_output_->set_data_type(NodeParam::kTexture);
|
||||
AddParameter(texture_output_);
|
||||
|
||||
@@ -53,6 +53,11 @@ void Node::AddParameter(NodeParam *param)
|
||||
connect(param, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SIGNAL(EdgeRemoved(NodeEdgePtr)));
|
||||
}
|
||||
|
||||
void Node::RemoveParameter(NodeParam *param)
|
||||
{
|
||||
delete param;
|
||||
}
|
||||
|
||||
void Node::InvalidateCache(const rational &start_range, const rational &end_range)
|
||||
{
|
||||
QList<NodeParam *> params = parameters();
|
||||
@@ -154,6 +159,25 @@ QList<Node *> Node::GetExclusiveDependencies()
|
||||
return deps;
|
||||
}
|
||||
|
||||
bool Node::OutputsTo(Node *n)
|
||||
{
|
||||
QList<NodeParam*> params = parameters();
|
||||
|
||||
foreach (NodeParam* param, params) {
|
||||
if (param->type() == NodeParam::kOutput) {
|
||||
QVector<NodeEdgePtr> edges = param->edges();
|
||||
|
||||
foreach (NodeEdgePtr edge, edges) {
|
||||
if (edge->input()->parent() == n) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant Node::PtrToValue(void *ptr)
|
||||
{
|
||||
return reinterpret_cast<quintptr>(ptr);
|
||||
|
||||
@@ -121,6 +121,11 @@ public:
|
||||
*/
|
||||
QList<Node*> GetExclusiveDependencies();
|
||||
|
||||
/**
|
||||
* @brief Returns whether this Node outputs data to the Node `n` in any way
|
||||
*/
|
||||
bool OutputsTo(Node* n);
|
||||
|
||||
/**
|
||||
* @brief Convert a pointer to a value that can be sent between NodeParams
|
||||
*/
|
||||
@@ -142,6 +147,13 @@ protected:
|
||||
*/
|
||||
void AddParameter(NodeParam* param);
|
||||
|
||||
/**
|
||||
* @brief Deletes the parameter from this Node
|
||||
*
|
||||
* The NodeParam object is destroyed in the process.
|
||||
*/
|
||||
void RemoveParameter(NodeParam* param);
|
||||
|
||||
/**
|
||||
* @brief Signal all dependent Nodes that anything cached between start_range and end_range is now invalid and
|
||||
* requires re-rendering
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(timeline)
|
||||
add_subdirectory(track)
|
||||
add_subdirectory(viewer)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
|
||||
@@ -26,19 +26,14 @@
|
||||
#include "node/graph.h"
|
||||
|
||||
TimelineOutput::TimelineOutput() :
|
||||
current_block_(this),
|
||||
attached_timeline_(nullptr)
|
||||
{
|
||||
}
|
||||
track_input_ = new NodeInput("track_in");
|
||||
track_input_->add_data_input(NodeParam::kTrack);
|
||||
AddParameter(track_input_);
|
||||
|
||||
Block::Type TimelineOutput::type()
|
||||
{
|
||||
return kEnd;
|
||||
}
|
||||
|
||||
Block *TimelineOutput::copy()
|
||||
{
|
||||
return new TimelineOutput();
|
||||
connect(this, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SLOT(TrackConnectionAdded(NodeEdgePtr)));
|
||||
connect(this, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(TrackConnectionRemoved(NodeEdgePtr)));
|
||||
}
|
||||
|
||||
QString TimelineOutput::Name()
|
||||
@@ -58,308 +53,110 @@ QString TimelineOutput::Category()
|
||||
|
||||
QString TimelineOutput::Description()
|
||||
{
|
||||
return tr("Node for communicating between a Timeline panel and the node graph. Also represents the end of a"
|
||||
"Sequence.");
|
||||
return tr("Node for communicating between a Timeline panel and the node graph.");
|
||||
}
|
||||
|
||||
void TimelineOutput::AttachTimeline(TimelinePanel *timeline)
|
||||
{
|
||||
if (attached_timeline_ != nullptr) {
|
||||
disconnect(attached_timeline_, SIGNAL(RequestInsertBlockAtIndex(Block*, int)), this, SLOT(InsertBlockAtIndex(Block*, int)));
|
||||
disconnect(attached_timeline_, SIGNAL(RequestPlaceBlock(Block*, rational)), this, SLOT(PlaceBlock(Block*, rational)));
|
||||
//TimelineView* view = attached_timeline_->view();
|
||||
|
||||
//disconnect(view, SIGNAL(RequestInsertBlockAtIndex(Block*, int)), this, SLOT(InsertBlockAtIndex(Block*, int)));
|
||||
//disconnect(view, SIGNAL(RequestPlaceBlock(Block*, rational)), this, SLOT(PlaceBlock(Block*, rational)));
|
||||
|
||||
// Remove existing UI objects from TimelinePanel
|
||||
attached_timeline_->Clear();
|
||||
}
|
||||
|
||||
attached_timeline_ = timeline;
|
||||
|
||||
if (attached_timeline_ != nullptr) {
|
||||
attached_timeline_->Clear();
|
||||
|
||||
// FIXME: TEST CODE ONLY
|
||||
attached_timeline_->SetTimebase(rational(1001, 30000));
|
||||
// END TEST CODE
|
||||
|
||||
Block* previous_block = attached_block();
|
||||
while (previous_block != nullptr) {
|
||||
|
||||
// FIXME: Dynamic cast is a dumb way of doing this
|
||||
ClipBlock* clip_block = dynamic_cast<ClipBlock*>(previous_block);
|
||||
|
||||
if (clip_block != nullptr) {
|
||||
attached_timeline_->AddClip(clip_block);
|
||||
}
|
||||
|
||||
previous_block = previous_block->previous();
|
||||
if (attached_track() != nullptr) {
|
||||
// Defer to the track to make all the block UI items necessary
|
||||
attached_track()->GenerateBlockWidgets();
|
||||
}
|
||||
|
||||
TimelineView* view = attached_timeline_->view();
|
||||
//TimelineView* view = attached_timeline_->view();
|
||||
|
||||
connect(view, SIGNAL(RequestInsertBlockAtIndex(Block*, int)), this, SLOT(InsertBlockAtIndex(Block*, int)));
|
||||
connect(view, SIGNAL(RequestPlaceBlock(Block*, rational)), this, SLOT(PlaceBlock(Block*, rational)));
|
||||
//connect(view, SIGNAL(RequestInsertBlockAtIndex(Block*, int)), this, SLOT(InsertBlockAtIndex(Block*, int)));
|
||||
//connect(view, SIGNAL(RequestPlaceBlock(Block*, rational)), this, SLOT(PlaceBlock(Block*, rational)));
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineOutput::set_length(const rational &)
|
||||
NodeInput *TimelineOutput::track_input()
|
||||
{
|
||||
// Prevent length changing on this Block
|
||||
}
|
||||
|
||||
void TimelineOutput::Refresh()
|
||||
{
|
||||
QVector<Block*> detect_attached_blocks;
|
||||
|
||||
Block* previous = attached_block();
|
||||
while (previous != nullptr) {
|
||||
detect_attached_blocks.prepend(previous);
|
||||
|
||||
if (attached_timeline_ != nullptr && !block_cache_.contains(previous)) {
|
||||
// FIXME: Remove the need to cast this
|
||||
attached_timeline_->AddClip(static_cast<ClipBlock*>(previous));
|
||||
}
|
||||
|
||||
previous = previous->previous();
|
||||
}
|
||||
|
||||
if (attached_timeline_ != nullptr) {
|
||||
foreach (Block* b, block_cache_) {
|
||||
if (!detect_attached_blocks.contains(b)) {
|
||||
attached_timeline_->RemoveClip(static_cast<ClipBlock*>(b));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
block_cache_ = detect_attached_blocks;
|
||||
|
||||
Block::Refresh();
|
||||
return track_input_;
|
||||
}
|
||||
|
||||
void TimelineOutput::Process(const rational &time)
|
||||
{
|
||||
// Run default node processing
|
||||
Block::Process(time);
|
||||
Q_UNUSED(time)
|
||||
}
|
||||
|
||||
// This node representso the end of the timeline, so being beyond its in point is considered the end of the sequence
|
||||
if (time >= in()) {
|
||||
texture_output()->set_value(0);
|
||||
current_block_ = this;
|
||||
TrackOutput *TimelineOutput::attached_track()
|
||||
{
|
||||
return ValueToPtr<TrackOutput>(track_input_->get_value(0));
|
||||
}
|
||||
|
||||
void TimelineOutput::TrackConnectionAdded(NodeEdgePtr edge)
|
||||
{
|
||||
if (edge->input() != track_input()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're here, we need to find the current clip to display
|
||||
// attached_block() is guaranteed to not be nullptr if we didn't return before
|
||||
current_block_ = attached_block();
|
||||
TrackOutput* track = attached_track();
|
||||
|
||||
// If the time requested is an earlier Block, traverse earlier until we find it
|
||||
while (time < current_block_->in()) {
|
||||
current_block_ = current_block_->previous();
|
||||
// Traverse through Tracks caching and connecting them
|
||||
while (track != nullptr) {
|
||||
track_cache_.append(track);
|
||||
|
||||
connect(track, SIGNAL(BlockAdded(Block*)), this, SLOT(TrackAddedBlock(Block*)));
|
||||
connect(track, SIGNAL(BlockRemoved(Block*)), this, SLOT(TrackRemovedBlock(Block*)));
|
||||
|
||||
track->GenerateBlockWidgets();
|
||||
|
||||
track = track->next_track();
|
||||
}
|
||||
|
||||
// If the time requested is in a later Block, traverse later
|
||||
while (time >= current_block_->out()) {
|
||||
current_block_ = current_block_->next();
|
||||
// FIXME: TEST CODE ONLY
|
||||
if (attached_timeline_ != nullptr) {
|
||||
attached_timeline_->SetTimebase(rational(1001, 30000));
|
||||
}
|
||||
|
||||
// At this point, we must have found the correct block so we use its texture output to produce the image
|
||||
texture_output()->set_value(current_block_->texture_output()->get_value(time));
|
||||
// END TEST CODE
|
||||
}
|
||||
|
||||
void TimelineOutput::InsertBlockBetweenBlocks(Block *block, Block *before, Block *after)
|
||||
void TimelineOutput::TrackConnectionRemoved(NodeEdgePtr edge)
|
||||
{
|
||||
AddBlockToGraph(block);
|
||||
|
||||
Block::DisconnectBlocks(before, after);
|
||||
Block::ConnectBlocks(before, block);
|
||||
Block::ConnectBlocks(block, after);
|
||||
}
|
||||
|
||||
void TimelineOutput::InsertBlockAfter(Block *block, Block *before)
|
||||
{
|
||||
InsertBlockBetweenBlocks(block, before, before->next());
|
||||
}
|
||||
|
||||
Block *TimelineOutput::attached_block()
|
||||
{
|
||||
return ValueToPtr<Block>(previous_input()->get_value(0));
|
||||
}
|
||||
|
||||
void TimelineOutput::PrependBlock(Block *block)
|
||||
{
|
||||
AddBlockToGraph(block);
|
||||
|
||||
if (block_cache_.isEmpty()) {
|
||||
ConnectBlockInternal(block);
|
||||
} else {
|
||||
Block::ConnectBlocks(block, block_cache_.first());
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineOutput::InsertBlockAtIndex(Block *block, int index)
|
||||
{
|
||||
AddBlockToGraph(block);
|
||||
|
||||
if (block_cache_.isEmpty()) {
|
||||
|
||||
// If there are no blocks connected, the index doesn't matter. Just connect it.
|
||||
ConnectBlockInternal(block);
|
||||
|
||||
} else if (index == 0) {
|
||||
|
||||
// If the index is 0, it goes at the very beginning
|
||||
PrependBlock(block);
|
||||
|
||||
} else if (index >= block_cache_.size()) {
|
||||
|
||||
// Append Block at the end
|
||||
AppendBlock(block);
|
||||
|
||||
} else {
|
||||
|
||||
// Insert Block just before the Block currently at that index so that it becomes the new Block at that index
|
||||
InsertBlockBetweenBlocks(block, block_cache_.at(index - 1), block_cache_.at(index));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineOutput::AppendBlock(Block *block)
|
||||
{
|
||||
AddBlockToGraph(block);
|
||||
|
||||
if (block_cache_.isEmpty()) {
|
||||
ConnectBlockInternal(block);
|
||||
} else {
|
||||
InsertBlockBetweenBlocks(block, block_cache_.last(), this);
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineOutput::ConnectBlockInternal(Block *block)
|
||||
{
|
||||
AddBlockToGraph(block);
|
||||
|
||||
Block::ConnectBlocks(block, this);
|
||||
}
|
||||
|
||||
void TimelineOutput::AddBlockToGraph(Block *block)
|
||||
{
|
||||
// Find the parent graph
|
||||
NodeGraph* graph = static_cast<NodeGraph*>(parent());
|
||||
graph->AddNodeWithDependencies(block);
|
||||
}
|
||||
|
||||
void TimelineOutput::PlaceBlock(Block *block, rational start)
|
||||
{
|
||||
AddBlockToGraph(block);
|
||||
|
||||
if (block->in() == start) {
|
||||
if (edge->input() != track_input()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Place block at the beginning
|
||||
if (start == 0) {
|
||||
// FIXME: Remove existing
|
||||
|
||||
PrependBlock(block);
|
||||
return;
|
||||
foreach (TrackOutput* track, track_cache_) {
|
||||
disconnect(track, SIGNAL(BlockAdded(Block*)), this, SLOT(TrackAddedBlock(Block*)));
|
||||
disconnect(track, SIGNAL(BlockRemoved(Block*)), this, SLOT(TrackRemovedBlock(Block*)));
|
||||
}
|
||||
|
||||
// Check if the placement location is past the end of the timeline
|
||||
if (start >= in()) {
|
||||
if (start > in()) {
|
||||
// If so, insert a gap here
|
||||
GapBlock* gap = new GapBlock();
|
||||
gap->set_length(start - in());
|
||||
track_cache_.clear();
|
||||
|
||||
// Then append them
|
||||
AppendBlock(gap);
|
||||
}
|
||||
|
||||
AppendBlock(block);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the Block is placed at the in point of an existing Block, in which case a simple insert between will
|
||||
// suffice
|
||||
for (int i=1;i<block_cache_.size();i++) {
|
||||
Block* comparison = block_cache_.at(i);
|
||||
|
||||
if (comparison->in() == start) {
|
||||
Block* previous = block_cache_.at(i-1);
|
||||
|
||||
// InsertBlockAtIndex() could work here, but this function is faster since we've already found the Blocks
|
||||
InsertBlockBetweenBlocks(block, previous, comparison);
|
||||
return;
|
||||
}
|
||||
if (attached_timeline_ != nullptr) {
|
||||
attached_timeline_->Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineOutput::RemoveBlock(Block *block)
|
||||
void TimelineOutput::TrackAddedBlock(Block *block)
|
||||
{
|
||||
GapBlock* gap = new GapBlock();
|
||||
gap->set_length(block->length());
|
||||
|
||||
Block* previous = block->previous();
|
||||
Block* next = block->next();
|
||||
|
||||
// Remove block
|
||||
RippleRemoveBlock(block);
|
||||
|
||||
if (previous == nullptr) {
|
||||
// Block must be at the beginning
|
||||
PrependBlock(gap);
|
||||
} else {
|
||||
InsertBlockBetweenBlocks(gap, previous, next);
|
||||
if (attached_timeline_ != nullptr) {
|
||||
attached_timeline_->view()->AddBlock(block);
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineOutput::RippleRemoveBlock(Block *block)
|
||||
void TimelineOutput::TrackRemovedBlock(Block *block)
|
||||
{
|
||||
Block* previous = block->previous();
|
||||
Block* next = block->next();
|
||||
|
||||
if (previous != nullptr) {
|
||||
Block::DisconnectBlocks(previous, block);
|
||||
}
|
||||
|
||||
if (next != nullptr) {
|
||||
Block::DisconnectBlocks(block, next);
|
||||
}
|
||||
|
||||
if (previous != nullptr && next != nullptr) {
|
||||
Block::ConnectBlocks(previous, next);
|
||||
if (attached_timeline_ != nullptr) {
|
||||
attached_timeline_->view()->RemoveBlock(block);
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineOutput::SplitBlock(Block *block, rational time)
|
||||
{
|
||||
if (time < block->in() || time >= block->out()) {
|
||||
return;
|
||||
}
|
||||
|
||||
rational original_length = block->length();
|
||||
|
||||
block->set_length(time - block->in());
|
||||
|
||||
Block* copy = block->copy();
|
||||
copy->set_length(original_length - block->length());
|
||||
InsertBlockAfter(copy, block);
|
||||
}
|
||||
|
||||
void TimelineOutput::SpliceBlock(Block *inner, Block *outer, rational inner_in)
|
||||
{
|
||||
Q_ASSERT(inner_in >= outer->in() && inner_in < outer->out());
|
||||
|
||||
// Cache original length
|
||||
rational original_length = outer->length();
|
||||
|
||||
// Set outer clip to the clip that PRECEDES the inner clip
|
||||
outer->set_length(inner_in - outer->in());
|
||||
|
||||
// Insert inner clip between BEFORE clip and its next clip
|
||||
InsertBlockAfter(inner, outer);
|
||||
|
||||
// Create the AFTER clip
|
||||
Block* copy = outer->copy();
|
||||
copy->set_length(original_length - outer->length() - inner->length());
|
||||
InsertBlockAfter(copy, inner);
|
||||
}
|
||||
|
||||
@@ -22,21 +22,18 @@
|
||||
#define TIMELINEOUTPUT_H
|
||||
|
||||
#include "node/block/block.h"
|
||||
#include "node/output/track/track.h"
|
||||
#include "panel/timeline/timeline.h"
|
||||
|
||||
/**
|
||||
* @brief Node that represents the end of the Timeline as well as a time traversal Node
|
||||
*/
|
||||
class TimelineOutput : public Block
|
||||
class TimelineOutput : public Node
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TimelineOutput();
|
||||
|
||||
virtual Type type() override;
|
||||
|
||||
virtual Block* copy() override;
|
||||
|
||||
virtual QString Name() override;
|
||||
virtual QString id() override;
|
||||
virtual QString Category() override;
|
||||
@@ -44,101 +41,44 @@ public:
|
||||
|
||||
void AttachTimeline(TimelinePanel* timeline);
|
||||
|
||||
virtual void set_length(const rational &length) override;
|
||||
|
||||
virtual void Refresh() override;
|
||||
NodeInput* track_input();
|
||||
|
||||
public slots:
|
||||
virtual void Process(const rational &time) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Sets this Block as the only block in the Timeline (creating essentially a one clip sequence)
|
||||
*/
|
||||
void ConnectBlockInternal(Block* block);
|
||||
|
||||
/**
|
||||
* @brief Disconnects t
|
||||
*/
|
||||
void RemoveBlockInternal();
|
||||
|
||||
/**
|
||||
* @brief Adds a Block to the parent graph so it can be connected to other Nodes
|
||||
*
|
||||
* Also runs through Node's dependencies (the Nodes whose outputs are connected to this Node's inputs)
|
||||
*/
|
||||
void AddBlockToGraph(Block* block);
|
||||
|
||||
Block* attached_block();
|
||||
|
||||
QVector<Block*> block_cache_;
|
||||
|
||||
Block* current_block_;
|
||||
TrackOutput* attached_track();
|
||||
|
||||
TimelinePanel* attached_timeline_;
|
||||
|
||||
NodeInput* track_input_;
|
||||
|
||||
/**
|
||||
* @brief A cache of connected Tracks
|
||||
*/
|
||||
QVector<TrackOutput*> track_cache_;
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Adds Block `block` at the very beginning of the Sequence before all other clips
|
||||
* @brief Slot for when the track connection is added
|
||||
*/
|
||||
void PrependBlock(Block* block);
|
||||
void TrackConnectionAdded(NodeEdgePtr edge);
|
||||
|
||||
/**
|
||||
* @brief Inserts Block `block` at a specific index (0 is the start of the timeline)
|
||||
*
|
||||
* If the index == 0, this function does the same as PrependBlock(). If the index >= the current number of blocks,
|
||||
* this function is the same as AppendBlock().
|
||||
* @brief Slot for when the track connection is removed
|
||||
*/
|
||||
void InsertBlockAtIndex(Block* block, int index);
|
||||
void TrackConnectionRemoved(NodeEdgePtr edge);
|
||||
|
||||
/**
|
||||
* @brief Inserts a Block between two other Blocks
|
||||
*
|
||||
* Disconnects `before` and `after`, and connects them to `block` with `block` in between.
|
||||
* @brief Slot for when a connected Track has added a Block so we can update the UI
|
||||
*/
|
||||
void InsertBlockBetweenBlocks(Block* block, Block* before, Block* after);
|
||||
void TrackAddedBlock(Block* block);
|
||||
|
||||
/**
|
||||
* @brief Inserts Block after another Block
|
||||
*
|
||||
* Equivalent to calling InsertBlockBetweenBlocks(block, before, before->next())
|
||||
* @brief Slot for when a connected Track has added a Block so we can update the UI
|
||||
*/
|
||||
void InsertBlockAfter(Block* block, Block* before);
|
||||
void TrackRemovedBlock(Block* block);
|
||||
|
||||
/**
|
||||
* @brief Adds Block `block` at the very end of the Sequence after all other clips
|
||||
*/
|
||||
void AppendBlock(Block* block);
|
||||
|
||||
/**
|
||||
* @brief Destructively places `block` at the in point `start`
|
||||
*
|
||||
* The Block is guaranteed to be placed at the starting point specified. If there are Blocks in this area, they are
|
||||
* either trimmed or removed to make space for this Block. Additionally, if the Block is placed beyond the end of
|
||||
* the Sequence, a GapBlock is inserted to compensate.
|
||||
*/
|
||||
void PlaceBlock(Block* block, rational start);
|
||||
|
||||
/**
|
||||
* @brief Removes a Block and places a Gap in its place
|
||||
*/
|
||||
void RemoveBlock(Block* block);
|
||||
|
||||
/**
|
||||
* @brief Removes a Block pushing all subsequent Blocks earlier to take up the space
|
||||
*/
|
||||
void RippleRemoveBlock(Block* block);
|
||||
|
||||
/**
|
||||
* @brief Splits `block` into two Blocks at the Sequence point `time`
|
||||
*/
|
||||
void SplitBlock(Block* block, rational time);
|
||||
|
||||
/**
|
||||
* @brief Inserts Block `inner` between Block `outer`, splitting and shortening it to fit without changing the overall
|
||||
* length
|
||||
*/
|
||||
void SpliceBlock(Block* inner, Block* outer, rational inner_in);
|
||||
};
|
||||
|
||||
#endif // TIMELINEOUTPUT_H
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2019 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/output/track/track.h
|
||||
node/output/track/track.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,350 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "track.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "node/block/gap/gap.h"
|
||||
#include "node/graph.h"
|
||||
|
||||
TrackOutput::TrackOutput() :
|
||||
current_block_(this)
|
||||
{
|
||||
track_input_ = new NodeInput("track_in");
|
||||
track_input_->add_data_input(NodeParam::kTrack);
|
||||
AddParameter(track_input_);
|
||||
|
||||
track_output_ = new NodeOutput("track_out");
|
||||
track_output_->set_data_type(NodeParam::kTrack);
|
||||
AddParameter(track_output_);
|
||||
}
|
||||
|
||||
Block::Type TrackOutput::type()
|
||||
{
|
||||
return kEnd;
|
||||
}
|
||||
|
||||
Block *TrackOutput::copy()
|
||||
{
|
||||
return new TrackOutput();
|
||||
}
|
||||
|
||||
QString TrackOutput::Name()
|
||||
{
|
||||
return tr("Track");
|
||||
}
|
||||
|
||||
QString TrackOutput::id()
|
||||
{
|
||||
return "org.olivevideoeditor.Olive.track";
|
||||
}
|
||||
|
||||
QString TrackOutput::Category()
|
||||
{
|
||||
return tr("Output");
|
||||
}
|
||||
|
||||
QString TrackOutput::Description()
|
||||
{
|
||||
return tr("Node for representing and processing a single array of Blocks sorted by time. Also represents the end of "
|
||||
"a Sequence.");
|
||||
}
|
||||
|
||||
void TrackOutput::set_length(const rational &)
|
||||
{
|
||||
// Prevent length changing on this Block
|
||||
}
|
||||
|
||||
void TrackOutput::Refresh()
|
||||
{
|
||||
QVector<Block*> detect_attached_blocks;
|
||||
|
||||
Block* previous = attached_block();
|
||||
while (previous != nullptr) {
|
||||
detect_attached_blocks.prepend(previous);
|
||||
|
||||
if (!block_cache_.contains(previous)) {
|
||||
emit BlockAdded(previous);
|
||||
}
|
||||
|
||||
previous = previous->previous();
|
||||
}
|
||||
|
||||
foreach (Block* b, block_cache_) {
|
||||
if (!detect_attached_blocks.contains(b)) {
|
||||
emit BlockRemoved(b);
|
||||
}
|
||||
}
|
||||
|
||||
block_cache_ = detect_attached_blocks;
|
||||
|
||||
Block::Refresh();
|
||||
}
|
||||
|
||||
void TrackOutput::GenerateBlockWidgets()
|
||||
{
|
||||
foreach (Block* block, block_cache_) {
|
||||
emit BlockAdded(block);
|
||||
}
|
||||
}
|
||||
|
||||
TrackOutput *TrackOutput::next_track()
|
||||
{
|
||||
return ValueToPtr<TrackOutput>(track_input_->get_value(0));
|
||||
}
|
||||
|
||||
NodeOutput* TrackOutput::track_output()
|
||||
{
|
||||
return track_output_;
|
||||
}
|
||||
|
||||
void TrackOutput::Process(const rational &time)
|
||||
{
|
||||
// Run default node processing
|
||||
Block::Process(time);
|
||||
|
||||
// Set track output correctly
|
||||
track_output_->set_value(PtrToValue(this));
|
||||
|
||||
// This node representso the end of the timeline, so being beyond its in point is considered the end of the sequence
|
||||
if (time >= in()) {
|
||||
texture_output()->set_value(0);
|
||||
current_block_ = this;
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're here, we need to find the current clip to display
|
||||
// attached_block() is guaranteed to not be nullptr if we didn't return before
|
||||
current_block_ = attached_block();
|
||||
|
||||
// If the time requested is an earlier Block, traverse earlier until we find it
|
||||
while (time < current_block_->in()) {
|
||||
current_block_ = current_block_->previous();
|
||||
}
|
||||
|
||||
// If the time requested is in a later Block, traverse later
|
||||
while (time >= current_block_->out()) {
|
||||
current_block_ = current_block_->next();
|
||||
}
|
||||
|
||||
// At this point, we must have found the correct block so we use its texture output to produce the image
|
||||
texture_output()->set_value(current_block_->texture_output()->get_value(time));
|
||||
}
|
||||
|
||||
void TrackOutput::InsertBlockBetweenBlocks(Block *block, Block *before, Block *after)
|
||||
{
|
||||
AddBlockToGraph(block);
|
||||
|
||||
Block::DisconnectBlocks(before, after);
|
||||
Block::ConnectBlocks(before, block);
|
||||
Block::ConnectBlocks(block, after);
|
||||
}
|
||||
|
||||
void TrackOutput::InsertBlockAfter(Block *block, Block *before)
|
||||
{
|
||||
InsertBlockBetweenBlocks(block, before, before->next());
|
||||
}
|
||||
|
||||
Block *TrackOutput::attached_block()
|
||||
{
|
||||
return ValueToPtr<Block>(previous_input()->get_value(0));
|
||||
}
|
||||
|
||||
void TrackOutput::PrependBlock(Block *block)
|
||||
{
|
||||
AddBlockToGraph(block);
|
||||
|
||||
if (block_cache_.isEmpty()) {
|
||||
ConnectBlockInternal(block);
|
||||
} else {
|
||||
Block::ConnectBlocks(block, block_cache_.first());
|
||||
}
|
||||
}
|
||||
|
||||
void TrackOutput::InsertBlockAtIndex(Block *block, int index)
|
||||
{
|
||||
AddBlockToGraph(block);
|
||||
|
||||
if (block_cache_.isEmpty()) {
|
||||
|
||||
// If there are no blocks connected, the index doesn't matter. Just connect it.
|
||||
ConnectBlockInternal(block);
|
||||
|
||||
} else if (index == 0) {
|
||||
|
||||
// If the index is 0, it goes at the very beginning
|
||||
PrependBlock(block);
|
||||
|
||||
} else if (index >= block_cache_.size()) {
|
||||
|
||||
// Append Block at the end
|
||||
AppendBlock(block);
|
||||
|
||||
} else {
|
||||
|
||||
// Insert Block just before the Block currently at that index so that it becomes the new Block at that index
|
||||
InsertBlockBetweenBlocks(block, block_cache_.at(index - 1), block_cache_.at(index));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void TrackOutput::AppendBlock(Block *block)
|
||||
{
|
||||
AddBlockToGraph(block);
|
||||
|
||||
if (block_cache_.isEmpty()) {
|
||||
ConnectBlockInternal(block);
|
||||
} else {
|
||||
InsertBlockBetweenBlocks(block, block_cache_.last(), this);
|
||||
}
|
||||
}
|
||||
|
||||
void TrackOutput::ConnectBlockInternal(Block *block)
|
||||
{
|
||||
AddBlockToGraph(block);
|
||||
|
||||
Block::ConnectBlocks(block, this);
|
||||
}
|
||||
|
||||
void TrackOutput::AddBlockToGraph(Block *block)
|
||||
{
|
||||
// Find the parent graph
|
||||
NodeGraph* graph = static_cast<NodeGraph*>(parent());
|
||||
graph->AddNodeWithDependencies(block);
|
||||
}
|
||||
|
||||
void TrackOutput::PlaceBlock(Block *block, rational start)
|
||||
{
|
||||
AddBlockToGraph(block);
|
||||
|
||||
if (block->in() == start) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Place block at the beginning
|
||||
if (start == 0) {
|
||||
// FIXME: Remove existing
|
||||
|
||||
PrependBlock(block);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the placement location is past the end of the timeline
|
||||
if (start >= in()) {
|
||||
if (start > in()) {
|
||||
// If so, insert a gap here
|
||||
GapBlock* gap = new GapBlock();
|
||||
gap->set_length(start - in());
|
||||
|
||||
// Then append them
|
||||
AppendBlock(gap);
|
||||
}
|
||||
|
||||
AppendBlock(block);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the Block is placed at the in point of an existing Block, in which case a simple insert between will
|
||||
// suffice
|
||||
for (int i=1;i<block_cache_.size();i++) {
|
||||
Block* comparison = block_cache_.at(i);
|
||||
|
||||
if (comparison->in() == start) {
|
||||
Block* previous = block_cache_.at(i-1);
|
||||
|
||||
// InsertBlockAtIndex() could work here, but this function is faster since we've already found the Blocks
|
||||
InsertBlockBetweenBlocks(block, previous, comparison);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrackOutput::RemoveBlock(Block *block)
|
||||
{
|
||||
GapBlock* gap = new GapBlock();
|
||||
gap->set_length(block->length());
|
||||
|
||||
Block* previous = block->previous();
|
||||
Block* next = block->next();
|
||||
|
||||
// Remove block
|
||||
RippleRemoveBlock(block);
|
||||
|
||||
if (previous == nullptr) {
|
||||
// Block must be at the beginning
|
||||
PrependBlock(gap);
|
||||
} else {
|
||||
InsertBlockBetweenBlocks(gap, previous, next);
|
||||
}
|
||||
}
|
||||
|
||||
void TrackOutput::RippleRemoveBlock(Block *block)
|
||||
{
|
||||
Block* previous = block->previous();
|
||||
Block* next = block->next();
|
||||
|
||||
if (previous != nullptr) {
|
||||
Block::DisconnectBlocks(previous, block);
|
||||
}
|
||||
|
||||
if (next != nullptr) {
|
||||
Block::DisconnectBlocks(block, next);
|
||||
}
|
||||
|
||||
if (previous != nullptr && next != nullptr) {
|
||||
Block::ConnectBlocks(previous, next);
|
||||
}
|
||||
}
|
||||
|
||||
void TrackOutput::SplitBlock(Block *block, rational time)
|
||||
{
|
||||
if (time < block->in() || time >= block->out()) {
|
||||
return;
|
||||
}
|
||||
|
||||
rational original_length = block->length();
|
||||
|
||||
block->set_length(time - block->in());
|
||||
|
||||
Block* copy = block->copy();
|
||||
copy->set_length(original_length - block->length());
|
||||
InsertBlockAfter(copy, block);
|
||||
}
|
||||
|
||||
void TrackOutput::SpliceBlock(Block *inner, Block *outer, rational inner_in)
|
||||
{
|
||||
Q_ASSERT(inner_in >= outer->in() && inner_in < outer->out());
|
||||
|
||||
// Cache original length
|
||||
rational original_length = outer->length();
|
||||
|
||||
// Set outer clip to the clip that PRECEDES the inner clip
|
||||
outer->set_length(inner_in - outer->in());
|
||||
|
||||
// Insert inner clip between BEFORE clip and its next clip
|
||||
InsertBlockAfter(inner, outer);
|
||||
|
||||
// Create the AFTER clip
|
||||
Block* copy = outer->copy();
|
||||
copy->set_length(original_length - outer->length() - inner->length());
|
||||
InsertBlockAfter(copy, inner);
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef TRACKOUTPUT_H
|
||||
#define TRACKOUTPUT_H
|
||||
|
||||
#include "node/block/block.h"
|
||||
#include "panel/timeline/timeline.h"
|
||||
|
||||
/**
|
||||
* @brief A time traversal Node for sorting through one channel/track of Blocks
|
||||
*/
|
||||
class TrackOutput : public Block
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TrackOutput();
|
||||
|
||||
virtual Type type() override;
|
||||
|
||||
virtual Block* copy() override;
|
||||
|
||||
virtual QString Name() override;
|
||||
virtual QString id() override;
|
||||
virtual QString Category() override;
|
||||
virtual QString Description() override;
|
||||
|
||||
virtual void set_length(const rational &length) override;
|
||||
|
||||
virtual void Refresh() override;
|
||||
|
||||
void GenerateBlockWidgets();
|
||||
|
||||
TrackOutput* next_track();
|
||||
|
||||
NodeOutput *track_output();
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Signal emitted when a Block is added to this Track
|
||||
*/
|
||||
void BlockAdded(Block* block);
|
||||
|
||||
/**
|
||||
* @brief Signal emitted when a Block is removed from this Track
|
||||
*/
|
||||
void BlockRemoved(Block* block);
|
||||
|
||||
public slots:
|
||||
virtual void Process(const rational &time) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Sets this Block as the only block in the Timeline (creating essentially a one clip sequence)
|
||||
*/
|
||||
void ConnectBlockInternal(Block* block);
|
||||
|
||||
/**
|
||||
* @brief Disconnects t
|
||||
*/
|
||||
void RemoveBlockInternal();
|
||||
|
||||
/**
|
||||
* @brief Adds a Block to the parent graph so it can be connected to other Nodes
|
||||
*
|
||||
* Also runs through Node's dependencies (the Nodes whose outputs are connected to this Node's inputs)
|
||||
*/
|
||||
void AddBlockToGraph(Block* block);
|
||||
|
||||
Block* attached_block();
|
||||
|
||||
QVector<Block*> block_cache_;
|
||||
|
||||
Block* current_block_;
|
||||
|
||||
NodeInput* track_input_;
|
||||
|
||||
NodeOutput* track_output_;
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Adds Block `block` at the very beginning of the Sequence before all other clips
|
||||
*/
|
||||
void PrependBlock(Block* block);
|
||||
|
||||
/**
|
||||
* @brief Inserts Block `block` at a specific index (0 is the start of the timeline)
|
||||
*
|
||||
* If the index == 0, this function does the same as PrependBlock(). If the index >= the current number of blocks,
|
||||
* this function is the same as AppendBlock().
|
||||
*/
|
||||
void InsertBlockAtIndex(Block* block, int index);
|
||||
|
||||
/**
|
||||
* @brief Inserts a Block between two other Blocks
|
||||
*
|
||||
* Disconnects `before` and `after`, and connects them to `block` with `block` in between.
|
||||
*/
|
||||
void InsertBlockBetweenBlocks(Block* block, Block* before, Block* after);
|
||||
|
||||
/**
|
||||
* @brief Inserts Block after another Block
|
||||
*
|
||||
* Equivalent to calling InsertBlockBetweenBlocks(block, before, before->next())
|
||||
*/
|
||||
void InsertBlockAfter(Block* block, Block* before);
|
||||
|
||||
/**
|
||||
* @brief Adds Block `block` at the very end of the Sequence after all other clips
|
||||
*/
|
||||
void AppendBlock(Block* block);
|
||||
|
||||
/**
|
||||
* @brief Destructively places `block` at the in point `start`
|
||||
*
|
||||
* The Block is guaranteed to be placed at the starting point specified. If there are Blocks in this area, they are
|
||||
* either trimmed or removed to make space for this Block. Additionally, if the Block is placed beyond the end of
|
||||
* the Sequence, a GapBlock is inserted to compensate.
|
||||
*/
|
||||
void PlaceBlock(Block* block, rational start);
|
||||
|
||||
/**
|
||||
* @brief Removes a Block and places a Gap in its place
|
||||
*/
|
||||
void RemoveBlock(Block* block);
|
||||
|
||||
/**
|
||||
* @brief Removes a Block pushing all subsequent Blocks earlier to take up the space
|
||||
*/
|
||||
void RippleRemoveBlock(Block* block);
|
||||
|
||||
/**
|
||||
* @brief Splits `block` into two Blocks at the Sequence point `time`
|
||||
*/
|
||||
void SplitBlock(Block* block, rational time);
|
||||
|
||||
/**
|
||||
* @brief Inserts Block `inner` between Block `outer`, splitting and shortening it to fit without changing the overall
|
||||
* length
|
||||
*/
|
||||
void SpliceBlock(Block* inner, Block* outer, rational inner_in);
|
||||
};
|
||||
|
||||
#endif // TRACKOUTPUT_H
|
||||
@@ -134,6 +134,18 @@ NodeEdgePtr NodeParam::ConnectEdge(NodeOutput *output, NodeInput *input)
|
||||
}
|
||||
}
|
||||
|
||||
// Refuse to make a connection that is incompatible
|
||||
if (!input->can_accept_type(output->data_type())) {
|
||||
qWarning() << tr("Tried to make an invalid Node connection");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ensure both nodes are in the same graph
|
||||
if (output->parent()->parent() != input->parent()->parent()) {
|
||||
qWarning() << tr("Tried to connect two nodes that aren't part of the same graph");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NodeEdgePtr edge = std::make_shared<NodeEdge>(output, input);
|
||||
|
||||
output->edges_.append(edge);
|
||||
@@ -196,6 +208,7 @@ QString NodeParam::GetDefaultDataTypeName(const DataType& type)
|
||||
case kMatrix: return tr("Matrix");
|
||||
case kBlock: return tr("Block");
|
||||
case kFootage: return tr("Footage");
|
||||
case kTrack: return tr("Track");
|
||||
case kAny: return tr("Any");
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ public:
|
||||
kMatrix,
|
||||
kBlock,
|
||||
kFootage,
|
||||
kTrack,
|
||||
kAny
|
||||
};
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
RendererProcessor::RendererProcessor() :
|
||||
started_(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QString RendererProcessor::Name()
|
||||
@@ -45,6 +44,8 @@ QString RendererProcessor::Description()
|
||||
|
||||
void RendererProcessor::Process(const rational &time)
|
||||
{
|
||||
Q_UNUSED(time)
|
||||
/*
|
||||
// Ensure we have started
|
||||
Start();
|
||||
|
||||
@@ -52,6 +53,8 @@ void RendererProcessor::Process(const rational &time)
|
||||
qWarning() << tr("An error occurred starting the Renderer node");
|
||||
return;
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
void RendererProcessor::Release()
|
||||
|
||||
@@ -67,6 +67,15 @@ public:
|
||||
*/
|
||||
void SetParameters(const int& width, const int& height, const olive::PixelFormat& format);
|
||||
|
||||
/**
|
||||
* @brief Return current instance of a RenderThread (or nullptr if there is none)
|
||||
*
|
||||
* This function attempts a dynamic_cast on QThread::currentThread() to RendererThread, which will return nullptr if
|
||||
* the cast fails (e.g. if this function is called from the main thread rather than a RendererThread).
|
||||
*/
|
||||
static RendererThread *CurrentThread();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Allocate and start the multithreaded backend
|
||||
*/
|
||||
@@ -77,15 +86,6 @@ public:
|
||||
*/
|
||||
void Stop();
|
||||
|
||||
/**
|
||||
* @brief Return current instance of a RenderThread (or nullptr if there is none)
|
||||
*
|
||||
* This function attempts a dynamic_cast on QThread::currentThread() to RendererThread, which will return nullptr if
|
||||
* the cast fails (e.g. if this function is called from the main thread rather than a RendererThread).
|
||||
*/
|
||||
static RendererThread *CurrentThread();
|
||||
|
||||
private:
|
||||
QVector<RendererThreadPtr> threads_;
|
||||
|
||||
bool started_;
|
||||
|
||||
@@ -55,16 +55,6 @@ void TimelinePanel::Clear()
|
||||
view_->Clear();
|
||||
}
|
||||
|
||||
void TimelinePanel::AddClip(ClipBlock *clip)
|
||||
{
|
||||
view_->AddBlock(clip);
|
||||
}
|
||||
|
||||
void TimelinePanel::RemoveClip(ClipBlock *clip)
|
||||
{
|
||||
view_->RemoveBlock(clip);
|
||||
}
|
||||
|
||||
void TimelinePanel::SetTimebase(const rational &timebase)
|
||||
{
|
||||
ruler_->SetTimebase(timebase);
|
||||
|
||||
@@ -33,10 +33,6 @@ public:
|
||||
|
||||
void Clear();
|
||||
|
||||
void AddClip(ClipBlock* clip);
|
||||
|
||||
void RemoveClip(ClipBlock* clip);
|
||||
|
||||
void SetTimebase(const rational& timebase);
|
||||
|
||||
TimelineView* view();
|
||||
|
||||
@@ -161,7 +161,7 @@ FramePtr PixelService::ConvertPixelFormat(FramePtr frame, const olive::PixelForm
|
||||
}
|
||||
break;
|
||||
}
|
||||
case olive::PIX_FMT_RGBA16F: // 16-bit Integer -> 16-bit Integer
|
||||
case olive::PIX_FMT_RGBA16F: // 16-bit Integer -> 16-bit Float
|
||||
{
|
||||
qfloat16* destination = reinterpret_cast<qfloat16*>(converted->data());
|
||||
for (int i=0;i<pix_count;i++) {
|
||||
@@ -169,7 +169,7 @@ FramePtr PixelService::ConvertPixelFormat(FramePtr frame, const olive::PixelForm
|
||||
}
|
||||
break;
|
||||
}
|
||||
case olive::PIX_FMT_RGBA32F: // 16-bit Integer -> 16-bit Integer
|
||||
case olive::PIX_FMT_RGBA32F: // 16-bit Integer -> 32-bit Float
|
||||
{
|
||||
float* destination = reinterpret_cast<float*>(converted->data());
|
||||
for (int i=0;i<pix_count;i++) {
|
||||
|
||||
@@ -49,6 +49,7 @@ void NodeParamViewWidgetBridge::CreateWidgets()
|
||||
case NodeParam::kBlock:
|
||||
case NodeParam::kTexture:
|
||||
case NodeParam::kMatrix:
|
||||
case NodeParam::kTrack:
|
||||
break;
|
||||
case NodeParam::kInt:
|
||||
// FIXME: LabelSlider in INTEGER mode
|
||||
@@ -118,6 +119,7 @@ void NodeParamViewWidgetBridge::WidgetCallback()
|
||||
case NodeParam::kBlock:
|
||||
case NodeParam::kTexture:
|
||||
case NodeParam::kMatrix:
|
||||
case NodeParam::kTrack:
|
||||
break;
|
||||
case NodeParam::kInt:
|
||||
// FIXME: LabelSlider in INTEGER mode
|
||||
|
||||
Reference in New Issue
Block a user