Merge branch 'master' into cache-update
This commit is contained in:
@@ -35,6 +35,7 @@ const QString ClipBlock::kSpeedInput = QStringLiteral("speed_in");
|
||||
const QString ClipBlock::kReverseInput = QStringLiteral("reverse_in");
|
||||
const QString ClipBlock::kMaintainAudioPitchInput = QStringLiteral("maintain_audio_pitch_in");
|
||||
const QString ClipBlock::kAutoCacheInput = QStringLiteral("autocache_in");
|
||||
const QString ClipBlock::kLoopModeInput = QStringLiteral("loop_in");
|
||||
|
||||
ClipBlock::ClipBlock() :
|
||||
in_transition_(nullptr),
|
||||
@@ -59,6 +60,8 @@ ClipBlock::ClipBlock() :
|
||||
//SetValueHintForInput(kBufferIn, ValueHint(NodeValue::kBuffer));
|
||||
|
||||
SetEffectInput(kBufferIn);
|
||||
|
||||
AddInput(kLoopModeInput, NodeValue::kCombo, 0, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||
}
|
||||
|
||||
QString ClipBlock::Name() const
|
||||
@@ -92,7 +95,7 @@ void ClipBlock::set_length_and_media_out(const rational &length)
|
||||
|
||||
if (reverse()) {
|
||||
// Calculate media_in adjustment
|
||||
rational proposed_media_in = SequenceToMediaTime(this->length() - length, true);
|
||||
rational proposed_media_in = SequenceToMediaTime(this->length() - length, kSTMIgnoreReverse | kSTMIgnoreLoop);
|
||||
set_media_in(proposed_media_in);
|
||||
}
|
||||
|
||||
@@ -107,8 +110,7 @@ void ClipBlock::set_length_and_media_in(const rational &length)
|
||||
|
||||
if (!reverse()) {
|
||||
// Calculate media_in adjustment
|
||||
rational proposed_media_in = SequenceToMediaTime(this->length() - length, false, true);
|
||||
set_media_in(proposed_media_in);
|
||||
set_media_in(SequenceToMediaTime(this->length() - length, kSTMIgnoreLoop));
|
||||
}
|
||||
|
||||
super::set_length_and_media_in(length);
|
||||
@@ -129,7 +131,7 @@ void ClipBlock::SetAutocache(bool e)
|
||||
SetStandardValue(kAutoCacheInput, e);
|
||||
}
|
||||
|
||||
rational ClipBlock::SequenceToMediaTime(const rational &sequence_time, bool ignore_reverse, bool ignore_speed) const
|
||||
rational ClipBlock::SequenceToMediaTime(const rational &sequence_time, uint64_t flags) const
|
||||
{
|
||||
// These constants are not considered "values" per se, so we don't modify them
|
||||
if (sequence_time == RATIONAL_MIN || sequence_time == RATIONAL_MAX) {
|
||||
@@ -138,11 +140,11 @@ rational ClipBlock::SequenceToMediaTime(const rational &sequence_time, bool igno
|
||||
|
||||
rational media_time = sequence_time;
|
||||
|
||||
if (reverse() && !ignore_reverse) {
|
||||
if (reverse() && !(flags & kSTMIgnoreReverse)) {
|
||||
media_time = length() - media_time;
|
||||
}
|
||||
|
||||
if (!ignore_speed) {
|
||||
if (!(flags & kSTMIgnoreSpeed)) {
|
||||
double speed_value = speed();
|
||||
if (qIsNull(speed_value)) {
|
||||
// Effectively holds the frame at the in point
|
||||
@@ -155,6 +157,23 @@ rational ClipBlock::SequenceToMediaTime(const rational &sequence_time, bool igno
|
||||
|
||||
media_time += media_in();
|
||||
|
||||
/*if (!(flags & kSTMIgnoreLoop)
|
||||
&& this->loop_mode() != kLoopModeOff
|
||||
&& connected_viewer_
|
||||
&& !connected_viewer_->GetLength().isNull()
|
||||
&& (media_time < 0 || media_time >= connected_viewer_->GetLength())) {
|
||||
if (loop_mode() == kLoopModeLoop) {
|
||||
while (media_time < 0) {
|
||||
media_time += connected_viewer_->GetLength();
|
||||
}
|
||||
while (media_time >= connected_viewer_->GetLength()) {
|
||||
media_time -= connected_viewer_->GetLength();
|
||||
}
|
||||
} else if (loop_mode() == kLoopModeClamp) {
|
||||
media_time = std::clamp(media_time, rational(0), connected_viewer_->GetLength()-connected_viewer_->GetVideoParams().frame_rate_as_time_base());
|
||||
}
|
||||
}*/
|
||||
|
||||
return media_time;
|
||||
}
|
||||
|
||||
@@ -412,6 +431,8 @@ void ClipBlock::Retranslate()
|
||||
SetInputName(kSpeedInput, tr("Speed"));
|
||||
SetInputName(kReverseInput, tr("Reverse"));
|
||||
SetInputName(kMaintainAudioPitchInput, tr("Maintain Audio Pitch"));
|
||||
SetInputName(kLoopModeInput, tr("Loop"));
|
||||
SetComboBoxStrings(kLoopModeInput, {tr("None"), tr("Loop"), tr("Clamp")});
|
||||
}
|
||||
|
||||
void ClipBlock::AddCachePassthroughFrom(ClipBlock *other)
|
||||
@@ -448,7 +469,7 @@ void ClipBlock::ConnectedToPreviewEvent()
|
||||
|
||||
TimeRange ClipBlock::media_range() const
|
||||
{
|
||||
return InputTimeAdjustment(kBufferIn, -1, range());
|
||||
return InputTimeAdjustment(kBufferIn, -1, TimeRange(0, length()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#define CLIPBLOCK_H
|
||||
|
||||
#include "audio/audiovisualwaveform.h"
|
||||
#include "codec/decoder.h"
|
||||
#include "node/block/block.h"
|
||||
#include "node/output/track/track.h"
|
||||
|
||||
@@ -179,11 +180,25 @@ public:
|
||||
|
||||
TimeRange media_range() const;
|
||||
|
||||
/**
|
||||
* @brief Get currently set loop mode
|
||||
*/
|
||||
Decoder::LoopMode loop_mode() const
|
||||
{
|
||||
return static_cast<Decoder::LoopMode>(GetStandardValue(kLoopModeInput).toInt());
|
||||
}
|
||||
|
||||
void set_loop_mode(Decoder::LoopMode l)
|
||||
{
|
||||
SetStandardValue(kLoopModeInput, int(l));
|
||||
}
|
||||
|
||||
static const QString kBufferIn;
|
||||
static const QString kMediaInInput;
|
||||
static const QString kSpeedInput;
|
||||
static const QString kReverseInput;
|
||||
static const QString kMaintainAudioPitchInput;
|
||||
static const QString kLoopModeInput;
|
||||
|
||||
static const QString kAutoCacheInput;
|
||||
|
||||
@@ -197,7 +212,15 @@ protected:
|
||||
virtual void InputValueChangedEvent(const QString& input, int element) override;
|
||||
|
||||
private:
|
||||
rational SequenceToMediaTime(const rational& sequence_time, bool ignore_reverse = false, bool ignore_speed = false) const;
|
||||
enum SequenceToMediaTimeFlag
|
||||
{
|
||||
kSTMNone = 0x0,
|
||||
kSTMIgnoreReverse = 0x1,
|
||||
kSTMIgnoreSpeed = 0x2,
|
||||
kSTMIgnoreLoop = 0x4
|
||||
};
|
||||
|
||||
rational SequenceToMediaTime(const rational& sequence_time, uint64_t flags = kSTMNone) const;
|
||||
|
||||
rational MediaToSequenceTime(const rational& media_time) const;
|
||||
|
||||
|
||||
@@ -53,13 +53,6 @@ ShaderCode CrossDissolveTransition::GetShaderCode(const ShaderRequest &request)
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/crossdissolve.frag"), QString());
|
||||
}
|
||||
|
||||
void CrossDissolveTransition::ShaderJobEvent(const NodeValueRow &value, ShaderJob &job) const
|
||||
{
|
||||
Q_UNUSED(value)
|
||||
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
}
|
||||
|
||||
void CrossDissolveTransition::SampleJobEvent(const SampleBuffer &from_samples, const SampleBuffer &to_samples, SampleBuffer &out_samples, double time_in) const
|
||||
{
|
||||
for (int i=0; i<out_samples.sample_count(); i++) {
|
||||
|
||||
@@ -43,8 +43,6 @@ public:
|
||||
virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
|
||||
|
||||
protected:
|
||||
virtual void ShaderJobEvent(const NodeValueRow &value, ShaderJob& job) const override;
|
||||
|
||||
virtual void SampleJobEvent(const SampleBuffer &from_samples, const SampleBuffer &to_samples, SampleBuffer &out_samples, double time_in) const override;
|
||||
|
||||
};
|
||||
|
||||
@@ -71,7 +71,6 @@ void CornerPinDistortNode::Value(const NodeValueRow &value, const NodeGlobals &g
|
||||
{
|
||||
ShaderJob job;
|
||||
job.Insert(value);
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
|
||||
|
||||
// Convert slider values to their pixel values and then convert to clip space (-1.0 ... 1.0) for overriding the
|
||||
|
||||
@@ -79,7 +79,6 @@ void CropDistortNode::Value(const NodeValueRow &value, const NodeGlobals &global
|
||||
{
|
||||
ShaderJob job;
|
||||
job.Insert(value);
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
job.SetWillChangeImageSize(false);
|
||||
|
||||
if (TexturePtr texture = job.Get(kTextureInput).toTexture()) {
|
||||
|
||||
@@ -44,7 +44,7 @@ ShaderCode MaskDistortNode::GetShaderCode(const ShaderRequest &request) const
|
||||
} else if (request.id == QStringLiteral("feather")) {
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/blur.frag")));
|
||||
} else {
|
||||
return ShaderCode();
|
||||
return super::GetShaderCode(request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ void MaskDistortNode::Retranslate()
|
||||
|
||||
void MaskDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
GenerateJob job = GetGenerateJob(value);
|
||||
ShaderJob job = GetGenerateJob(value);
|
||||
|
||||
if (value[kBaseInput].toTexture()) {
|
||||
// Push as merge node
|
||||
@@ -80,7 +80,6 @@ void MaskDistortNode::Value(const NodeValueRow &value, const NodeGlobals &global
|
||||
feather.Insert(BlurFilterNode::kRadiusInput, NodeValue(NodeValue::kFloat, value[kFeatherInput].toDouble(), this));
|
||||
feather.SetIterations(2, BlurFilterNode::kTextureInput);
|
||||
feather.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
|
||||
feather.SetAlphaChannelRequired(ShaderJob::kAlphaForceOn);
|
||||
|
||||
merge.Insert(QStringLiteral("tex_b"), NodeValue(NodeValue::kTexture, feather, this));
|
||||
} else {
|
||||
|
||||
@@ -22,12 +22,9 @@
|
||||
|
||||
#include <QGuiApplication>
|
||||
|
||||
#include "common/range.h"
|
||||
#include "core.h"
|
||||
#include "node/traverser.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
const QString TransformDistortNode::kParentInput = QStringLiteral("parent_in");
|
||||
const QString TransformDistortNode::kTextureInput = QStringLiteral("tex_in");
|
||||
const QString TransformDistortNode::kAutoscaleInput = QStringLiteral("autoscale_in");
|
||||
const QString TransformDistortNode::kInterpolationInput = QStringLiteral("interpolation_in");
|
||||
@@ -36,6 +33,8 @@ const QString TransformDistortNode::kInterpolationInput = QStringLiteral("interp
|
||||
|
||||
TransformDistortNode::TransformDistortNode()
|
||||
{
|
||||
AddInput(kParentInput, NodeValue::kMatrix);
|
||||
|
||||
AddInput(kAutoscaleInput, NodeValue::kCombo, 0);
|
||||
|
||||
AddInput(kInterpolationInput, NodeValue::kCombo, 2);
|
||||
@@ -73,6 +72,7 @@ void TransformDistortNode::Retranslate()
|
||||
{
|
||||
super::Retranslate();
|
||||
|
||||
SetInputName(kParentInput, tr("Parent"));
|
||||
SetInputName(kAutoscaleInput, tr("Auto-Scale"));
|
||||
SetInputName(kTextureInput, tr("Texture"));
|
||||
SetInputName(kInterpolationInput, tr("Interpolation"));
|
||||
@@ -84,12 +84,12 @@ void TransformDistortNode::Retranslate()
|
||||
void TransformDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
// Generate matrix
|
||||
QMatrix4x4 generated_matrix = GenerateMatrix(value, false, false, false);
|
||||
QMatrix4x4 generated_matrix = GenerateMatrix(value, false, false, false, value[kParentInput].toMatrix());
|
||||
|
||||
// Pop texture
|
||||
NodeValue texture_meta = value[kTextureInput];
|
||||
|
||||
bool pushed_job = false;
|
||||
QVariant job_to_push;
|
||||
|
||||
// If we have a texture, generate a matrix and make it happen
|
||||
if (TexturePtr texture = texture_meta.toTexture()) {
|
||||
@@ -103,19 +103,17 @@ void TransformDistortNode::Value(const NodeValueRow &value, const NodeGlobals &g
|
||||
job.Insert(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, real_matrix, this));
|
||||
job.SetInterpolation(QStringLiteral("ove_maintex"), static_cast<Texture::Interpolation>(value[kInterpolationInput].toInt()));
|
||||
|
||||
// FIXME: This should be optimized, we can use matrix math to determine if this operation will
|
||||
// end up with gaps in the screen that will require an alpha channel.
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
|
||||
pushed_job = true;
|
||||
job_to_push = QVariant::fromValue(job);
|
||||
}
|
||||
}
|
||||
|
||||
if (!pushed_job) {
|
||||
table->Push(NodeValue::kMatrix, QVariant::fromValue(generated_matrix), this);
|
||||
|
||||
if (job_to_push.isNull()) {
|
||||
// Re-push whatever value we received
|
||||
table->Push(texture_meta);
|
||||
} else {
|
||||
table->Push(NodeValue::kTexture, job_to_push, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +131,7 @@ void TransformDistortNode::GizmoDragStart(const NodeValueRow &row, double x, dou
|
||||
|
||||
if (gizmo == anchor_gizmo_) {
|
||||
|
||||
gizmo_inverted_transform_ = GenerateMatrix(row, true, true, false).toTransform().inverted();
|
||||
gizmo_inverted_transform_ = GenerateMatrix(row, true, true, false, row[kParentInput].toMatrix()).toTransform().inverted();
|
||||
|
||||
} else if (IsAScaleGizmo(gizmo)) {
|
||||
|
||||
@@ -175,7 +173,7 @@ void TransformDistortNode::GizmoDragStart(const NodeValueRow &row, double x, dou
|
||||
}
|
||||
|
||||
// Store current matrix
|
||||
gizmo_inverted_transform_ = GenerateMatrix(row, true, true, true).toTransform().inverted();
|
||||
gizmo_inverted_transform_ = GenerateMatrix(row, true, true, true, row[kParentInput].toMatrix()).toTransform().inverted();
|
||||
|
||||
} else if (gizmo == rotation_gizmo_) {
|
||||
|
||||
@@ -360,7 +358,7 @@ void TransformDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const N
|
||||
// Fold values into a matrix for the rectangle
|
||||
QMatrix4x4 rectangle_matrix;
|
||||
rectangle_matrix.scale(sequence_half_res);
|
||||
rectangle_matrix *= AdjustMatrixByResolutions(GenerateMatrix(row, false, false, false),
|
||||
rectangle_matrix *= AdjustMatrixByResolutions(GenerateMatrix(row, false, false, false, row[kParentInput].toMatrix()),
|
||||
sequence_res,
|
||||
tex_sz,
|
||||
tex_offset,
|
||||
@@ -380,7 +378,7 @@ void TransformDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const N
|
||||
// Draw anchor point
|
||||
QMatrix4x4 anchor_matrix;
|
||||
anchor_matrix.scale(sequence_half_res);
|
||||
anchor_matrix *= AdjustMatrixByResolutions(GenerateMatrix(row, true, false, false),
|
||||
anchor_matrix *= AdjustMatrixByResolutions(GenerateMatrix(row, true, false, false, row[kParentInput].toMatrix()),
|
||||
sequence_res,
|
||||
tex_sz,
|
||||
tex_offset,
|
||||
@@ -406,7 +404,8 @@ void TransformDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const N
|
||||
QTransform TransformDistortNode::GizmoTransformation(const NodeValueRow &row, const NodeGlobals &globals) const
|
||||
{
|
||||
if (TexturePtr texture = row[kTextureInput].toTexture()) {
|
||||
auto m = GenerateMatrix(row, false, false, false);
|
||||
//auto m = GenerateMatrix(row, false, false, false, row[kParentInput].toMatrix());
|
||||
auto m = GenerateMatrix(row, false, false, false, QMatrix4x4());
|
||||
return GenerateAutoScaledMatrix(m, row, globals, texture->params()).toTransform();
|
||||
}
|
||||
return super::GizmoTransformation(row, globals);
|
||||
|
||||
@@ -84,6 +84,7 @@ public:
|
||||
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
|
||||
virtual QTransform GizmoTransformation(const NodeValueRow &row, const NodeGlobals &globals) const override;
|
||||
|
||||
static const QString kParentInput;
|
||||
static const QString kTextureInput;
|
||||
static const QString kAutoscaleInput;
|
||||
static const QString kInterpolationInput;
|
||||
|
||||
@@ -52,7 +52,6 @@ void OpacityEffect::Value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
// If there's no texture, no need to run an operation
|
||||
if (job.Get(kTextureInput).toTexture()) {
|
||||
if (!qFuzzyCompare(job.Get(kValueInput).toDouble(), 1.0)) {
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
} else {
|
||||
// 1.0 float is a no-op, so just push the texture
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
#include "project/folder/folder.h"
|
||||
#include "project/footage/footage.h"
|
||||
#include "project/sequence/sequence.h"
|
||||
#include "time/timeformat/timeformat.h"
|
||||
#include "time/timeoffset/timeoffsetnode.h"
|
||||
#include "time/timeremap/timeremap.h"
|
||||
|
||||
@@ -291,6 +292,8 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id)
|
||||
return new MaskDistortNode();
|
||||
case kDropShadowFilter:
|
||||
return new DropShadowFilter();
|
||||
case kTimeFormat:
|
||||
return new TimeFormatNode();
|
||||
|
||||
case kInternalNodeCount:
|
||||
break;
|
||||
|
||||
@@ -75,6 +75,7 @@ public:
|
||||
kChromaKey,
|
||||
kMaskDistort,
|
||||
kDropShadowFilter,
|
||||
kTimeFormat,
|
||||
|
||||
// Count value
|
||||
kInternalNodeCount
|
||||
|
||||
@@ -159,11 +159,6 @@ void BlurFilterNode::Value(const NodeValueRow &value, const NodeGlobals &globals
|
||||
}
|
||||
|
||||
if (can_push_job) {
|
||||
// If we're not repeating pixels, expect an alpha channel to appear
|
||||
if (!job.Get(kRepeatEdgePixelsInput).toBool()) {
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
}
|
||||
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
} else {
|
||||
// If we're not performing the blur job, just push the texture
|
||||
|
||||
@@ -90,11 +90,11 @@ void MatrixGenerator::Retranslate()
|
||||
void MatrixGenerator::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
// Push matrix output
|
||||
QMatrix4x4 mat = GenerateMatrix(value, false, false, false);
|
||||
QMatrix4x4 mat = GenerateMatrix(value, false, false, false, QMatrix4x4());
|
||||
table->Push(NodeValue::kMatrix, mat, this);
|
||||
}
|
||||
|
||||
QMatrix4x4 MatrixGenerator::GenerateMatrix(const NodeValueRow &value, bool ignore_anchor, bool ignore_position, bool ignore_scale) const
|
||||
QMatrix4x4 MatrixGenerator::GenerateMatrix(const NodeValueRow &value, bool ignore_anchor, bool ignore_position, bool ignore_scale, const QMatrix4x4 &mat) const
|
||||
{
|
||||
QVector2D anchor;
|
||||
QVector2D position;
|
||||
@@ -116,17 +116,17 @@ QMatrix4x4 MatrixGenerator::GenerateMatrix(const NodeValueRow &value, bool ignor
|
||||
value[kRotationInput].toDouble(),
|
||||
scale,
|
||||
value[kUniformScaleInput].toBool(),
|
||||
anchor);
|
||||
anchor,
|
||||
mat);
|
||||
}
|
||||
|
||||
QMatrix4x4 MatrixGenerator::GenerateMatrix(const QVector2D& pos,
|
||||
const float& rot,
|
||||
const QVector2D& scale,
|
||||
bool uniform_scale,
|
||||
const QVector2D& anchor)
|
||||
const QVector2D& anchor,
|
||||
QMatrix4x4 mat)
|
||||
{
|
||||
QMatrix4x4 mat;
|
||||
|
||||
// Position
|
||||
mat.translate(pos);
|
||||
|
||||
|
||||
@@ -53,12 +53,13 @@ public:
|
||||
static const QString kAnchorInput;
|
||||
|
||||
protected:
|
||||
QMatrix4x4 GenerateMatrix(const NodeValueRow &value, bool ignore_anchor, bool ignore_position, bool ignore_scale) const;
|
||||
QMatrix4x4 GenerateMatrix(const NodeValueRow &value, bool ignore_anchor, bool ignore_position, bool ignore_scale, const QMatrix4x4 &mat) const;
|
||||
static QMatrix4x4 GenerateMatrix(const QVector2D &pos,
|
||||
const float &rot,
|
||||
const QVector2D &scale,
|
||||
bool uniform_scale,
|
||||
const QVector2D &anchor);
|
||||
const QVector2D &anchor,
|
||||
QMatrix4x4 mat);
|
||||
|
||||
virtual void InputValueChangedEvent(const QString& input, int element) override;
|
||||
|
||||
|
||||
@@ -23,8 +23,6 @@
|
||||
#include <QGuiApplication>
|
||||
#include <QVector2D>
|
||||
|
||||
#include "common/cpuoptimize.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
const QString PolygonGenerator::kPointsInput = QStringLiteral("points_in");
|
||||
@@ -89,20 +87,25 @@ void PolygonGenerator::Retranslate()
|
||||
SetInputName(kColorInput, tr("Color"));
|
||||
}
|
||||
|
||||
GenerateJob PolygonGenerator::GetGenerateJob(const NodeValueRow &value) const
|
||||
ShaderJob PolygonGenerator::GetGenerateJob(const NodeValueRow &value) const
|
||||
{
|
||||
GenerateJob job;
|
||||
|
||||
job.Insert(value);
|
||||
job.SetRequestedFormat(VideoParams::kFormatFloat32);
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
job.SetRequestedFormat(VideoParams::kFormatUnsigned8);
|
||||
|
||||
return job;
|
||||
// Conversion to RGB
|
||||
ShaderJob rgb;
|
||||
rgb.SetShaderID(QStringLiteral("rgb"));
|
||||
rgb.Insert(QStringLiteral("texture_in"), NodeValue(NodeValue::kTexture, job, this));
|
||||
rgb.Insert(QStringLiteral("color_in"), value[kColorInput]);
|
||||
|
||||
return rgb;
|
||||
}
|
||||
|
||||
void PolygonGenerator::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
GenerateJob job = GetGenerateJob(value);
|
||||
ShaderJob job = GetGenerateJob(value);
|
||||
|
||||
PushMergableJob(value, QVariant::fromValue(job), table);
|
||||
}
|
||||
@@ -113,7 +116,7 @@ void PolygonGenerator::GenerateFrame(FramePtr frame, const GenerateJob &job) con
|
||||
// QImages only support integer pixels and we use float pixels, so what we do here is draw onto
|
||||
// a single-channel QImage (alpha only) and then transplant that alpha channel to our float buffer
|
||||
// with correct float RGB.
|
||||
QImage img(frame->width(), frame->height(), QImage::Format_Grayscale8);
|
||||
QImage img((uchar *) frame->data(), frame->width(), frame->height(), frame->linesize_bytes(), QImage::Format_RGBA8888_Premultiplied);
|
||||
img.fill(Qt::transparent);
|
||||
|
||||
QVector<NodeValue> points = job.Get(kPointsInput).value< QVector<NodeValue> >();
|
||||
@@ -128,34 +131,6 @@ void PolygonGenerator::GenerateFrame(FramePtr frame, const GenerateJob &job) con
|
||||
p.setPen(Qt::NoPen);
|
||||
|
||||
p.drawPath(path);
|
||||
|
||||
// Transplant alpha channel to frame
|
||||
Color rgba = job.Get(kColorInput).toColor();
|
||||
#if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM)
|
||||
__m128 sse_color = _mm_loadu_ps(rgba.data());
|
||||
#endif
|
||||
|
||||
float *frame_dst = reinterpret_cast<float*>(frame->data());
|
||||
for (int y=0; y<frame->height(); y++) {
|
||||
uchar *src_y = img.bits() + img.bytesPerLine() * y;
|
||||
float *dst_y = frame_dst + y*frame->linesize_pixels()*VideoParams::kRGBAChannelCount;
|
||||
|
||||
for (int x=0; x<frame->width(); x++) {
|
||||
float alpha = float(src_y[x]) / 255.0f;
|
||||
float *dst = dst_y + x*VideoParams::kRGBAChannelCount;
|
||||
|
||||
#if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM)
|
||||
__m128 sse_alpha = _mm_load1_ps(&alpha);
|
||||
__m128 sse_res = _mm_mul_ps(sse_color, sse_alpha);
|
||||
|
||||
_mm_store_ps(dst, sse_res);
|
||||
#else
|
||||
for (int i=0; i<VideoParams::kRGBAChannelCount; i++) {
|
||||
dst[i] = rgba.data()[i] * alpha;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -241,6 +216,15 @@ void PolygonGenerator::UpdateGizmoPositions(const NodeValueRow &row, const NodeG
|
||||
poly_gizmo_->SetPath(GeneratePath(points).translated(half_res));
|
||||
}
|
||||
|
||||
ShaderCode PolygonGenerator::GetShaderCode(const ShaderRequest &request) const
|
||||
{
|
||||
if (request.id == QStringLiteral("rgb")) {
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/rgb.frag"));
|
||||
} else {
|
||||
return super::GetShaderCode(request);
|
||||
}
|
||||
}
|
||||
|
||||
void PolygonGenerator::GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers)
|
||||
{
|
||||
DraggableGizmo *gizmo = static_cast<DraggableGizmo*>(sender());
|
||||
|
||||
@@ -54,11 +54,13 @@ public:
|
||||
|
||||
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
|
||||
|
||||
virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
|
||||
|
||||
static const QString kPointsInput;
|
||||
static const QString kColorInput;
|
||||
|
||||
protected:
|
||||
GenerateJob GetGenerateJob(const NodeValueRow &value) const;
|
||||
ShaderJob GetGenerateJob(const NodeValueRow &value) const;
|
||||
|
||||
protected slots:
|
||||
virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) override;
|
||||
|
||||
@@ -25,10 +25,14 @@ namespace olive {
|
||||
#define super ShapeNodeBase
|
||||
|
||||
QString ShapeNode::kTypeInput = QStringLiteral("type_in");
|
||||
QString ShapeNode::kRadiusInput = QStringLiteral("radius_in");
|
||||
|
||||
ShapeNode::ShapeNode()
|
||||
{
|
||||
PrependInput(kTypeInput, NodeValue::kCombo);
|
||||
|
||||
AddInput(kRadiusInput, NodeValue::kFloat, 20.0);
|
||||
SetInputProperty(kRadiusInput, QStringLiteral("min"), 0.0);
|
||||
}
|
||||
|
||||
QString ShapeNode::Name() const
|
||||
@@ -56,9 +60,10 @@ void ShapeNode::Retranslate()
|
||||
super::Retranslate();
|
||||
|
||||
SetInputName(kTypeInput, tr("Type"));
|
||||
SetInputName(kRadiusInput, tr("Radius"));
|
||||
|
||||
// Coordinate with Type enum
|
||||
SetComboBoxStrings(kTypeInput, {tr("Rectangle"), tr("Ellipse")});
|
||||
SetComboBoxStrings(kTypeInput, {tr("Rectangle"), tr("Ellipse"), tr("Rounded Rectangle")});
|
||||
}
|
||||
|
||||
ShaderCode ShapeNode::GetShaderCode(const ShaderRequest &request) const
|
||||
@@ -76,10 +81,23 @@ void ShapeNode::Value(const NodeValueRow &value, const NodeGlobals &globals, Nod
|
||||
|
||||
job.Insert(value);
|
||||
job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
job.SetShaderID(QStringLiteral("shape"));
|
||||
|
||||
PushMergableJob(value, QVariant::fromValue(job), table);
|
||||
}
|
||||
|
||||
void ShapeNode::InputValueChangedEvent(const QString &input, int element)
|
||||
{
|
||||
if (input == kTypeInput) {
|
||||
InputFlags i = GetInputFlags(kRadiusInput);
|
||||
if (GetStandardValue(kTypeInput).toInt() == kRoundedRectangle) {
|
||||
i &= InputFlag(~kInputFlagHidden);
|
||||
} else {
|
||||
i |= kInputFlagHidden;
|
||||
}
|
||||
SetInputFlags(kRadiusInput, i);
|
||||
}
|
||||
super::InputValueChangedEvent(input, element);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@ public:
|
||||
|
||||
enum Type {
|
||||
kRectangle,
|
||||
kEllipse
|
||||
kEllipse,
|
||||
kRoundedRectangle
|
||||
};
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(ShapeNode)
|
||||
@@ -49,6 +50,10 @@ public:
|
||||
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
|
||||
|
||||
static QString kTypeInput;
|
||||
static QString kRadiusInput;
|
||||
|
||||
protected:
|
||||
virtual void InputValueChangedEvent(const QString &input, int element) override;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -94,7 +94,6 @@ void TextGeneratorV1::Value(const NodeValueRow &value, const NodeGlobals &global
|
||||
{
|
||||
GenerateJob job;
|
||||
job.Insert(value);
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
|
||||
if (!job.Get(kTextInput).toString().isEmpty()) {
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
|
||||
@@ -96,7 +96,6 @@ void TextGeneratorV2::Value(const NodeValueRow &value, const NodeGlobals &global
|
||||
{
|
||||
GenerateJob job;
|
||||
job.Insert(value);
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
job.SetRequestedFormat(VideoParams::kFormatFloat32);
|
||||
|
||||
if (!job.Get(kTextInput).toString().isEmpty()) {
|
||||
|
||||
@@ -38,6 +38,9 @@ enum TextVerticalAlign {
|
||||
};
|
||||
|
||||
const QString TextGeneratorV3::kTextInput = QStringLiteral("text_in");
|
||||
const QString TextGeneratorV3::kVerticalAlignmentInput = QStringLiteral("valign_in");
|
||||
const QString TextGeneratorV3::kUseArgsInput = QStringLiteral("use_args_in");
|
||||
const QString TextGeneratorV3::kArgsInput = QStringLiteral("args_in");
|
||||
|
||||
TextGeneratorV3::TextGeneratorV3() :
|
||||
ShapeNodeBase(false)
|
||||
@@ -47,8 +50,17 @@ TextGeneratorV3::TextGeneratorV3() :
|
||||
|
||||
SetStandardValue(kSizeInput, QVector2D(400, 300));
|
||||
|
||||
AddInput(kVerticalAlignmentInput, NodeValue::kCombo);
|
||||
|
||||
AddInput(kUseArgsInput, NodeValue::kBoolean, true, InputFlags(kInputFlagHidden | kInputFlagStatic));
|
||||
|
||||
AddInput(kArgsInput, NodeValue::kText, InputFlags(kInputFlagArray));
|
||||
SetInputProperty(kArgsInput, QStringLiteral("arraystart"), 1);
|
||||
|
||||
text_gizmo_ = new TextGizmo(this);
|
||||
text_gizmo_->SetInput(NodeInput(this, kTextInput));
|
||||
connect(text_gizmo_, &TextGizmo::Activated, this, &TextGeneratorV3::GizmoActivated);
|
||||
connect(text_gizmo_, &TextGizmo::Deactivated, this, &TextGeneratorV3::GizmoDeactivated);
|
||||
}
|
||||
|
||||
QString TextGeneratorV3::Name() const
|
||||
@@ -76,15 +88,32 @@ void TextGeneratorV3::Retranslate()
|
||||
super::Retranslate();
|
||||
|
||||
SetInputName(kTextInput, tr("Text"));
|
||||
SetInputName(kVerticalAlignmentInput, tr("Vertical Alignment"));
|
||||
SetComboBoxStrings(kVerticalAlignmentInput, {tr("Top"), tr("Middle"), tr("Bottom")});
|
||||
SetInputName(kArgsInput, tr("Arguments"));
|
||||
}
|
||||
|
||||
void TextGeneratorV3::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
GenerateJob job;
|
||||
job.Insert(value);
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
job.SetRequestedFormat(VideoParams::kFormatUnsigned8);
|
||||
|
||||
if (value[kUseArgsInput].toBool()) {
|
||||
auto args = value[kArgsInput].toArray();
|
||||
if (!args.empty()) {
|
||||
QStringList list;
|
||||
list.reserve(args.size());
|
||||
for (int i=0; i<args.size(); i++) {
|
||||
list.append(args[i].toString());
|
||||
}
|
||||
|
||||
NodeValue v = job.Get(kTextInput);
|
||||
v.set_value(FormatString(v.toString(), list));
|
||||
job.Insert(kTextInput, v);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Provide user override for this
|
||||
job.SetColorspace(project()->color_manager()->GetDefaultInputColorSpace());
|
||||
|
||||
@@ -123,6 +152,18 @@ void TextGeneratorV3::GenerateFrame(FramePtr frame, const GenerateJob& job) cons
|
||||
p.translate(frame->video_params().width()/2, frame->video_params().height()/2);
|
||||
p.setClipRect(0, 0, size.x(), size.y());
|
||||
|
||||
switch (static_cast<VerticalAlignment>(job.Get(kVerticalAlignmentInput).toInt())) {
|
||||
case kVAlignTop:
|
||||
// Do nothing
|
||||
break;
|
||||
case kVAlignMiddle:
|
||||
p.translate(0, size.y()/2-text_doc.size().height()/2);
|
||||
break;
|
||||
case kVAlignBottom:
|
||||
p.translate(0, size.y()-text_doc.size().height());
|
||||
break;
|
||||
}
|
||||
|
||||
// Ensure default text color is white
|
||||
QAbstractTextDocumentLayout::PaintContext ctx;
|
||||
ctx.palette.setColor(QPalette::Text, Qt::white);
|
||||
@@ -139,4 +180,52 @@ void TextGeneratorV3::UpdateGizmoPositions(const NodeValueRow &row, const NodeGl
|
||||
text_gizmo_->SetHtml(row[kTextInput].toString());
|
||||
}
|
||||
|
||||
QString TextGeneratorV3::FormatString(const QString &input, const QStringList &args)
|
||||
{
|
||||
QString output;
|
||||
output.reserve(input.size());
|
||||
|
||||
for (int i=0; i<input.size(); i++) {
|
||||
const QChar &this_char = input.at(i);
|
||||
|
||||
if (i < input.size()-1 && this_char == '%') {
|
||||
const QChar &next_char = input.at(i+1);
|
||||
if (next_char == '%') {
|
||||
// Double percent, append a single percent
|
||||
output.append('%');
|
||||
i++;
|
||||
} else if (next_char.isDigit()) {
|
||||
// Find length of number
|
||||
QString num;
|
||||
i++;
|
||||
while (i < input.size() && input.at(i).isDigit()) {
|
||||
num.append(input.at(i));
|
||||
i++;
|
||||
}
|
||||
i--;
|
||||
int index = num.toInt()-1;
|
||||
if (index >= 0 && index < args.size()) {
|
||||
output.append(args.at(index));
|
||||
}
|
||||
} else {
|
||||
output.append(this_char);
|
||||
}
|
||||
} else {
|
||||
output.append(this_char);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void TextGeneratorV3::GizmoActivated()
|
||||
{
|
||||
SetStandardValue(kUseArgsInput, false);
|
||||
}
|
||||
|
||||
void TextGeneratorV3::GizmoDeactivated()
|
||||
{
|
||||
SetStandardValue(kUseArgsInput, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -47,11 +47,27 @@ public:
|
||||
|
||||
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
|
||||
|
||||
enum VerticalAlignment
|
||||
{
|
||||
kVAlignTop,
|
||||
kVAlignMiddle,
|
||||
kVAlignBottom
|
||||
};
|
||||
|
||||
static const QString kTextInput;
|
||||
static const QString kVerticalAlignmentInput;
|
||||
static const QString kUseArgsInput;
|
||||
static const QString kArgsInput;
|
||||
|
||||
static QString FormatString(const QString &input, const QStringList &args);
|
||||
|
||||
private:
|
||||
TextGizmo *text_gizmo_;
|
||||
|
||||
private slots:
|
||||
void GizmoActivated();
|
||||
void GizmoDeactivated();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -28,4 +28,9 @@ NodeGizmo::NodeGizmo(QObject *parent) :
|
||||
setParent(parent);
|
||||
}
|
||||
|
||||
NodeGizmo::~NodeGizmo()
|
||||
{
|
||||
setParent(nullptr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ class NodeGizmo : public QObject
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit NodeGizmo(QObject *parent = nullptr);
|
||||
virtual ~NodeGizmo() override;
|
||||
|
||||
virtual void Draw(QPainter *p) const {}
|
||||
|
||||
|
||||
@@ -42,6 +42,10 @@ public:
|
||||
|
||||
void UpdateInputHtml(const QString &s, const rational &time);
|
||||
|
||||
signals:
|
||||
void Activated();
|
||||
void Deactivated();
|
||||
|
||||
private:
|
||||
QRectF rect_;
|
||||
|
||||
|
||||
@@ -132,7 +132,6 @@ void ChromaKeyNode::Value(const NodeValueRow &value, const NodeGlobals &globals,
|
||||
ColorTransformJob job;
|
||||
|
||||
job.Insert(value);
|
||||
job.SetAlphaChannelRequired(ColorTransformJob::kAlphaForceOn);
|
||||
job.SetColorProcessor(processor());
|
||||
job.SetInputTexture(value[kTextureInput].toTexture());
|
||||
job.SetNeedsCustomShader(this);
|
||||
|
||||
@@ -95,7 +95,6 @@ void ColorDifferenceKeyNode::Value(const NodeValueRow &value, const NodeGlobals
|
||||
{
|
||||
ShaderJob job;
|
||||
job.Insert(value);
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
|
||||
// If there's no texture, no need to run an operation
|
||||
if (job.Get(kTextureInput).toTexture()) {
|
||||
|
||||
@@ -372,9 +372,6 @@ void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QSt
|
||||
// Replace with adjusted matrix
|
||||
job.Insert(val_a.type() == NodeValue::kTexture ? param_b_in : param_a_in,
|
||||
NodeValue(NodeValue::kMatrix, adjusted_matrix, this));
|
||||
|
||||
// It's likely an alpha channel will result from this operation
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,12 +90,6 @@ void MergeNode::Value(const NodeValueRow &value, const NodeGlobals &globals, Nod
|
||||
// We only have a base texture, no need to alpha over
|
||||
table->Push(job.Get(kBaseIn));
|
||||
} else {
|
||||
// We have both textures, push the job
|
||||
if (base_tex->channel_count() < VideoParams::kRGBAChannelCount) {
|
||||
// Base has no alpha, therefore this merge operation will not add an alpha channel
|
||||
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOff);
|
||||
}
|
||||
|
||||
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ TrackList::TrackList(Sequence *parent, const Track::Type &type, const QString &t
|
||||
|
||||
Track *TrackList::GetTrackAt(int index) const
|
||||
{
|
||||
if (index < track_cache_.size()) {
|
||||
if (index >= 0 && index < track_cache_.size()) {
|
||||
return track_cache_.at(index);
|
||||
} else {
|
||||
return nullptr;
|
||||
|
||||
@@ -398,6 +398,20 @@ void ViewerOutput::ConnectedToPreviewEvent()
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerOutput::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
if (HasInputWithID(kTextureInput)) {
|
||||
NodeValue repush = value[kTextureInput];
|
||||
repush.set_tag(Track::Reference(Track::kVideo, 0).ToString());
|
||||
table->Push(repush);
|
||||
}
|
||||
if (HasInputWithID(kSamplesInput)) {
|
||||
NodeValue repush = value[kSamplesInput];
|
||||
repush.set_tag(Track::Reference(Track::kAudio, 0).ToString());
|
||||
table->Push(value[kSamplesInput]);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerOutput::InputValueChangedEvent(const QString &input, int element)
|
||||
{
|
||||
if (element == 0) {
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#ifndef VIEWER_H
|
||||
#define VIEWER_H
|
||||
|
||||
#include "codec/encoder.h"
|
||||
#include "common/rational.h"
|
||||
#include "node/node.h"
|
||||
#include "node/output/track/track.h"
|
||||
@@ -126,7 +127,7 @@ public:
|
||||
return InputArraySize(kSubtitleParamsInput);
|
||||
}
|
||||
|
||||
int GetTotalStreamCount() const
|
||||
virtual int GetTotalStreamCount() const
|
||||
{
|
||||
return GetVideoStreamCount() + GetAudioStreamCount() + GetSubtitleStreamCount();
|
||||
}
|
||||
@@ -186,6 +187,11 @@ public:
|
||||
bool IsVideoAutoCacheEnabled() const { qDebug() << "sequence ac is a stub"; return false; }
|
||||
void SetVideoAutoCacheEnabled(bool e) { qDebug() << "sequence ac is a stub"; }
|
||||
|
||||
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
|
||||
|
||||
const EncodingParams &GetLastUsedEncodingParams() const { return last_used_encoding_params_; }
|
||||
void SetLastUsedEncodingParams(const EncodingParams &p) { last_used_encoding_params_ = p; }
|
||||
|
||||
static const QString kVideoParamsInput;
|
||||
static const QString kAudioParamsInput;
|
||||
static const QString kSubtitleParamsInput;
|
||||
@@ -243,6 +249,8 @@ private:
|
||||
bool autocache_input_video_;
|
||||
bool autocache_input_audio_;
|
||||
|
||||
EncodingParams last_used_encoding_params_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ enum InputFlag {
|
||||
kInputFlagArray = 0x1,
|
||||
kInputFlagNotKeyframable = 0x2,
|
||||
kInputFlagNotConnectable = 0x4,
|
||||
kInputFlagStatic = kInputFlagNotKeyframable | kInputFlagNotConnectable,
|
||||
kInputFlagHidden = 0x8
|
||||
};
|
||||
|
||||
|
||||
@@ -73,6 +73,21 @@ bool Folder::ChildExistsWithName(const QString &s) const
|
||||
return ChildExistsWithNameInternal(this, s);
|
||||
}
|
||||
|
||||
bool Folder::HasChildRecursive(Node *child) const
|
||||
{
|
||||
for (Node *i : item_children_) {
|
||||
if (i == child) {
|
||||
return true;
|
||||
} else if (Folder *f = dynamic_cast<Folder*>(i)) {
|
||||
if (f->HasChildRecursive(child)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int Folder::index_of_child_in_array(Node *item) const
|
||||
{
|
||||
int index_of_item = item_children_.indexOf(item);
|
||||
|
||||
@@ -65,6 +65,8 @@ public:
|
||||
|
||||
bool ChildExistsWithName(const QString& s) const;
|
||||
|
||||
bool HasChildRecursive(Node *child) const;
|
||||
|
||||
int item_child_count() const
|
||||
{
|
||||
return item_children_.size();
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
namespace olive {
|
||||
|
||||
const QString Footage::kFilenameInput = QStringLiteral("file_in");
|
||||
const QString Footage::kLoopModeInput = QStringLiteral("loop_in");
|
||||
|
||||
#define super ViewerOutput
|
||||
|
||||
@@ -45,10 +44,9 @@ Footage::Footage(const QString &filename) :
|
||||
ViewerOutput(false, false),
|
||||
timestamp_(0),
|
||||
valid_(false),
|
||||
cancelled_(nullptr)
|
||||
cancelled_(nullptr),
|
||||
total_stream_count_(0)
|
||||
{
|
||||
PrependInput(kLoopModeInput, NodeValue::kCombo, 0, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||
|
||||
PrependInput(kFilenameInput, NodeValue::kFile, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||
|
||||
Clear();
|
||||
@@ -70,8 +68,6 @@ void Footage::Retranslate()
|
||||
super::Retranslate();
|
||||
|
||||
SetInputName(kFilenameInput, tr("Filename"));
|
||||
SetInputName(kLoopModeInput, tr("Loop Mode"));
|
||||
SetComboBoxStrings(kLoopModeInput, {tr("None"), tr("Loop"), tr("Clamp")});
|
||||
}
|
||||
|
||||
void Footage::InputValueChangedEvent(const QString &input, int element)
|
||||
@@ -130,6 +126,9 @@ void Footage::Clear()
|
||||
// Clear decoder link
|
||||
decoder_.clear();
|
||||
|
||||
// Clear total stream count
|
||||
total_stream_count_ = 0;
|
||||
|
||||
// Reset ready state
|
||||
valid_ = false;
|
||||
}
|
||||
@@ -139,11 +138,6 @@ void Footage::SetValid()
|
||||
valid_ = true;
|
||||
}
|
||||
|
||||
Footage::LoopMode Footage::loop_mode() const
|
||||
{
|
||||
return static_cast<LoopMode>(GetStandardValue(kLoopModeInput).toInt());
|
||||
}
|
||||
|
||||
QString Footage::filename() const
|
||||
{
|
||||
return GetStandardValue(kFilenameInput).toString();
|
||||
@@ -263,17 +257,15 @@ void Footage::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeV
|
||||
// Pop filename from table
|
||||
QString file = value[kFilenameInput].toString();
|
||||
|
||||
LoopMode loop_mode = static_cast<LoopMode>(value[kLoopModeInput].toInt());
|
||||
|
||||
// If the file exists and the reference is valid, push a footage job to the renderer
|
||||
if (QFileInfo(file).exists()) {
|
||||
if (QFileInfo::exists(file)) {
|
||||
// Push length
|
||||
table->Push(NodeValue::kRational, QVariant::fromValue(GetLength()), this, false, QStringLiteral("length"));
|
||||
table->Push(NodeValue::kRational, QVariant::fromValue(GetLength()), this, QStringLiteral("length"));
|
||||
|
||||
// Push each stream as a footage job
|
||||
for (int i=0; i<GetTotalStreamCount(); i++) {
|
||||
Track::Reference ref = GetReferenceFromRealIndex(i);
|
||||
FootageJob job(decoder_, filename(), ref.type(), GetLength(), loop_mode);
|
||||
FootageJob job(decoder_, filename(), ref.type(), GetLength());
|
||||
|
||||
NodeValue::Type type;
|
||||
|
||||
@@ -294,7 +286,7 @@ void Footage::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeV
|
||||
type = NodeValue::kSamples;
|
||||
}
|
||||
|
||||
table->Push(type, QVariant::fromValue(job), this, false, ref.ToString());
|
||||
table->Push(type, QVariant::fromValue(job), this, ref.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -339,7 +331,7 @@ bool TimeIsOutOfBounds(const rational& time, const rational& length)
|
||||
return time < 0 || time >= length;
|
||||
}
|
||||
|
||||
rational Footage::AdjustTimeByLoopMode(rational time, Footage::LoopMode loop_mode, const rational &length, VideoParams::Type type, const rational& timebase)
|
||||
rational Footage::AdjustTimeByLoopMode(rational time, Decoder::LoopMode loop_mode, const rational &length, VideoParams::Type type, const rational& timebase)
|
||||
{
|
||||
if (type == VideoParams::kVideoTypeStill) {
|
||||
// No looping for still images
|
||||
@@ -348,15 +340,15 @@ rational Footage::AdjustTimeByLoopMode(rational time, Footage::LoopMode loop_mod
|
||||
|
||||
if (TimeIsOutOfBounds(time, length)) {
|
||||
switch (loop_mode) {
|
||||
case kLoopModeOff:
|
||||
case Decoder::kLoopModeOff:
|
||||
// Return no time to indicate no frame should be shown here
|
||||
time = rational::NaN;
|
||||
break;
|
||||
case kLoopModeClamp:
|
||||
case Decoder::kLoopModeClamp:
|
||||
// Clamp footage time to length
|
||||
time = clamp(time, rational(0), length - timebase);
|
||||
break;
|
||||
case kLoopModeLoop:
|
||||
case Decoder::kLoopModeLoop:
|
||||
// Loop footage time around job length
|
||||
do {
|
||||
if (time >= length) {
|
||||
@@ -509,6 +501,8 @@ void Footage::Reprobe()
|
||||
SetStream(Track::kSubtitle, QVariant::fromValue(footage_info.GetSubtitleStreams().at(i)), i);
|
||||
}
|
||||
|
||||
total_stream_count_ = footage_info.GetStreamCount();
|
||||
|
||||
SetValid();
|
||||
}
|
||||
|
||||
@@ -524,10 +518,13 @@ VideoParams Footage::MergeVideoStream(const VideoParams &base, const VideoParams
|
||||
merged.set_interlacing(over.interlacing());
|
||||
merged.set_colorspace(over.colorspace());
|
||||
merged.set_premultiplied_alpha(over.premultiplied_alpha());
|
||||
if (merged.video_type() == VideoParams::kVideoTypeImageSequence && over.video_type() == VideoParams::kVideoTypeImageSequence) {
|
||||
merged.set_video_type(over.video_type());
|
||||
merged.set_color_range(over.color_range());
|
||||
if (merged.video_type() == VideoParams::kVideoTypeImageSequence) {
|
||||
merged.set_start_time(over.start_time());
|
||||
merged.set_duration(over.duration());
|
||||
merged.set_frame_rate(over.frame_rate());
|
||||
merged.set_time_base(over.time_base());
|
||||
}
|
||||
|
||||
return merged;
|
||||
|
||||
@@ -24,10 +24,12 @@
|
||||
#include <QList>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "codec/decoder.h"
|
||||
#include "common/rational.h"
|
||||
#include "footagedescription.h"
|
||||
#include "node/output/viewer/viewer.h"
|
||||
#include "render/audioparams.h"
|
||||
#include "render/cancelatom.h"
|
||||
#include "render/videoparams.h"
|
||||
|
||||
namespace olive {
|
||||
@@ -43,12 +45,6 @@ class Footage : public ViewerOutput
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum LoopMode {
|
||||
kLoopModeOff,
|
||||
kLoopModeLoop,
|
||||
kLoopModeClamp
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Footage Constructor
|
||||
*/
|
||||
@@ -100,11 +96,6 @@ public:
|
||||
*/
|
||||
void SetValid();
|
||||
|
||||
/**
|
||||
* @brief Get currently set loop mode
|
||||
*/
|
||||
LoopMode loop_mode() const;
|
||||
|
||||
/**
|
||||
* @brief Return the current filename of this Footage object
|
||||
*/
|
||||
@@ -141,7 +132,7 @@ public:
|
||||
*/
|
||||
void set_timestamp(const qint64 &t);
|
||||
|
||||
void SetCancelPointer(const QAtomicInt* c)
|
||||
void SetCancelPointer(CancelAtom *c)
|
||||
{
|
||||
cancelled_ = c;
|
||||
}
|
||||
@@ -182,15 +173,16 @@ public:
|
||||
|
||||
virtual Node *GetConnectedSampleOutput() override;
|
||||
|
||||
static rational AdjustTimeByLoopMode(rational time, LoopMode loop_mode, const rational& length, VideoParams::Type type, const rational &timebase);
|
||||
static rational AdjustTimeByLoopMode(rational time, Decoder::LoopMode loop_mode, const rational& length, VideoParams::Type type, const rational &timebase);
|
||||
|
||||
virtual void LoadFinishedEvent() override;
|
||||
|
||||
virtual qint64 creation_time() const override;
|
||||
virtual qint64 mod_time() const override;
|
||||
|
||||
virtual int GetTotalStreamCount() const override { return total_stream_count_; }
|
||||
|
||||
static const QString kFilenameInput;
|
||||
static const QString kLoopModeInput;
|
||||
|
||||
protected:
|
||||
virtual void InputValueChangedEvent(const QString &input, int element) override;
|
||||
@@ -232,7 +224,9 @@ private:
|
||||
|
||||
bool valid_;
|
||||
|
||||
const QAtomicInt* cancelled_;
|
||||
CancelAtom *cancelled_;
|
||||
|
||||
int total_stream_count_;
|
||||
|
||||
private slots:
|
||||
void CheckFootage();
|
||||
|
||||
@@ -43,9 +43,11 @@ bool FootageDescription::Load(const QString &filename)
|
||||
// Default to first version of metadata (which wasn't versioned at all)
|
||||
unsigned version = 1;
|
||||
|
||||
XMLAttributeLoop((&reader), attr) {
|
||||
if (attr.name() == QStringLiteral("version")) {
|
||||
version = attr.value().toUInt();
|
||||
{
|
||||
XMLAttributeLoop((&reader), attr) {
|
||||
if (attr.name() == QStringLiteral("version")) {
|
||||
version = attr.value().toUInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +60,14 @@ bool FootageDescription::Load(const QString &filename)
|
||||
if (reader.name() == QStringLiteral("decoder")) {
|
||||
decoder_ = reader.readElementText();
|
||||
} else if (reader.name() == QStringLiteral("streams")) {
|
||||
{
|
||||
XMLAttributeLoop((&reader), attr) {
|
||||
if (attr.name() == QStringLiteral("count")) {
|
||||
total_stream_count_ = attr.value().toInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("video")) {
|
||||
VideoParams vp;
|
||||
@@ -116,6 +126,8 @@ bool FootageDescription::Save(const QString &filename) const
|
||||
|
||||
writer.writeStartElement(QStringLiteral("streams"));
|
||||
|
||||
writer.writeAttribute(QStringLiteral("count"), QString::number(total_stream_count_));
|
||||
|
||||
foreach (const VideoParams& vp, video_streams_) {
|
||||
writer.writeStartElement(QStringLiteral("video"));
|
||||
vp.Save(&writer);
|
||||
|
||||
@@ -32,7 +32,8 @@ class FootageDescription
|
||||
{
|
||||
public:
|
||||
FootageDescription(const QString& decoder = QString()) :
|
||||
decoder_(decoder)
|
||||
decoder_(decoder),
|
||||
total_stream_count_(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -118,6 +119,9 @@ public:
|
||||
return StreamIsVideo(index) || StreamIsAudio(index) || StreamIsSubtitle(index);
|
||||
}
|
||||
|
||||
int GetStreamCount() const { return total_stream_count_; }
|
||||
void SetStreamCount(int s) { total_stream_count_ = s; }
|
||||
|
||||
bool Load(const QString& filename);
|
||||
|
||||
bool Save(const QString& filename) const;
|
||||
@@ -138,7 +142,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr unsigned kFootageMetaVersion = 2;
|
||||
static constexpr unsigned kFootageMetaVersion = 5;
|
||||
|
||||
QString decoder_;
|
||||
|
||||
@@ -148,6 +152,8 @@ private:
|
||||
|
||||
QVector<SubtitleParams> subtitle_streams_;
|
||||
|
||||
int total_stream_count_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
|
||||
#include "common/qtutils.h"
|
||||
#include "common/xmlutils.h"
|
||||
#include "core.h"
|
||||
#include "dialog/progress/progress.h"
|
||||
@@ -173,16 +174,7 @@ void Project::RegenerateUuid()
|
||||
|
||||
Project *Project::GetProjectFromObject(const QObject *o)
|
||||
{
|
||||
QObject *t = o->parent();
|
||||
|
||||
while (t) {
|
||||
if (Project *p = dynamic_cast<Project*>(t)) {
|
||||
return p;
|
||||
}
|
||||
t = t->parent();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return QtUtils::GetParentOfType<Project>(o);
|
||||
}
|
||||
|
||||
void Project::ColorManagerValueChanged(const NodeInput &input, const TimeRange &range)
|
||||
|
||||
@@ -170,6 +170,11 @@ QVariant ProjectViewModel::data(const QModelIndex &index, int role) const
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Qt::EditRole:
|
||||
if (column_type == kName) {
|
||||
return internal_item->GetLabel();
|
||||
}
|
||||
break;
|
||||
case Qt::DecorationRole:
|
||||
// If this is the first column, return the Item's icon
|
||||
if (column_type == kName) {
|
||||
|
||||
@@ -159,6 +159,11 @@ ProjectSerializer::Result ProjectSerializer::Save(const SaveData &data, const QS
|
||||
|
||||
Result inner_result = Save(&writer, data, type);
|
||||
|
||||
if (writer.hasError()) {
|
||||
Result r(kXmlError);
|
||||
return r;
|
||||
}
|
||||
|
||||
project_file.close();
|
||||
|
||||
if (inner_result != kSuccess) {
|
||||
@@ -262,7 +267,7 @@ ProjectSerializer::Result ProjectSerializer::LoadWithSerializerVersion(uint vers
|
||||
Result r(kSuccess);
|
||||
if (reader->hasError()) {
|
||||
r = Result(kXmlError);
|
||||
r.SetDetails(reader->errorString());
|
||||
r.SetDetails(QCoreApplication::translate("Serializer", "%1 on line %2").arg(reader->errorString(), QString::number(reader->lineNumber())));
|
||||
}
|
||||
r.SetLoadData(ld);
|
||||
return r;
|
||||
|
||||
@@ -25,6 +25,20 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
// These wrappers may appear to do nothing, but they seem to address a bug where sometimes
|
||||
// QXmlStreamWriter would insert nonsense null characters into a project. This is probably some
|
||||
// real edge case like compiler optimization or some bullshit like that. I don't even know if it
|
||||
// affects all platforms, but it definitely affected me on Linux.
|
||||
void WriteStartElement(QXmlStreamWriter *writer, const QString &s)
|
||||
{
|
||||
writer->writeStartElement(s);
|
||||
}
|
||||
|
||||
void WriteEndElement(QXmlStreamWriter *writer)
|
||||
{
|
||||
writer->writeEndElement();
|
||||
}
|
||||
|
||||
ProjectSerializer220403::LoadData ProjectSerializer220403::Load(Project *project, QXmlStreamReader *reader, void *reserved) const
|
||||
{
|
||||
QMap<quintptr, QMap<QString, QString> > properties;
|
||||
@@ -328,23 +342,23 @@ void ProjectSerializer220403::Save(QXmlStreamWriter *writer, const SaveData &dat
|
||||
{
|
||||
if (!data.GetOnlySerializeMarkers().empty()) {
|
||||
|
||||
writer->writeStartElement(QStringLiteral("markers"));
|
||||
WriteStartElement(writer, QStringLiteral("markers"));
|
||||
|
||||
for (auto it=data.GetOnlySerializeMarkers().cbegin(); it!=data.GetOnlySerializeMarkers().cend(); it++) {
|
||||
TimelineMarker *marker = *it;
|
||||
|
||||
writer->writeStartElement(QStringLiteral("marker"));
|
||||
WriteStartElement(writer, QStringLiteral("marker"));
|
||||
|
||||
SaveMarker(writer, marker);
|
||||
|
||||
writer->writeEndElement(); // marker
|
||||
WriteEndElement(writer); // marker
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // markers
|
||||
WriteEndElement(writer); // markers
|
||||
|
||||
} else if (!data.GetOnlySerializeKeyframes().empty()) {
|
||||
|
||||
writer->writeStartElement(QStringLiteral("keyframes"));
|
||||
WriteStartElement(writer, QStringLiteral("keyframes"));
|
||||
|
||||
// Organize keyframes into node+input
|
||||
QHash<QString, QHash<QString, QMap<int, QMap<int, QVector<NodeKeyframe*> > > > > organized;
|
||||
@@ -355,57 +369,57 @@ void ProjectSerializer220403::Save(QXmlStreamWriter *writer, const SaveData &dat
|
||||
}
|
||||
|
||||
for (auto it=organized.cbegin(); it!=organized.cend(); it++) {
|
||||
writer->writeStartElement(QStringLiteral("node"));
|
||||
WriteStartElement(writer, QStringLiteral("node"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("id"), it.key());
|
||||
|
||||
for (auto jt=it.value().cbegin(); jt!=it.value().cend(); jt++) {
|
||||
writer->writeStartElement(QStringLiteral("input"));
|
||||
WriteStartElement(writer, QStringLiteral("input"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("id"), jt.key());
|
||||
|
||||
for (auto kt=jt.value().cbegin(); kt!=jt.value().cend(); kt++) {
|
||||
writer->writeStartElement(QStringLiteral("element"));
|
||||
WriteStartElement(writer, QStringLiteral("element"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("id"), QString::number(kt.key()));
|
||||
|
||||
for (auto lt=kt.value().cbegin(); lt!=kt.value().cend(); lt++) {
|
||||
const QVector<NodeKeyframe *> &keys = lt.value();
|
||||
|
||||
writer->writeStartElement(QStringLiteral("track"));
|
||||
WriteStartElement(writer, QStringLiteral("track"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("id"), QString::number(lt.key()));
|
||||
|
||||
for (NodeKeyframe *key : keys) {
|
||||
writer->writeStartElement(QStringLiteral("key"));
|
||||
WriteStartElement(writer, QStringLiteral("key"));
|
||||
SaveKeyframe(writer, key, key->parent()->GetInputDataType(key->input()));
|
||||
writer->writeEndElement(); // key
|
||||
WriteEndElement(writer); // key
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // track
|
||||
WriteEndElement(writer); // track
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // element
|
||||
WriteEndElement(writer); // element
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // input
|
||||
WriteEndElement(writer); // input
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // node;
|
||||
WriteEndElement(writer); // node;
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // keyframes
|
||||
WriteEndElement(writer); // keyframes
|
||||
|
||||
} else if (Project *project = data.GetProject()) {
|
||||
|
||||
writer->writeTextElement(QStringLiteral("uuid"), project->GetUuid().toString());
|
||||
|
||||
writer->writeStartElement(QStringLiteral("nodes"));
|
||||
WriteStartElement(writer, QStringLiteral("nodes"));
|
||||
|
||||
const QVector<Node*> &using_node_list = (data.GetOnlySerializeNodes().isEmpty()) ? project->nodes() : data.GetOnlySerializeNodes();
|
||||
|
||||
foreach (Node* node, using_node_list) {
|
||||
writer->writeStartElement(QStringLiteral("node"));
|
||||
WriteStartElement(writer, QStringLiteral("node"));
|
||||
|
||||
if (node == project->root()) {
|
||||
writer->writeAttribute(QStringLiteral("root"), QStringLiteral("1"));
|
||||
@@ -419,39 +433,39 @@ void ProjectSerializer220403::Save(QXmlStreamWriter *writer, const SaveData &dat
|
||||
|
||||
SaveNode(node, writer);
|
||||
|
||||
writer->writeEndElement(); // node
|
||||
WriteEndElement(writer); // node
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // nodes
|
||||
WriteEndElement(writer); // nodes
|
||||
|
||||
writer->writeStartElement(QStringLiteral("positions"));
|
||||
WriteStartElement(writer, QStringLiteral("positions"));
|
||||
|
||||
foreach (Node* context, using_node_list) {
|
||||
const Node::PositionMap &map = context->GetContextPositions();
|
||||
|
||||
if (!map.isEmpty()) {
|
||||
writer->writeStartElement(QStringLiteral("context"));
|
||||
WriteStartElement(writer, QStringLiteral("context"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(context)));
|
||||
|
||||
for (auto jt=map.cbegin(); jt!=map.cend(); jt++) {
|
||||
if (data.GetOnlySerializeNodes().isEmpty() || data.GetOnlySerializeNodes().contains(jt.key())) {
|
||||
writer->writeStartElement(QStringLiteral("node"));
|
||||
WriteStartElement(writer, QStringLiteral("node"));
|
||||
SavePosition(writer, jt.key(), jt.value());
|
||||
writer->writeEndElement(); // node
|
||||
WriteEndElement(writer); // node
|
||||
}
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // context
|
||||
WriteEndElement(writer); // context
|
||||
}
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // positions
|
||||
WriteEndElement(writer); // positions
|
||||
|
||||
writer->writeStartElement(QStringLiteral("properties"));
|
||||
WriteStartElement(writer, QStringLiteral("properties"));
|
||||
|
||||
for (auto it=data.GetProperties().cbegin(); it!=data.GetProperties().cend(); it++) {
|
||||
writer->writeStartElement(QStringLiteral("node"));
|
||||
WriteStartElement(writer, QStringLiteral("node"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(it.key())));
|
||||
|
||||
@@ -459,10 +473,10 @@ void ProjectSerializer220403::Save(QXmlStreamWriter *writer, const SaveData &dat
|
||||
writer->writeTextElement(jt.key(), jt.value());
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // node
|
||||
WriteEndElement(writer); // node
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // properties
|
||||
WriteEndElement(writer); // properties
|
||||
|
||||
// Save main window project layout
|
||||
project->GetLayoutInfo().toXml(writer);
|
||||
@@ -583,59 +597,59 @@ void ProjectSerializer220403::SaveNode(Node *node, QXmlStreamWriter *writer) con
|
||||
writer->writeTextElement(QStringLiteral("color"), QString::number(node->GetOverrideColor()));
|
||||
|
||||
foreach (const QString& input, node->inputs()) {
|
||||
writer->writeStartElement(QStringLiteral("input"));
|
||||
WriteStartElement(writer, QStringLiteral("input"));
|
||||
|
||||
SaveInput(node, writer, input);
|
||||
|
||||
writer->writeEndElement(); // input
|
||||
WriteEndElement(writer); // input
|
||||
}
|
||||
|
||||
writer->writeStartElement(QStringLiteral("links"));
|
||||
WriteStartElement(writer, QStringLiteral("links"));
|
||||
foreach (Node* link, node->links()) {
|
||||
writer->writeTextElement(QStringLiteral("link"), QString::number(reinterpret_cast<quintptr>(link)));
|
||||
}
|
||||
writer->writeEndElement(); // links
|
||||
WriteEndElement(writer); // links
|
||||
|
||||
writer->writeStartElement(QStringLiteral("connections"));
|
||||
WriteStartElement(writer, QStringLiteral("connections"));
|
||||
for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) {
|
||||
writer->writeStartElement(QStringLiteral("connection"));
|
||||
WriteStartElement(writer, QStringLiteral("connection"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("input"), it->first.input());
|
||||
writer->writeAttribute(QStringLiteral("element"), QString::number(it->first.element()));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("output"), QString::number(reinterpret_cast<quintptr>(it->second)));
|
||||
|
||||
writer->writeEndElement(); // connection
|
||||
WriteEndElement(writer); // connection
|
||||
}
|
||||
writer->writeEndElement(); // connections
|
||||
WriteEndElement(writer); // connections
|
||||
|
||||
writer->writeStartElement(QStringLiteral("hints"));
|
||||
WriteStartElement(writer, QStringLiteral("hints"));
|
||||
for (auto it=node->GetValueHints().cbegin(); it!=node->GetValueHints().cend(); it++) {
|
||||
writer->writeStartElement(QStringLiteral("hint"));
|
||||
WriteStartElement(writer, QStringLiteral("hint"));
|
||||
|
||||
writer->writeAttribute(QStringLiteral("input"), it.key().input);
|
||||
writer->writeAttribute(QStringLiteral("element"), QString::number(it.key().element));
|
||||
|
||||
SaveValueHint(&it.value(), writer);
|
||||
|
||||
writer->writeEndElement(); // hint
|
||||
WriteEndElement(writer); // hint
|
||||
}
|
||||
writer->writeEndElement();
|
||||
WriteEndElement(writer); // hints
|
||||
|
||||
writer->writeStartElement(QStringLiteral("caches"));
|
||||
WriteStartElement(writer, QStringLiteral("caches"));
|
||||
|
||||
writer->writeTextElement(QStringLiteral("audio"), node->audio_playback_cache()->GetUuid().toString());
|
||||
writer->writeTextElement(QStringLiteral("video"), node->video_frame_cache()->GetUuid().toString());
|
||||
writer->writeTextElement(QStringLiteral("thumb"), node->thumbnail_cache()->GetUuid().toString());
|
||||
writer->writeTextElement(QStringLiteral("waveform"), node->waveform_cache()->GetUuid().toString());
|
||||
|
||||
writer->writeEndElement(); // caches
|
||||
WriteEndElement(writer); // caches
|
||||
|
||||
writer->writeStartElement(QStringLiteral("custom"));
|
||||
WriteStartElement(writer, QStringLiteral("custom"));
|
||||
|
||||
SaveNodeCustom(writer, node);
|
||||
|
||||
writer->writeEndElement(); // custom
|
||||
WriteEndElement(writer); // custom
|
||||
}
|
||||
|
||||
void ProjectSerializer220403::LoadInput(Node *node, QXmlStreamReader *reader, XMLNodeData &xml_node_data) const
|
||||
@@ -705,27 +719,27 @@ void ProjectSerializer220403::SaveInput(Node *node, QXmlStreamWriter *writer, co
|
||||
{
|
||||
writer->writeAttribute(QStringLiteral("id"), id);
|
||||
|
||||
writer->writeStartElement(QStringLiteral("primary"));
|
||||
WriteStartElement(writer, QStringLiteral("primary"));
|
||||
|
||||
SaveImmediate(writer, node, id, -1);
|
||||
|
||||
writer->writeEndElement(); // primary
|
||||
WriteEndElement(writer); // primary
|
||||
|
||||
writer->writeStartElement(QStringLiteral("subelements"));
|
||||
WriteStartElement(writer, QStringLiteral("subelements"));
|
||||
|
||||
int arr_sz = node->InputArraySize(id);
|
||||
|
||||
writer->writeAttribute(QStringLiteral("count"), QString::number(arr_sz));
|
||||
|
||||
for (int i=0; i<arr_sz; i++) {
|
||||
writer->writeStartElement(QStringLiteral("element"));
|
||||
WriteStartElement(writer, QStringLiteral("element"));
|
||||
|
||||
SaveImmediate(writer, node, id, i);
|
||||
|
||||
writer->writeEndElement(); // element
|
||||
WriteEndElement(writer); // element
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // subelements
|
||||
WriteEndElement(writer); // subelements
|
||||
}
|
||||
|
||||
void ProjectSerializer220403::LoadImmediate(QXmlStreamReader *reader, Node *node, const QString& input, int element, XMLNodeData &xml_node_data) const
|
||||
@@ -835,10 +849,10 @@ void ProjectSerializer220403::SaveImmediate(QXmlStreamWriter *writer, Node *node
|
||||
NodeValue::Type data_type = node->GetInputDataType(input);
|
||||
|
||||
// Write standard value
|
||||
writer->writeStartElement(QStringLiteral("standard"));
|
||||
WriteStartElement(writer, QStringLiteral("standard"));
|
||||
|
||||
foreach (const QVariant& v, node->GetSplitStandardValue(input, element)) {
|
||||
writer->writeStartElement(QStringLiteral("track"));
|
||||
WriteStartElement(writer, QStringLiteral("track"));
|
||||
|
||||
if (data_type == NodeValue::kVideoParams) {
|
||||
v.value<VideoParams>().Save(writer);
|
||||
@@ -848,29 +862,29 @@ void ProjectSerializer220403::SaveImmediate(QXmlStreamWriter *writer, Node *node
|
||||
writer->writeCharacters(NodeValue::ValueToString(data_type, v, true));
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // track
|
||||
WriteEndElement(writer); // track
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // standard
|
||||
WriteEndElement(writer); // standard
|
||||
|
||||
// Write keyframes
|
||||
writer->writeStartElement(QStringLiteral("keyframes"));
|
||||
WriteStartElement(writer, QStringLiteral("keyframes"));
|
||||
|
||||
for (const NodeKeyframeTrack& track : node->GetKeyframeTracks(input, element)) {
|
||||
writer->writeStartElement(QStringLiteral("track"));
|
||||
WriteStartElement(writer, QStringLiteral("track"));
|
||||
|
||||
for (NodeKeyframe* key : track) {
|
||||
writer->writeStartElement(QStringLiteral("key"));
|
||||
WriteStartElement(writer, QStringLiteral("key"));
|
||||
|
||||
SaveKeyframe(writer, key, data_type);
|
||||
|
||||
writer->writeEndElement(); // key
|
||||
WriteEndElement(writer); // key
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // track
|
||||
WriteEndElement(writer); // track
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // keyframes
|
||||
WriteEndElement(writer); // keyframes
|
||||
|
||||
if (data_type == NodeValue::kColor) {
|
||||
// Save color management information
|
||||
@@ -1115,9 +1129,9 @@ void ProjectSerializer220403::SaveNodeCustom(QXmlStreamWriter *writer, Node *nod
|
||||
{
|
||||
if (ViewerOutput *viewer = dynamic_cast<ViewerOutput*>(node)) {
|
||||
// Write TimelinePoints
|
||||
writer->writeStartElement(QStringLiteral("points"));
|
||||
WriteStartElement(writer, QStringLiteral("points"));
|
||||
SaveTimelinePoints(writer, viewer);
|
||||
writer->writeEndElement(); // points
|
||||
WriteEndElement(writer); // points
|
||||
|
||||
if (Footage *footage = dynamic_cast<Footage*>(node)) {
|
||||
writer->writeTextElement(QStringLiteral("timestamp"), QString::number(footage->timestamp()));
|
||||
@@ -1125,10 +1139,10 @@ void ProjectSerializer220403::SaveNodeCustom(QXmlStreamWriter *writer, Node *nod
|
||||
} else if (Track *track = dynamic_cast<Track*>(node)) {
|
||||
writer->writeTextElement(QStringLiteral("height"), QString::number(track->GetTrackHeight()));
|
||||
} else if (NodeGroup *group = dynamic_cast<NodeGroup*>(node)) {
|
||||
writer->writeStartElement(QStringLiteral("inputpassthroughs"));
|
||||
WriteStartElement(writer, QStringLiteral("inputpassthroughs"));
|
||||
|
||||
foreach (const NodeGroup::InputPassthrough &ip, group->GetInputPassthroughs()) {
|
||||
writer->writeStartElement(QStringLiteral("inputpassthrough"));
|
||||
WriteStartElement(writer, QStringLiteral("inputpassthrough"));
|
||||
|
||||
// Reference to inner input
|
||||
writer->writeTextElement(QStringLiteral("node"), QString::number(reinterpret_cast<quintptr>(ip.second.node())));
|
||||
@@ -1149,20 +1163,20 @@ void ProjectSerializer220403::SaveNodeCustom(QXmlStreamWriter *writer, Node *nod
|
||||
|
||||
writer->writeTextElement(QStringLiteral("default"), NodeValue::ValueToString(data_type, group_input.GetDefaultValue(), false));
|
||||
|
||||
writer->writeStartElement(QStringLiteral("properties"));
|
||||
WriteStartElement(writer, QStringLiteral("properties"));
|
||||
auto p = group_input.GetProperties();
|
||||
for (auto it=p.cbegin(); it!=p.cend(); it++) {
|
||||
writer->writeStartElement(QStringLiteral("property"));
|
||||
WriteStartElement(writer, QStringLiteral("property"));
|
||||
writer->writeTextElement(QStringLiteral("key"), it.key());
|
||||
writer->writeTextElement(QStringLiteral("value"), it.value().toString());
|
||||
writer->writeEndElement(); // property
|
||||
WriteEndElement(writer); // property
|
||||
}
|
||||
writer->writeEndElement(); // properties
|
||||
WriteEndElement(writer); // properties
|
||||
|
||||
writer->writeEndElement(); // input
|
||||
WriteEndElement(writer); // input
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // inputpassthroughs
|
||||
WriteEndElement(writer); // inputpassthroughs
|
||||
|
||||
writer->writeTextElement(QStringLiteral("outputpassthrough"), QString::number(reinterpret_cast<quintptr>(group->GetOutputPassthrough())));
|
||||
}
|
||||
@@ -1183,13 +1197,13 @@ void ProjectSerializer220403::LoadTimelinePoints(QXmlStreamReader *reader, Viewe
|
||||
|
||||
void ProjectSerializer220403::SaveTimelinePoints(QXmlStreamWriter *writer, ViewerOutput *viewer) const
|
||||
{
|
||||
writer->writeStartElement(QStringLiteral("workarea"));
|
||||
WriteStartElement(writer, QStringLiteral("workarea"));
|
||||
SaveWorkArea(writer, viewer->GetWorkArea());
|
||||
writer->writeEndElement(); // workarea
|
||||
WriteEndElement(writer); // workarea
|
||||
|
||||
writer->writeStartElement(QStringLiteral("markers"));
|
||||
WriteStartElement(writer, QStringLiteral("markers"));
|
||||
SaveMarkerList(writer, viewer->GetMarkers());
|
||||
writer->writeEndElement(); // markers
|
||||
WriteEndElement(writer); // markers
|
||||
}
|
||||
|
||||
void ProjectSerializer220403::LoadMarker(QXmlStreamReader *reader, TimelineMarker *marker) const
|
||||
@@ -1270,11 +1284,11 @@ void ProjectSerializer220403::SaveMarkerList(QXmlStreamWriter *writer, TimelineM
|
||||
for (auto it=markers->cbegin(); it!=markers->cend(); it++) {
|
||||
TimelineMarker* marker = *it;
|
||||
|
||||
writer->writeStartElement(QStringLiteral("marker"));
|
||||
WriteStartElement(writer, QStringLiteral("marker"));
|
||||
|
||||
SaveMarker(writer, marker);
|
||||
|
||||
writer->writeEndElement(); // marker
|
||||
WriteEndElement(writer); // marker
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1305,13 +1319,13 @@ void ProjectSerializer220403::LoadValueHint(Node::ValueHint *hint, QXmlStreamRea
|
||||
|
||||
void ProjectSerializer220403::SaveValueHint(const Node::ValueHint *hint, QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeStartElement(QStringLiteral("types"));
|
||||
WriteStartElement(writer, QStringLiteral("types"));
|
||||
|
||||
for (auto it=hint->types().cbegin(); it!=hint->types().cend(); it++) {
|
||||
writer->writeTextElement(QStringLiteral("type"), QString::number(*it));
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // types
|
||||
WriteEndElement(writer); // types
|
||||
|
||||
writer->writeTextElement(QStringLiteral("index"), QString::number(hint->index()));
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(timeformat)
|
||||
add_subdirectory(timeoffset)
|
||||
add_subdirectory(timeremap)
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
node/time/timeformat/timeformat.cpp
|
||||
node/time/timeformat/timeformat.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,79 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 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 "timeformat.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
namespace olive {
|
||||
|
||||
#define super Node
|
||||
|
||||
const QString TimeFormatNode::kTimeInput = QStringLiteral("time_in");
|
||||
const QString TimeFormatNode::kFormatInput = QStringLiteral("format_in");
|
||||
const QString TimeFormatNode::kLocalTimeInput = QStringLiteral("localtime_in");
|
||||
|
||||
TimeFormatNode::TimeFormatNode()
|
||||
{
|
||||
AddInput(kTimeInput, NodeValue::kFloat);
|
||||
AddInput(kFormatInput, NodeValue::kText, QStringLiteral("hh:mm:ss"));
|
||||
AddInput(kLocalTimeInput, NodeValue::kBoolean);
|
||||
}
|
||||
|
||||
QString TimeFormatNode::Name() const
|
||||
{
|
||||
return tr("Time Format");
|
||||
}
|
||||
|
||||
QString TimeFormatNode::id() const
|
||||
{
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.timeformat");
|
||||
}
|
||||
|
||||
QVector<Node::CategoryID> TimeFormatNode::Category() const
|
||||
{
|
||||
return {kCategoryGenerator};
|
||||
}
|
||||
|
||||
QString TimeFormatNode::Description() const
|
||||
{
|
||||
return tr("Format time (in Unix epoch seconds) into a string.");
|
||||
}
|
||||
|
||||
void TimeFormatNode::Retranslate()
|
||||
{
|
||||
super::Retranslate();
|
||||
|
||||
SetInputName(kTimeInput, tr("Time"));
|
||||
SetInputName(kFormatInput, tr("Format"));
|
||||
SetInputName(kLocalTimeInput, tr("Interpret time as local time"));
|
||||
}
|
||||
|
||||
void TimeFormatNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
|
||||
{
|
||||
qint64 ms_since_epoch = value[kTimeInput].toDouble()*1000;
|
||||
bool time_is_local = value[kLocalTimeInput].toBool();
|
||||
QDateTime dt = QDateTime::fromMSecsSinceEpoch(ms_since_epoch, time_is_local ? Qt::LocalTime : Qt::UTC);
|
||||
QString format = value[kFormatInput].toString();
|
||||
QString output = dt.toString(format);
|
||||
table->Push(NodeValue(NodeValue::kText, output, this));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 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 TIMEFORMAT_H
|
||||
#define TIMEFORMAT_H
|
||||
|
||||
#include "node/node.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class TimeFormatNode : public Node
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TimeFormatNode();
|
||||
|
||||
NODE_DEFAULT_FUNCTIONS(TimeFormatNode)
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
|
||||
|
||||
static const QString kTimeInput;
|
||||
static const QString kFormatInput;
|
||||
static const QString kLocalTimeInput;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TIMEFORMAT_H
|
||||
+12
-31
@@ -21,6 +21,7 @@
|
||||
#include "traverser.h"
|
||||
|
||||
#include "node.h"
|
||||
#include "node/block/clip/clip.h"
|
||||
#include "render/job/footagejob.h"
|
||||
#include "render/rendermanager.h"
|
||||
|
||||
@@ -30,6 +31,12 @@ NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const TimeRa
|
||||
{
|
||||
NodeValueDatabase database;
|
||||
|
||||
// HACK: Pick up loop mode from clips
|
||||
Decoder::LoopMode old_loop_mode = loop_mode_;
|
||||
if (const ClipBlock *clip = dynamic_cast<const ClipBlock*>(node)) {
|
||||
loop_mode_ = clip->loop_mode();
|
||||
}
|
||||
|
||||
// We need to insert tables into the database for each input
|
||||
foreach (const QString& input, node->inputs()) {
|
||||
if (IsCancelled()) {
|
||||
@@ -39,6 +46,8 @@ NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const TimeRa
|
||||
database.Insert(input, ProcessInput(node, input, range));
|
||||
}
|
||||
|
||||
loop_mode_ = old_loop_mode;
|
||||
|
||||
return database;
|
||||
}
|
||||
|
||||
@@ -167,34 +176,6 @@ NodeGlobals NodeTraverser::GenerateGlobals(const VideoParams ¶ms, const Time
|
||||
|
||||
int NodeTraverser::GetChannelCountFromJob(const GenerateJob &job)
|
||||
{
|
||||
int max_channel_count = 0;
|
||||
|
||||
// Find maximum channel count
|
||||
for (auto it=job.GetValues().cbegin(); it!=job.GetValues().cend(); it++) {
|
||||
if (it.value().type() == NodeValue::kTexture) {
|
||||
if (TexturePtr tex = it.value().toTexture()) {
|
||||
max_channel_count = qMax(max_channel_count, tex->channel_count());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (max_channel_count == 0) {
|
||||
max_channel_count = VideoParams::kRGBChannelCount;
|
||||
}
|
||||
|
||||
switch (job.GetAlphaChannelRequired()) {
|
||||
case GenerateJob::kAlphaForceOn:
|
||||
return VideoParams::kRGBAChannelCount;
|
||||
case GenerateJob::kAlphaForceOff:
|
||||
if (max_channel_count >= 1 && max_channel_count < VideoParams::kRGBChannelCount) {
|
||||
return max_channel_count;
|
||||
} else {
|
||||
return VideoParams::kRGBChannelCount;
|
||||
}
|
||||
case GenerateJob::kAlphaAuto:
|
||||
return max_channel_count;
|
||||
}
|
||||
|
||||
// Default fallback, should never get here
|
||||
return VideoParams::kRGBAChannelCount;
|
||||
}
|
||||
|
||||
@@ -268,8 +249,8 @@ NodeValueTable NodeTraverser::ProcessInput(const Node* node, const QString& inpu
|
||||
|
||||
NodeTraverser::NodeTraverser() :
|
||||
cancel_(nullptr),
|
||||
heard_cancel_(false),
|
||||
transform_(nullptr)
|
||||
transform_(nullptr),
|
||||
loop_mode_(Decoder::kLoopModeOff)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -459,7 +440,7 @@ void NodeTraverser::ResolveJobs(NodeValue &val, const TimeRange &range)
|
||||
|
||||
if (job.type() == Track::kVideo) {
|
||||
|
||||
rational footage_time = Footage::AdjustTimeByLoopMode(range.in(), job.loop_mode(), job.length(), job.video_params().video_type(), job.video_params().frame_rate_as_time_base());
|
||||
rational footage_time = Footage::AdjustTimeByLoopMode(range.in(), loop_mode_, job.length(), job.video_params().video_type(), job.video_params().frame_rate_as_time_base());
|
||||
|
||||
TexturePtr tex;
|
||||
|
||||
|
||||
+12
-15
@@ -27,6 +27,8 @@
|
||||
#include "common/cancelableobject.h"
|
||||
#include "node/output/track/track.h"
|
||||
#include "render/job/cachejob.h"
|
||||
#include "render/cancelatom.h"
|
||||
#include "render/job/footagejob.h"
|
||||
#include "render/job/colortransformjob.h"
|
||||
#include "render/job/footagejob.h"
|
||||
#include "value.h"
|
||||
@@ -127,24 +129,16 @@ protected:
|
||||
|
||||
bool IsCancelled()
|
||||
{
|
||||
bool c = cancel_ && *cancel_;
|
||||
if (c) {
|
||||
heard_cancel_ = true;
|
||||
}
|
||||
return c;
|
||||
return cancel_ && cancel_->IsCancelled();
|
||||
}
|
||||
|
||||
bool HeardCancel() const { return heard_cancel_; }
|
||||
|
||||
const QAtomicInt *GetCancelPointer() const
|
||||
bool HeardCancel() const
|
||||
{
|
||||
return cancel_;
|
||||
return cancel_ && cancel_->HeardCancel();
|
||||
}
|
||||
|
||||
void SetCancelPointer(const QAtomicInt *cancel)
|
||||
{
|
||||
cancel_ = cancel;
|
||||
}
|
||||
CancelAtom *GetCancelPointer() const { return cancel_; }
|
||||
void SetCancelPointer(CancelAtom *cancel) { cancel_ = cancel; }
|
||||
|
||||
void ResolveJobs(NodeValue &value, const TimeRange &range);
|
||||
|
||||
@@ -153,6 +147,8 @@ protected:
|
||||
return block_stack_.empty() ? nullptr : block_stack_.back();
|
||||
}
|
||||
|
||||
Decoder::LoopMode loop_mode() const { return loop_mode_; }
|
||||
|
||||
private:
|
||||
void PreProcessRow(const TimeRange &range, NodeValueRow &row);
|
||||
|
||||
@@ -162,8 +158,7 @@ private:
|
||||
|
||||
AudioParams audio_params_;
|
||||
|
||||
const QAtomicInt *cancel_;
|
||||
bool heard_cancel_;
|
||||
CancelAtom *cancel_;
|
||||
|
||||
const Node *transform_start_;
|
||||
const Node *transform_now_;
|
||||
@@ -171,6 +166,8 @@ private:
|
||||
|
||||
std::list<Block*> block_stack_;
|
||||
|
||||
Decoder::LoopMode loop_mode_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -208,6 +208,12 @@ public:
|
||||
set_value(data);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
NodeValue(Type type, const T& data, const Node* from, const QString& tag) :
|
||||
NodeValue(type, data, from, false, tag)
|
||||
{
|
||||
}
|
||||
|
||||
Type type() const
|
||||
{
|
||||
return type_;
|
||||
@@ -238,6 +244,11 @@ public:
|
||||
return tag_;
|
||||
}
|
||||
|
||||
void set_tag(const QString& tag)
|
||||
{
|
||||
tag_ = tag;
|
||||
}
|
||||
|
||||
const Node* source() const
|
||||
{
|
||||
return from_;
|
||||
@@ -328,6 +339,7 @@ public:
|
||||
QVector3D toVec3() const { return value<QVector3D>(); }
|
||||
QVector4D toVec4() const { return value<QVector4D>(); }
|
||||
Bezier toBezier() const { return value<Bezier>(); }
|
||||
QVector<NodeValue> toArray() const { return value<QVector<NodeValue> >(); }
|
||||
|
||||
private:
|
||||
Type type_;
|
||||
@@ -375,6 +387,12 @@ public:
|
||||
Push(NodeValue(type, data, from, array, tag));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Push(NodeValue::Type type, const T& data, const Node *from, const QString& tag)
|
||||
{
|
||||
Push(NodeValue(type, data, from, false, tag));
|
||||
}
|
||||
|
||||
void Prepend(const NodeValue& value)
|
||||
{
|
||||
values_.prepend(value);
|
||||
@@ -386,6 +404,12 @@ public:
|
||||
Prepend(NodeValue(type, data, from, array, tag));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Prepend(NodeValue::Type type, const T& data, const Node *from, const QString& tag)
|
||||
{
|
||||
Prepend(NodeValue(type, data, from, false, tag));
|
||||
}
|
||||
|
||||
const NodeValue& at(int index) const
|
||||
{
|
||||
return values_.at(index);
|
||||
|
||||
Reference in New Issue
Block a user