Merge branch 'master' of https://github.com/olive-editor/olive
This commit is contained in:
@@ -229,7 +229,7 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
|
||||
// Update renderer
|
||||
// FIXME: This is going to be VERY slow since it will need to hash every single frame. It would be better to have a
|
||||
// the renderer save the map as some sort of file that this can load.
|
||||
preview_viewer_->video_renderer()->InvalidateCache(TimeRange(0, viewer_node_->Length()));
|
||||
preview_viewer_->video_renderer()->InvalidateCache(TimeRange(0, viewer_node_->Length()), nullptr);
|
||||
|
||||
progress_timer_.setInterval(1000);
|
||||
connect(&progress_timer_, &QTimer::timeout, this, &ExportDialog::UpdateTimeLabels);
|
||||
|
||||
@@ -50,9 +50,6 @@ int main(int argc, char *argv[]) {
|
||||
format.setProfile(QSurfaceFormat::CoreProfile);
|
||||
QSurfaceFormat::setDefaultFormat(format);
|
||||
|
||||
// Try to share OpenGL contexts
|
||||
QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
|
||||
|
||||
// Create application instance
|
||||
QApplication a(argc, argv);
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
add_subdirectory(audio)
|
||||
add_subdirectory(block)
|
||||
add_subdirectory(distort)
|
||||
add_subdirectory(generator)
|
||||
add_subdirectory(input)
|
||||
add_subdirectory(math)
|
||||
add_subdirectory(output)
|
||||
|
||||
@@ -40,7 +40,7 @@ Block::Block() :
|
||||
length_input_->SetConnectable(false);
|
||||
length_input_->set_is_keyframable(false);
|
||||
AddInput(length_input_);
|
||||
connect(length_input_, SIGNAL(ValueChanged(const rational&, const rational&)), this, SLOT(LengthInputChanged()));
|
||||
connect(length_input_, &NodeInput::ValueChanged, this, &Block::LengthInputChanged);
|
||||
|
||||
media_in_input_ = new NodeInput("media_in_in", NodeParam::kRational);
|
||||
media_in_input_->SetConnectable(false);
|
||||
@@ -343,14 +343,14 @@ NodeInput *Block::speed_input() const
|
||||
return speed_input_;
|
||||
}
|
||||
|
||||
void Block::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from)
|
||||
void Block::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source)
|
||||
{
|
||||
// We ignore length changes since they don't have an effect on our frames
|
||||
if (from == length_input_) {
|
||||
return;
|
||||
}
|
||||
|
||||
Node::InvalidateCache(start_range, end_range, from);
|
||||
Node::InvalidateCache(range, from, source);
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -97,7 +97,7 @@ public:
|
||||
NodeInput* media_in_input() const;
|
||||
NodeInput* speed_input() const;
|
||||
|
||||
virtual void InvalidateCache(const rational& start_range, const rational& end_range, NodeInput* from = nullptr) override;
|
||||
virtual void InvalidateCache(const TimeRange& range, NodeInput* from, NodeInput* source) override;
|
||||
|
||||
public slots:
|
||||
|
||||
|
||||
@@ -59,12 +59,12 @@ NodeInput *ClipBlock::texture_input() const
|
||||
return texture_input_;
|
||||
}
|
||||
|
||||
void ClipBlock::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from)
|
||||
void ClipBlock::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source)
|
||||
{
|
||||
// If signal is from texture input, transform all times from media time to sequence time
|
||||
if (from == texture_input_) {
|
||||
rational start = MediaToSequenceTime(start_range);
|
||||
rational end = MediaToSequenceTime(end_range);
|
||||
rational start = MediaToSequenceTime(range.in());
|
||||
rational end = MediaToSequenceTime(range.out());
|
||||
|
||||
// Ensure range actually covers this clip's area
|
||||
if (!(end < in() || start > out())) {
|
||||
@@ -73,12 +73,12 @@ void ClipBlock::InvalidateCache(const rational &start_range, const rational &end
|
||||
start = qMax(start, in());
|
||||
end = qMin(end, out());
|
||||
|
||||
Node::InvalidateCache(start, end, from);
|
||||
Node::InvalidateCache(TimeRange(start, end), from, source);
|
||||
|
||||
}
|
||||
} else {
|
||||
// Otherwise, pass signal along normally
|
||||
Node::InvalidateCache(start_range, end_range, from);
|
||||
Node::InvalidateCache(range, from, source);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
|
||||
NodeInput* texture_input() const;
|
||||
|
||||
virtual void InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from = nullptr) override;
|
||||
virtual void InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput* source) override;
|
||||
|
||||
virtual TimeRange InputTimeAdjustment(NodeInput* input, const TimeRange& input_time) const override;
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "block/clip/clip.h"
|
||||
#include "block/gap/gap.h"
|
||||
#include "block/transition/externaltransition.h"
|
||||
#include "distort/transform/transform.h"
|
||||
#include "generator/matrix/matrix.h"
|
||||
#include "input/media/video/video.h"
|
||||
#include "input/media/audio/audio.h"
|
||||
#include "input/time/timeinput.h"
|
||||
@@ -141,8 +141,8 @@ Node *NodeFactory::CreateInternal(const NodeFactory::InternalID &id)
|
||||
return new ClipBlock();
|
||||
case kGapBlock:
|
||||
return new GapBlock();
|
||||
case kTransformDistort:
|
||||
return new TransformDistort();
|
||||
case kMatrixGenerator:
|
||||
return new MatrixGenerator();
|
||||
case kVideoInput:
|
||||
return new VideoInput();
|
||||
case kAudioInput:
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ public:
|
||||
kClipBlock,
|
||||
kGapBlock,
|
||||
kAudioInput,
|
||||
kTransformDistort,
|
||||
kMatrixGenerator,
|
||||
kVideoInput,
|
||||
kTrackOutput,
|
||||
kAudioVolume,
|
||||
|
||||
@@ -14,7 +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(transform)
|
||||
add_subdirectory(matrix)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
+2
-2
@@ -16,7 +16,7 @@
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/distort/transform/transform.h
|
||||
node/distort/transform/transform.cpp
|
||||
node/generator/matrix/matrix.h
|
||||
node/generator/matrix/matrix.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -18,14 +18,14 @@
|
||||
|
||||
***/
|
||||
|
||||
#include "transform.h"
|
||||
#include "matrix.h"
|
||||
|
||||
#include <QMatrix4x4>
|
||||
#include <QVector2D>
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
TransformDistort::TransformDistort()
|
||||
MatrixGenerator::MatrixGenerator()
|
||||
{
|
||||
position_input_ = new NodeInput("pos_in", NodeParam::kVec2);
|
||||
AddInput(position_input_);
|
||||
@@ -42,39 +42,39 @@ TransformDistort::TransformDistort()
|
||||
uniform_scale_input_ = new NodeInput("uniform_scale_in", NodeParam::kBoolean, true);
|
||||
uniform_scale_input_->set_is_keyframable(false);
|
||||
uniform_scale_input_->SetConnectable(false);
|
||||
connect(uniform_scale_input_, &NodeInput::ValueChanged, this, &TransformDistort::UniformScaleChanged);
|
||||
connect(uniform_scale_input_, &NodeInput::ValueChanged, this, &MatrixGenerator::UniformScaleChanged);
|
||||
AddInput(uniform_scale_input_);
|
||||
|
||||
anchor_input_ = new NodeInput("anchor_in", NodeParam::kVec2);
|
||||
AddInput(anchor_input_);
|
||||
}
|
||||
|
||||
Node *TransformDistort::copy() const
|
||||
Node *MatrixGenerator::copy() const
|
||||
{
|
||||
return new TransformDistort();
|
||||
return new MatrixGenerator();
|
||||
}
|
||||
|
||||
QString TransformDistort::Name() const
|
||||
QString MatrixGenerator::Name() const
|
||||
{
|
||||
return tr("Transform");
|
||||
return tr("Orthographic Matrix");
|
||||
}
|
||||
|
||||
QString TransformDistort::id() const
|
||||
QString MatrixGenerator::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.transform");
|
||||
}
|
||||
|
||||
QString TransformDistort::Category() const
|
||||
QString MatrixGenerator::Category() const
|
||||
{
|
||||
return tr("Distort");
|
||||
return tr("Generator");
|
||||
}
|
||||
|
||||
QString TransformDistort::Description() const
|
||||
QString MatrixGenerator::Description() const
|
||||
{
|
||||
return tr("Apply transformations to position, rotation, and scale.");
|
||||
return tr("Generate an orthographic matrix using position, rotation, and scale.");
|
||||
}
|
||||
|
||||
void TransformDistort::Retranslate()
|
||||
void MatrixGenerator::Retranslate()
|
||||
{
|
||||
position_input_->set_name(tr("Position"));
|
||||
rotation_input_->set_name(tr("Rotation"));
|
||||
@@ -83,7 +83,7 @@ void TransformDistort::Retranslate()
|
||||
anchor_input_->set_name(tr("Anchor Point"));
|
||||
}
|
||||
|
||||
NodeValueTable TransformDistort::Value(NodeValueDatabase &value) const
|
||||
NodeValueTable MatrixGenerator::Value(NodeValueDatabase &value) const
|
||||
{
|
||||
QMatrix4x4 mat;
|
||||
|
||||
@@ -110,7 +110,7 @@ NodeValueTable TransformDistort::Value(NodeValueDatabase &value) const
|
||||
return output;
|
||||
}
|
||||
|
||||
void TransformDistort::UniformScaleChanged()
|
||||
void MatrixGenerator::UniformScaleChanged()
|
||||
{
|
||||
scale_input_->set_property("disabley", uniform_scale_input_->get_standard_value().toBool());
|
||||
}
|
||||
@@ -18,18 +18,18 @@
|
||||
|
||||
***/
|
||||
|
||||
#ifndef TRANSFORMDISTORT_H
|
||||
#define TRANSFORMDISTORT_H
|
||||
#ifndef MATRIXGENERATOR_H
|
||||
#define MATRIXGENERATOR_H
|
||||
|
||||
#include "node/node.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
class TransformDistort : public Node
|
||||
class MatrixGenerator : public Node
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TransformDistort();
|
||||
MatrixGenerator();
|
||||
|
||||
virtual Node* copy() const override;
|
||||
|
||||
+45
-7
@@ -60,7 +60,7 @@ NodeInput::NodeInput(const QString &id, const NodeParam::DataType &type) :
|
||||
Init(type);
|
||||
}
|
||||
|
||||
bool NodeInput::IsArray()
|
||||
bool NodeInput::IsArray() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -273,7 +273,6 @@ void NodeInput::SaveConnections(QXmlStreamWriter *writer) const
|
||||
writer->writeEndElement(); // connections
|
||||
}
|
||||
|
||||
|
||||
const NodeParam::DataType &NodeInput::data_type() const
|
||||
{
|
||||
return data_type_;
|
||||
@@ -363,6 +362,45 @@ QVariant NodeInput::StringToValue(const DataType& data_type, const QString &stri
|
||||
}
|
||||
}
|
||||
|
||||
void NodeInput::GetDependencies(QList<Node *> &list, bool traverse, bool exclusive_only) const
|
||||
{
|
||||
if (IsConnected()
|
||||
&& (get_connected_output()->edges().size() == 1 || !exclusive_only)) {
|
||||
Node* connected = get_connected_node();
|
||||
|
||||
if (!list.contains(connected)) {
|
||||
list.append(connected);
|
||||
|
||||
if (traverse) {
|
||||
QList<NodeInput*> connected_inputs = connected->GetInputsIncludingArrays();
|
||||
|
||||
foreach (NodeInput* i, connected_inputs) {
|
||||
i->GetDependencies(list, traverse, exclusive_only);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QList<Node *> NodeInput::GetDependencies(bool traverse, bool exclusive_only) const
|
||||
{
|
||||
QList<Node *> list;
|
||||
|
||||
GetDependencies(list, traverse, exclusive_only);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
QList<Node *> NodeInput::GetExclusiveDependencies() const
|
||||
{
|
||||
return GetDependencies(true, true);
|
||||
}
|
||||
|
||||
QList<Node *> NodeInput::GetImmediateDependencies() const
|
||||
{
|
||||
return GetDependencies(false, false);
|
||||
}
|
||||
|
||||
QVariant NodeInput::StringToValue(const QString &string, QList<XMLNodeData::FootageConnection>& footage_connections)
|
||||
{
|
||||
if (data_type_ == NodeParam::kFootage) {
|
||||
@@ -757,7 +795,7 @@ void NodeInput::KeyframeBezierInChanged()
|
||||
start = keyframe_tracks_.at(key->track()).at(keyframe_index - 1)->time();
|
||||
}
|
||||
|
||||
emit ValueChanged(start, end);
|
||||
emit ValueChanged(TimeRange(start, end));
|
||||
}
|
||||
|
||||
void NodeInput::KeyframeBezierOutChanged()
|
||||
@@ -772,7 +810,7 @@ void NodeInput::KeyframeBezierOutChanged()
|
||||
end = keyframe_tracks_.at(key->track()).at(keyframe_index + 1)->time();
|
||||
}
|
||||
|
||||
emit ValueChanged(start, end);
|
||||
emit ValueChanged(TimeRange(start, end));
|
||||
}
|
||||
|
||||
int NodeInput::FindIndexOfKeyframeFromRawPtr(NodeKeyframe *raw_ptr) const
|
||||
@@ -855,7 +893,7 @@ TimeRange NodeInput::get_range_around_index(int index, int track) const
|
||||
|
||||
void NodeInput::emit_time_range(const TimeRange &range)
|
||||
{
|
||||
emit ValueChanged(range.in(), range.out());
|
||||
emit ValueChanged(range);
|
||||
}
|
||||
|
||||
void NodeInput::emit_range_affected_by_keyframe(NodeKeyframe *key)
|
||||
@@ -915,7 +953,7 @@ void NodeInput::set_standard_value(const QVariant &value, int track)
|
||||
|
||||
if (is_using_standard_value(track)) {
|
||||
// If this standard value is being used, we need to send a value changed signal
|
||||
emit ValueChanged(RATIONAL_MIN, RATIONAL_MAX);
|
||||
emit ValueChanged(TimeRange(RATIONAL_MIN, RATIONAL_MAX));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -964,7 +1002,7 @@ void NodeInput::CopyValues(NodeInput *source, NodeInput *dest, bool include_conn
|
||||
}
|
||||
}
|
||||
|
||||
emit dest->ValueChanged(RATIONAL_MIN, RATIONAL_MAX);
|
||||
emit dest->ValueChanged(TimeRange(RATIONAL_MIN, RATIONAL_MAX));
|
||||
}
|
||||
|
||||
void NodeInput::set_property(const QString &key, const QVariant &value)
|
||||
|
||||
+10
-2
@@ -49,7 +49,7 @@ public:
|
||||
NodeInput(const QString &id, const DataType& type, const QVariant& default_value);
|
||||
NodeInput(const QString &id, const DataType& type);
|
||||
|
||||
virtual bool IsArray();
|
||||
virtual bool IsArray() const;
|
||||
|
||||
/**
|
||||
* @brief Returns kInput
|
||||
@@ -270,8 +270,16 @@ public:
|
||||
|
||||
static QVariant StringToValue(const DataType &data_type, const QString &string);
|
||||
|
||||
void GetDependencies(QList<Node*>& list, bool traverse, bool exclusive_only) const;
|
||||
|
||||
QList<Node*> GetDependencies(bool traverse = true, bool exclusive_only = false) const;
|
||||
|
||||
QList<Node*> GetExclusiveDependencies() const;
|
||||
|
||||
QList<Node*> GetImmediateDependencies() const;
|
||||
|
||||
signals:
|
||||
void ValueChanged(const rational& start, const rational& end);
|
||||
void ValueChanged(const TimeRange& range);
|
||||
|
||||
void KeyframeEnableChanged(bool);
|
||||
|
||||
|
||||
@@ -22,10 +22,6 @@
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
AudioInput::AudioInput()
|
||||
{
|
||||
}
|
||||
|
||||
Node *AudioInput::copy() const
|
||||
{
|
||||
return new AudioInput();
|
||||
|
||||
@@ -28,7 +28,7 @@ OLIVE_NAMESPACE_ENTER
|
||||
class AudioInput : public MediaInput
|
||||
{
|
||||
public:
|
||||
AudioInput();
|
||||
AudioInput() = default;
|
||||
|
||||
virtual Node* copy() const override;
|
||||
|
||||
@@ -36,7 +36,6 @@ public:
|
||||
virtual QString id() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -31,7 +31,7 @@ MediaInput::MediaInput() :
|
||||
footage_input_ = new NodeInput("footage_in", NodeInput::kFootage);
|
||||
footage_input_->SetConnectable(false);
|
||||
footage_input_->set_is_keyframable(false);
|
||||
connect(footage_input_, SIGNAL(ValueChanged(const rational&, const rational&)), this, SLOT(FootageChanged()));
|
||||
connect(footage_input_, &NodeInput::ValueChanged, this, &MediaInput::FootageChanged);
|
||||
AddInput(footage_input_);
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ NodeValueTable MediaInput::Value(NodeValueDatabase &value) const
|
||||
}
|
||||
|
||||
// Push buffer to the top of the stack
|
||||
NodeValue buffer = value[footage_input_].GetWithMeta(NodeParam::kSamples);
|
||||
NodeValue buffer = value[footage_input_].GetWithMeta(NodeParam::kBuffer);
|
||||
if (buffer.type() != NodeParam::kNone) {
|
||||
table.Push(buffer);
|
||||
}
|
||||
@@ -96,7 +96,7 @@ void MediaInput::FootageChanged()
|
||||
|
||||
void MediaInput::FootageParametersChanged()
|
||||
{
|
||||
InvalidateCache(0, RATIONAL_MAX, footage_input_);
|
||||
InvalidateCache(TimeRange(0, RATIONAL_MAX), footage_input_, footage_input_);
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -31,12 +31,6 @@
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
VideoInput::VideoInput()
|
||||
{
|
||||
matrix_input_ = new NodeInput("matrix_in", NodeInput::kMatrix);
|
||||
AddInput(matrix_input_);
|
||||
}
|
||||
|
||||
Node *VideoInput::copy() const
|
||||
{
|
||||
return new VideoInput();
|
||||
@@ -57,31 +51,4 @@ QString VideoInput::Description() const
|
||||
return tr("Import a video footage stream.");
|
||||
}
|
||||
|
||||
NodeInput *VideoInput::matrix_input() const
|
||||
{
|
||||
return matrix_input_;
|
||||
}
|
||||
|
||||
Node::Capabilities VideoInput::GetCapabilities(const NodeValueDatabase &) const
|
||||
{
|
||||
return kShader;
|
||||
}
|
||||
|
||||
QString VideoInput::ShaderVertexCode(const NodeValueDatabase&) const
|
||||
{
|
||||
return ReadFileAsString(":/shaders/videoinput.vert");
|
||||
}
|
||||
|
||||
QString VideoInput::ShaderFragmentCode(const NodeValueDatabase&) const
|
||||
{
|
||||
return ReadFileAsString(":/shaders/videoinput.frag");
|
||||
}
|
||||
|
||||
void VideoInput::Retranslate()
|
||||
{
|
||||
MediaInput::Retranslate();
|
||||
|
||||
matrix_input_->set_name(tr("Transform"));
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -31,7 +31,7 @@ OLIVE_NAMESPACE_ENTER
|
||||
class VideoInput : public MediaInput
|
||||
{
|
||||
public:
|
||||
VideoInput();
|
||||
VideoInput() = default;
|
||||
|
||||
virtual Node* copy() const override;
|
||||
|
||||
@@ -39,19 +39,6 @@ public:
|
||||
virtual QString id() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
NodeInput* matrix_input() const;
|
||||
|
||||
virtual Capabilities GetCapabilities(const NodeValueDatabase&) const override;
|
||||
virtual QString ShaderVertexCode(const NodeValueDatabase&) const override;
|
||||
virtual QString ShaderFragmentCode(const NodeValueDatabase&) const override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
protected:
|
||||
|
||||
private:
|
||||
NodeInput* matrix_input_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -33,7 +33,7 @@ NodeInputArray::NodeInputArray(const QString &id, const DataType &type, const QV
|
||||
{
|
||||
}
|
||||
|
||||
bool NodeInputArray::IsArray()
|
||||
bool NodeInputArray::IsArray() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -79,8 +79,8 @@ void NodeInputArray::SetSize(int size)
|
||||
sub_params_.replace(i, new_param);
|
||||
|
||||
connect(new_param, &NodeInput::ValueChanged, this, &NodeInput::ValueChanged);
|
||||
connect(new_param, &NodeInput::EdgeAdded, this, &NodeInput::EdgeAdded);
|
||||
connect(new_param, &NodeInput::EdgeRemoved, this, &NodeInput::EdgeRemoved);
|
||||
connect(new_param, &NodeInput::EdgeAdded, this, &NodeInputArray::SubParamEdgeAdded);
|
||||
connect(new_param, &NodeInput::EdgeRemoved, this, &NodeInputArray::SubParamEdgeRemoved);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ class NodeInputArray : public NodeInput
|
||||
public:
|
||||
NodeInputArray(const QString &id, const DataType& type, const QVariant& default_value = 0);
|
||||
|
||||
virtual bool IsArray() override;
|
||||
virtual bool IsArray() const override;
|
||||
|
||||
int GetSize() const;
|
||||
|
||||
@@ -54,6 +54,10 @@ public:
|
||||
signals:
|
||||
void SizeChanged(int size);
|
||||
|
||||
void SubParamEdgeAdded(NodeEdgePtr edge);
|
||||
|
||||
void SubParamEdgeRemoved(NodeEdgePtr edge);
|
||||
|
||||
protected:
|
||||
virtual void LoadInternal(QXmlStreamReader* reader, XMLNodeData& xml_node_data, const QAtomicInt *cancelled) override;
|
||||
|
||||
|
||||
+103
-91
@@ -131,9 +131,9 @@ QString MathNode::ShaderFragmentCode(const NodeValueDatabase &input) const
|
||||
|
||||
// Override the operation for this operation since we multiply texture COORDS by the matrix rather than
|
||||
NodeParam* tex_in = (type_a == NodeParam::kTexture) ? param_a_in_ : param_b_in_;
|
||||
NodeParam* mat_in = (type_a == NodeParam::kTexture) ? param_b_in_ : param_a_in_;
|
||||
|
||||
operation = QStringLiteral("texture(%1, (vec4(ove_texcoord, 0.0, 1.0) * %2).xy)").arg(tex_in->id(), mat_in->id());
|
||||
// No-op frag shader (can we return QString() instead?)
|
||||
operation = QStringLiteral("texture(%1, ove_texcoord)").arg(tex_in->id());
|
||||
|
||||
} else {
|
||||
switch (GetOperation()) {
|
||||
@@ -176,6 +176,25 @@ QString MathNode::ShaderFragmentCode(const NodeValueDatabase &input) const
|
||||
operation);
|
||||
}
|
||||
|
||||
QString MathNode::ShaderVertexCode(const NodeValueDatabase &input) const
|
||||
{
|
||||
PairingCalculator calc(input[param_a_in_], input[param_b_in_]);
|
||||
|
||||
if (calc.GetMostLikelyPairing() == kPairTextureMatrix && GetOperation() == kOpMultiply) {
|
||||
|
||||
NodeParam::DataType type_a = calc.GetMostLikelyValueA().type();
|
||||
|
||||
// Override the operation for this operation since we multiply texture COORDS by the matrix rather than
|
||||
NodeParam* tex_in = (type_a == NodeParam::kTexture) ? param_a_in_ : param_b_in_;
|
||||
NodeParam* mat_in = (type_a == NodeParam::kTexture) ? param_b_in_ : param_a_in_;
|
||||
|
||||
return ReadFileAsString(":/shaders/matrix.vert").arg(mat_in->id(), tex_in->id());
|
||||
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
NodeValue MathNode::InputValueFromTable(NodeInput *input, NodeValueDatabase &db, bool take) const
|
||||
{
|
||||
if (input == param_a_in_ || input == param_b_in_) {
|
||||
@@ -382,6 +401,11 @@ MathNode::Operation MathNode::GetOperation() const
|
||||
return static_cast<Operation>(method_in_->get_standard_value().toInt());
|
||||
}
|
||||
|
||||
void MathNode::SetOperation(MathNode::Operation o)
|
||||
{
|
||||
method_in_->set_standard_value(o);
|
||||
}
|
||||
|
||||
QString MathNode::GetShaderUniformType(const NodeParam::DataType &type)
|
||||
{
|
||||
switch (type) {
|
||||
@@ -405,6 +429,81 @@ QString MathNode::GetShaderVariableCall(const QString &input_id, const NodeParam
|
||||
return input_id;
|
||||
}
|
||||
|
||||
QVector4D MathNode::RetrieveVector(const NodeValue &val)
|
||||
{
|
||||
// QVariant doesn't know that QVector*D can convert themselves so we do it here
|
||||
switch (val.type()) {
|
||||
case NodeParam::kVec2:
|
||||
return val.data().value<QVector2D>();
|
||||
case NodeParam::kVec3:
|
||||
return val.data().value<QVector3D>();
|
||||
case NodeParam::kVec4:
|
||||
default:
|
||||
return val.data().value<QVector4D>();
|
||||
}
|
||||
}
|
||||
|
||||
void MathNode::PushVector(NodeValueTable *output, NodeParam::DataType type, const QVector4D &vec)
|
||||
{
|
||||
switch (type) {
|
||||
case NodeParam::kVec2:
|
||||
output->Push(type, QVector2D(vec));
|
||||
break;
|
||||
case NodeParam::kVec3:
|
||||
output->Push(type, QVector3D(vec));
|
||||
break;
|
||||
case NodeParam::kVec4:
|
||||
output->Push(type, vec);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float MathNode::RetrieveNumber(const NodeValue &val)
|
||||
{
|
||||
if (val.type() == NodeParam::kRational) {
|
||||
return val.data().value<rational>().toDouble();
|
||||
} else {
|
||||
return val.data().toFloat();
|
||||
}
|
||||
}
|
||||
|
||||
MathNode::PairingCalculator::PairingCalculator(const NodeValueTable &table_a, const NodeValueTable &table_b)
|
||||
{
|
||||
QVector<int> pair_likelihood_a = GetPairLikelihood(table_a);
|
||||
QVector<int> pair_likelihood_b = GetPairLikelihood(table_b);
|
||||
|
||||
int weight_a = qMax(0, table_b.Count() - table_a.Count());
|
||||
int weight_b = qMax(0, table_a.Count() - table_b.Count());
|
||||
|
||||
QVector<int> likelihoods(kPairCount);
|
||||
|
||||
for (int i=0;i<kPairCount;i++) {
|
||||
if (pair_likelihood_a.at(i) == -1 || pair_likelihood_b.at(i) == -1) {
|
||||
likelihoods.replace(i, -1);
|
||||
} else {
|
||||
likelihoods.replace(i, pair_likelihood_a.at(i) + weight_a + pair_likelihood_b.at(i) + weight_b);
|
||||
}
|
||||
}
|
||||
|
||||
most_likely_pairing_ = kPairNone;
|
||||
|
||||
for (int i=0;i<likelihoods.size();i++) {
|
||||
if (likelihoods.at(i) > -1) {
|
||||
if (most_likely_pairing_ == kPairNone
|
||||
|| likelihoods.at(i) > likelihoods.at(most_likely_pairing_)) {
|
||||
most_likely_pairing_ = static_cast<Pairing>(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (most_likely_pairing_ != kPairNone) {
|
||||
most_likely_value_a_ = table_a.At(pair_likelihood_a.at(most_likely_pairing_));
|
||||
most_likely_value_b_ = table_b.At(pair_likelihood_b.at(most_likely_pairing_));
|
||||
}
|
||||
}
|
||||
|
||||
QVector<int> MathNode::PairingCalculator::GetPairLikelihood(const NodeValueTable &table)
|
||||
{
|
||||
// FIXME: When we introduce a manual override, placing it here would be the least problematic
|
||||
@@ -448,88 +547,6 @@ QVector<int> MathNode::PairingCalculator::GetPairLikelihood(const NodeValueTable
|
||||
return likelihood;
|
||||
}
|
||||
|
||||
MathNode::Pairing MathNode::PairingCalculator::GetMostLikelyPairingInternal(const QVector<int> &a,
|
||||
const QVector<int> &b,
|
||||
const int& weight_a,
|
||||
const int& weight_b)
|
||||
{
|
||||
QVector<int> likelihoods(kPairCount);
|
||||
|
||||
for (int i=0;i<likelihoods.size();i++) {
|
||||
if (a.at(i) == -1 || b.at(i) == -1) {
|
||||
likelihoods.replace(i, -1);
|
||||
} else {
|
||||
likelihoods.replace(i, a.at(i) + weight_a + b.at(i) + weight_b);
|
||||
}
|
||||
}
|
||||
|
||||
Pairing pairing = kPairNone;
|
||||
|
||||
for (int i=0;i<likelihoods.size();i++) {
|
||||
if (likelihoods.at(i) > -1) {
|
||||
if (pairing == kPairNone
|
||||
|| likelihoods.at(i) > likelihoods.at(pairing)) {
|
||||
pairing = static_cast<Pairing>(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pairing;
|
||||
}
|
||||
|
||||
QVector4D MathNode::RetrieveVector(const NodeValue &val)
|
||||
{
|
||||
// QVariant doesn't know that QVector*D can convert themselves so we do it here
|
||||
switch (val.type()) {
|
||||
case NodeParam::kVec2:
|
||||
return val.data().value<QVector2D>();
|
||||
case NodeParam::kVec3:
|
||||
return val.data().value<QVector3D>();
|
||||
case NodeParam::kVec4:
|
||||
default:
|
||||
return val.data().value<QVector4D>();
|
||||
}
|
||||
}
|
||||
|
||||
void MathNode::PushVector(NodeValueTable *output, NodeParam::DataType type, const QVector4D &vec)
|
||||
{
|
||||
switch (type) {
|
||||
case NodeParam::kVec2:
|
||||
output->Push(type, QVector2D(vec));
|
||||
break;
|
||||
case NodeParam::kVec3:
|
||||
output->Push(type, QVector3D(vec));
|
||||
break;
|
||||
case NodeParam::kVec4:
|
||||
output->Push(type, vec);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float MathNode::RetrieveNumber(const NodeValue &val)
|
||||
{
|
||||
if (val.type() == NodeParam::kRational) {
|
||||
return val.data().value<rational>().toDouble();
|
||||
} else {
|
||||
return val.data().toFloat();
|
||||
}
|
||||
}
|
||||
|
||||
MathNode::PairingCalculator::PairingCalculator(const NodeValueTable &table_a, const NodeValueTable &table_b) :
|
||||
table_a_(table_a),
|
||||
table_b_(table_b)
|
||||
{
|
||||
pair_likelihood_a_ = GetPairLikelihood(table_a_);
|
||||
pair_likelihood_b_ = GetPairLikelihood(table_b_);
|
||||
|
||||
most_likely_pairing_ = GetMostLikelyPairingInternal(pair_likelihood_a_,
|
||||
pair_likelihood_b_,
|
||||
qMax(0, table_b_.Count() - table_a_.Count()),
|
||||
qMax(0, table_a_.Count() - table_b_.Count()));
|
||||
}
|
||||
|
||||
bool MathNode::PairingCalculator::FoundMostLikelyPairing() const
|
||||
{
|
||||
return (most_likely_pairing_ > kPairNone && most_likely_pairing_ < kPairCount);
|
||||
@@ -542,17 +559,12 @@ MathNode::Pairing MathNode::PairingCalculator::GetMostLikelyPairing() const
|
||||
|
||||
const NodeValue &MathNode::PairingCalculator::GetMostLikelyValueA() const
|
||||
{
|
||||
return GetMostLikelyValue(table_a_, pair_likelihood_a_);
|
||||
return most_likely_value_a_;
|
||||
}
|
||||
|
||||
const NodeValue &MathNode::PairingCalculator::GetMostLikelyValueB() const
|
||||
{
|
||||
return GetMostLikelyValue(table_b_, pair_likelihood_b_);
|
||||
}
|
||||
|
||||
const NodeValue& MathNode::PairingCalculator::GetMostLikelyValue(const NodeValueTable &table, const QVector<int> &likelihood) const
|
||||
{
|
||||
return table.At(likelihood.at(most_likely_pairing_));
|
||||
return most_likely_value_b_;
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
|
||||
@@ -42,6 +42,7 @@ public:
|
||||
virtual Capabilities GetCapabilities(const NodeValueDatabase&) const override;
|
||||
virtual QString ShaderID(const NodeValueDatabase&) const override;
|
||||
virtual QString ShaderFragmentCode(const NodeValueDatabase&) const override;
|
||||
virtual QString ShaderVertexCode(const NodeValueDatabase&input) const override;
|
||||
|
||||
virtual NodeValue InputValueFromTable(NodeInput* input, NodeValueDatabase &db, bool take) const override;
|
||||
|
||||
@@ -53,7 +54,6 @@ public:
|
||||
NodeInput* param_a_in() const;
|
||||
NodeInput* param_b_in() const;
|
||||
|
||||
private:
|
||||
enum Operation {
|
||||
kOpAdd,
|
||||
kOpSubtract,
|
||||
@@ -63,7 +63,9 @@ private:
|
||||
};
|
||||
|
||||
Operation GetOperation() const;
|
||||
void SetOperation(Operation o);
|
||||
|
||||
private:
|
||||
enum Pairing {
|
||||
kPairNone = -1,
|
||||
|
||||
@@ -96,21 +98,13 @@ private:
|
||||
const NodeValue& GetMostLikelyValueB() const;
|
||||
|
||||
private:
|
||||
static Pairing GetMostLikelyPairingInternal(const QVector<int> &a, const QVector<int> &b, const int &weight_a, const int &weight_b);
|
||||
|
||||
static QVector<int> GetPairLikelihood(const NodeValueTable& table);
|
||||
|
||||
const NodeValue &GetMostLikelyValue(const NodeValueTable& table, const QVector<int>& likelihood) const;
|
||||
|
||||
Pairing most_likely_pairing_;
|
||||
|
||||
const NodeValueTable& table_a_;
|
||||
NodeValue most_likely_value_a_;
|
||||
|
||||
const NodeValueTable& table_b_;
|
||||
|
||||
QVector<int> pair_likelihood_a_;
|
||||
|
||||
QVector<int> pair_likelihood_b_;
|
||||
NodeValue most_likely_value_b_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
+63
-130
@@ -75,7 +75,13 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const QAto
|
||||
continue;
|
||||
}
|
||||
|
||||
NodeParam* param = GetParameterWithID(param_id);
|
||||
NodeParam* param;
|
||||
|
||||
if (reader->name() == QStringLiteral("input")) {
|
||||
param = GetInputWithID(param_id);
|
||||
} else {
|
||||
param = GetOutputWithID(param_id);
|
||||
}
|
||||
|
||||
if (!param) {
|
||||
qDebug() << "No parameter in" << id() << "with parameter" << param_id;
|
||||
@@ -153,21 +159,21 @@ NodeValueTable Node::Value(NodeValueDatabase &value) const
|
||||
return value.Merge();
|
||||
}
|
||||
|
||||
void Node::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from)
|
||||
void Node::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source)
|
||||
{
|
||||
Q_UNUSED(from)
|
||||
|
||||
SendInvalidateCache(start_range, end_range);
|
||||
SendInvalidateCache(range, source);
|
||||
}
|
||||
|
||||
void Node::InvalidateVisible(NodeInput *from)
|
||||
void Node::InvalidateVisible(NodeInput *from, NodeInput* source)
|
||||
{
|
||||
Q_UNUSED(from)
|
||||
|
||||
foreach (NodeParam* param, params_) {
|
||||
if (param->type() == NodeParam::kOutput) {
|
||||
foreach (NodeEdgePtr edge, param->edges()) {
|
||||
edge->input()->parentNode()->InvalidateVisible(edge->input());
|
||||
edge->input()->parentNode()->InvalidateVisible(edge->input(), source);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,7 +191,7 @@ TimeRange Node::OutputTimeAdjustment(NodeInput *, const TimeRange &input_time) c
|
||||
return input_time;
|
||||
}
|
||||
|
||||
void Node::SendInvalidateCache(const rational &start_range, const rational &end_range)
|
||||
void Node::SendInvalidateCache(const TimeRange &range, NodeInput *source)
|
||||
{
|
||||
// Loop through all parameters (there should be no children that are not NodeParams)
|
||||
foreach (NodeParam* param, params_) {
|
||||
@@ -199,25 +205,7 @@ void Node::SendInvalidateCache(const rational &start_range, const rational &end_
|
||||
Node* connected_node = connected_input->parentNode();
|
||||
|
||||
// Send clear cache signal to the Node
|
||||
connected_node->InvalidateCache(start_range, end_range, connected_input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Node::DependentEdgeChanged(NodeInput *from)
|
||||
{
|
||||
Q_UNUSED(from)
|
||||
|
||||
foreach (NodeParam* p, params_) {
|
||||
if (p->type() == NodeParam::kOutput && p->IsConnected()) {
|
||||
NodeOutput* out = static_cast<NodeOutput*>(p);
|
||||
|
||||
foreach (NodeEdgePtr edge, out->edges()) {
|
||||
NodeInput* connected_input = edge->input();
|
||||
Node* connected_node = connected_input->parentNode();
|
||||
|
||||
connected_node->DependentEdgeChanged(connected_input);
|
||||
connected_node->InvalidateCache(range, connected_input, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -300,57 +288,6 @@ void Node::CopyInputs(Node *source, Node *destination, bool include_connections)
|
||||
}
|
||||
}
|
||||
|
||||
void DuplicateConnectionsBetweenListsInternal(const QList<Node *> &source, const QList<Node *> &destination, NodeInput* source_input, NodeInput* dest_input)
|
||||
{
|
||||
if (source_input->IsConnected()) {
|
||||
// Get this input's connected outputs
|
||||
NodeOutput* source_output = source_input->get_connected_output();
|
||||
Node* source_output_node = source_output->parentNode();
|
||||
|
||||
// Find equivalent in destination list
|
||||
Node* dest_output_node = destination.at(source.indexOf(source_output_node));
|
||||
|
||||
Q_ASSERT(dest_output_node->id() == source_output_node->id());
|
||||
|
||||
NodeOutput* dest_output = static_cast<NodeOutput*>(dest_output_node->GetParameterWithID(source_output->id()));
|
||||
|
||||
NodeParam::ConnectEdge(dest_output, dest_input);
|
||||
}
|
||||
|
||||
// If inputs are arrays, duplicate their connections too
|
||||
if (source_input->IsArray()) {
|
||||
NodeInputArray* source_array = static_cast<NodeInputArray*>(source_input);
|
||||
NodeInputArray* dest_array = static_cast<NodeInputArray*>(dest_input);
|
||||
|
||||
for (int i=0;i<source_array->GetSize();i++) {
|
||||
DuplicateConnectionsBetweenListsInternal(source, destination, source_array->At(i), dest_array->At(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Node::DuplicateConnectionsBetweenLists(const QList<Node *> &source, const QList<Node *> &destination)
|
||||
{
|
||||
Q_ASSERT(source.size() == destination.size());
|
||||
|
||||
for (int i=0;i<source.size();i++) {
|
||||
Node* source_input_node = source.at(i);
|
||||
Node* dest_input_node = destination.at(i);
|
||||
|
||||
Q_ASSERT(source_input_node->id() == dest_input_node->id());
|
||||
|
||||
for (int j=0;j<source_input_node->params_.size();j++) {
|
||||
NodeParam* source_param = source_input_node->params_.at(j);
|
||||
|
||||
if (source_param->type() == NodeInput::kInput) {
|
||||
NodeInput* source_input = static_cast<NodeInput*>(source_param);
|
||||
NodeInput* dest_input = static_cast<NodeInput*>(dest_input_node->params_.at(j));
|
||||
|
||||
DuplicateConnectionsBetweenListsInternal(source, destination, source_input, dest_input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Node::CanBeDeleted() const
|
||||
{
|
||||
return can_be_deleted_;
|
||||
@@ -381,29 +318,6 @@ int Node::IndexOfParameter(NodeParam *param) const
|
||||
return params_.indexOf(param);
|
||||
}
|
||||
|
||||
void Node::TraverseInputInternal(QList<Node*>& list, NodeInput* input, bool traverse, bool exclusive_only) {
|
||||
if (input->IsConnected()
|
||||
&& (input->get_connected_output()->edges().size() == 1 || !exclusive_only)) {
|
||||
Node* connected = input->get_connected_node();
|
||||
|
||||
if (!list.contains(connected)) {
|
||||
list.append(connected);
|
||||
|
||||
if (traverse) {
|
||||
GetDependenciesInternal(connected, list, traverse, exclusive_only);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (input->IsArray()) {
|
||||
NodeInputArray* input_array = static_cast<NodeInputArray*>(input);
|
||||
|
||||
for (int i=0;i<input_array->GetSize();i++) {
|
||||
TraverseInputInternal(list, input_array->At(i), traverse, exclusive_only);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Recursively collects dependencies of Node `n` and appends them to QList `list`
|
||||
*
|
||||
@@ -412,41 +326,30 @@ void Node::TraverseInputInternal(QList<Node*>& list, NodeInput* input, bool trav
|
||||
* TRUE to recursively traverse each node for a complete dependency graph. FALSE to return only the immediate
|
||||
* dependencies.
|
||||
*/
|
||||
void Node::GetDependenciesInternal(const Node* n, QList<Node*>& list, bool traverse, bool exclusive_only) {
|
||||
foreach (NodeParam* p, n->parameters()) {
|
||||
if (p->type() == NodeParam::kInput) {
|
||||
NodeInput* input = static_cast<NodeInput*>(p);
|
||||
QList<Node*> Node::GetDependenciesInternal(bool traverse, bool exclusive_only) const {
|
||||
QList<NodeInput*> inputs = GetInputsIncludingArrays();
|
||||
QList<Node*> list;
|
||||
|
||||
TraverseInputInternal(list, input, traverse, exclusive_only);
|
||||
}
|
||||
foreach (NodeInput* i, inputs) {
|
||||
i->GetDependencies(list, traverse, exclusive_only);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
QList<Node *> Node::GetDependencies() const
|
||||
{
|
||||
QList<Node *> node_list;
|
||||
|
||||
GetDependenciesInternal(this, node_list, true, false);
|
||||
|
||||
return node_list;
|
||||
return GetDependenciesInternal(true, false);
|
||||
}
|
||||
|
||||
QList<Node *> Node::GetExclusiveDependencies() const
|
||||
{
|
||||
QList<Node *> node_list;
|
||||
|
||||
GetDependenciesInternal(this, node_list, true, true);
|
||||
|
||||
return node_list;
|
||||
return GetDependenciesInternal(true, true);
|
||||
}
|
||||
|
||||
QList<Node *> Node::GetImmediateDependencies() const
|
||||
{
|
||||
QList<Node *> node_list;
|
||||
|
||||
GetDependenciesInternal(this, node_list, false, false);
|
||||
|
||||
return node_list;
|
||||
return GetDependenciesInternal(false, false);
|
||||
}
|
||||
|
||||
Node::Capabilities Node::GetCapabilities(const NodeValueDatabase &) const
|
||||
@@ -479,7 +382,7 @@ NodeInput *Node::ShaderIterativeInput() const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NodeInput* Node::ProcessesSamplesFrom(const NodeValueDatabase &value) const
|
||||
NodeInput* Node::ProcessesSamplesFrom(const NodeValueDatabase &) const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
@@ -488,11 +391,25 @@ void Node::ProcessSamples(const NodeValueDatabase &, const AudioRenderingParams&
|
||||
{
|
||||
}
|
||||
|
||||
NodeParam *Node::GetParameterWithID(const QString &id) const
|
||||
NodeInput *Node::GetInputWithID(const QString &id) const
|
||||
{
|
||||
foreach (NodeParam* param, params_) {
|
||||
if (param->id() == id) {
|
||||
return param;
|
||||
QList<NodeInput*> inputs = GetInputsIncludingArrays();
|
||||
|
||||
foreach (NodeInput* i, inputs) {
|
||||
if (i->id() == id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NodeOutput *Node::GetOutputWithID(const QString &id) const
|
||||
{
|
||||
foreach (NodeParam* p, params_) {
|
||||
if (p->type() == NodeParam::kOutput
|
||||
&& p->id() == id) {
|
||||
return static_cast<NodeOutput*>(p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -665,25 +582,41 @@ void Node::ConnectInput(NodeInput *input)
|
||||
connect(input, &NodeInput::ValueChanged, this, &Node::InputChanged);
|
||||
connect(input, &NodeInput::EdgeAdded, this, &Node::InputConnectionChanged);
|
||||
connect(input, &NodeInput::EdgeRemoved, this, &Node::InputConnectionChanged);
|
||||
|
||||
if (input->IsArray()) {
|
||||
NodeInputArray* array = static_cast<NodeInputArray*>(input);
|
||||
|
||||
connect(array, &NodeInputArray::SubParamEdgeAdded, this, &Node::InputConnectionChanged);
|
||||
connect(array, &NodeInputArray::SubParamEdgeRemoved, this, &Node::InputConnectionChanged);
|
||||
connect(array, &NodeInputArray::SubParamEdgeAdded, this, &Node::EdgeAdded);
|
||||
connect(array, &NodeInputArray::SubParamEdgeRemoved, this, &Node::EdgeRemoved);
|
||||
}
|
||||
}
|
||||
|
||||
void Node::DisconnectInput(NodeInput *input)
|
||||
{
|
||||
if (input->IsArray()) {
|
||||
NodeInputArray* array = static_cast<NodeInputArray*>(input);
|
||||
|
||||
disconnect(array, &NodeInputArray::SubParamEdgeAdded, this, &Node::InputConnectionChanged);
|
||||
disconnect(array, &NodeInputArray::SubParamEdgeRemoved, this, &Node::InputConnectionChanged);
|
||||
disconnect(array, &NodeInputArray::SubParamEdgeAdded, this, &Node::EdgeAdded);
|
||||
disconnect(array, &NodeInputArray::SubParamEdgeRemoved, this, &Node::EdgeRemoved);
|
||||
}
|
||||
|
||||
disconnect(input, &NodeInput::ValueChanged, this, &Node::InputChanged);
|
||||
disconnect(input, &NodeInput::EdgeAdded, this, &Node::InputConnectionChanged);
|
||||
disconnect(input, &NodeInput::EdgeRemoved, this, &Node::InputConnectionChanged);
|
||||
}
|
||||
|
||||
void Node::InputChanged(rational start, rational end)
|
||||
void Node::InputChanged(const TimeRange& range)
|
||||
{
|
||||
InvalidateCache(start, end, static_cast<NodeInput*>(sender()));
|
||||
InvalidateCache(range, static_cast<NodeInput*>(sender()), static_cast<NodeInput*>(sender()));
|
||||
}
|
||||
|
||||
void Node::InputConnectionChanged(NodeEdgePtr edge)
|
||||
{
|
||||
DependentEdgeChanged(edge->input());
|
||||
|
||||
InvalidateCache(RATIONAL_MIN, RATIONAL_MAX, static_cast<NodeInput*>(sender()));
|
||||
InvalidateCache(TimeRange(RATIONAL_MIN, RATIONAL_MAX), edge->input(), edge->input());
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
+12
-17
@@ -29,7 +29,6 @@
|
||||
#include "codec/samplebuffer.h"
|
||||
#include "common/rational.h"
|
||||
#include "common/xmlutils.h"
|
||||
#include "node/dependency.h"
|
||||
#include "node/input.h"
|
||||
#include "node/inputarray.h"
|
||||
#include "node/output.h"
|
||||
@@ -196,9 +195,14 @@ public:
|
||||
virtual void ProcessSamples(const NodeValueDatabase &values, const AudioRenderingParams& params, const SampleBufferPtr input, SampleBufferPtr output, int index) const;
|
||||
|
||||
/**
|
||||
* @brief Returns the parameter with the specified ID (or nullptr if it doesn't exist)
|
||||
* @brief Returns the input with the specified ID (or nullptr if it doesn't exist)
|
||||
*/
|
||||
NodeParam* GetParameterWithID(const QString& id) const;
|
||||
NodeInput* GetInputWithID(const QString& id) const;
|
||||
|
||||
/**
|
||||
* @brief Returns the output with the specified ID (or nullptr if it doesn't exist)
|
||||
*/
|
||||
NodeOutput* GetOutputWithID(const QString& id) const;
|
||||
|
||||
/**
|
||||
* @brief Returns whether this Node outputs data to the Node `n` in any way
|
||||
@@ -261,12 +265,12 @@ public:
|
||||
* the DAG. Even if the time needs to be transformed somehow (e.g. converting media time to sequence time), you can
|
||||
* call this function with transformed time and relay the signal that way.
|
||||
*/
|
||||
virtual void InvalidateCache(const rational& start_range, const rational& end_range, NodeInput* from = nullptr);
|
||||
virtual void InvalidateCache(const TimeRange& range, NodeInput* from, NodeInput* source);
|
||||
|
||||
/**
|
||||
* @brief Signal through node graph to only invalidate frames that are currently visible on a ViewerWidget
|
||||
*/
|
||||
virtual void InvalidateVisible(NodeInput *from);
|
||||
virtual void InvalidateVisible(NodeInput *from, NodeInput* source);
|
||||
|
||||
/**
|
||||
* @brief Adjusts time that should be sent to nodes connected to certain inputs.
|
||||
@@ -288,11 +292,6 @@ public:
|
||||
*/
|
||||
static void CopyInputs(Node* source, Node* destination, bool include_connections = true);
|
||||
|
||||
/**
|
||||
* @brief For a list of copies nodes, this function will duplicate all the connections in the source list to the destination list
|
||||
*/
|
||||
static void DuplicateConnectionsBetweenLists(const QList<Node*>& source, const QList<Node *> &destination);
|
||||
|
||||
/**
|
||||
* @brief Return whether this Node can be deleted or not
|
||||
*/
|
||||
@@ -358,9 +357,7 @@ protected:
|
||||
|
||||
void ClearCachedValuesInParameters(const rational& start_range, const rational& end_range);
|
||||
|
||||
void SendInvalidateCache(const rational& start_range, const rational& end_range);
|
||||
|
||||
virtual void DependentEdgeChanged(NodeInput* from);
|
||||
void SendInvalidateCache(const TimeRange &range, NodeInput *source);
|
||||
|
||||
virtual void LoadInternal(QXmlStreamReader* reader, XMLNodeData& xml_node_data);
|
||||
|
||||
@@ -403,9 +400,7 @@ private:
|
||||
|
||||
void DisconnectInput(NodeInput* input);
|
||||
|
||||
static void TraverseInputInternal(QList<Node*>& list, NodeInput* input, bool traverse, bool exclusive_only);
|
||||
|
||||
static void GetDependenciesInternal(const Node* n, QList<Node*>& list, bool traverse, bool exclusive_only);
|
||||
QList<Node *> GetDependenciesInternal(bool traverse, bool exclusive_only) const;
|
||||
|
||||
QList<NodeParam *> params_;
|
||||
|
||||
@@ -425,7 +420,7 @@ private:
|
||||
QPointF position_;
|
||||
|
||||
private slots:
|
||||
void InputChanged(rational start, rational end);
|
||||
void InputChanged(const TimeRange &range);
|
||||
|
||||
void InputConnectionChanged(NodeEdgePtr edge);
|
||||
|
||||
|
||||
+131
-125
@@ -38,9 +38,8 @@ TrackOutput::TrackOutput() :
|
||||
block_input_ = new NodeInputArray("block_in", NodeParam::kAny);
|
||||
block_input_->set_is_keyframable(false);
|
||||
AddInput(block_input_);
|
||||
connect(block_input_, &NodeInputArray::EdgeAdded, this, &TrackOutput::BlockConnected);
|
||||
connect(block_input_, &NodeInputArray::EdgeRemoved, this, &TrackOutput::BlockDisconnected);
|
||||
connect(block_input_, &NodeInputArray::SizeChanged, this, &TrackOutput::BlockListSizeChanged);
|
||||
connect(block_input_, &NodeInputArray::SubParamEdgeAdded, this, &TrackOutput::BlockConnected);
|
||||
connect(block_input_, &NodeInputArray::SubParamEdgeRemoved, this, &TrackOutput::BlockDisconnected);
|
||||
|
||||
muted_input_ = new NodeInput("muted_in", NodeParam::kBoolean);
|
||||
muted_input_->set_is_keyframable(false);
|
||||
@@ -128,6 +127,8 @@ const int &TrackOutput::Index()
|
||||
void TrackOutput::SetIndex(const int &index)
|
||||
{
|
||||
index_ = index;
|
||||
|
||||
emit IndexChanged(index);
|
||||
}
|
||||
|
||||
Block *TrackOutput::BlockContainingTime(const rational &time) const
|
||||
@@ -231,15 +232,15 @@ QList<Block *> TrackOutput::BlocksAtTimeRange(const TimeRange &range) const
|
||||
return list;
|
||||
}
|
||||
|
||||
const QVector<Block *> &TrackOutput::Blocks() const
|
||||
const QList<Block *> &TrackOutput::Blocks() const
|
||||
{
|
||||
return block_cache_;
|
||||
}
|
||||
|
||||
void TrackOutput::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from)
|
||||
void TrackOutput::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source)
|
||||
{
|
||||
if (block_invalidate_cache_stack_ == 0) {
|
||||
Node::InvalidateCache(qMax(start_range, rational(0)), qMin(end_range, track_length()), from);
|
||||
Node::InvalidateCache(TimeRange(qMax(range.in(), rational(0)), qMin(range.out(), track_length())), from, source);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,35 +264,42 @@ void TrackOutput::InsertBlockAfter(Block *block, Block *before)
|
||||
|
||||
void TrackOutput::PrependBlock(Block *block)
|
||||
{
|
||||
InsertBlockAtIndex(block, 0);
|
||||
BlockInvalidateCache();
|
||||
|
||||
block_input_->Prepend();
|
||||
NodeParam::ConnectEdge(block->output(), block_input_->First());
|
||||
|
||||
UnblockInvalidateCache();
|
||||
|
||||
// Everything has shifted at this point
|
||||
InvalidateCache(TimeRange(0, track_length()), block_input_, block_input_);
|
||||
}
|
||||
|
||||
void TrackOutput::InsertBlockAtIndex(Block *block, int index)
|
||||
{
|
||||
BlockInvalidateCache();
|
||||
|
||||
block_input_->InsertAt(index);
|
||||
int insert_index = GetInputIndexFromCacheIndex(index);
|
||||
block_input_->InsertAt(insert_index);
|
||||
NodeParam::ConnectEdge(block->output(),
|
||||
block_input_->At(index));
|
||||
block_input_->At(insert_index));
|
||||
|
||||
UnblockInvalidateCache();
|
||||
|
||||
InvalidateCache(block->in(), track_length());
|
||||
InvalidateCache(TimeRange(block->in(), track_length()), block_input_, block_input_);
|
||||
}
|
||||
|
||||
void TrackOutput::AppendBlock(Block *block)
|
||||
{
|
||||
BlockInvalidateCache();
|
||||
|
||||
int last_index = block_input_->GetSize();
|
||||
block_input_->Append();
|
||||
NodeParam::ConnectEdge(block->output(),
|
||||
block_input_->At(last_index));
|
||||
NodeParam::ConnectEdge(block->output(), block_input_->Last());
|
||||
|
||||
UnblockInvalidateCache();
|
||||
|
||||
// Invalidate area that block was added to
|
||||
InvalidateCache(block->in(), track_length());
|
||||
InvalidateCache(TimeRange(block->in(), track_length()), block_input_, block_input_);
|
||||
}
|
||||
|
||||
void TrackOutput::BlockInvalidateCache()
|
||||
@@ -310,20 +318,18 @@ void TrackOutput::RippleRemoveBlock(Block *block)
|
||||
|
||||
rational remove_in = block->in();
|
||||
|
||||
int index_of_block_to_remove = block_cache_.indexOf(block);
|
||||
|
||||
block_input_->RemoveAt(index_of_block_to_remove);
|
||||
block_input_->RemoveAt(GetInputIndexFromCacheIndex(block));
|
||||
|
||||
UnblockInvalidateCache();
|
||||
|
||||
InvalidateCache(remove_in, track_length());
|
||||
InvalidateCache(TimeRange(remove_in, track_length()), block_input_, block_input_);
|
||||
}
|
||||
|
||||
void TrackOutput::ReplaceBlock(Block *old, Block *replace)
|
||||
{
|
||||
BlockInvalidateCache();
|
||||
|
||||
int index_of_old_block = block_cache_.indexOf(old);
|
||||
int index_of_old_block = GetInputIndexFromCacheIndex(old);
|
||||
|
||||
NodeParam::DisconnectEdge(old->output(),
|
||||
block_input_->At(index_of_old_block));
|
||||
@@ -334,9 +340,9 @@ void TrackOutput::ReplaceBlock(Block *old, Block *replace)
|
||||
UnblockInvalidateCache();
|
||||
|
||||
if (old->length() == replace->length()) {
|
||||
InvalidateCache(replace->in(), replace->out());
|
||||
InvalidateCache(TimeRange(replace->in(), replace->out()), block_input_, block_input_);
|
||||
} else {
|
||||
InvalidateCache(replace->in(), RATIONAL_MAX);
|
||||
InvalidateCache(TimeRange(replace->in(), RATIONAL_MAX), block_input_, block_input_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,6 +413,11 @@ bool TrackOutput::IsLocked() const
|
||||
return locked_;
|
||||
}
|
||||
|
||||
NodeInputArray *TrackOutput::block_input() const
|
||||
{
|
||||
return block_input_;
|
||||
}
|
||||
|
||||
void TrackOutput::SetTrackName(const QString &name)
|
||||
{
|
||||
track_name_ = name;
|
||||
@@ -415,7 +426,7 @@ void TrackOutput::SetTrackName(const QString &name)
|
||||
void TrackOutput::SetMuted(bool e)
|
||||
{
|
||||
muted_input_->set_standard_value(e);
|
||||
InvalidateCache(0, track_length());
|
||||
InvalidateCache(TimeRange(0, track_length()), block_input_, block_input_);
|
||||
}
|
||||
|
||||
void TrackOutput::SetLocked(bool e)
|
||||
@@ -425,107 +436,107 @@ void TrackOutput::SetLocked(bool e)
|
||||
|
||||
void TrackOutput::UpdateInOutFrom(int index)
|
||||
{
|
||||
Q_ASSERT(index >= 0);
|
||||
Q_ASSERT(index < block_cache_.size());
|
||||
|
||||
rational new_track_length;
|
||||
|
||||
// Find block just before this one to find the last out point
|
||||
for (int i=index-1;i>=0;i--) {
|
||||
rational last_out = (index == 0) ? 0 : block_cache_.at(index - 1)->out();
|
||||
|
||||
// Iterate through all blocks updating their in/outs
|
||||
for (int i=index; i<block_cache_.size(); i++) {
|
||||
Block* b = block_cache_.at(i);
|
||||
|
||||
if (b) {
|
||||
new_track_length = b->out();
|
||||
break;
|
||||
}
|
||||
}
|
||||
b->set_in(last_out);
|
||||
|
||||
for (int i=index;i<block_cache_.size();i++) {
|
||||
Block* b = block_cache_.at(i);
|
||||
last_out += b->length();
|
||||
|
||||
if (b) {
|
||||
// Set in
|
||||
b->set_in(new_track_length);
|
||||
b->set_out(last_out);
|
||||
|
||||
// Set out
|
||||
new_track_length += b->length();
|
||||
b->set_out(new_track_length);
|
||||
|
||||
emit b->Refreshed();
|
||||
}
|
||||
emit b->Refreshed();
|
||||
}
|
||||
|
||||
// Update track length
|
||||
if (new_track_length != track_length_) {
|
||||
if (last_out != track_length_) {
|
||||
rational old_track_length = track_length_;
|
||||
|
||||
track_length_ = new_track_length;
|
||||
track_length_ = last_out;
|
||||
emit TrackLengthChanged();
|
||||
|
||||
InvalidateCache(qMin(old_track_length, new_track_length),
|
||||
qMax(old_track_length, new_track_length));
|
||||
InvalidateCache(TimeRange(qMin(old_track_length, last_out), qMax(old_track_length, last_out)),
|
||||
block_input_,
|
||||
block_input_);
|
||||
}
|
||||
}
|
||||
|
||||
void TrackOutput::UpdatePreviousAndNextOfIndex(int index)
|
||||
int TrackOutput::GetInputIndexFromCacheIndex(int cache_index)
|
||||
{
|
||||
Block* ref = block_cache_.at(index);
|
||||
return GetInputIndexFromCacheIndex(block_cache_.at(cache_index));
|
||||
}
|
||||
|
||||
Block* previous = nullptr;
|
||||
Block* next = nullptr;
|
||||
|
||||
// Find previous
|
||||
for (int i=index-1;i>=0;i--) {
|
||||
previous = block_cache_.at(i);
|
||||
|
||||
if (previous) {
|
||||
break;
|
||||
int TrackOutput::GetInputIndexFromCacheIndex(Block *block)
|
||||
{
|
||||
for (int i=0; i<block_input_->GetSize(); i++) {
|
||||
if (block_input_->At(i)->get_connected_node() == block) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// Find next
|
||||
for (int i=index+1;i<block_cache_.size();i++) {
|
||||
next = block_cache_.at(i);
|
||||
|
||||
if (next) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ref) {
|
||||
// Link blocks together
|
||||
ref->set_previous(previous);
|
||||
ref->set_next(next);
|
||||
|
||||
if (previous)
|
||||
previous->set_next(ref);
|
||||
|
||||
if (next)
|
||||
next->set_previous(ref);
|
||||
} else {
|
||||
// Link previous and next together
|
||||
if (previous)
|
||||
previous->set_next(next);
|
||||
|
||||
if (next)
|
||||
next->set_previous(previous);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void TrackOutput::BlockConnected(NodeEdgePtr edge)
|
||||
{
|
||||
int block_index = block_input_->IndexOfSubParameter(edge->input());
|
||||
|
||||
Q_ASSERT(block_index >= 0);
|
||||
|
||||
// Determine what node was just connected
|
||||
Node* connected_node = edge->output()->parentNode();
|
||||
Block* connected_block = connected_node->IsBlock() ? static_cast<Block*>(connected_node) : nullptr;
|
||||
block_cache_.replace(block_index, connected_block);
|
||||
UpdatePreviousAndNextOfIndex(block_index);
|
||||
UpdateInOutFrom(block_index);
|
||||
|
||||
if (connected_block) {
|
||||
connect(connected_block, SIGNAL(LengthChanged(const rational&)), this, SLOT(BlockLengthChanged()));
|
||||
// If this node is a block, we can do something with it
|
||||
if (connected_node->IsBlock()) {
|
||||
Block* connected_block = static_cast<Block*>(connected_node);
|
||||
|
||||
// See where this input falls in our internal "block cache"
|
||||
Block* next = nullptr;
|
||||
for (int i=block_input_->IndexOfSubParameter(edge->input())+1; i<block_input_->GetSize(); i++) {
|
||||
Node* that_node = block_input_->At(i)->get_connected_node();
|
||||
|
||||
// If we find a block, this is the block that will follow the one just connected
|
||||
if (that_node && that_node->IsBlock()) {
|
||||
next = static_cast<Block*>(that_node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int real_block_index;
|
||||
|
||||
// Either insert or append depending on if we found a "next" block
|
||||
if (next) {
|
||||
// Insert block before this next block
|
||||
real_block_index = block_cache_.indexOf(next);
|
||||
block_cache_.insert(real_block_index, connected_block);
|
||||
|
||||
// Update values with next
|
||||
next->set_previous(connected_block);
|
||||
connected_block->set_next(next);
|
||||
} else {
|
||||
// No "next", this block must come at the end
|
||||
real_block_index = block_cache_.size();
|
||||
block_cache_.append(connected_block);
|
||||
|
||||
// Update next value
|
||||
connected_block->set_next(nullptr);
|
||||
}
|
||||
|
||||
// For all blocks after the block we inserted (including it), update the "previous" and "next"
|
||||
// fields as well as the in/out values
|
||||
if (real_block_index == 0) {
|
||||
connected_block->set_previous(nullptr);
|
||||
} else {
|
||||
Block* prev = block_cache_.at(real_block_index - 1);
|
||||
|
||||
connected_block->set_previous(prev);
|
||||
prev->set_next(connected_block);
|
||||
}
|
||||
|
||||
UpdateInOutFrom(real_block_index);
|
||||
|
||||
// Make connections to this block
|
||||
connect(connected_block, &Block::LengthChanged, this, &TrackOutput::BlockLengthChanged);
|
||||
|
||||
emit BlockAdded(connected_block);
|
||||
}
|
||||
@@ -533,46 +544,41 @@ void TrackOutput::BlockConnected(NodeEdgePtr edge)
|
||||
|
||||
void TrackOutput::BlockDisconnected(NodeEdgePtr edge)
|
||||
{
|
||||
int block_index = block_input_->IndexOfSubParameter(edge->input());
|
||||
|
||||
Q_ASSERT(block_index >= 0);
|
||||
|
||||
block_cache_.replace(block_index, nullptr);
|
||||
UpdatePreviousAndNextOfIndex(block_index);
|
||||
UpdateInOutFrom(block_index);
|
||||
|
||||
// See what kind of node was just connected
|
||||
Node* connected_node = edge->output()->parentNode();
|
||||
Block* connected_block = connected_node->IsBlock() ? static_cast<Block*>(connected_node) : nullptr;
|
||||
if (connected_block) {
|
||||
disconnect(connected_block, SIGNAL(LengthChanged(const rational&)), this, SLOT(BlockLengthChanged()));
|
||||
|
||||
// Update previous and next references
|
||||
// If this was a block, we would have put it in our block cache in BlockConnected()
|
||||
if (connected_node->IsBlock()) {
|
||||
Block* connected_block = static_cast<Block*>(connected_node);
|
||||
|
||||
// Determine what index this block was in our cache and remove it
|
||||
int index_of_block = block_cache_.indexOf(connected_block);
|
||||
block_cache_.removeAt(index_of_block);
|
||||
|
||||
// If there were blocks following this one, update their ins/outs
|
||||
UpdateInOutFrom(index_of_block);
|
||||
|
||||
// Join the previous and next blocks together
|
||||
if (connected_block->previous()) {
|
||||
connected_block->previous()->set_next(connected_block->next());
|
||||
}
|
||||
|
||||
if (connected_block->next()) {
|
||||
connected_block->next()->set_previous(connected_block->previous());
|
||||
}
|
||||
|
||||
disconnect(connected_block, &Block::LengthChanged, this, &TrackOutput::BlockLengthChanged);
|
||||
|
||||
emit BlockRemoved(connected_block);
|
||||
}
|
||||
}
|
||||
|
||||
void TrackOutput::BlockListSizeChanged(int size)
|
||||
{
|
||||
int old_size = block_cache_.size();
|
||||
|
||||
block_cache_.resize(size);
|
||||
|
||||
// Fill new slots with nullptr
|
||||
for (int i=old_size;i<size;i++) {
|
||||
block_cache_.replace(i, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void TrackOutput::BlockLengthChanged()
|
||||
{
|
||||
// Assumes sender is a Block
|
||||
Block* b = static_cast<Block*>(sender());
|
||||
|
||||
int index = block_cache_.indexOf(b);
|
||||
|
||||
Q_ASSERT(index >= 0);
|
||||
|
||||
UpdateInOutFrom(index);
|
||||
UpdateInOutFrom(block_cache_.indexOf(b));
|
||||
}
|
||||
|
||||
void TrackOutput::MutedInputValueChanged()
|
||||
|
||||
@@ -82,9 +82,9 @@ public:
|
||||
Block* BlockAtTime(const rational& time) const;
|
||||
QList<Block*> BlocksAtTimeRange(const TimeRange& range) const;
|
||||
|
||||
const QVector<Block*>& Blocks() const;
|
||||
const QList<Block *> &Blocks() const;
|
||||
|
||||
virtual void InvalidateCache(const rational& start_range, const rational& end_range, NodeInput* from = nullptr) override;
|
||||
virtual void InvalidateCache(const TimeRange& range, NodeInput* from, NodeInput *source) override;
|
||||
|
||||
/**
|
||||
* @brief Adds Block `block` at the very beginning of the Sequence before all other clips
|
||||
@@ -150,6 +150,8 @@ public:
|
||||
|
||||
bool IsLocked() const;
|
||||
|
||||
NodeInputArray* block_input() const;
|
||||
|
||||
public slots:
|
||||
void SetTrackName(const QString& name);
|
||||
|
||||
@@ -183,14 +185,20 @@ signals:
|
||||
*/
|
||||
void MutedChanged(bool e);
|
||||
|
||||
/**
|
||||
* @brief Signal emitted when the index has changed
|
||||
*/
|
||||
void IndexChanged(int i);
|
||||
|
||||
protected:
|
||||
|
||||
private:
|
||||
void UpdateInOutFrom(int index);
|
||||
|
||||
void UpdatePreviousAndNextOfIndex(int index);
|
||||
int GetInputIndexFromCacheIndex(int cache_index);
|
||||
int GetInputIndexFromCacheIndex(Block* block);
|
||||
|
||||
QVector<Block*> block_cache_;
|
||||
QList<Block*> block_cache_;
|
||||
|
||||
NodeInputArray* block_input_;
|
||||
|
||||
@@ -215,8 +223,6 @@ private slots:
|
||||
|
||||
void BlockDisconnected(NodeEdgePtr edge);
|
||||
|
||||
void BlockListSizeChanged(int size);
|
||||
|
||||
void BlockLengthChanged();
|
||||
|
||||
void MutedInputValueChanged();
|
||||
|
||||
@@ -31,9 +31,8 @@ TrackList::TrackList(ViewerOutput *parent, const Timeline::TrackType &type, Node
|
||||
track_input_(track_input),
|
||||
type_(type)
|
||||
{
|
||||
connect(track_input, &NodeInputArray::EdgeAdded, this, &TrackList::TrackConnected);
|
||||
connect(track_input, &NodeInputArray::EdgeRemoved, this, &TrackList::TrackDisconnected);
|
||||
connect(track_input, &NodeInputArray::SizeChanged, this, &TrackList::TrackListSizeChanged);
|
||||
connect(track_input, &NodeInputArray::SubParamEdgeAdded, this, &TrackList::TrackConnected);
|
||||
connect(track_input, &NodeInputArray::SubParamEdgeRemoved, this, &TrackList::TrackDisconnected);
|
||||
}
|
||||
|
||||
const Timeline::TrackType &TrackList::type() const
|
||||
@@ -51,38 +50,22 @@ void TrackList::TrackRemovedBlock(Block *block)
|
||||
emit BlockRemoved(block);
|
||||
}
|
||||
|
||||
void TrackList::TrackListSizeChanged(int size)
|
||||
{
|
||||
int old_size = track_cache_.size();
|
||||
|
||||
track_cache_.resize(size);
|
||||
|
||||
// Fill new slots with nullptr
|
||||
for (int i=old_size;i<size;i++) {
|
||||
track_cache_.replace(i, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
const QVector<TrackOutput *> &TrackList::Tracks() const
|
||||
const QVector<TrackOutput *> &TrackList::GetTracks() const
|
||||
{
|
||||
return track_cache_;
|
||||
}
|
||||
|
||||
TrackOutput *TrackList::TrackAt(int index) const
|
||||
TrackOutput *TrackList::GetTrackAt(int index) const
|
||||
{
|
||||
if (index < 0 || index >= track_cache_.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return track_cache_.at(index);
|
||||
}
|
||||
|
||||
const rational &TrackList::TrackLength() const
|
||||
const rational &TrackList::GetTotalLength() const
|
||||
{
|
||||
return total_length_;
|
||||
}
|
||||
|
||||
int TrackList::TrackCount() const
|
||||
int TrackList::GetTrackCount() const
|
||||
{
|
||||
return track_cache_.size();
|
||||
}
|
||||
@@ -98,7 +81,7 @@ TrackOutput* TrackList::AddTrack()
|
||||
NodeParam::ConnectEdge(track->output(),
|
||||
track_input_->At(track_input_->GetSize() - 1));
|
||||
|
||||
// FIXME: Test code only
|
||||
// Auto-merge with previous track
|
||||
if (track_input_->GetSize() > 1) {
|
||||
TrackOutput* last_track = nullptr;
|
||||
|
||||
@@ -120,8 +103,8 @@ TrackOutput* TrackList::AddTrack()
|
||||
Node* blend = NodeFactory::CreateFromID(QStringLiteral("org.olivevideoeditor.Olive.alphaoverblend"));
|
||||
GetParentGraph()->AddNode(blend);
|
||||
|
||||
NodeParam::ConnectEdge(track->output(), static_cast<NodeInput*>(blend->GetParameterWithID("blend_in")));
|
||||
NodeParam::ConnectEdge(last_track->output(), static_cast<NodeInput*>(blend->GetParameterWithID("base_in")));
|
||||
NodeParam::ConnectEdge(track->output(), static_cast<NodeInput*>(blend->GetInputWithID("blend_in")));
|
||||
NodeParam::ConnectEdge(last_track->output(), static_cast<NodeInput*>(blend->GetInputWithID("base_in")));
|
||||
NodeParam::ConnectEdge(blend->output(), edge->input());
|
||||
break;
|
||||
}
|
||||
@@ -142,7 +125,6 @@ TrackOutput* TrackList::AddTrack()
|
||||
}
|
||||
}
|
||||
}
|
||||
// End test code
|
||||
|
||||
return track;
|
||||
}
|
||||
@@ -173,14 +155,38 @@ void TrackList::TrackConnected(NodeEdgePtr edge)
|
||||
if (connected_node->IsTrack()) {
|
||||
TrackOutput* connected_track = static_cast<TrackOutput*>(connected_node);
|
||||
|
||||
track_cache_.replace(track_index, connected_track);
|
||||
{
|
||||
// Find "real" index
|
||||
TrackOutput* next = nullptr;
|
||||
for (int i=track_index+1; i<track_input_->GetSize(); i++) {
|
||||
Node* that_track = track_input_->At(i)->get_connected_node();
|
||||
|
||||
if (that_track && that_track->IsTrack()) {
|
||||
next = static_cast<TrackOutput*>(that_track);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int track_index;
|
||||
|
||||
if (next) {
|
||||
// Insert track before "next"
|
||||
track_index = track_cache_.indexOf(next);
|
||||
track_cache_.insert(track_index, connected_track);
|
||||
} else {
|
||||
// No "next", this track must come at the end
|
||||
track_index = track_cache_.size();
|
||||
track_cache_.append(connected_track);
|
||||
}
|
||||
|
||||
connected_track->SetIndex(track_index);
|
||||
}
|
||||
|
||||
connect(connected_track, &TrackOutput::BlockAdded, this, &TrackList::TrackAddedBlock);
|
||||
connect(connected_track, &TrackOutput::BlockRemoved, this, &TrackList::TrackRemovedBlock);
|
||||
connect(connected_track, &TrackOutput::TrackLengthChanged, this, &TrackList::UpdateTotalLength);
|
||||
connect(connected_track, &TrackOutput::TrackHeightChanged, this, &TrackList::TrackHeightChangedSlot);
|
||||
|
||||
connected_track->SetIndex(track_index);
|
||||
connected_track->set_track_type(type_);
|
||||
|
||||
emit TrackListChanged();
|
||||
@@ -201,10 +207,17 @@ void TrackList::TrackDisconnected(NodeEdgePtr edge)
|
||||
Q_ASSERT(track_index >= 0);
|
||||
|
||||
Node* connected_node = edge->output()->parentNode();
|
||||
TrackOutput* track = connected_node->IsTrack() ? static_cast<TrackOutput*>(connected_node) : nullptr;
|
||||
|
||||
if (track) {
|
||||
track_cache_.replace(track_index, nullptr);
|
||||
if (connected_node->IsTrack()) {
|
||||
TrackOutput* track = static_cast<TrackOutput*>(connected_node);
|
||||
|
||||
int index_of_track = track_cache_.indexOf(track);
|
||||
track_cache_.removeAt(index_of_track);
|
||||
|
||||
// Update indices for all subsequent tracks
|
||||
for (int i=index_of_track; i<track_cache_.size(); i++) {
|
||||
track_cache_.at(i)->SetIndex(i);
|
||||
}
|
||||
|
||||
// Traverse through Tracks uncaching and disconnecting them
|
||||
emit TrackRemoved(track);
|
||||
|
||||
@@ -38,17 +38,17 @@ public:
|
||||
|
||||
const Timeline::TrackType& type() const;
|
||||
|
||||
const QVector<TrackOutput*>& Tracks() const;
|
||||
const QVector<TrackOutput*>& GetTracks() const;
|
||||
|
||||
TrackOutput* TrackAt(int index) const;
|
||||
TrackOutput* GetTrackAt(int index) const;
|
||||
|
||||
TrackOutput *AddTrack();
|
||||
|
||||
void RemoveTrack();
|
||||
|
||||
const rational& TrackLength() const;
|
||||
const rational& GetTotalLength() const;
|
||||
|
||||
int TrackCount() const;
|
||||
int GetTrackCount() const;
|
||||
|
||||
NodeGraph* GetParentGraph() const;
|
||||
|
||||
@@ -100,11 +100,6 @@ private slots:
|
||||
*/
|
||||
void TrackRemovedBlock(Block* block);
|
||||
|
||||
/**
|
||||
* @brief Slot for when the count of tracks in the track input changes
|
||||
*/
|
||||
void TrackListSizeChanged(int size);
|
||||
|
||||
/**
|
||||
* @brief Slot for when any of the track's length changes so we can update the length of the tracklist
|
||||
*/
|
||||
|
||||
@@ -92,26 +92,24 @@ NodeInput *ViewerOutput::samples_input() const
|
||||
return samples_input_;
|
||||
}
|
||||
|
||||
void ViewerOutput::InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from)
|
||||
void ViewerOutput::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source)
|
||||
{
|
||||
Node::InvalidateCache(start_range, end_range, from);
|
||||
|
||||
if (from == texture_input()) {
|
||||
emit VideoChangedBetween(TimeRange(start_range, end_range));
|
||||
emit VideoChangedBetween(range, source);
|
||||
} else if (from == samples_input()) {
|
||||
emit AudioChangedBetween(TimeRange(start_range, end_range));
|
||||
emit AudioChangedBetween(range, source);
|
||||
}
|
||||
|
||||
SendInvalidateCache(start_range, end_range);
|
||||
Node::InvalidateCache(range, from, source);
|
||||
}
|
||||
|
||||
void ViewerOutput::InvalidateVisible(NodeInput* from)
|
||||
void ViewerOutput::InvalidateVisible(NodeInput* from, NodeInput *source)
|
||||
{
|
||||
if (from == texture_input()) {
|
||||
emit VisibleInvalidated();
|
||||
emit VisibleInvalidated(source);
|
||||
}
|
||||
|
||||
Node::InvalidateVisible(from);
|
||||
Node::InvalidateVisible(from, source);
|
||||
}
|
||||
|
||||
const VideoParams &ViewerOutput::video_params() const
|
||||
@@ -164,26 +162,13 @@ const QUuid &ViewerOutput::uuid() const
|
||||
return uuid_;
|
||||
}
|
||||
|
||||
void ViewerOutput::DependentEdgeChanged(NodeInput *from)
|
||||
{
|
||||
if (from == texture_input_) {
|
||||
emit VideoGraphChanged();
|
||||
} else if (from == samples_input_) {
|
||||
emit AudioGraphChanged();
|
||||
}
|
||||
|
||||
Node::DependentEdgeChanged(from);
|
||||
}
|
||||
|
||||
void ViewerOutput::UpdateTrackCache()
|
||||
{
|
||||
track_cache_.clear();
|
||||
|
||||
foreach (TrackList* list, track_lists_) {
|
||||
foreach (TrackOutput* track, list->Tracks()) {
|
||||
if (track) {
|
||||
track_cache_.append(track);
|
||||
}
|
||||
foreach (TrackOutput* track, list->GetTracks()) {
|
||||
track_cache_.append(track);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -206,7 +191,7 @@ void ViewerOutput::UpdateLength(const rational &length)
|
||||
rational new_length = 0;
|
||||
|
||||
foreach (TrackList* list, track_lists_) {
|
||||
new_length = qMax(new_length, list->TrackLength());
|
||||
new_length = qMax(new_length, list->GetTotalLength());
|
||||
}
|
||||
|
||||
if (new_length != timeline_length_) {
|
||||
|
||||
@@ -55,8 +55,8 @@ public:
|
||||
NodeInput* texture_input() const;
|
||||
NodeInput* samples_input() const;
|
||||
|
||||
virtual void InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from = nullptr) override;
|
||||
virtual void InvalidateVisible(NodeInput *from) override;
|
||||
virtual void InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput* source) override;
|
||||
virtual void InvalidateVisible(NodeInput *from, NodeInput* source) override;
|
||||
|
||||
const VideoParams& video_params() const;
|
||||
const AudioParams& audio_params() const;
|
||||
@@ -79,21 +79,14 @@ public:
|
||||
const QString& media_name() const;
|
||||
void set_media_name(const QString& name);
|
||||
|
||||
protected:
|
||||
virtual void DependentEdgeChanged(NodeInput* from) override;
|
||||
|
||||
signals:
|
||||
void TimebaseChanged(const rational&);
|
||||
|
||||
void VideoChangedBetween(const TimeRange& range);
|
||||
void VideoChangedBetween(const TimeRange& range, NodeInput* source);
|
||||
|
||||
void AudioChangedBetween(const TimeRange& range);
|
||||
void AudioChangedBetween(const TimeRange& range, NodeInput* source);
|
||||
|
||||
void VisibleInvalidated();
|
||||
|
||||
void VideoGraphChanged();
|
||||
|
||||
void AudioGraphChanged();
|
||||
void VisibleInvalidated(NodeInput* source);
|
||||
|
||||
void LengthChanged(const rational& length);
|
||||
|
||||
|
||||
@@ -32,9 +32,16 @@ CurvePanel::CurvePanel(QWidget *parent) :
|
||||
Retranslate();
|
||||
}
|
||||
|
||||
NodeInput *CurvePanel::GetInput() const
|
||||
{
|
||||
return static_cast<CurveWidget*>(GetTimeBasedWidget())->GetInput();
|
||||
}
|
||||
|
||||
void CurvePanel::SetInput(NodeInput *input)
|
||||
{
|
||||
static_cast<CurveWidget*>(GetTimeBasedWidget())->SetInput(input);
|
||||
|
||||
Retranslate();
|
||||
}
|
||||
|
||||
void CurvePanel::SetTimeTarget(Node *target)
|
||||
@@ -59,6 +66,13 @@ void CurvePanel::Retranslate()
|
||||
TimeBasedPanel::Retranslate();
|
||||
|
||||
SetTitle(tr("Curve Editor"));
|
||||
|
||||
NodeInput* connected_input = static_cast<CurveWidget*>(GetTimeBasedWidget())->GetInput();
|
||||
if (connected_input) {
|
||||
SetSubtitle(connected_input->name());
|
||||
} else {
|
||||
SetSubtitle(QString());
|
||||
}
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -32,6 +32,8 @@ class CurvePanel : public TimeBasedPanel
|
||||
public:
|
||||
CurvePanel(QWidget* parent);
|
||||
|
||||
NodeInput* GetInput() const;
|
||||
|
||||
public slots:
|
||||
void SetInput(NodeInput* input);
|
||||
|
||||
|
||||
@@ -144,7 +144,10 @@ void PanelManager::FocusChanged(QWidget *old, QWidget *now)
|
||||
void PanelManager::SetPanelsLocked(bool locked)
|
||||
{
|
||||
foreach (PanelWidget* panel, focus_history_) {
|
||||
panel->SetMovementLocked(locked);
|
||||
// Only affect panels actually in our layout
|
||||
if (!panel->isFloating()) {
|
||||
panel->SetMovementLocked(locked);
|
||||
}
|
||||
}
|
||||
|
||||
locked_ = locked;
|
||||
|
||||
@@ -177,6 +177,10 @@ T *PanelManager::CreatePanel(QWidget *parent)
|
||||
|
||||
panel->SetMovementLocked(locked_);
|
||||
|
||||
// Sane default for panel geometry
|
||||
panel->resize(parent->size() / 3);
|
||||
panel->move(panel->mapFromGlobal(parent->mapToGlobal(parent->pos())));
|
||||
|
||||
// Connect destroy signal so we can remove it from focus history
|
||||
connect(panel, &PanelWidget::destroyed, this, &PanelManager::PanelDestroyed);
|
||||
|
||||
|
||||
@@ -20,15 +20,19 @@
|
||||
|
||||
#include "param.h"
|
||||
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
ParamPanel::ParamPanel(QWidget* parent) :
|
||||
TimeBasedPanel(QStringLiteral("ParamPanel"), parent)
|
||||
{
|
||||
NodeParamView* view = new NodeParamView();
|
||||
connect(view, &NodeParamView::SelectedInputChanged, this, &ParamPanel::SelectedInputChanged);
|
||||
connect(view, &NodeParamView::InputDoubleClicked, this, &ParamPanel::CreateCurvePanel);
|
||||
connect(view, &NodeParamView::TimeTargetChanged, this, &ParamPanel::TimeTargetChanged);
|
||||
connect(view, &NodeParamView::RequestSelectNode, this, &ParamPanel::RequestSelectNode);
|
||||
connect(view, &NodeParamView::OpenedNode, this, &ParamPanel::OpeningNode);
|
||||
connect(view, &NodeParamView::ClosedNode, this, &ParamPanel::ClosingNode);
|
||||
SetTimeBasedWidget(view);
|
||||
|
||||
Retranslate();
|
||||
@@ -41,6 +45,20 @@ void ParamPanel::SetNodes(QList<Node *> nodes)
|
||||
Retranslate();
|
||||
}
|
||||
|
||||
void ParamPanel::SetTimestamp(const int64_t ×tamp)
|
||||
{
|
||||
TimeBasedPanel::SetTimestamp(timestamp);
|
||||
|
||||
// Ensure all CurvePanels are updated with this time too
|
||||
QHash<NodeInput*, CurvePanel*>::const_iterator i;
|
||||
|
||||
for (i=open_curve_panels_.begin(); i!=open_curve_panels_.end(); i++) {
|
||||
if (i.value() && i.value() != sender()) {
|
||||
i.value()->SetTimestamp(timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParamPanel::Retranslate()
|
||||
{
|
||||
SetTitle(tr("Parameter Editor"));
|
||||
@@ -56,4 +74,67 @@ void ParamPanel::Retranslate()
|
||||
}
|
||||
}
|
||||
|
||||
void ParamPanel::CreateCurvePanel(NodeInput *input)
|
||||
{
|
||||
if (!input->is_keyframable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CurvePanel* panel = open_curve_panels_.value(input);
|
||||
|
||||
if (panel) {
|
||||
panel->raise();
|
||||
return;
|
||||
}
|
||||
|
||||
NodeParamView* view = static_cast<NodeParamView*>(GetTimeBasedWidget());
|
||||
|
||||
panel = Core::instance()->main_window()->AppendCurvePanel();
|
||||
|
||||
panel->SetInput(input);
|
||||
panel->SetTimebase(view->timebase());
|
||||
panel->SetTimestamp(view->GetTimestamp());
|
||||
|
||||
connect(view, &NodeParamView::TimebaseChanged, panel, &CurvePanel::SetTimebase);
|
||||
connect(view, &NodeParamView::TimeChanged, panel, &CurvePanel::SetTimestamp);
|
||||
connect(panel, &CurvePanel::TimeChanged, view, &NodeParamView::SetTimestamp);
|
||||
connect(panel, &CurvePanel::TimeChanged, view, &NodeParamView::TimeChanged);
|
||||
connect(panel, &CurvePanel::CloseRequested, this, &ParamPanel::ClosingCurvePanel);
|
||||
|
||||
open_curve_panels_.insert(input, panel);
|
||||
}
|
||||
|
||||
void ParamPanel::OpeningNode(Node *n)
|
||||
{
|
||||
QList<NodeInput*> inputs = n->GetInputsIncludingArrays();
|
||||
|
||||
foreach (NodeInput* i, inputs) {
|
||||
if (open_curve_panels_.contains(i)) {
|
||||
// We had a CurvePanel open for this input that was closed in ClosingNode(), re-open it
|
||||
CreateCurvePanel(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParamPanel::ClosingNode(Node *n)
|
||||
{
|
||||
QList<NodeInput*> inputs = n->GetInputsIncludingArrays();
|
||||
|
||||
foreach (NodeInput* i, inputs) {
|
||||
CurvePanel* panel = open_curve_panels_.value(i);
|
||||
|
||||
// Close the panel (this also destroys it), but keep a reference in the hash
|
||||
if (panel) {
|
||||
panel->close();
|
||||
open_curve_panels_.insert(i, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParamPanel::ClosingCurvePanel()
|
||||
{
|
||||
CurvePanel* panel = static_cast<CurvePanel*>(sender());
|
||||
open_curve_panels_.remove(panel->GetInput());
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
+15
-2
@@ -21,6 +21,7 @@
|
||||
#ifndef PARAM_H
|
||||
#define PARAM_H
|
||||
|
||||
#include "panel/curve/curve.h"
|
||||
#include "panel/timebased/timebased.h"
|
||||
#include "widget/nodeparamview/nodeparamview.h"
|
||||
|
||||
@@ -35,9 +36,9 @@ public:
|
||||
public slots:
|
||||
void SetNodes(QList<Node*> nodes);
|
||||
|
||||
signals:
|
||||
void SelectedInputChanged(NodeInput* input);
|
||||
virtual void SetTimestamp(const int64_t& timestamp) override;
|
||||
|
||||
signals:
|
||||
void TimeTargetChanged(Node* node);
|
||||
|
||||
void RequestSelectNode(const QList<Node*>& target);
|
||||
@@ -45,6 +46,18 @@ signals:
|
||||
protected:
|
||||
virtual void Retranslate() override;
|
||||
|
||||
private slots:
|
||||
void CreateCurvePanel(NodeInput* input);
|
||||
|
||||
void OpeningNode(Node* n);
|
||||
|
||||
void ClosingNode(Node* n);
|
||||
|
||||
void ClosingCurvePanel();
|
||||
|
||||
private:
|
||||
QHash<NodeInput*, CurvePanel*> open_curve_panels_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -84,19 +84,10 @@ QString ScopePanel::TypeToName(ScopePanel::Type t)
|
||||
return QString();
|
||||
}
|
||||
|
||||
void ScopePanel::SetDisplayReferredTexture(OpenGLTexture *texture)
|
||||
{
|
||||
Q_UNUSED(texture)
|
||||
}
|
||||
|
||||
void ScopePanel::SetReferenceBuffer(Frame *frame)
|
||||
{
|
||||
histogram_->SetBuffer(frame);
|
||||
}
|
||||
|
||||
void ScopePanel::SetReferenceTexture(OpenGLTexture *texture)
|
||||
{
|
||||
waveform_view_->SetTexture(texture);
|
||||
waveform_view_->SetBuffer(frame);
|
||||
}
|
||||
|
||||
void ScopePanel::SetColorManager(ColorManager *manager)
|
||||
|
||||
@@ -50,12 +50,8 @@ public:
|
||||
static QString TypeToName(Type t);
|
||||
|
||||
public slots:
|
||||
void SetDisplayReferredTexture(OpenGLTexture* texture);
|
||||
|
||||
void SetReferenceBuffer(Frame* frame);
|
||||
|
||||
void SetReferenceTexture(OpenGLTexture* texture);
|
||||
|
||||
void SetColorManager(ColorManager* manager);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -68,7 +68,7 @@ void TimeBasedPanel::SetTimebase(const rational &timebase)
|
||||
widget_->SetTimebase(timebase);
|
||||
}
|
||||
|
||||
void TimeBasedPanel::SetTime(const int64_t ×tamp)
|
||||
void TimeBasedPanel::SetTimestamp(const int64_t ×tamp)
|
||||
{
|
||||
widget_->SetTimestamp(timestamp);
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ public:
|
||||
public slots:
|
||||
void SetTimebase(const rational& timebase);
|
||||
|
||||
void SetTime(const int64_t& timestamp);
|
||||
virtual void SetTimestamp(const int64_t& timestamp);
|
||||
|
||||
signals:
|
||||
void TimeChanged(const int64_t& time);
|
||||
|
||||
@@ -25,8 +25,7 @@
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
ViewerPanelBase::ViewerPanelBase(const QString& object_name, QWidget *parent) :
|
||||
TimeBasedPanel(object_name, parent),
|
||||
scope_panel_count_(0)
|
||||
TimeBasedPanel(object_name, parent)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -98,36 +97,13 @@ void ViewerPanelBase::CreateScopePanel(ScopePanel::Type type)
|
||||
|
||||
p->SetType(type);
|
||||
|
||||
// We treat our scope panels as kind of children, and destroy them if we're ever destroyed
|
||||
connect(this, &ViewerPanelBase::destroyed, p, &ScopePanel::deleteLater);
|
||||
|
||||
// If the scope closes, reduce the count (we do this because if no scopes are open, we can optimize the viewer slightly)
|
||||
connect(p, &ScopePanel::CloseRequested, this, &ViewerPanelBase::ScopePanelClosed);
|
||||
|
||||
// Connect viewer widget texture drawing to scope panel
|
||||
connect(vw, &ViewerWidget::DrewManagedTexture, p, &ScopePanel::SetDisplayReferredTexture);
|
||||
connect(vw, &ViewerWidget::LoadedBuffer, p, &ScopePanel::SetReferenceBuffer);
|
||||
connect(vw, &ViewerWidget::LoadedTexture, p, &ScopePanel::SetReferenceTexture);
|
||||
connect(vw, &ViewerWidget::ColorManagerChanged, p, &ScopePanel::SetColorManager);
|
||||
|
||||
p->SetColorManager(vw->color_manager());
|
||||
|
||||
if (!scope_panel_count_) {
|
||||
vw->SetEmitDrewManagedTextureEnabled(true);
|
||||
}
|
||||
|
||||
scope_panel_count_++;
|
||||
|
||||
vw->ForceUpdate();
|
||||
}
|
||||
|
||||
void ViewerPanelBase::ScopePanelClosed()
|
||||
{
|
||||
scope_panel_count_--;
|
||||
|
||||
if (!scope_panel_count_) {
|
||||
static_cast<ViewerWidget*>(GetTimeBasedWidget())->SetEmitDrewManagedTextureEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -59,12 +59,6 @@ public:
|
||||
protected:
|
||||
void CreateScopePanel(ScopePanel::Type type);
|
||||
|
||||
private:
|
||||
int scope_panel_count_;
|
||||
|
||||
private slots:
|
||||
void ScopePanelClosed();
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -39,7 +39,7 @@ bool AudioBackend::InitInternal()
|
||||
// Initiate one thread per CPU core
|
||||
for (int i=0;i<threads().size();i++) {
|
||||
// Create one processor object for each thread
|
||||
AudioWorker* processor = new AudioWorker(©_map_);
|
||||
AudioWorker* processor = new AudioWorker(&node_copy_map_);
|
||||
processor->SetParameters(params());
|
||||
processors_.append(processor);
|
||||
}
|
||||
@@ -51,18 +51,6 @@ void AudioBackend::CloseInternal()
|
||||
{
|
||||
}
|
||||
|
||||
bool AudioBackend::CompileInternal()
|
||||
{
|
||||
// This backend doesn't compile anything yet
|
||||
return AudioRenderBackend::CompileInternal();
|
||||
}
|
||||
|
||||
void AudioBackend::DecompileInternal()
|
||||
{
|
||||
// This backend doesn't compile anything yet
|
||||
AudioRenderBackend::DecompileInternal();
|
||||
}
|
||||
|
||||
void AudioBackend::ConnectWorkerToThis(RenderWorker *worker)
|
||||
{
|
||||
AudioRenderBackend::ConnectWorkerToThis(worker);
|
||||
|
||||
@@ -40,10 +40,6 @@ protected:
|
||||
|
||||
virtual void CloseInternal() override;
|
||||
|
||||
virtual bool CompileInternal() override;
|
||||
|
||||
virtual void DecompileInternal() override;
|
||||
|
||||
virtual void ConnectWorkerToThis(RenderWorker* worker) override;
|
||||
|
||||
private slots:
|
||||
|
||||
@@ -58,31 +58,15 @@ void AudioRenderBackend::SetParameters(const AudioRenderingParams ¶ms)
|
||||
void AudioRenderBackend::ConnectViewer(ViewerOutput *node)
|
||||
{
|
||||
connect(node, &ViewerOutput::AudioChangedBetween, this, &AudioRenderBackend::InvalidateCache);
|
||||
connect(node, &ViewerOutput::AudioGraphChanged, this, &AudioRenderBackend::QueueRecompile);
|
||||
connect(node, &ViewerOutput::LengthChanged, this, &AudioRenderBackend::TruncateCache);
|
||||
}
|
||||
|
||||
void AudioRenderBackend::DisconnectViewer(ViewerOutput *node)
|
||||
{
|
||||
disconnect(node, &ViewerOutput::AudioChangedBetween, this, &AudioRenderBackend::InvalidateCache);
|
||||
disconnect(node, &ViewerOutput::AudioGraphChanged, this, &AudioRenderBackend::QueueRecompile);
|
||||
disconnect(node, &ViewerOutput::LengthChanged, this, &AudioRenderBackend::TruncateCache);
|
||||
}
|
||||
|
||||
bool AudioRenderBackend::CompileInternal()
|
||||
{
|
||||
for (int i=0;i<copied_graph_.nodes().size();i++) {
|
||||
copy_map_.insert(copied_graph_.nodes().at(i), source_node_list_.at(i));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AudioRenderBackend::DecompileInternal()
|
||||
{
|
||||
copy_map_.clear();
|
||||
}
|
||||
|
||||
bool AudioRenderBackend::GenerateCacheIDInternal(QCryptographicHash &hash)
|
||||
{
|
||||
if (!params_.is_valid()) {
|
||||
@@ -187,7 +171,7 @@ void AudioRenderBackend::ConformUnavailable(StreamPtr stream, TimeRange range, r
|
||||
} else if (audio_stream->has_conformed_version(params)) {
|
||||
|
||||
// Index JUST finished, requeue this time
|
||||
InvalidateCache(range);
|
||||
InvalidateCache(range, nullptr);
|
||||
|
||||
} else {
|
||||
|
||||
@@ -215,7 +199,7 @@ void AudioRenderBackend::ConformUpdated(Stream *stream, AudioRenderingParams par
|
||||
|
||||
// Send invalidate cache signal
|
||||
ic_from_conform_ = true;
|
||||
InvalidateCache(copy.affected_range);
|
||||
InvalidateCache(copy.affected_range, nullptr);
|
||||
ic_from_conform_ = false;
|
||||
|
||||
}
|
||||
|
||||
@@ -54,10 +54,6 @@ protected:
|
||||
|
||||
virtual void DisconnectViewer(ViewerOutput* node) override;
|
||||
|
||||
virtual bool CompileInternal() override;
|
||||
|
||||
virtual void DecompileInternal() override;
|
||||
|
||||
/**
|
||||
* @brief Internal function for generating the cache ID
|
||||
*/
|
||||
@@ -73,8 +69,6 @@ protected:
|
||||
|
||||
virtual void InvalidateCacheInternal(const rational &start_range, const rational &end_range) override;
|
||||
|
||||
QHash<Node*, Node*> copy_map_;
|
||||
|
||||
private:
|
||||
struct ConformWaitInfo {
|
||||
StreamPtr stream;
|
||||
|
||||
@@ -100,7 +100,7 @@ NodeValueTable AudioRenderWorker::RenderBlock(const TrackOutput *track, const Ti
|
||||
|
||||
{
|
||||
// Save waveform to file
|
||||
Block* src_block = static_cast<Block*>(copy_map_->value(b));
|
||||
Block* src_block = static_cast<Block*>(copy_map_->key(b));
|
||||
QDir local_appdata_dir(Config::Current()["DiskCachePath"].toString());
|
||||
QDir waveform_loc = local_appdata_dir.filePath(QStringLiteral("waveform"));
|
||||
waveform_loc.mkpath(".");
|
||||
|
||||
@@ -291,14 +291,14 @@ void Exporter::EncoderOpenedSuccessfully()
|
||||
video_backend_->SetOperatingMode(VideoRenderWorker::kHashOnly);
|
||||
connect(video_backend_, &VideoRenderBackend::QueueComplete, this, &Exporter::VideoHashesComplete);
|
||||
|
||||
video_backend_->InvalidateCache(export_range_);
|
||||
video_backend_->InvalidateCache(export_range_, nullptr);
|
||||
}
|
||||
|
||||
if (!audio_done_) {
|
||||
// We set the audio backend to render the full sequence to the disk
|
||||
connect(audio_backend_, &AudioRenderBackend::AudioComplete, this, &Exporter::AudioRendered);
|
||||
|
||||
audio_backend_->InvalidateCache(export_range_);
|
||||
audio_backend_->InvalidateCache(export_range_, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +344,7 @@ void Exporter::VideoHashesComplete()
|
||||
}
|
||||
|
||||
foreach (const TimeRange& range, ranges) {
|
||||
video_backend_->InvalidateCache(range);
|
||||
video_backend_->InvalidateCache(range, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,15 +89,6 @@ void OpenGLBackend::CloseInternal()
|
||||
VideoRenderBackend::CloseInternal();
|
||||
}
|
||||
|
||||
bool OpenGLBackend::CompileInternal()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenGLBackend::DecompileInternal()
|
||||
{
|
||||
}
|
||||
|
||||
void OpenGLBackend::ParamsChangedEvent()
|
||||
{
|
||||
// If we're initiated, we need to recreate the texture. Otherwise this backend isn't active so it doesn't matter.
|
||||
|
||||
@@ -43,10 +43,6 @@ protected:
|
||||
|
||||
virtual void CloseInternal() override;
|
||||
|
||||
virtual bool CompileInternal() override;
|
||||
|
||||
virtual void DecompileInternal() override;
|
||||
|
||||
virtual void ParamsChangedEvent() override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -132,7 +132,7 @@ void OpenGLProxy::FrameToValue(FramePtr frame, StreamPtr stream, NodeValueTable*
|
||||
|
||||
VideoRenderingParams footage_params(frame->width(), frame->height(), frame->format());
|
||||
|
||||
footage_tex_ref = texture_cache_.Get(ctx_, footage_params, frame->data(), frame->linesize_pixels());
|
||||
footage_tex_ref = texture_cache_.Get(ctx_, footage_params, frame);
|
||||
|
||||
if (ocio_method == ColorManager::kOCIOFast) {
|
||||
if (!color_processor->IsEnabled()) {
|
||||
@@ -212,8 +212,8 @@ void OpenGLProxy::RunNodeAccelerated(const Node *node, const TimeRange &range, N
|
||||
if (!shader) {
|
||||
// Since we have shader code, compile it now
|
||||
|
||||
QString frag_code = node->ShaderFragmentCode(input_params);
|
||||
QString vert_code = node->ShaderVertexCode(input_params);
|
||||
QString frag_code = node->ShaderFragmentCode(input_params);
|
||||
|
||||
if (frag_code.isEmpty()) {
|
||||
frag_code = OpenGLShader::CodeDefaultFragment();
|
||||
@@ -258,7 +258,7 @@ void OpenGLProxy::RunNodeAccelerated(const Node *node, const TimeRange &range, N
|
||||
NodeInput* input = static_cast<NodeInput*>(param);
|
||||
|
||||
// Get value from database at this input
|
||||
NodeValue meta_value = node->InputValueFromTable(input, input_params, true);
|
||||
NodeValue meta_value = node->InputValueFromTable(input, input_params, false);
|
||||
const QVariant& value = meta_value.data();
|
||||
|
||||
NodeParam::DataType data_type;
|
||||
|
||||
@@ -37,23 +37,23 @@ const GLfloat blit_vertices[] = {
|
||||
};
|
||||
|
||||
const GLfloat blit_texcoords[] = {
|
||||
0.0, 0.0,
|
||||
1.0, 0.0,
|
||||
1.0, 1.0,
|
||||
0.0f, 0.0f,
|
||||
1.0f, 0.0f,
|
||||
1.0f, 1.0f,
|
||||
|
||||
0.0, 0.0,
|
||||
0.0, 1.0,
|
||||
1.0, 1.0
|
||||
0.0f, 0.0f,
|
||||
0.0f, 1.0f,
|
||||
1.0f, 1.0f
|
||||
};
|
||||
|
||||
const GLfloat flipped_blit_texcoords[] = {
|
||||
0.0, 1.0,
|
||||
1.0, 1.0,
|
||||
1.0, 0.0,
|
||||
0.0f, 1.0f,
|
||||
1.0f, 1.0f,
|
||||
1.0f, 0.0f,
|
||||
|
||||
0.0, 1.0,
|
||||
0.0, 0.0,
|
||||
1.0, 0.0
|
||||
0.0f, 1.0f,
|
||||
0.0f, 0.0f,
|
||||
1.0f, 0.0f
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -65,7 +65,8 @@ const GLfloat flipped_blit_texcoords[] = {
|
||||
*
|
||||
* Currently active QOpenGLFunctions object (use context()->functions() if unsure).
|
||||
*/
|
||||
void OpenGLRenderFunctions::PrepareToDraw(QOpenGLFunctions* f) {
|
||||
void OpenGLRenderFunctions::PrepareToDraw(QOpenGLFunctions* f)
|
||||
{
|
||||
f->glGenerateMipmap(GL_TEXTURE_2D);
|
||||
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
@@ -141,7 +142,6 @@ void OpenGLRenderFunctions::Blit(OpenGLShaderPtr pipeline, bool flipped, QMatrix
|
||||
|
||||
void OpenGLRenderFunctions::Blit(OpenGLShader *pipeline, bool flipped, QMatrix4x4 matrix)
|
||||
{
|
||||
// FIXME: is currentContext() reliable here?
|
||||
QOpenGLFunctions* func = QOpenGLContext::currentContext()->functions();
|
||||
|
||||
PrepareToDraw(func);
|
||||
@@ -153,13 +153,13 @@ void OpenGLRenderFunctions::Blit(OpenGLShader *pipeline, bool flipped, QMatrix4x
|
||||
QOpenGLBuffer m_vbo;
|
||||
m_vbo.create();
|
||||
m_vbo.bind();
|
||||
m_vbo.allocate(blit_vertices, 18 * static_cast<int>(sizeof(GLfloat)));
|
||||
m_vbo.allocate(blit_vertices, 18 * sizeof(GLfloat));
|
||||
m_vbo.release();
|
||||
|
||||
QOpenGLBuffer m_vbo2;
|
||||
m_vbo2.create();
|
||||
m_vbo2.bind();
|
||||
m_vbo2.allocate(flipped ? flipped_blit_texcoords : blit_texcoords, 12 * static_cast<int>(sizeof(GLfloat)));
|
||||
m_vbo2.allocate(flipped ? flipped_blit_texcoords : blit_texcoords, 12 * sizeof(GLfloat));
|
||||
m_vbo2.release();
|
||||
|
||||
pipeline->bind();
|
||||
@@ -167,13 +167,13 @@ void OpenGLRenderFunctions::Blit(OpenGLShader *pipeline, bool flipped, QMatrix4x
|
||||
pipeline->setUniformValue("ove_mvpmat", matrix);
|
||||
pipeline->setUniformValue("ove_maintex", 0);
|
||||
|
||||
GLuint vertex_location = static_cast<GLuint>(pipeline->attributeLocation("a_position"));
|
||||
int vertex_location = pipeline->attributeLocation("a_position");
|
||||
m_vbo.bind();
|
||||
func->glEnableVertexAttribArray(vertex_location);
|
||||
func->glVertexAttribPointer(vertex_location, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
|
||||
m_vbo.release();
|
||||
|
||||
GLuint tex_location = static_cast<GLuint>(pipeline->attributeLocation("a_texcoord"));
|
||||
int tex_location = pipeline->attributeLocation("a_texcoord");
|
||||
m_vbo2.bind();
|
||||
func->glEnableVertexAttribArray(tex_location);
|
||||
func->glVertexAttribPointer(tex_location, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
|
||||
|
||||
@@ -61,8 +61,8 @@ OpenGLShaderPtr OpenGLShader::CreateOCIO(QOpenGLContext* ctx,
|
||||
shaderDesc.setLut3DEdgeLen(OCIO_LUT3D_EDGE_SIZE);
|
||||
|
||||
// Compute LUT
|
||||
GLfloat* ocio_lut_data = new GLfloat[OCIO_NUM_3D_ENTRIES];
|
||||
processor->getGpuLut3D(ocio_lut_data, shaderDesc);
|
||||
std::vector<float> ocio_lut_data(OCIO_NUM_3D_ENTRIES);
|
||||
processor->getGpuLut3D(&ocio_lut_data[0], shaderDesc);
|
||||
|
||||
// Create LUT texture
|
||||
xf->glGenTextures(1, &lut_texture);
|
||||
@@ -81,10 +81,7 @@ OpenGLShaderPtr OpenGLShader::CreateOCIO(QOpenGLContext* ctx,
|
||||
// Allocate storage for texture
|
||||
xf->glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F,
|
||||
OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE,
|
||||
0, GL_RGB, GL_FLOAT, ocio_lut_data);
|
||||
|
||||
// Delete local copy
|
||||
delete [] ocio_lut_data;
|
||||
0, GL_RGB, GL_FLOAT, &ocio_lut_data[0]);
|
||||
|
||||
// Create OCIO shader code
|
||||
QString shader_text;
|
||||
|
||||
@@ -74,6 +74,11 @@ void OpenGLTexture::Create(QOpenGLContext *ctx, int width, int height, const Pix
|
||||
}
|
||||
|
||||
void OpenGLTexture::Create(QOpenGLContext *ctx, FramePtr frame)
|
||||
{
|
||||
Create(ctx, frame.get());
|
||||
}
|
||||
|
||||
void OpenGLTexture::Create(QOpenGLContext *ctx, Frame *frame)
|
||||
{
|
||||
Create(ctx, frame->width(), frame->height(), frame->format(), frame->data(), frame->linesize_pixels());
|
||||
}
|
||||
@@ -120,6 +125,16 @@ const GLuint &OpenGLTexture::texture() const
|
||||
return texture_;
|
||||
}
|
||||
|
||||
void OpenGLTexture::Upload(FramePtr frame)
|
||||
{
|
||||
Upload(frame.get());
|
||||
}
|
||||
|
||||
void OpenGLTexture::Upload(Frame *frame)
|
||||
{
|
||||
Upload(frame->data(), frame->linesize_pixels());
|
||||
}
|
||||
|
||||
void OpenGLTexture::Upload(const void *data, int linesize)
|
||||
{
|
||||
if (!IsCreated()) {
|
||||
|
||||
@@ -44,6 +44,7 @@ public:
|
||||
void Create(QOpenGLContext* ctx, int width, int height, const PixelFormat::Format &format, const void *data, int linesize);
|
||||
void Create(QOpenGLContext* ctx, int width, int height, const PixelFormat::Format &format);
|
||||
void Create(QOpenGLContext* ctx, FramePtr frame);
|
||||
void Create(QOpenGLContext* ctx, Frame* frame);
|
||||
|
||||
bool IsCreated() const;
|
||||
|
||||
@@ -59,6 +60,8 @@ public:
|
||||
|
||||
const GLuint& texture() const;
|
||||
|
||||
void Upload(FramePtr frame);
|
||||
void Upload(Frame* frame);
|
||||
void Upload(const void *data, int linesize);
|
||||
|
||||
public slots:
|
||||
|
||||
@@ -29,6 +29,16 @@ OpenGLTextureCache::~OpenGLTextureCache()
|
||||
}
|
||||
}
|
||||
|
||||
OpenGLTextureCache::ReferencePtr OpenGLTextureCache::Get(QOpenGLContext *ctx, const VideoRenderingParams ¶ms, FramePtr frame)
|
||||
{
|
||||
return Get(ctx, params, frame.get());
|
||||
}
|
||||
|
||||
OpenGLTextureCache::ReferencePtr OpenGLTextureCache::Get(QOpenGLContext *ctx, const VideoRenderingParams ¶ms, Frame *frame)
|
||||
{
|
||||
return Get(ctx, params, frame->data(), frame->linesize_pixels());
|
||||
}
|
||||
|
||||
OpenGLTextureCache::ReferencePtr OpenGLTextureCache::Get(QOpenGLContext* ctx, const VideoRenderingParams ¶ms, const void *data, int linesize)
|
||||
{
|
||||
OpenGLTexturePtr texture = nullptr;
|
||||
|
||||
@@ -57,6 +57,8 @@ public:
|
||||
|
||||
DISABLE_COPY_MOVE(OpenGLTextureCache)
|
||||
|
||||
ReferencePtr Get(QOpenGLContext *ctx, const VideoRenderingParams& params, FramePtr frame);
|
||||
ReferencePtr Get(QOpenGLContext *ctx, const VideoRenderingParams& params, Frame* frame);
|
||||
ReferencePtr Get(QOpenGLContext *ctx, const VideoRenderingParams& params, const void *data, int linesize);
|
||||
ReferencePtr Get(QOpenGLContext *ctx, const VideoRenderingParams& params);
|
||||
|
||||
|
||||
@@ -31,12 +31,9 @@ OLIVE_NAMESPACE_ENTER
|
||||
|
||||
RenderBackend::RenderBackend(QObject *parent) :
|
||||
QObject(parent),
|
||||
compiled_(false),
|
||||
started_(false),
|
||||
viewer_node_(nullptr),
|
||||
copied_viewer_node_(nullptr),
|
||||
recompile_queued_(false),
|
||||
input_update_queued_(false)
|
||||
copied_viewer_node_(nullptr)
|
||||
{
|
||||
// FIXME: Don't create in CLI mode
|
||||
cancel_dialog_ = new RenderCancelDialog(Core::instance()->main_window());
|
||||
@@ -84,7 +81,7 @@ void RenderBackend::Close()
|
||||
|
||||
CancelQueue();
|
||||
|
||||
Decompile();
|
||||
SetViewerNode(nullptr);
|
||||
|
||||
CloseInternal();
|
||||
|
||||
@@ -114,23 +111,30 @@ const QString &RenderBackend::GetError() const
|
||||
|
||||
void RenderBackend::SetViewerNode(ViewerOutput *viewer_node)
|
||||
{
|
||||
if (viewer_node_ != nullptr) {
|
||||
if (viewer_node_) {
|
||||
CancelQueue();
|
||||
|
||||
DisconnectViewer(viewer_node_);
|
||||
|
||||
Decompile();
|
||||
copied_graph_.Clear();
|
||||
copied_viewer_node_ = nullptr;
|
||||
node_copy_map_.clear();
|
||||
}
|
||||
|
||||
viewer_node_ = viewer_node;
|
||||
|
||||
if (viewer_node_ != nullptr) {
|
||||
if (viewer_node_) {
|
||||
ConnectViewer(viewer_node_);
|
||||
|
||||
RegenerateCacheID();
|
||||
}
|
||||
|
||||
InvalidateCache(TimeRange(0, RATIONAL_MAX));
|
||||
copied_viewer_node_ = static_cast<ViewerOutput*>(viewer_node_->copy());
|
||||
copied_graph_.AddNode(copied_viewer_node_);
|
||||
node_copy_map_.insert(viewer_node_, copied_viewer_node_);
|
||||
|
||||
InvalidateCache(TimeRange(0, RATIONAL_MAX),
|
||||
static_cast<NodeInput*>(viewer_node_->GetInputWithID(GetDependentInput()->id())));
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderBackend::IsInitiated()
|
||||
@@ -138,58 +142,6 @@ bool RenderBackend::IsInitiated()
|
||||
return started_;
|
||||
}
|
||||
|
||||
bool RenderBackend::Compile()
|
||||
{
|
||||
if (compiled_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get dependencies of viewer node
|
||||
source_node_list_.append(viewer_node_);
|
||||
source_node_list_.append(viewer_node_->GetDependencies());
|
||||
|
||||
// Copy all dependencies into graph
|
||||
foreach (Node* n, source_node_list_) {
|
||||
Node* copy = n->copy();
|
||||
|
||||
Node::CopyInputs(n, copy, false);
|
||||
|
||||
copied_graph_.AddNode(copy);
|
||||
}
|
||||
|
||||
// We just copied the inputs, so if an input update is queued, it's unnecessary
|
||||
input_update_queued_ = false;
|
||||
|
||||
// We know that the first node will be the viewer node since we appended that first in the copy
|
||||
copied_viewer_node_ = static_cast<ViewerOutput*>(copied_graph_.nodes().first());
|
||||
|
||||
// Copy connections
|
||||
Node::DuplicateConnectionsBetweenLists(source_node_list_, copied_graph_.nodes());
|
||||
|
||||
compiled_ = CompileInternal();
|
||||
|
||||
if (!compiled_) {
|
||||
Decompile();
|
||||
}
|
||||
|
||||
return compiled_;
|
||||
}
|
||||
|
||||
void RenderBackend::Decompile()
|
||||
{
|
||||
if (!compiled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
DecompileInternal();
|
||||
|
||||
copied_graph_.Clear();
|
||||
copied_viewer_node_ = nullptr;
|
||||
source_node_list_.clear();
|
||||
|
||||
compiled_ = false;
|
||||
}
|
||||
|
||||
void RenderBackend::RegenerateCacheID()
|
||||
{
|
||||
QCryptographicHash hash(QCryptographicHash::Sha1);
|
||||
@@ -261,34 +213,19 @@ void RenderBackend::CacheNext()
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Init()
|
||||
|| !ViewerIsConnected()
|
||||
|| !CanRender()) {
|
||||
if (!ViewerIsConnected()
|
||||
|| !CanRender()
|
||||
|| !Init()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((input_update_queued_ || recompile_queued_) && !AllProcessorsAreAvailable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (recompile_queued_) {
|
||||
Decompile();
|
||||
recompile_queued_ = false;
|
||||
}
|
||||
|
||||
if (!compiled_ && !Compile()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (input_update_queued_) {
|
||||
for (int i=0;i<source_node_list_.size();i++) {
|
||||
Node* src = source_node_list_.at(i);
|
||||
Node* dst = copied_graph_.nodes().at(i);
|
||||
|
||||
Node::CopyInputs(src, dst, false);
|
||||
while (!input_update_queued_.isEmpty()) {
|
||||
if (!AllProcessorsAreAvailable()) {
|
||||
// To update the inputs, we need all workers to stop
|
||||
return;
|
||||
}
|
||||
|
||||
input_update_queued_ = false;
|
||||
CopyNodeInputValue(input_update_queued_.takeFirst());
|
||||
}
|
||||
|
||||
Node* node_connected_to_viewer = GetDependentInput()->get_connected_node();
|
||||
@@ -356,12 +293,8 @@ void RenderBackend::CancelQueue()
|
||||
cancel_dialog_->RunIfWorkersAreBusy();
|
||||
}
|
||||
|
||||
void RenderBackend::InvalidateCache(const TimeRange &range)
|
||||
void RenderBackend::InvalidateCache(const TimeRange &range, NodeInput *from)
|
||||
{
|
||||
if (!CanRender()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Adjust range to min/max values
|
||||
rational start_range_adj = qMax(rational(0), range.in());
|
||||
rational end_range_adj = qMin(GetSequenceLength(), range.out());
|
||||
@@ -371,15 +304,18 @@ void RenderBackend::InvalidateCache(const TimeRange &range)
|
||||
<< "and"
|
||||
<< end_range_adj.toDouble();
|
||||
|
||||
// Queue value update
|
||||
QueueValueUpdate();
|
||||
if (from) {
|
||||
// Queue value update
|
||||
qDebug() << " from" << from->parentNode()->id() << "::" << from->id();
|
||||
QueueValueUpdate(from);
|
||||
}
|
||||
|
||||
InvalidateCacheInternal(start_range_adj, end_range_adj);
|
||||
}
|
||||
|
||||
bool RenderBackend::ViewerIsConnected() const
|
||||
{
|
||||
return viewer_node_ != nullptr;
|
||||
return viewer_node_;
|
||||
}
|
||||
|
||||
const QString &RenderBackend::cache_id() const
|
||||
@@ -387,9 +323,23 @@ const QString &RenderBackend::cache_id() const
|
||||
return cache_id_;
|
||||
}
|
||||
|
||||
void RenderBackend::QueueValueUpdate()
|
||||
void RenderBackend::QueueValueUpdate(NodeInput* from)
|
||||
{
|
||||
input_update_queued_ = true;
|
||||
if (!input_update_queued_.isEmpty()) {
|
||||
// Remove any inputs that are dependents of this input since they may have been removed since
|
||||
// it was queued
|
||||
QList<Node*> deps = from->GetDependencies();
|
||||
|
||||
for (int i=0;i<input_update_queued_.size();i++) {
|
||||
if (deps.contains(input_update_queued_.at(i)->parentNode())) {
|
||||
// We don't need to queue this value since this input supersedes it
|
||||
input_update_queued_.removeAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input_update_queued_.append(from);
|
||||
}
|
||||
|
||||
bool RenderBackend::WorkerIsBusy(RenderWorker *worker) const
|
||||
@@ -402,6 +352,87 @@ void RenderBackend::SetWorkerBusyState(RenderWorker *worker, bool busy)
|
||||
processor_busy_state_.replace(processors_.indexOf(worker), busy);
|
||||
}
|
||||
|
||||
void RenderBackend::CopyNodeInputValue(NodeInput *input)
|
||||
{
|
||||
// Find our copy of this parameter
|
||||
Node* our_copy_node = node_copy_map_.value(input->parentNode());
|
||||
NodeInput* our_copy = our_copy_node->GetInputWithID(input->id());
|
||||
|
||||
// Copy the standard/keyframe values between these two inputs
|
||||
NodeInput::CopyValues(input,
|
||||
our_copy,
|
||||
false);
|
||||
|
||||
// Handle connections
|
||||
if (input->IsConnected() || our_copy->IsConnected()) {
|
||||
// If one of the inputs is connected, it's likely this change came from connecting or
|
||||
// disconnecting whatever was connected to it
|
||||
|
||||
{
|
||||
// We start by removing all old dependencies from the map
|
||||
QList<Node*> old_deps = our_copy->GetExclusiveDependencies();
|
||||
|
||||
foreach (Node* i, old_deps) {
|
||||
Node* n = node_copy_map_.take(node_copy_map_.key(i));
|
||||
copied_graph_.TakeNode(n);
|
||||
delete n;
|
||||
}
|
||||
}
|
||||
|
||||
// Then we copy all node dependencies and connections (if there are any)
|
||||
CopyNodeMakeConnection(input, our_copy);
|
||||
}
|
||||
|
||||
// Call on sub-elements too
|
||||
if (input->IsArray()) {
|
||||
foreach (NodeInput* i, static_cast<NodeInputArray*>(input)->sub_params()) {
|
||||
CopyNodeInputValue(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Node* RenderBackend::CopyNodeConnections(Node* src_node)
|
||||
{
|
||||
// Check if this node is already in the map
|
||||
Node* dst_node = node_copy_map_.value(src_node);
|
||||
|
||||
// If not, create it now
|
||||
if (!dst_node) {
|
||||
dst_node = src_node->copy();
|
||||
copied_graph_.AddNode(dst_node);
|
||||
node_copy_map_.insert(src_node, dst_node);
|
||||
}
|
||||
|
||||
// Make sure its values are copied
|
||||
Node::CopyInputs(src_node, dst_node, false);
|
||||
|
||||
// Copy all connections
|
||||
QList<NodeInput*> src_node_inputs = src_node->GetInputsIncludingArrays();
|
||||
QList<NodeInput*> dst_node_inputs = dst_node->GetInputsIncludingArrays();
|
||||
|
||||
for (int i=0;i<src_node_inputs.size();i++) {
|
||||
NodeInput* src_input = src_node_inputs.at(i);
|
||||
|
||||
CopyNodeMakeConnection(src_input, dst_node_inputs.at(i));
|
||||
}
|
||||
|
||||
return dst_node;
|
||||
}
|
||||
|
||||
void RenderBackend::CopyNodeMakeConnection(NodeInput* src_input, NodeInput* dst_input)
|
||||
{
|
||||
//qDebug() << "Copying input" << src_input->id() << "from" << src_input->parentNode()->id();
|
||||
|
||||
if (src_input->IsConnected()) {
|
||||
Node* dst_node = CopyNodeConnections(src_input->get_connected_node());
|
||||
|
||||
NodeOutput* corresponding_output = dst_node->GetOutputWithID(src_input->get_connected_output()->id());
|
||||
|
||||
NodeParam::ConnectEdge(corresponding_output,
|
||||
dst_input);
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderBackend::AllProcessorsAreAvailable() const
|
||||
{
|
||||
foreach (bool busy, processor_busy_state_) {
|
||||
@@ -455,11 +486,6 @@ void RenderBackend::InitWorkers()
|
||||
processor_busy_state_.fill(false);
|
||||
}
|
||||
|
||||
void RenderBackend::QueueRecompile()
|
||||
{
|
||||
recompile_queued_ = true;
|
||||
}
|
||||
|
||||
void RenderBackend::FootageUnavailable(StreamPtr stream, Decoder::RetrieveState state, const TimeRange &range, const rational &stream_time)
|
||||
{
|
||||
if (state == Decoder::kFailedToOpen){
|
||||
@@ -484,7 +510,7 @@ void RenderBackend::FootageUnavailable(StreamPtr stream, Decoder::RetrieveState
|
||||
|| (stream->type() == Stream::kAudio && std::static_pointer_cast<AudioStream>(stream)->index_done())) {
|
||||
|
||||
// Index JUST finished, requeue this time
|
||||
InvalidateCache(range);
|
||||
InvalidateCache(range, nullptr);
|
||||
|
||||
} else {
|
||||
|
||||
@@ -529,7 +555,7 @@ void RenderBackend::IndexUpdated(Stream* stream)
|
||||
}
|
||||
|
||||
if (footage_ready) {
|
||||
InvalidateCache(info.affected_range);
|
||||
InvalidateCache(info.affected_range, nullptr);
|
||||
footage_wait_info_.removeAt(i);
|
||||
i--;
|
||||
}
|
||||
|
||||
@@ -52,11 +52,7 @@ public:
|
||||
void CancelQueue();
|
||||
|
||||
public slots:
|
||||
void InvalidateCache(const TimeRange &range);
|
||||
|
||||
bool Compile();
|
||||
|
||||
void Decompile();
|
||||
void InvalidateCache(const TimeRange &range, NodeInput *from);
|
||||
|
||||
signals:
|
||||
void QueueComplete();
|
||||
@@ -68,10 +64,6 @@ protected:
|
||||
|
||||
virtual void CloseInternal();
|
||||
|
||||
virtual bool CompileInternal() = 0;
|
||||
|
||||
virtual void DecompileInternal() = 0;
|
||||
|
||||
virtual bool CanRender();
|
||||
|
||||
virtual TimeRange PopNextFrameFromQueue();
|
||||
@@ -111,7 +103,7 @@ protected:
|
||||
|
||||
const QString& cache_id() const;
|
||||
|
||||
void QueueValueUpdate();
|
||||
void QueueValueUpdate(NodeInput *from);
|
||||
|
||||
bool AllProcessorsAreAvailable() const;
|
||||
bool WorkerIsBusy(RenderWorker* worker) const;
|
||||
@@ -121,18 +113,17 @@ protected:
|
||||
|
||||
QVector<RenderWorker*> processors_;
|
||||
|
||||
bool compiled_;
|
||||
|
||||
QHash<TimeRange, qint64> render_job_info_;
|
||||
|
||||
QList<Node*> source_node_list_;
|
||||
QHash<Node*, Node*> node_copy_map_;
|
||||
|
||||
NodeGraph copied_graph_;
|
||||
|
||||
protected slots:
|
||||
void QueueRecompile();
|
||||
|
||||
private:
|
||||
void CopyNodeInputValue(NodeInput* input);
|
||||
Node *CopyNodeConnections(Node *src_node);
|
||||
void CopyNodeMakeConnection(NodeInput *src_input, NodeInput *dst_input);
|
||||
|
||||
/**
|
||||
* @brief Internal list of RenderProcessThreads
|
||||
*/
|
||||
@@ -160,8 +151,7 @@ private:
|
||||
|
||||
QString cache_id_;
|
||||
|
||||
bool recompile_queued_;
|
||||
bool input_update_queued_;
|
||||
QList<NodeInput*> input_update_queued_;
|
||||
|
||||
QVector<bool> processor_busy_state_;
|
||||
|
||||
|
||||
@@ -49,14 +49,12 @@ VideoRenderBackend::VideoRenderBackend(QObject *parent) :
|
||||
void VideoRenderBackend::ConnectViewer(ViewerOutput *node)
|
||||
{
|
||||
connect(node, &ViewerOutput::VideoChangedBetween, this, &VideoRenderBackend::InvalidateCache);
|
||||
connect(node, &ViewerOutput::VideoGraphChanged, this, &VideoRenderBackend::QueueRecompile);
|
||||
connect(node, &ViewerOutput::LengthChanged, this, &VideoRenderBackend::TruncateFrameCacheLength);
|
||||
}
|
||||
|
||||
void VideoRenderBackend::DisconnectViewer(ViewerOutput *node)
|
||||
{
|
||||
disconnect(node, &ViewerOutput::VideoChangedBetween, this, &VideoRenderBackend::InvalidateCache);
|
||||
disconnect(node, &ViewerOutput::VideoGraphChanged, this, &VideoRenderBackend::QueueRecompile);
|
||||
disconnect(node, &ViewerOutput::LengthChanged, this, &VideoRenderBackend::TruncateFrameCacheLength);
|
||||
|
||||
frame_cache_.Clear();
|
||||
|
||||
@@ -261,7 +261,10 @@ void VideoRenderWorker::SetFrameGenerationParams(int width, int height, const QM
|
||||
|
||||
bool VideoRenderWorker::InitInternal()
|
||||
{
|
||||
ResizeDownloadBuffer();
|
||||
if (video_params_.is_valid()) {
|
||||
ResizeDownloadBuffer();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#version 150
|
||||
|
||||
uniform mat4 matrix_in;
|
||||
uniform mat4 %1;
|
||||
|
||||
uniform vec2 footage_in_resolution;
|
||||
uniform vec2 %2_resolution;
|
||||
uniform vec2 ove_resolution;
|
||||
|
||||
in vec4 a_position;
|
||||
@@ -27,10 +27,10 @@ void main() {
|
||||
transform *= scale_mat4(vec3(1.0 / ove_resolution, 1.0));
|
||||
|
||||
// Multiply by received matrix
|
||||
transform *= matrix_in;
|
||||
transform *= %1;
|
||||
|
||||
// Scale back out to footage size
|
||||
transform *= scale_mat4(vec3(footage_in_resolution, 1.0));
|
||||
transform *= scale_mat4(vec3(%2_resolution, 1.0));
|
||||
|
||||
gl_Position = transform * a_position;
|
||||
ove_texcoord = a_texcoord;
|
||||
@@ -17,7 +17,6 @@
|
||||
<file>solid.xml</file>
|
||||
<file>stroke.frag</file>
|
||||
<file>stroke.xml</file>
|
||||
<file>videoinput.frag</file>
|
||||
<file>videoinput.vert</file>
|
||||
<file>matrix.vert</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
#version 150
|
||||
|
||||
uniform sampler2D footage_in;
|
||||
|
||||
in vec2 ove_texcoord;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main(void) {
|
||||
fragColor = texture(footage_in, ove_texcoord);
|
||||
}
|
||||
@@ -108,6 +108,11 @@ CurveWidget::~CurveWidget()
|
||||
view_->Clear();
|
||||
}
|
||||
|
||||
NodeInput *CurveWidget::GetInput() const
|
||||
{
|
||||
return input_;
|
||||
}
|
||||
|
||||
void CurveWidget::SetInput(NodeInput *input)
|
||||
{
|
||||
if (bridge_) {
|
||||
|
||||
@@ -41,6 +41,7 @@ public:
|
||||
|
||||
virtual ~CurveWidget() override;
|
||||
|
||||
NodeInput* GetInput() const;
|
||||
void SetInput(NodeInput* input);
|
||||
|
||||
const double& GetVerticalScale();
|
||||
|
||||
@@ -174,7 +174,7 @@ void KeyframeViewBase::mouseMoveEvent(QMouseEvent *event)
|
||||
// frame gets rendered in this time
|
||||
input_parent->blockSignals(false);
|
||||
|
||||
input_parent->parentNode()->InvalidateVisible(input_parent);
|
||||
input_parent->parentNode()->InvalidateVisible(input_parent, input_parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -377,7 +377,7 @@ void KeyframeViewBase::ProcessBezierDrag(QPointF mouse_diff_scaled, bool include
|
||||
|
||||
input_parent->blockSignals(false);
|
||||
|
||||
input_parent->parentNode()->InvalidateVisible(input_parent);
|
||||
input_parent->parentNode()->InvalidateVisible(input_parent, input_parent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -126,6 +126,8 @@ void NodeParamView::SetNodes(QList<Node *> nodes)
|
||||
|
||||
// If we already have item widgets, delete them all now
|
||||
foreach (NodeParamViewItem* item, items_) {
|
||||
emit ClosedNode(item->GetNode());
|
||||
|
||||
delete item;
|
||||
}
|
||||
items_.clear();
|
||||
@@ -149,12 +151,14 @@ void NodeParamView::SetNodes(QList<Node *> nodes)
|
||||
connect(item, &NodeParamViewItem::KeyframeAdded, keyframe_view_, &KeyframeView::AddKeyframe);
|
||||
connect(item, &NodeParamViewItem::KeyframeRemoved, keyframe_view_, &KeyframeView::RemoveKeyframe);
|
||||
connect(item, &NodeParamViewItem::RequestSetTime, this, &NodeParamView::ItemRequestedTimeChanged);
|
||||
connect(item, &NodeParamViewItem::InputClicked, this, &NodeParamView::SelectedInputChanged);
|
||||
connect(item, &NodeParamViewItem::InputDoubleClicked, this, &NodeParamView::InputDoubleClicked);
|
||||
connect(item, &NodeParamViewItem::RequestSelectNode, this, &NodeParamView::RequestSelectNode);
|
||||
|
||||
items_.append(item);
|
||||
|
||||
QTimer::singleShot(1, item, &NodeParamViewItem::SignalAllKeyframes);
|
||||
|
||||
emit OpenedNode(node);
|
||||
}
|
||||
|
||||
ViewerOutput* viewer = nodes_.first()->FindOutputNode<ViewerOutput>();
|
||||
|
||||
@@ -41,12 +41,16 @@ public:
|
||||
const QList<Node*>& nodes();
|
||||
|
||||
signals:
|
||||
void SelectedInputChanged(NodeInput* input);
|
||||
void InputDoubleClicked(NodeInput* input);
|
||||
|
||||
void TimeTargetChanged(Node* target);
|
||||
|
||||
void RequestSelectNode(const QList<Node*>& target);
|
||||
|
||||
void OpenedNode(Node* n);
|
||||
|
||||
void ClosedNode(Node* n);
|
||||
|
||||
protected:
|
||||
virtual void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
|
||||
@@ -42,6 +42,8 @@ NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(NodeInput *input, QWidg
|
||||
connect(connected_to_lbl_, &ClickableLabel::MouseClicked, this, &NodeParamViewConnectedLabel::ConnectionClicked);
|
||||
layout->addWidget(connected_to_lbl_);
|
||||
|
||||
layout->addStretch();
|
||||
|
||||
// Set up "link" font
|
||||
QFont link_font = connected_to_lbl_->font();
|
||||
link_font.setUnderline(true);
|
||||
|
||||
@@ -46,7 +46,6 @@ NodeParamViewItem::NodeParamViewItem(Node *node, QWidget *parent) :
|
||||
QHBoxLayout* title_bar_layout = new QHBoxLayout(title_bar_);
|
||||
|
||||
title_bar_collapse_btn_ = new CollapseButton();
|
||||
connect(title_bar_collapse_btn_, &QPushButton::toggled, this, &NodeParamViewItemBody::setVisible);
|
||||
title_bar_layout->addWidget(title_bar_collapse_btn_);
|
||||
|
||||
title_bar_lbl_ = new QLabel(title_bar_);
|
||||
@@ -66,11 +65,12 @@ NodeParamViewItem::NodeParamViewItem(Node *node, QWidget *parent) :
|
||||
}
|
||||
|
||||
body_ = new NodeParamViewItemBody(inputs);
|
||||
connect(body_, &NodeParamViewItemBody::InputClicked, this, &NodeParamViewItem::InputClicked);
|
||||
connect(body_, &NodeParamViewItemBody::InputDoubleClicked, this, &NodeParamViewItem::InputDoubleClicked);
|
||||
connect(body_, &NodeParamViewItemBody::RequestSelectNode, this, &NodeParamViewItem::RequestSelectNode);
|
||||
connect(body_, &NodeParamViewItemBody::RequestSetTime, this, &NodeParamViewItem::RequestSetTime);
|
||||
connect(body_, &NodeParamViewItemBody::KeyframeAdded, this, &NodeParamViewItem::KeyframeAdded);
|
||||
connect(body_, &NodeParamViewItemBody::KeyframeRemoved, this, &NodeParamViewItem::KeyframeRemoved);
|
||||
connect(title_bar_collapse_btn_, &QPushButton::toggled, body_, &NodeParamViewItemBody::setVisible);
|
||||
main_layout->addWidget(body_);
|
||||
|
||||
Retranslate();
|
||||
@@ -88,6 +88,11 @@ void NodeParamViewItem::SetTime(const rational &time)
|
||||
body_->SetTime(time_);
|
||||
}
|
||||
|
||||
Node *NodeParamViewItem::GetNode() const
|
||||
{
|
||||
return node_;
|
||||
}
|
||||
|
||||
void NodeParamViewItem::SignalAllKeyframes()
|
||||
{
|
||||
body_->SignalAllKeyframes();
|
||||
@@ -142,7 +147,7 @@ NodeParamViewItemBody::NodeParamViewItemBody(const QVector<NodeInput *> &inputs,
|
||||
|
||||
// Add descriptor label
|
||||
ui_objects.main_label = new ClickableLabel();
|
||||
connect(ui_objects.main_label, &ClickableLabel::MouseClicked, this, &NodeParamViewItemBody::LabelClicked);
|
||||
connect(ui_objects.main_label, &ClickableLabel::MouseDoubleClicked, this, &NodeParamViewItemBody::LabelDoubleClicked);
|
||||
|
||||
if (input->IsArray()) {
|
||||
QHBoxLayout* array_label_layout = new QHBoxLayout();
|
||||
@@ -164,7 +169,7 @@ NodeParamViewItemBody::NodeParamViewItemBody(const QVector<NodeInput *> &inputs,
|
||||
connect(sub_body, &NodeParamViewItemBody::KeyframeAdded, this, &NodeParamViewItemBody::KeyframeAdded);
|
||||
connect(sub_body, &NodeParamViewItemBody::KeyframeRemoved, this, &NodeParamViewItemBody::KeyframeRemoved);
|
||||
connect(sub_body, &NodeParamViewItemBody::RequestSetTime, this, &NodeParamViewItemBody::RequestSetTime);
|
||||
connect(sub_body, &NodeParamViewItemBody::InputClicked, this, &NodeParamViewItemBody::InputClicked);
|
||||
connect(sub_body, &NodeParamViewItemBody::InputDoubleClicked, this, &NodeParamViewItemBody::InputDoubleClicked);
|
||||
connect(sub_body, &NodeParamViewItemBody::RequestSelectNode, this, &NodeParamViewItemBody::RequestSelectNode);
|
||||
} else {
|
||||
content_layout->addWidget(ui_objects.main_label, row_count, 0);
|
||||
@@ -330,12 +335,13 @@ void NodeParamViewItemBody::InputAddedKeyframe(NodeKeyframePtr key)
|
||||
InputAddedKeyframeInternal(input, key);
|
||||
}
|
||||
|
||||
void NodeParamViewItemBody::LabelClicked()
|
||||
void NodeParamViewItemBody::LabelDoubleClicked()
|
||||
{
|
||||
QMap<NodeInput*, InputUI>::const_iterator iterator;
|
||||
|
||||
for (iterator=input_ui_map_.begin(); iterator!=input_ui_map_.end(); iterator++) {
|
||||
if (iterator.value().connected_label == sender()) {
|
||||
emit InputClicked(iterator.key());
|
||||
if (iterator.value().main_label == sender()) {
|
||||
emit InputDoubleClicked(iterator.key());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ signals:
|
||||
|
||||
void RequestSetTime(const rational& time);
|
||||
|
||||
void InputClicked(NodeInput* input);
|
||||
void InputDoubleClicked(NodeInput* input);
|
||||
|
||||
void RequestSelectNode(const QList<Node*>& node);
|
||||
|
||||
@@ -93,7 +93,7 @@ private slots:
|
||||
|
||||
void InputAddedKeyframe(NodeKeyframePtr key);
|
||||
|
||||
void LabelClicked();
|
||||
void LabelDoubleClicked();
|
||||
|
||||
void ConnectionClicked();
|
||||
|
||||
@@ -109,6 +109,8 @@ public:
|
||||
|
||||
void SetTime(const rational& time);
|
||||
|
||||
Node* GetNode() const;
|
||||
|
||||
public slots:
|
||||
void SignalAllKeyframes();
|
||||
|
||||
@@ -119,7 +121,7 @@ signals:
|
||||
|
||||
void RequestSetTime(const rational& time);
|
||||
|
||||
void InputClicked(NodeInput* input);
|
||||
void InputDoubleClicked(NodeInput* input);
|
||||
|
||||
void RequestSelectNode(const QList<Node*>& node);
|
||||
|
||||
|
||||
@@ -255,7 +255,7 @@ void NodeParamViewWidgetBridge::ProcessSlider(SliderBase *slider, const QVariant
|
||||
|
||||
input_->blockSignals(false);
|
||||
|
||||
input_->parentNode()->InvalidateVisible(input_);
|
||||
input_->parentNode()->InvalidateVisible(input_, input_);
|
||||
|
||||
} else {
|
||||
if (dragging_) {
|
||||
@@ -513,9 +513,9 @@ rational NodeParamViewWidgetBridge::GetCurrentTimeAsNodeTime() const
|
||||
return GetAdjustedTime(GetTimeTarget(), input_->parentNode(), time_, NodeParam::kInput);
|
||||
}
|
||||
|
||||
void NodeParamViewWidgetBridge::InputValueChanged(const rational &start, const rational &end)
|
||||
void NodeParamViewWidgetBridge::InputValueChanged(const TimeRange &range)
|
||||
{
|
||||
if (!dragging_ && start <= time_ && end >= time_) {
|
||||
if (!dragging_ && range.in() <= time_ && range.out() >= time_) {
|
||||
// We'll need to update the widgets because the values have changed on our current time
|
||||
UpdateWidgetValues();
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ private:
|
||||
private slots:
|
||||
void WidgetCallback();
|
||||
|
||||
void InputValueChanged(const rational& start, const rational& end);
|
||||
void InputValueChanged(const TimeRange& range);
|
||||
|
||||
void PropertyChanged(const QString& key, const QVariant& value);
|
||||
|
||||
|
||||
@@ -28,27 +28,35 @@ OLIVE_NAMESPACE_ENTER
|
||||
|
||||
WaveformScope::WaveformScope(QWidget* parent) :
|
||||
ManagedDisplayWidget(parent),
|
||||
texture_(nullptr)
|
||||
buffer_(nullptr)
|
||||
{
|
||||
EnableDefaultContextMenu();
|
||||
}
|
||||
|
||||
void WaveformScope::SetTexture(OpenGLTexture *texture)
|
||||
void WaveformScope::SetBuffer(Frame *frame)
|
||||
{
|
||||
texture_ = texture;
|
||||
buffer_ = frame;
|
||||
|
||||
update();
|
||||
UploadTextureFromBuffer();
|
||||
}
|
||||
|
||||
void WaveformScope::initializeGL()
|
||||
{
|
||||
ManagedDisplayWidget::initializeGL();
|
||||
|
||||
makeCurrent();
|
||||
pipeline_ = OpenGLShader::Create();
|
||||
pipeline_->create();
|
||||
pipeline_->addShaderFromSourceCode(QOpenGLShader::Vertex, OpenGLShader::CodeDefaultVertex());
|
||||
pipeline_->addShaderFromSourceCode(QOpenGLShader::Fragment, Node::ReadFileAsString(":/shaders/rgbwaveform.frag"));
|
||||
pipeline_->link();
|
||||
doneCurrent();
|
||||
|
||||
connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &WaveformScope::CleanUp, Qt::DirectConnection);
|
||||
|
||||
if (buffer_) {
|
||||
UploadTextureFromBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
void WaveformScope::paintGL()
|
||||
@@ -56,12 +64,12 @@ void WaveformScope::paintGL()
|
||||
context()->functions()->glClearColor(0, 0, 0, 0);
|
||||
context()->functions()->glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (!pipeline_ || !texture_) {
|
||||
if (!pipeline_ || !texture_.IsCreated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pipeline_->bind();
|
||||
pipeline_->setUniformValue("ove_resolution", texture_->width(), texture_->height());
|
||||
pipeline_->setUniformValue("ove_resolution", texture_.width(), texture_.height());
|
||||
pipeline_->setUniformValue("ove_viewport", width(), height());
|
||||
|
||||
// The general size of a pixel
|
||||
@@ -69,17 +77,42 @@ void WaveformScope::paintGL()
|
||||
|
||||
pipeline_->release();
|
||||
|
||||
texture_->Bind();
|
||||
texture_.Bind();
|
||||
|
||||
OpenGLRenderFunctions::Blit(pipeline_);
|
||||
|
||||
texture_->Release();
|
||||
texture_.Release();
|
||||
}
|
||||
|
||||
void WaveformScope::UploadTextureFromBuffer()
|
||||
{
|
||||
makeCurrent();
|
||||
|
||||
if (!texture_.IsCreated()
|
||||
|| texture_.width() != buffer_->width()
|
||||
|| texture_.height() != buffer_->height()
|
||||
|| texture_.format() != buffer_->format()) {
|
||||
texture_.Destroy();
|
||||
texture_.Create(context(), buffer_);
|
||||
} else {
|
||||
texture_.Upload(buffer_);
|
||||
}
|
||||
|
||||
doneCurrent();
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void WaveformScope::CleanUp()
|
||||
{
|
||||
qDebug() << "Cleaned up...";
|
||||
|
||||
makeCurrent();
|
||||
|
||||
pipeline_ = nullptr;
|
||||
texture_ = nullptr;
|
||||
texture_.Destroy();
|
||||
|
||||
doneCurrent();
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
WaveformScope(QWidget* parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void SetTexture(OpenGLTexture* texture);
|
||||
void SetBuffer(Frame* frame);
|
||||
|
||||
protected:
|
||||
virtual void initializeGL() override;
|
||||
@@ -44,9 +44,13 @@ protected:
|
||||
virtual void paintGL() override;
|
||||
|
||||
private:
|
||||
void UploadTextureFromBuffer();
|
||||
|
||||
OpenGLShaderPtr pipeline_;
|
||||
|
||||
OpenGLTexture* texture_;
|
||||
OpenGLTexture texture_;
|
||||
|
||||
Frame* buffer_;
|
||||
|
||||
private slots:
|
||||
void CleanUp();
|
||||
|
||||
@@ -241,7 +241,7 @@ void TimelineWidget::ConnectNodeInternal(ViewerOutput *n)
|
||||
view->ConnectTrackList(track_list);
|
||||
|
||||
// Defer to the track to make all the block UI items necessary
|
||||
foreach (TrackOutput* track, n->track_list(track_type)->Tracks()) {
|
||||
foreach (TrackOutput* track, n->track_list(track_type)->GetTracks()) {
|
||||
AddTrack(track, track_type);
|
||||
}
|
||||
}
|
||||
@@ -881,7 +881,7 @@ void TimelineWidget::InsertGapsAt(const rational &earliest_point, const rational
|
||||
|
||||
TrackOutput *TimelineWidget::GetTrackFromReference(const TrackReference &ref)
|
||||
{
|
||||
return GetConnectedNode()->track_list(ref.type())->TrackAt(ref.index());
|
||||
return GetConnectedNode()->track_list(ref.type())->GetTrackAt(ref.index());
|
||||
}
|
||||
|
||||
int TimelineWidget::GetTrackY(const TrackReference &ref)
|
||||
@@ -1017,9 +1017,7 @@ void TimelineWidget::AddBlock(Block *block, TrackReference track)
|
||||
|
||||
void TimelineWidget::RemoveBlock(Block *block)
|
||||
{
|
||||
delete block_items_[block];
|
||||
|
||||
block_items_.remove(block);
|
||||
delete block_items_.take(block);
|
||||
}
|
||||
|
||||
void TimelineWidget::AddTrack(TrackOutput *track, Timeline::TrackType type)
|
||||
@@ -1027,15 +1025,32 @@ void TimelineWidget::AddTrack(TrackOutput *track, Timeline::TrackType type)
|
||||
foreach (Block* b, track->Blocks()) {
|
||||
AddBlock(b, TrackReference(type, track->Index()));
|
||||
}
|
||||
|
||||
connect(track, &TrackOutput::IndexChanged, this, &TimelineWidget::TrackIndexChanged);
|
||||
}
|
||||
|
||||
void TimelineWidget::RemoveTrack(TrackOutput *track)
|
||||
{
|
||||
disconnect(track, &TrackOutput::IndexChanged, this, &TimelineWidget::TrackIndexChanged);
|
||||
|
||||
foreach (Block* b, track->Blocks()) {
|
||||
RemoveBlock(b);
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::TrackIndexChanged()
|
||||
{
|
||||
TrackOutput* track = static_cast<TrackOutput*>(sender());
|
||||
TrackReference ref(track->track_type(), track->Index());
|
||||
|
||||
foreach (Block* b, track->Blocks()) {
|
||||
TimelineViewBlockItem* item = block_items_.value(b);
|
||||
|
||||
item->SetYCoords(GetTrackY(ref), GetTrackHeight(ref));
|
||||
item->SetTrack(ref);
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::ViewSelectionChanged()
|
||||
{
|
||||
if (rubberband_.isVisible()) {
|
||||
|
||||
@@ -486,6 +486,7 @@ private slots:
|
||||
|
||||
void AddTrack(TrackOutput* track, Timeline::TrackType type);
|
||||
void RemoveTrack(TrackOutput* track);
|
||||
void TrackIndexChanged();
|
||||
|
||||
void ViewSelectionChanged();
|
||||
|
||||
|
||||
@@ -30,9 +30,10 @@
|
||||
#include "core.h"
|
||||
#include "dialog/sequence/sequence.h"
|
||||
#include "node/audio/volume/volume.h"
|
||||
#include "node/distort/transform/transform.h"
|
||||
#include "node/generator/matrix/matrix.h"
|
||||
#include "node/input/media/audio/audio.h"
|
||||
#include "node/input/media/video/video.h"
|
||||
#include "node/math/math/math.h"
|
||||
#include "project/item/sequence/sequence.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
@@ -397,15 +398,17 @@ void TimelineWidget::ImportTool::DropGhosts(bool insert)
|
||||
VideoInput* video_input = new VideoInput();
|
||||
video_input->SetFootage(footage_stream);
|
||||
new NodeAddCommand(dst_graph, video_input, command);
|
||||
new NodeEdgeAddCommand(video_input->output(), clip->texture_input(), command);
|
||||
|
||||
TransformDistort* transform = new TransformDistort();
|
||||
new NodeAddCommand(dst_graph, transform, command);
|
||||
new NodeEdgeAddCommand(transform->output(), video_input->matrix_input(), command);
|
||||
MatrixGenerator* matrix = new MatrixGenerator();
|
||||
new NodeAddCommand(dst_graph, matrix, command);
|
||||
|
||||
//OpacityNode* opacity = new OpacityNode();
|
||||
//NodeParam::ConnectEdge(opacity->texture_output(), clip->texture_input());
|
||||
//NodeParam::ConnectEdge(media->texture_output(), opacity->texture_input());
|
||||
MathNode* multiply = new MathNode();
|
||||
multiply->SetOperation(MathNode::kOpMultiply);
|
||||
new NodeAddCommand(dst_graph, multiply, command);
|
||||
|
||||
new NodeEdgeAddCommand(video_input->output(), multiply->param_a_in(), command);
|
||||
new NodeEdgeAddCommand(matrix->output(), multiply->param_b_in(), command);
|
||||
new NodeEdgeAddCommand(multiply->output(), clip->texture_input(), command);
|
||||
break;
|
||||
}
|
||||
case Stream::kAudio:
|
||||
|
||||
@@ -81,7 +81,7 @@ void TrackView::ConnectTrackList(TrackList *list)
|
||||
list_ = list;
|
||||
|
||||
if (list_ != nullptr) {
|
||||
foreach (TrackOutput* track, list_->Tracks()) {
|
||||
foreach (TrackOutput* track, list_->GetTracks()) {
|
||||
TrackViewItem* item = new TrackViewItem(track);
|
||||
items_.append(item);
|
||||
splitter_->Insert(track->Index(), track->GetTrackHeight(), item);
|
||||
@@ -120,7 +120,7 @@ void TrackView::ScrollbarRangeChanged(int, int max)
|
||||
|
||||
void TrackView::TrackHeightChanged(int index, int height)
|
||||
{
|
||||
list_->TrackAt(index)->SetTrackHeight(height);
|
||||
list_->GetTrackAt(index)->SetTrackHeight(height);
|
||||
}
|
||||
|
||||
void TrackView::InsertTrack(TrackOutput *track)
|
||||
|
||||
@@ -141,7 +141,7 @@ void TrackViewSplitter::Remove(int index)
|
||||
QList<int> sz = sizes();
|
||||
|
||||
if (alignment_ == Qt::AlignBottom) {
|
||||
index = count() - index;
|
||||
index = count() - 1 - index;
|
||||
}
|
||||
|
||||
sz.removeAt(index);
|
||||
|
||||
@@ -274,7 +274,9 @@ void TrackRippleRemoveAreaCommand::redo_internal()
|
||||
|
||||
track_->UnblockInvalidateCache();
|
||||
|
||||
track_->InvalidateCache(in_, insert_ ? out_ : RATIONAL_MAX);
|
||||
track_->InvalidateCache(TimeRange(in_, insert_ ? out_ : RATIONAL_MAX),
|
||||
track_->block_input(),
|
||||
track_->block_input());
|
||||
}
|
||||
|
||||
void TrackRippleRemoveAreaCommand::undo_internal()
|
||||
@@ -328,7 +330,7 @@ void TrackRippleRemoveAreaCommand::undo_internal()
|
||||
|
||||
track_->UnblockInvalidateCache();
|
||||
|
||||
track_->InvalidateCache(in_, insert_ ? out_ : RATIONAL_MAX);
|
||||
track_->InvalidateCache(TimeRange(in_, insert_ ? out_ : RATIONAL_MAX), track_->block_input(), track_->block_input());
|
||||
}
|
||||
|
||||
TrackPlaceBlockCommand::TrackPlaceBlockCommand(TrackList *timeline, int track, Block *block, rational in, QUndoCommand *parent) :
|
||||
@@ -345,13 +347,13 @@ void TrackPlaceBlockCommand::redo_internal()
|
||||
added_track_count_ = 0;
|
||||
|
||||
// Get track (or make it if necessary)
|
||||
while (track_index_ >= timeline_->Tracks().size()) {
|
||||
while (track_index_ >= timeline_->GetTracks().size()) {
|
||||
timeline_->AddTrack();
|
||||
|
||||
added_track_count_++;
|
||||
}
|
||||
|
||||
track_ = timeline_->TrackAt(track_index_);
|
||||
track_ = timeline_->GetTrackAt(track_index_);
|
||||
|
||||
append_ = (in_ >= track_->track_length());
|
||||
|
||||
@@ -608,36 +610,34 @@ void TrackCleanGapsCommand::redo_internal()
|
||||
GapBlock* on_gap = nullptr;
|
||||
QList<GapBlock*> consecutive_gaps;
|
||||
|
||||
TrackOutput* track = track_list_->TrackAt(track_index_);
|
||||
TrackOutput* track = track_list_->GetTrackAt(track_index_);
|
||||
|
||||
foreach (Block* b, track->Blocks()) {
|
||||
if (b) {
|
||||
if (b->type() == Block::kGap) {
|
||||
if (on_gap) {
|
||||
consecutive_gaps.append(static_cast<GapBlock*>(b));
|
||||
} else {
|
||||
on_gap = static_cast<GapBlock*>(b);
|
||||
}
|
||||
} else if (on_gap) {
|
||||
merged_gaps_.append({on_gap, on_gap->length(), consecutive_gaps});
|
||||
|
||||
// Remove each gap and add to the length of the merged
|
||||
// We can block the IC signal because merging gaps won't actually change anything
|
||||
track->BlockInvalidateCache();
|
||||
rational new_gap_length = on_gap->length();
|
||||
foreach (GapBlock* gap, consecutive_gaps) {
|
||||
track->RippleRemoveBlock(gap);
|
||||
static_cast<NodeGraph*>(track->parent())->TakeNode(gap, &memory_manager_);
|
||||
|
||||
new_gap_length += gap->length();
|
||||
}
|
||||
on_gap->set_length_and_media_out(new_gap_length);
|
||||
track->UnblockInvalidateCache();
|
||||
|
||||
// Reset state
|
||||
on_gap = nullptr;
|
||||
consecutive_gaps.clear();
|
||||
if (b->type() == Block::kGap) {
|
||||
if (on_gap) {
|
||||
consecutive_gaps.append(static_cast<GapBlock*>(b));
|
||||
} else {
|
||||
on_gap = static_cast<GapBlock*>(b);
|
||||
}
|
||||
} else if (on_gap) {
|
||||
merged_gaps_.append({on_gap, on_gap->length(), consecutive_gaps});
|
||||
|
||||
// Remove each gap and add to the length of the merged
|
||||
// We can block the IC signal because merging gaps won't actually change anything
|
||||
track->BlockInvalidateCache();
|
||||
rational new_gap_length = on_gap->length();
|
||||
foreach (GapBlock* gap, consecutive_gaps) {
|
||||
track->RippleRemoveBlock(gap);
|
||||
static_cast<NodeGraph*>(track->parent())->TakeNode(gap, &memory_manager_);
|
||||
|
||||
new_gap_length += gap->length();
|
||||
}
|
||||
on_gap->set_length_and_media_out(new_gap_length);
|
||||
track->UnblockInvalidateCache();
|
||||
|
||||
// Reset state
|
||||
on_gap = nullptr;
|
||||
consecutive_gaps.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -655,7 +655,7 @@ void TrackCleanGapsCommand::redo_internal()
|
||||
|
||||
void TrackCleanGapsCommand::undo_internal()
|
||||
{
|
||||
TrackOutput* track = track_list_->TrackAt(track_index_);
|
||||
TrackOutput* track = track_list_->GetTrackAt(track_index_);
|
||||
|
||||
// Restored removed end gaps
|
||||
foreach (GapBlock* gap, removed_end_gaps_) {
|
||||
|
||||
@@ -28,8 +28,6 @@ set(OLIVE_SOURCES
|
||||
widget/timelinewidget/view/timelineviewbase.cpp
|
||||
widget/timelinewidget/view/timelineviewblockitem.h
|
||||
widget/timelinewidget/view/timelineviewblockitem.cpp
|
||||
widget/timelinewidget/view/timelineviewenditem.h
|
||||
widget/timelinewidget/view/timelineviewenditem.cpp
|
||||
widget/timelinewidget/view/timelineviewghostitem.h
|
||||
widget/timelinewidget/view/timelineviewghostitem.cpp
|
||||
PARENT_SCOPE
|
||||
|
||||
@@ -189,11 +189,7 @@ void TimelineView::drawBackground(QPainter *painter, const QRectF &rect)
|
||||
|
||||
int line_y = 0;
|
||||
|
||||
foreach (TrackOutput* track, connected_track_list_->Tracks()) {
|
||||
if (!track) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (TrackOutput* track, connected_track_list_->GetTracks()) {
|
||||
line_y += track->GetTrackHeight();
|
||||
|
||||
// One px gap between tracks
|
||||
@@ -230,6 +226,17 @@ void TimelineView::ToolChangedEvent(Tool::Item tool)
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineView::SceneRectUpdateEvent(QRectF &rect)
|
||||
{
|
||||
if (alignment() & Qt::AlignTop) {
|
||||
rect.setTop(0);
|
||||
rect.setBottom(GetHeightOfAllTracks() + height() / 2);
|
||||
} else if (alignment() & Qt::AlignBottom) {
|
||||
rect.setBottom(0);
|
||||
rect.setTop(GetHeightOfAllTracks() - height() / 2);
|
||||
}
|
||||
}
|
||||
|
||||
Timeline::TrackType TimelineView::ConnectedTrackType()
|
||||
{
|
||||
if (connected_track_list_) {
|
||||
@@ -279,8 +286,25 @@ TimelineViewMouseEvent TimelineView::CreateMouseEvent(const QPoint& pos, Qt::Key
|
||||
return timeline_event;
|
||||
}
|
||||
|
||||
int TimelineView::GetTrackY(int track_index)
|
||||
int TimelineView::GetHeightOfAllTracks() const
|
||||
{
|
||||
if (connected_track_list_) {
|
||||
if (alignment() & Qt::AlignTop) {
|
||||
return GetTrackY(connected_track_list_->GetTrackCount());
|
||||
} else {
|
||||
return GetTrackY(connected_track_list_->GetTrackCount() - 1);
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int TimelineView::GetTrackY(int track_index) const
|
||||
{
|
||||
if (!connected_track_list_ || !connected_track_list_->GetTrackCount()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int y = 0;
|
||||
|
||||
if (alignment() & Qt::AlignBottom) {
|
||||
@@ -301,16 +325,16 @@ int TimelineView::GetTrackY(int track_index)
|
||||
return y;
|
||||
}
|
||||
|
||||
int TimelineView::GetTrackHeight(int track_index)
|
||||
int TimelineView::GetTrackHeight(int track_index) const
|
||||
{
|
||||
if (!connected_track_list_ || track_index >= connected_track_list_->TrackCount()) {
|
||||
if (!connected_track_list_ || track_index >= connected_track_list_->GetTrackCount()) {
|
||||
return TrackOutput::GetDefaultTrackHeight();
|
||||
}
|
||||
|
||||
return connected_track_list_->TrackAt(track_index)->GetTrackHeight();
|
||||
return connected_track_list_->GetTrackAt(track_index)->GetTrackHeight();
|
||||
}
|
||||
|
||||
QPoint TimelineView::GetScrollCoordinates()
|
||||
QPoint TimelineView::GetScrollCoordinates() const
|
||||
{
|
||||
return QPoint(horizontalScrollBar()->value(), verticalScrollBar()->value());
|
||||
}
|
||||
|
||||
@@ -53,10 +53,10 @@ public:
|
||||
|
||||
void DeselectAll();
|
||||
|
||||
int GetTrackY(int track_index);
|
||||
int GetTrackHeight(int track_index);
|
||||
int GetTrackY(int track_index) const;
|
||||
int GetTrackHeight(int track_index) const;
|
||||
|
||||
QPoint GetScrollCoordinates();
|
||||
QPoint GetScrollCoordinates() const;
|
||||
void SetScrollCoordinates(const QPoint& pt);
|
||||
|
||||
void ConnectTrackList(TrackList* list);
|
||||
@@ -91,6 +91,8 @@ protected:
|
||||
|
||||
virtual void ToolChangedEvent(Tool::Item tool) override;
|
||||
|
||||
virtual void SceneRectUpdateEvent(QRectF& rect) override;
|
||||
|
||||
private:
|
||||
Timeline::TrackType ConnectedTrackType();
|
||||
Stream::Type TrackTypeToStreamType(Timeline::TrackType track_type);
|
||||
@@ -100,6 +102,8 @@ private:
|
||||
|
||||
TimelineViewMouseEvent CreateMouseEvent(const QPoint &pos, Qt::KeyboardModifiers modifiers);
|
||||
|
||||
int GetHeightOfAllTracks() const;
|
||||
|
||||
int SceneToTrack(double y);
|
||||
|
||||
void UserSetTime(const int64_t& time);
|
||||
|
||||
@@ -44,10 +44,6 @@ TimelineViewBase::TimelineViewBase(QWidget *parent) :
|
||||
{
|
||||
setScene(&scene_);
|
||||
|
||||
// Create end item
|
||||
end_item_ = new TimelineViewEndItem();
|
||||
scene_.addItem(end_item_);
|
||||
|
||||
// Set default scale
|
||||
SetScale(1.0);
|
||||
|
||||
@@ -223,54 +219,28 @@ qreal TimelineViewBase::GetPlayheadX()
|
||||
|
||||
void TimelineViewBase::SetEndTime(const rational &length)
|
||||
{
|
||||
end_item_->SetEndTime(length);
|
||||
end_time_ = length;
|
||||
|
||||
UpdateSceneRect();
|
||||
}
|
||||
|
||||
void TimelineViewBase::UpdateSceneRect()
|
||||
{
|
||||
QRectF bounding_rect = scene_.itemsBoundingRect();
|
||||
|
||||
if (limit_y_axis_) {
|
||||
// Make a gap of half the viewport height
|
||||
if (alignment() & Qt::AlignBottom) {
|
||||
bounding_rect.setTop(bounding_rect.top() - height()/2);
|
||||
} else {
|
||||
bounding_rect.setBottom(bounding_rect.bottom() + height()/2);
|
||||
}
|
||||
|
||||
// Ensure the scene height is always AT LEAST the height of the view
|
||||
// The scrollbar appears to have a 1px margin on the top and bottom, hence the -2
|
||||
int minimum_height = height() - horizontalScrollBar()->height() - 2;
|
||||
|
||||
if (alignment() & Qt::AlignBottom) {
|
||||
// Ensure the scene left and bottom are always 0
|
||||
bounding_rect.setBottomLeft(QPointF(0, 0));
|
||||
|
||||
if (bounding_rect.top() > minimum_height) {
|
||||
bounding_rect.setTop(-minimum_height);
|
||||
}
|
||||
} else {
|
||||
// Ensure the scene left and top are always 0
|
||||
bounding_rect.setTopLeft(QPointF(0, 0));
|
||||
|
||||
if (bounding_rect.height() < minimum_height) {
|
||||
bounding_rect.setHeight(minimum_height);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We'll still limit the X to 0 since that behavior is desired by all TimelineViewBase derivatives
|
||||
bounding_rect.setLeft(0);
|
||||
}
|
||||
// There's no need for a timeline to ever go below 0 on the X scale
|
||||
bounding_rect.setLeft(0);
|
||||
|
||||
// Ensure the scene is always the full length of the timeline with a gap at the end to work with
|
||||
end_item_->SetEndPadding(width()/2);
|
||||
bounding_rect.setRight(TimeToScene(end_time_) + width() / 2);
|
||||
|
||||
// Any further rect processing from derivatives can be done here
|
||||
SceneRectUpdateEvent(bounding_rect);
|
||||
|
||||
// If the scene is already this rect, do nothing
|
||||
if (scene_.sceneRect() == bounding_rect) {
|
||||
return;
|
||||
if (scene_.sceneRect() != bounding_rect) {
|
||||
scene_.setSceneRect(bounding_rect);
|
||||
}
|
||||
|
||||
scene_.setSceneRect(bounding_rect);
|
||||
}
|
||||
|
||||
void TimelineViewBase::PageScrollToPlayhead()
|
||||
@@ -299,9 +269,10 @@ void TimelineViewBase::ScaleChangedEvent(const double &scale)
|
||||
{
|
||||
TimelineScaledObject::ScaleChangedEvent(scale);
|
||||
|
||||
end_item_->SetScale(scale);
|
||||
// Update scene rect
|
||||
UpdateSceneRect();
|
||||
|
||||
// Force redraw for playhead
|
||||
// Force redraw for playhead if the above function didn't do it
|
||||
viewport()->update();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
#include "core.h"
|
||||
#include "timelineplayhead.h"
|
||||
#include "timelineviewenditem.h"
|
||||
#include "widget/timelinewidget/timelinescaledobject.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
@@ -57,6 +56,8 @@ protected:
|
||||
|
||||
virtual void ScaleChangedEvent(const double& scale) override;
|
||||
|
||||
virtual void SceneRectUpdateEvent(QRectF&){}
|
||||
|
||||
bool HandleZoomFromScroll(QWheelEvent* event);
|
||||
|
||||
bool WheelEventIsAZoomEvent(QWheelEvent* event);
|
||||
@@ -95,14 +96,14 @@ private:
|
||||
bool dragging_hand_;
|
||||
DragMode pre_hand_drag_mode_;
|
||||
|
||||
TimelineViewEndItem* end_item_;
|
||||
|
||||
QGraphicsScene scene_;
|
||||
|
||||
bool limit_y_axis_;
|
||||
|
||||
DragMode default_drag_mode_;
|
||||
|
||||
rational end_time_;
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Slot called whenever the view resizes or the scene contents change to enforce minimum scene sizes
|
||||
|
||||
@@ -134,7 +134,11 @@ void TimelineViewBlockItem::paint(QPainter *painter, const QStyleOptionGraphicsI
|
||||
} else {
|
||||
painter->setPen(Qt::lightGray);
|
||||
}
|
||||
painter->drawText(rect(), static_cast<int>(Qt::AlignLeft | Qt::AlignTop), block_->block_name());
|
||||
|
||||
int text_top = TrackOutput::GetTrackHeightMinimum() / 2 - painter->fontMetrics().height() / 2;
|
||||
QRectF text_rect = rect();
|
||||
text_rect.adjust(0, text_top, 0, 0);
|
||||
painter->drawText(text_rect, Qt::AlignLeft | Qt::AlignTop, block_->block_name());
|
||||
|
||||
// Linked clips are underlined
|
||||
if (block_->HasLinks()) {
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
/***
|
||||
|
||||
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 "timelineviewenditem.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
TimelineViewEndItem::TimelineViewEndItem(QGraphicsItem *parent) :
|
||||
TimelineViewRect(parent),
|
||||
end_padding_(0)
|
||||
{
|
||||
}
|
||||
|
||||
void TimelineViewEndItem::SetEndTime(const rational &time)
|
||||
{
|
||||
end_time_ = time;
|
||||
|
||||
UpdateRect();
|
||||
}
|
||||
|
||||
void TimelineViewEndItem::SetEndPadding(int padding)
|
||||
{
|
||||
end_padding_ = padding;
|
||||
|
||||
UpdateRect();
|
||||
}
|
||||
|
||||
void TimelineViewEndItem::UpdateRect()
|
||||
{
|
||||
// Doesn't need to be more than one pixel
|
||||
setRect(0, 0, 1, 1);
|
||||
|
||||
setPos(TimeToScene(end_time_) + end_padding_, 0);
|
||||
}
|
||||
|
||||
void TimelineViewEndItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
||||
{
|
||||
// Item is invisible, this is a no-op
|
||||
Q_UNUSED(painter)
|
||||
Q_UNUSED(option)
|
||||
Q_UNUSED(widget)
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
@@ -1,53 +0,0 @@
|
||||
/***
|
||||
|
||||
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 TIMELINEVIEWENDITEM_H
|
||||
#define TIMELINEVIEWENDITEM_H
|
||||
|
||||
#include "timelineviewrect.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
/**
|
||||
* @brief An item placed at the end point of the Timeline to ensure the correct scene size
|
||||
*/
|
||||
class TimelineViewEndItem : public TimelineViewRect
|
||||
{
|
||||
public:
|
||||
TimelineViewEndItem(QGraphicsItem* parent = nullptr);
|
||||
|
||||
void SetEndTime(const rational& time);
|
||||
|
||||
void SetEndPadding(int padding);
|
||||
|
||||
virtual void UpdateRect() override;
|
||||
|
||||
protected:
|
||||
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
|
||||
|
||||
private:
|
||||
rational end_time_;
|
||||
|
||||
int end_padding_;
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // TIMELINEVIEWENDITEM_H
|
||||
@@ -24,8 +24,8 @@ set(OLIVE_SOURCES
|
||||
widget/viewer/pixelsamplerwidget.cpp
|
||||
widget/viewer/viewer.h
|
||||
widget/viewer/viewer.cpp
|
||||
widget/viewer/viewerglwidget.h
|
||||
widget/viewer/viewerglwidget.cpp
|
||||
widget/viewer/viewerdisplay.h
|
||||
widget/viewer/viewerdisplay.cpp
|
||||
widget/viewer/viewersafemargininfo.h
|
||||
widget/viewer/viewersizer.h
|
||||
widget/viewer/viewersizer.cpp
|
||||
|
||||
@@ -35,7 +35,7 @@ FootageViewerWidget::FootageViewerWidget(QWidget *parent) :
|
||||
audio_node_ = new AudioInput();
|
||||
viewer_node_ = new ViewerOutput();
|
||||
|
||||
connect(main_gl_widget(), &ViewerGLWidget::DragStarted, this, &FootageViewerWidget::StartFootageDrag);
|
||||
connect(main_gl_widget(), &ViewerDisplayWidget::DragStarted, this, &FootageViewerWidget::StartFootageDrag);
|
||||
|
||||
controls_->SetAudioVideoDragButtonsVisible(true);
|
||||
connect(controls_, &PlaybackControls::VideoPressed, this, &FootageViewerWidget::StartVideoDrag);
|
||||
|
||||
@@ -62,15 +62,13 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
|
||||
sizer_ = new ViewerSizer();
|
||||
stack_->addWidget(sizer_);
|
||||
|
||||
ViewerGLWidget* main_widget = new ViewerGLWidget();
|
||||
connect(main_widget, &ViewerGLWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu);
|
||||
connect(main_widget, &ViewerGLWidget::CursorColor, this, &ViewerWidget::CursorColor);
|
||||
connect(main_widget, &ViewerGLWidget::LoadedBuffer, this, &ViewerWidget::LoadedBuffer);
|
||||
connect(main_widget, &ViewerGLWidget::LoadedTexture, this, &ViewerWidget::LoadedTexture);
|
||||
connect(main_widget, &ViewerGLWidget::DrewManagedTexture, this, &ViewerWidget::DrewManagedTexture);
|
||||
connect(main_widget, &ViewerGLWidget::ColorProcessorChanged, this, &ViewerWidget::ColorProcessorChanged);
|
||||
connect(main_widget, &ViewerGLWidget::ColorManagerChanged, this, &ViewerWidget::ColorManagerChanged);
|
||||
connect(sizer_, &ViewerSizer::RequestMatrix, main_widget, &ViewerGLWidget::SetMatrix);
|
||||
ViewerDisplayWidget* main_widget = new ViewerDisplayWidget();
|
||||
connect(main_widget, &ViewerDisplayWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu);
|
||||
connect(main_widget, &ViewerDisplayWidget::CursorColor, this, &ViewerWidget::CursorColor);
|
||||
connect(main_widget, &ViewerDisplayWidget::LoadedBuffer, this, &ViewerWidget::LoadedBuffer);
|
||||
connect(main_widget, &ViewerDisplayWidget::ColorProcessorChanged, this, &ViewerWidget::ColorProcessorChanged);
|
||||
connect(main_widget, &ViewerDisplayWidget::ColorManagerChanged, this, &ViewerWidget::ColorManagerChanged);
|
||||
connect(sizer_, &ViewerSizer::RequestMatrix, main_widget, &ViewerDisplayWidget::SetMatrix);
|
||||
sizer_->SetWidget(main_widget);
|
||||
gl_widgets_.append(main_widget);
|
||||
|
||||
@@ -152,8 +150,8 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n)
|
||||
connect(n, &ViewerOutput::LengthChanged, this, &ViewerWidget::LengthChangedSlot);
|
||||
connect(n, &ViewerOutput::VideoParamsChanged, this, &ViewerWidget::UpdateRendererParameters);
|
||||
connect(n, &ViewerOutput::VisibleInvalidated, this, &ViewerWidget::InvalidateVisible);
|
||||
connect(n, &ViewerOutput::VideoGraphChanged, this, &ViewerWidget::UpdateStack);
|
||||
connect(n, &ViewerOutput::AudioGraphChanged, this, &ViewerWidget::UpdateStack);
|
||||
connect(n, &ViewerOutput::VideoChangedBetween, this, &ViewerWidget::UpdateStack);
|
||||
connect(n, &ViewerOutput::AudioChangedBetween, this, &ViewerWidget::UpdateStack);
|
||||
|
||||
SizeChangedSlot(n->video_params().width(), n->video_params().height());
|
||||
LengthChangedSlot(n->Length());
|
||||
@@ -168,7 +166,7 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n)
|
||||
using_manager = nullptr;
|
||||
}
|
||||
|
||||
foreach (ViewerGLWidget* glw, gl_widgets_) {
|
||||
foreach (ViewerDisplayWidget* glw, gl_widgets_) {
|
||||
glw->ConnectColorManager(using_manager);
|
||||
}
|
||||
|
||||
@@ -194,13 +192,13 @@ void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n)
|
||||
disconnect(n, &ViewerOutput::LengthChanged, this, &ViewerWidget::LengthChangedSlot);
|
||||
disconnect(n, &ViewerOutput::VideoParamsChanged, this, &ViewerWidget::UpdateRendererParameters);
|
||||
disconnect(n, &ViewerOutput::VisibleInvalidated, this, &ViewerWidget::InvalidateVisible);
|
||||
disconnect(n, &ViewerOutput::VideoGraphChanged, this, &ViewerWidget::UpdateStack);
|
||||
disconnect(n, &ViewerOutput::AudioGraphChanged, this, &ViewerWidget::UpdateStack);
|
||||
disconnect(n, &ViewerOutput::VideoChangedBetween, this, &ViewerWidget::UpdateStack);
|
||||
disconnect(n, &ViewerOutput::AudioChangedBetween, this, &ViewerWidget::UpdateStack);
|
||||
|
||||
// Effectively disables the viewer and clears the state
|
||||
SizeChangedSlot(0, 0);
|
||||
|
||||
foreach (ViewerGLWidget* glw, gl_widgets_) {
|
||||
foreach (ViewerDisplayWidget* glw, gl_widgets_) {
|
||||
glw->DisconnectColorManager();
|
||||
}
|
||||
|
||||
@@ -234,12 +232,12 @@ void ViewerWidget::resizeEvent(QResizeEvent *event)
|
||||
UpdateMinimumScale();
|
||||
}
|
||||
|
||||
const QList<ViewerGLWidget*> &ViewerWidget::gl_widgets() const
|
||||
const QList<ViewerDisplayWidget*> &ViewerWidget::gl_widgets() const
|
||||
{
|
||||
return gl_widgets_;
|
||||
}
|
||||
|
||||
ViewerGLWidget *ViewerWidget::main_gl_widget() const
|
||||
ViewerDisplayWidget *ViewerWidget::main_gl_widget() const
|
||||
{
|
||||
return gl_widgets_.first();
|
||||
}
|
||||
@@ -280,7 +278,7 @@ void ViewerWidget::SetOverrideSize(int width, int height)
|
||||
|
||||
void ViewerWidget::SetMatrix(const QMatrix4x4 &mat)
|
||||
{
|
||||
foreach (ViewerGLWidget* glw, gl_widgets_) {
|
||||
foreach (ViewerDisplayWidget* glw, gl_widgets_) {
|
||||
glw->SetMatrix(mat);
|
||||
}
|
||||
}
|
||||
@@ -309,7 +307,7 @@ void ViewerWidget::SetFullScreen(QScreen *screen)
|
||||
vw->gl_widget()->ConnectColorManager(main_gl_widget()->color_manager());
|
||||
main_gl_widget()->ConnectSibling(vw->gl_widget());
|
||||
connect(vw, &ViewerWindow::destroyed, this, &ViewerWidget::WindowAboutToClose);
|
||||
connect(vw->gl_widget(), &ViewerGLWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu);
|
||||
connect(vw->gl_widget(), &ViewerDisplayWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu);
|
||||
|
||||
if (GetConnectedNode()) {
|
||||
vw->SetResolution(GetConnectedNode()->video_params().width(), GetConnectedNode()->video_params().height());
|
||||
@@ -373,7 +371,7 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only)
|
||||
controls_->ShowPauseButton();
|
||||
|
||||
if (stack_->currentWidget() == sizer_) {
|
||||
connect(main_gl_widget(), &ViewerGLWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate);
|
||||
connect(main_gl_widget(), &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate);
|
||||
} else {
|
||||
connect(AudioManager::instance(), &AudioManager::OutputNotified, this, &ViewerWidget::PlaybackTimerUpdate);
|
||||
}
|
||||
@@ -427,17 +425,21 @@ void ViewerWidget::UpdateMinimumScale()
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::SetColorTransform(const ColorTransform &transform, ViewerGLWidget *sender)
|
||||
void ViewerWidget::SetColorTransform(const ColorTransform &transform, ViewerDisplayWidget *sender)
|
||||
{
|
||||
sender->SetColorTransform(transform);
|
||||
}
|
||||
|
||||
void ViewerWidget::UpdateStack()
|
||||
{
|
||||
if (!GetConnectedNode() || GetConnectedNode()->texture_input()->IsConnected()) {
|
||||
stack_->setCurrentWidget(sizer_);
|
||||
} else {
|
||||
if (GetConnectedNode()
|
||||
&& !GetConnectedNode()->texture_input()->IsConnected()
|
||||
&& GetConnectedNode()->samples_input()->IsConnected()) {
|
||||
// If we have a node AND video is disconnected AND audio is connected, show waveform view
|
||||
stack_->setCurrentWidget(waveform_view_);
|
||||
} else {
|
||||
// Otherwise show regular display
|
||||
stack_->setCurrentWidget(sizer_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,7 +529,7 @@ void ViewerWidget::UpdateRendererParameters()
|
||||
|
||||
if (video_renderer_->params() != vparam) {
|
||||
video_renderer_->SetParameters(vparam);
|
||||
video_renderer_->InvalidateCache(TimeRange(0, GetConnectedNode()->Length()));
|
||||
video_renderer_->InvalidateCache(TimeRange(0, GetConnectedNode()->Length()), nullptr);
|
||||
}
|
||||
|
||||
AudioRenderingParams aparam(GetConnectedNode()->audio_params(),
|
||||
@@ -535,7 +537,7 @@ void ViewerWidget::UpdateRendererParameters()
|
||||
|
||||
if (audio_renderer_->params() != aparam) {
|
||||
audio_renderer_->SetParameters(aparam);
|
||||
audio_renderer_->InvalidateCache(TimeRange(0, GetConnectedNode()->Length()));
|
||||
audio_renderer_->InvalidateCache(TimeRange(0, GetConnectedNode()->Length()), nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -543,7 +545,7 @@ void ViewerWidget::ShowContextMenu(const QPoint &pos)
|
||||
{
|
||||
Menu menu(static_cast<QWidget*>(sender()));
|
||||
|
||||
context_menu_widget_ = static_cast<ViewerGLWidget*>(sender());
|
||||
context_menu_widget_ = static_cast<ViewerDisplayWidget*>(sender());
|
||||
|
||||
// Color options
|
||||
if (context_menu_widget_->color_manager() && color_menu_enabled_) {
|
||||
@@ -683,7 +685,7 @@ void ViewerWidget::Pause()
|
||||
controls_->ShowPlayButton();
|
||||
|
||||
if (stack_->currentWidget() == sizer_) {
|
||||
disconnect(main_gl_widget(), &ViewerGLWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate);
|
||||
disconnect(main_gl_widget(), &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate);
|
||||
} else {
|
||||
disconnect(AudioManager::instance(), &AudioManager::OutputNotified, this, &ViewerWidget::PlaybackTimerUpdate);
|
||||
}
|
||||
@@ -736,16 +738,11 @@ void ViewerWidget::SetColorTransform(const ColorTransform &transform)
|
||||
|
||||
void ViewerWidget::SetSignalCursorColorEnabled(bool e)
|
||||
{
|
||||
foreach (ViewerGLWidget* glw, gl_widgets_) {
|
||||
foreach (ViewerDisplayWidget* glw, gl_widgets_) {
|
||||
glw->SetSignalCursorColorEnabled(e);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::SetEmitDrewManagedTextureEnabled(bool e)
|
||||
{
|
||||
main_gl_widget()->SetEmitDrewManagedTextureEnabled(e);
|
||||
}
|
||||
|
||||
void ViewerWidget::TimebaseChangedEvent(const rational &timebase)
|
||||
{
|
||||
TimeBasedWidget::TimebaseChangedEvent(timebase);
|
||||
@@ -869,9 +866,9 @@ void ViewerWidget::SetZoomFromMenu(QAction *action)
|
||||
sizer_->SetZoom(action->data().toInt());
|
||||
}
|
||||
|
||||
void ViewerWidget::InvalidateVisible()
|
||||
void ViewerWidget::InvalidateVisible(NodeInput* source)
|
||||
{
|
||||
video_renderer_->InvalidateCache(TimeRange(GetTime(), GetTime()));
|
||||
video_renderer_->InvalidateCache(TimeRange(GetTime(), GetTime()), source);
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
#include "render/backend/opengl/openglbackend.h"
|
||||
#include "render/backend/opengl/opengltexture.h"
|
||||
#include "render/backend/audio/audiobackend.h"
|
||||
#include "viewerglwidget.h"
|
||||
#include "viewerdisplay.h"
|
||||
#include "viewersizer.h"
|
||||
#include "viewerwindow.h"
|
||||
#include "widget/playbackcontrols/playbackcontrols.h"
|
||||
@@ -107,11 +107,6 @@ public slots:
|
||||
*/
|
||||
void SetSignalCursorColorEnabled(bool e);
|
||||
|
||||
/**
|
||||
* @brief Wrapper for ViewerGLWidget::SetEmitDrewManagedTextureEnabled()
|
||||
*/
|
||||
void SetEmitDrewManagedTextureEnabled(bool e);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Wrapper for ViewerGLWidget::CursorColor()
|
||||
@@ -123,16 +118,6 @@ signals:
|
||||
*/
|
||||
void LoadedBuffer(Frame* load_buffer);
|
||||
|
||||
/**
|
||||
* @brief Wrapper for ViewerGLWidget::LoadedTexture()
|
||||
*/
|
||||
void LoadedTexture(OpenGLTexture* texture);
|
||||
|
||||
/**
|
||||
* @brief Wrapper for ViewerGLWidget::DrewManagedTexture()
|
||||
*/
|
||||
void DrewManagedTexture(OpenGLTexture* texture);
|
||||
|
||||
/**
|
||||
* @brief Request a scope panel
|
||||
*
|
||||
@@ -167,8 +152,8 @@ protected:
|
||||
|
||||
PlaybackControls* controls_;
|
||||
|
||||
const QList<ViewerGLWidget *> &gl_widgets() const;
|
||||
ViewerGLWidget* main_gl_widget() const;
|
||||
const QList<ViewerDisplayWidget *> &gl_widgets() const;
|
||||
ViewerDisplayWidget* main_gl_widget() const;
|
||||
|
||||
private:
|
||||
void UpdateTimeInternal(int64_t i);
|
||||
@@ -183,7 +168,7 @@ private:
|
||||
|
||||
void UpdateMinimumScale();
|
||||
|
||||
void SetColorTransform(const ColorTransform& transform, ViewerGLWidget* sender);
|
||||
void SetColorTransform(const ColorTransform& transform, ViewerDisplayWidget* sender);
|
||||
|
||||
QStackedWidget* stack_;
|
||||
|
||||
@@ -212,9 +197,9 @@ private:
|
||||
|
||||
QList<ViewerWindow*> windows_;
|
||||
|
||||
QList<ViewerGLWidget*> gl_widgets_;
|
||||
QList<ViewerDisplayWidget*> gl_widgets_;
|
||||
|
||||
ViewerGLWidget* context_menu_widget_;
|
||||
ViewerDisplayWidget* context_menu_widget_;
|
||||
|
||||
private slots:
|
||||
void PlaybackTimerUpdate();
|
||||
@@ -233,7 +218,7 @@ private slots:
|
||||
|
||||
void SetZoomFromMenu(QAction* action);
|
||||
|
||||
void InvalidateVisible();
|
||||
void InvalidateVisible(NodeInput *source);
|
||||
|
||||
void UpdateStack();
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
***/
|
||||
|
||||
#include "viewerglwidget.h"
|
||||
#include "viewerdisplay.h"
|
||||
|
||||
#include <OpenImageIO/imagebuf.h>
|
||||
#include <QFileInfo>
|
||||
@@ -37,30 +37,28 @@
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
bool ViewerGLWidget::nouveau_check_done_ = false;
|
||||
bool ViewerDisplayWidget::nouveau_check_done_ = false;
|
||||
#endif
|
||||
|
||||
ViewerGLWidget::ViewerGLWidget(QWidget *parent) :
|
||||
ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent) :
|
||||
ManagedDisplayWidget(parent),
|
||||
managed_copy_pipeline_(nullptr),
|
||||
has_image_(false),
|
||||
signal_cursor_color_(false),
|
||||
enable_display_referred_signal_(false)
|
||||
signal_cursor_color_(false)
|
||||
{
|
||||
}
|
||||
|
||||
ViewerGLWidget::~ViewerGLWidget()
|
||||
ViewerDisplayWidget::~ViewerDisplayWidget()
|
||||
{
|
||||
ContextCleanup();
|
||||
}
|
||||
|
||||
void ViewerGLWidget::SetMatrix(const QMatrix4x4 &mat)
|
||||
void ViewerDisplayWidget::SetMatrix(const QMatrix4x4 &mat)
|
||||
{
|
||||
matrix_ = mat;
|
||||
update();
|
||||
}
|
||||
|
||||
void ViewerGLWidget::SetImage(const QString &fn)
|
||||
void ViewerDisplayWidget::SetImage(const QString &fn)
|
||||
{
|
||||
has_image_ = false;
|
||||
|
||||
@@ -91,14 +89,12 @@ void ViewerGLWidget::SetImage(const QString &fn)
|
||||
input->read_image(input->spec().format, load_buffer_.data(), OIIO::AutoStride, load_buffer_.linesize_bytes());
|
||||
input->close();
|
||||
|
||||
emit LoadedBuffer(&load_buffer_);
|
||||
|
||||
texture_.Upload(load_buffer_.data(), load_buffer_.linesize_pixels());
|
||||
|
||||
emit LoadedTexture(&texture_);
|
||||
texture_.Upload(&load_buffer_);
|
||||
|
||||
doneCurrent();
|
||||
|
||||
emit LoadedBuffer(&load_buffer_);
|
||||
|
||||
has_image_ = true;
|
||||
|
||||
#if OIIO_VERSION < 10903
|
||||
@@ -119,13 +115,13 @@ void ViewerGLWidget::SetImage(const QString &fn)
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerGLWidget::SetSignalCursorColorEnabled(bool e)
|
||||
void ViewerDisplayWidget::SetSignalCursorColorEnabled(bool e)
|
||||
{
|
||||
signal_cursor_color_ = e;
|
||||
setMouseTracking(e);
|
||||
}
|
||||
|
||||
void ViewerGLWidget::SetImageFromLoadBuffer(Frame *in_buffer)
|
||||
void ViewerDisplayWidget::SetImageFromLoadBuffer(Frame *in_buffer)
|
||||
{
|
||||
has_image_ = in_buffer;
|
||||
|
||||
@@ -138,7 +134,7 @@ void ViewerGLWidget::SetImageFromLoadBuffer(Frame *in_buffer)
|
||||
|| texture_.format() != in_buffer->format()) {
|
||||
texture_.Create(context(), in_buffer->width(), in_buffer->height(), in_buffer->format(), in_buffer->data(), load_buffer_.linesize_pixels());
|
||||
} else {
|
||||
texture_.Upload(in_buffer->data(), load_buffer_.linesize_pixels());
|
||||
texture_.Upload(in_buffer);
|
||||
}
|
||||
|
||||
doneCurrent();
|
||||
@@ -147,44 +143,32 @@ void ViewerGLWidget::SetImageFromLoadBuffer(Frame *in_buffer)
|
||||
update();
|
||||
}
|
||||
|
||||
void ViewerGLWidget::SetEmitDrewManagedTextureEnabled(bool e)
|
||||
void ViewerDisplayWidget::ConnectSibling(ViewerDisplayWidget *sibling)
|
||||
{
|
||||
enable_display_referred_signal_ = e;
|
||||
|
||||
if (!enable_display_referred_signal_) {
|
||||
// Destroy the texture now
|
||||
managed_texture_.Destroy();
|
||||
managed_copy_pipeline_ = nullptr;
|
||||
framebuffer_.Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerGLWidget::ConnectSibling(ViewerGLWidget *sibling)
|
||||
{
|
||||
connect(this, &ViewerGLWidget::LoadedBuffer, sibling, &ViewerGLWidget::SetImageFromLoadBuffer, Qt::QueuedConnection);
|
||||
connect(this, &ViewerDisplayWidget::LoadedBuffer, sibling, &ViewerDisplayWidget::SetImageFromLoadBuffer, Qt::QueuedConnection);
|
||||
sibling->SetImageFromLoadBuffer(&load_buffer_);
|
||||
}
|
||||
|
||||
const ViewerSafeMarginInfo &ViewerGLWidget::GetSafeMargin() const
|
||||
const ViewerSafeMarginInfo &ViewerDisplayWidget::GetSafeMargin() const
|
||||
{
|
||||
return safe_margin_;
|
||||
}
|
||||
|
||||
void ViewerGLWidget::SetSafeMargins(const ViewerSafeMarginInfo &safe_margin)
|
||||
void ViewerDisplayWidget::SetSafeMargins(const ViewerSafeMarginInfo &safe_margin)
|
||||
{
|
||||
safe_margin_ = safe_margin;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void ViewerGLWidget::mousePressEvent(QMouseEvent *event)
|
||||
void ViewerDisplayWidget::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
QOpenGLWidget::mousePressEvent(event);
|
||||
|
||||
emit DragStarted();
|
||||
}
|
||||
|
||||
void ViewerGLWidget::mouseMoveEvent(QMouseEvent *event)
|
||||
void ViewerDisplayWidget::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
QOpenGLWidget::mouseMoveEvent(event);
|
||||
|
||||
@@ -209,11 +193,11 @@ void ViewerGLWidget::mouseMoveEvent(QMouseEvent *event)
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerGLWidget::initializeGL()
|
||||
void ViewerDisplayWidget::initializeGL()
|
||||
{
|
||||
ManagedDisplayWidget::initializeGL();
|
||||
|
||||
connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ViewerGLWidget::ContextCleanup, Qt::DirectConnection);
|
||||
connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ViewerDisplayWidget::ContextCleanup, Qt::DirectConnection);
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
if (!nouveau_check_done_) {
|
||||
@@ -231,7 +215,7 @@ void ViewerGLWidget::initializeGL()
|
||||
#endif
|
||||
}
|
||||
|
||||
void ViewerGLWidget::paintGL()
|
||||
void ViewerDisplayWidget::paintGL()
|
||||
{
|
||||
// Get functions attached to this context (they will already be initialized)
|
||||
QOpenGLFunctions* f = context()->functions();
|
||||
@@ -243,33 +227,6 @@ void ViewerGLWidget::paintGL()
|
||||
// We only draw if we have a pipeline
|
||||
if (has_image_ && color_service() && texture_.IsCreated()) {
|
||||
|
||||
// If we're distributing our display-referred final buffer, we'll have to make a copy of it
|
||||
if (enable_display_referred_signal_) {
|
||||
|
||||
if (!managed_texture_.IsCreated()
|
||||
|| managed_texture_.width() != texture_.width()
|
||||
|| managed_texture_.height() != texture_.height()
|
||||
|| managed_texture_.format() != texture_.format()) {
|
||||
managed_texture_.Destroy();
|
||||
|
||||
managed_texture_.Create(context(), texture_.width(), texture_.height(), texture_.format());
|
||||
}
|
||||
|
||||
if (!managed_copy_pipeline_) {
|
||||
managed_copy_pipeline_ = OpenGLShader::CreateDefault();
|
||||
}
|
||||
|
||||
if (!framebuffer_.IsCreated()) {
|
||||
framebuffer_.Create(context());
|
||||
}
|
||||
|
||||
framebuffer_.Attach(&managed_texture_);
|
||||
framebuffer_.Bind();
|
||||
|
||||
context()->functions()->glViewport(0, 0, managed_texture_.width(), managed_texture_.height());
|
||||
|
||||
}
|
||||
|
||||
// Bind retrieved texture
|
||||
f->glBindTexture(GL_TEXTURE_2D, texture_.texture());
|
||||
|
||||
@@ -279,24 +236,6 @@ void ViewerGLWidget::paintGL()
|
||||
// Release retrieved texture
|
||||
f->glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
if (enable_display_referred_signal_) {
|
||||
|
||||
framebuffer_.Release();
|
||||
framebuffer_.Detach();
|
||||
|
||||
emit DrewManagedTexture(&managed_texture_);
|
||||
|
||||
// Bind retrieved texture
|
||||
managed_texture_.Bind();
|
||||
|
||||
context()->functions()->glViewport(0, 0, width(), height());
|
||||
|
||||
OpenGLRenderFunctions::Blit(managed_copy_pipeline_);
|
||||
|
||||
// Bind retrieved texture
|
||||
managed_texture_.Release();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Draw action/title safe areas
|
||||
@@ -333,7 +272,7 @@ void ViewerGLWidget::paintGL()
|
||||
}
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
void ViewerGLWidget::ShowNouveauWarning()
|
||||
void ViewerDisplayWidget::ShowNouveauWarning()
|
||||
{
|
||||
QMessageBox::warning(this,
|
||||
tr("Driver Warning"),
|
||||
@@ -344,14 +283,11 @@ void ViewerGLWidget::ShowNouveauWarning()
|
||||
}
|
||||
#endif
|
||||
|
||||
void ViewerGLWidget::ContextCleanup()
|
||||
void ViewerDisplayWidget::ContextCleanup()
|
||||
{
|
||||
makeCurrent();
|
||||
|
||||
managed_copy_pipeline_ = nullptr;
|
||||
texture_.Destroy();
|
||||
managed_texture_.Destroy();
|
||||
framebuffer_.Destroy();
|
||||
|
||||
doneCurrent();
|
||||
}
|
||||
@@ -49,7 +49,7 @@ OLIVE_NAMESPACE_ENTER
|
||||
* the same texture object, use SetTexture() since it will nearly always be faster to just set it than to check *and*
|
||||
* set it.
|
||||
*/
|
||||
class ViewerGLWidget : public ManagedDisplayWidget
|
||||
class ViewerDisplayWidget : public ManagedDisplayWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -60,9 +60,9 @@ public:
|
||||
*
|
||||
* QWidget parent.
|
||||
*/
|
||||
ViewerGLWidget(QWidget* parent = nullptr);
|
||||
ViewerDisplayWidget(QWidget* parent = nullptr);
|
||||
|
||||
virtual ~ViewerGLWidget() override;
|
||||
virtual ~ViewerDisplayWidget() override;
|
||||
|
||||
/**
|
||||
* @brief Set an image to load and display on screen
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
|
||||
const QMatrix4x4& GetMatrix();
|
||||
|
||||
void ConnectSibling(ViewerGLWidget* sibling);
|
||||
void ConnectSibling(ViewerDisplayWidget* sibling);
|
||||
|
||||
const ViewerSafeMarginInfo& GetSafeMargin() const;
|
||||
void SetSafeMargins(const ViewerSafeMarginInfo& safe_margin);
|
||||
@@ -101,15 +101,6 @@ public slots:
|
||||
*/
|
||||
void SetImageFromLoadBuffer(Frame* in_buffer);
|
||||
|
||||
/**
|
||||
* @brief Enables or disables DrewManagedTexture()
|
||||
*
|
||||
* To emit a display referred texture, it needs to be copied after the color transform is complete. This naturally
|
||||
* adds extra GPU cycles that are wasted if there's nothing receiving the signal. Therefore, the signal is disabled
|
||||
* by default.
|
||||
*/
|
||||
void SetEmitDrewManagedTextureEnabled(bool e);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Signal emitted when the user starts dragging from the viewer
|
||||
@@ -130,18 +121,6 @@ signals:
|
||||
*/
|
||||
void LoadedBuffer(Frame* load_buffer);
|
||||
|
||||
/**
|
||||
* @brief Signal emitted when a buffer is loaded into a texture
|
||||
*
|
||||
* This texture will be the direct output of the renderer in reference space in GPU VRAM.
|
||||
*/
|
||||
void LoadedTexture(OpenGLTexture* texture);
|
||||
|
||||
/**
|
||||
* @brief Emitted when the a texture has been transformed to display
|
||||
*/
|
||||
void DrewManagedTexture(OpenGLTexture* texture);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Override the mouse press event simply to emit the DragStarted() signal
|
||||
@@ -173,23 +152,6 @@ private:
|
||||
*/
|
||||
OpenGLTexture texture_;
|
||||
|
||||
/**
|
||||
* @brief Internal framebuffer used to draw to managed_texture_
|
||||
*/
|
||||
OpenGLFramebuffer framebuffer_;
|
||||
|
||||
/**
|
||||
* @brief Internal referenceto the OpenGL texture that's been managed
|
||||
*
|
||||
* Kept so that scopes can use the display-referred buffer without having to transform again.
|
||||
*/
|
||||
OpenGLTexture managed_texture_;
|
||||
|
||||
/**
|
||||
* @brief Pipeline used to draw to managed_texture_
|
||||
*/
|
||||
OpenGLShaderPtr managed_copy_pipeline_;
|
||||
|
||||
/**
|
||||
* @brief Drawing matrix (defaults to identity)
|
||||
*/
|
||||
@@ -210,8 +172,6 @@ private:
|
||||
|
||||
ViewerSafeMarginInfo safe_margin_;
|
||||
|
||||
bool enable_display_referred_signal_;
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Slot to connect just before the OpenGL context is destroyed to clean up resources
|
||||
@@ -32,11 +32,11 @@ ViewerWindow::ViewerWindow(QWidget *parent) :
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(0);
|
||||
|
||||
gl_widget_ = new ViewerGLWidget();
|
||||
gl_widget_ = new ViewerDisplayWidget();
|
||||
layout->addWidget(gl_widget_);
|
||||
}
|
||||
|
||||
ViewerGLWidget *ViewerWindow::gl_widget() const
|
||||
ViewerDisplayWidget *ViewerWindow::gl_widget() const
|
||||
{
|
||||
return gl_widget_;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "viewerglwidget.h"
|
||||
#include "viewerdisplay.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
@@ -32,7 +32,7 @@ class ViewerWindow : public QWidget
|
||||
public:
|
||||
ViewerWindow(QWidget* parent = nullptr);
|
||||
|
||||
ViewerGLWidget* gl_widget() const;
|
||||
ViewerDisplayWidget* gl_widget() const;
|
||||
|
||||
/**
|
||||
* @brief Used to adjust resulting picture to be the right aspect ratio
|
||||
@@ -45,7 +45,7 @@ protected:
|
||||
virtual void closeEvent(QCloseEvent* e) override;
|
||||
|
||||
private:
|
||||
ViewerGLWidget* gl_widget_;
|
||||
ViewerDisplayWidget* gl_widget_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user