Merge branch 'master' into pr/1913

This commit is contained in:
itsmattkc
2022-05-10 09:54:34 -07:00
264 changed files with 4674 additions and 2118 deletions
+2 -6
View File
@@ -18,13 +18,9 @@ set(OLIVE_SOURCES
${OLIVE_SOURCES}
audio/audiomanager.cpp
audio/audiomanager.h
audio/audioprocessor.cpp
audio/audioprocessor.h
audio/audiovisualwaveform.cpp
audio/audiovisualwaveform.h
audio/packedprocessor.cpp
audio/packedprocessor.h
audio/planarprocessor.cpp
audio/planarprocessor.h
audio/tempoprocessor.cpp
audio/tempoprocessor.h
PARENT_SCOPE
)
+23 -8
View File
@@ -26,7 +26,6 @@
#include <QApplication>
#include "audio/packedprocessor.h"
#include "config/config.h"
namespace olive {
@@ -81,10 +80,11 @@ int InputCallback(const void *input, void *output, unsigned long frameCount, con
return paContinue;
}
void AudioManager::PushToOutput(const AudioParams &params, const QByteArray &samples)
bool AudioManager::PushToOutput(const AudioParams &params, const QByteArray &samples, QString *error)
{
if (output_device_ == paNoDevice) {
return;
if (error) *error = tr("No output device is set");
return false;
}
if (output_params_ != params || output_stream_ == nullptr) {
@@ -94,7 +94,13 @@ void AudioManager::PushToOutput(const AudioParams &params, const QByteArray &sam
PaStreamParameters p = GetPortAudioParams(params, output_device_);
Pa_OpenStream(&output_stream_, nullptr, &p, output_params_.sample_rate(), paFramesPerBufferUnspecified, paNoFlag, OutputCallback, output_buffer_);
PaError r = Pa_OpenStream(&output_stream_, nullptr, &p, output_params_.sample_rate(), paFramesPerBufferUnspecified, paNoFlag, OutputCallback, output_buffer_);
if (r != paNoError) {
// Unhandled error
//qCritical() << "Failed to open output stream:" << Pa_GetErrorText(r);
if (error) *error = Pa_GetErrorText(r);
return false;
}
output_buffer_->set_bytes_per_frame(output_params_.samples_to_bytes(1));
}
@@ -104,6 +110,8 @@ void AudioManager::PushToOutput(const AudioParams &params, const QByteArray &sam
if (!Pa_IsStreamActive(output_stream_)) {
Pa_StartStream(output_stream_);
}
return true;
}
void AudioManager::ClearBufferedOutput()
@@ -189,7 +197,7 @@ void AudioManager::HardReset()
Pa_Initialize();
}
bool AudioManager::StartRecording(const EncodingParams &params)
bool AudioManager::StartRecording(const EncodingParams &params, QString *error_str)
{
if (input_device_ == paNoDevice) {
return false;
@@ -203,12 +211,19 @@ bool AudioManager::StartRecording(const EncodingParams &params)
PaStreamParameters p = GetPortAudioParams(params.audio_params(), input_device_);
if (Pa_OpenStream(&input_stream_, &p, nullptr, params.audio_params().sample_rate(), paFramesPerBufferUnspecified, paNoFlag, InputCallback, input_encoder_) == paNoError) {
if (Pa_StartStream(input_stream_) == paNoError) {
PaError r = Pa_OpenStream(&input_stream_, &p, nullptr, params.audio_params().sample_rate(), paFramesPerBufferUnspecified, paNoFlag, InputCallback, input_encoder_);
if (r == paNoError) {
//const PaStreamInfo* info = Pa_GetStreamInfo(input_stream_);
r = Pa_StartStream(input_stream_);
if (r == paNoError) {
return true;
}
}
if (error_str) {
*error_str = Pa_GetErrorText(r);
}
StopRecording();
return false;
}
@@ -235,7 +250,7 @@ PaDeviceIndex AudioManager::FindConfigDeviceByName(bool is_output_device)
{
QString entry = is_output_device ? QStringLiteral("AudioOutput") : QStringLiteral("AudioInput");
return FindDeviceByName(Config::Current()[entry].toString(), is_output_device);
return FindDeviceByName(OLIVE_CONFIG_STR(entry).toString(), is_output_device);
}
PaDeviceIndex AudioManager::FindDeviceByName(const QString &s, bool is_output_device)
+6 -2
View File
@@ -27,6 +27,7 @@
#include <portaudio.h>
#include "audiovisualwaveform.h"
#include "audio/audioprocessor.h"
#include "common/define.h"
#include "codec/ffmpeg/ffmpegencoder.h"
#include "render/audioparams.h"
@@ -52,7 +53,7 @@ public:
void SetOutputNotifyInterval(int n);
void PushToOutput(const AudioParams &params, const QByteArray& samples);
bool PushToOutput(const AudioParams &params, const QByteArray& samples, QString *error = nullptr);
void ClearBufferedOutput();
@@ -74,7 +75,7 @@ public:
void HardReset();
bool StartRecording(const EncodingParams &params);
bool StartRecording(const EncodingParams &params, QString *error_str = nullptr);
void StopRecording();
@@ -86,6 +87,8 @@ public:
signals:
void OutputNotify();
void OutputParamsChanged();
private:
AudioManager();
@@ -104,6 +107,7 @@ private:
PaDeviceIndex input_device_;
PaStream *input_stream_;
FFmpegEncoder *input_encoder_;
};
+302
View File
@@ -0,0 +1,302 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 "audioprocessor.h"
extern "C" {
#include <libavfilter/buffersrc.h>
#include <libavfilter/buffersink.h>
}
#include "common/ffmpegutils.h"
namespace olive {
AudioProcessor::AudioProcessor()
{
filter_graph_ = nullptr;
in_frame_ = nullptr;
out_frame_ = nullptr;
}
AudioProcessor::~AudioProcessor()
{
Close();
}
bool AudioProcessor::Open(const AudioParams &from, const AudioParams &to, double tempo)
{
if (filter_graph_) {
qWarning() << "Tried to open a processor that was already open";
return false;
}
filter_graph_ = avfilter_graph_alloc();
if (!filter_graph_) {
qCritical() << "Failed to allocate filter graph";
return false;
}
from_fmt_ = FFmpegUtils::GetFFmpegSampleFormat(from.format());
to_fmt_ = FFmpegUtils::GetFFmpegSampleFormat(to.format());
// Set up audio buffer args
char filter_args[200];
snprintf(filter_args, 200, "time_base=%d/%d:sample_rate=%d:sample_fmt=%d:channel_layout=0x%" PRIx64,
1,
from.sample_rate(),
from.sample_rate(),
from_fmt_,
from.channel_layout());
int r;
// Create buffersrc (input)
r = avfilter_graph_create_filter(&buffersrc_ctx_, avfilter_get_by_name("abuffer"), "in", filter_args, nullptr, filter_graph_);
if (r < 0) {
qCritical() << "Failed to create buffersrc:" << r;
Close();
return false;
}
// Store "previous" filter for linking
AVFilterContext *previous_filter = buffersrc_ctx_;
// Create tempo
bool create_tempo;
if ((create_tempo = !qFuzzyCompare(tempo, 1.0))) {
// Create audio tempo filters: FFmpeg's atempo can only be set between 0.5 and 2.0. If the requested speed is outside
// those boundaries, we need to daisychain more than one together.
double base = (tempo > 1.0) ? 2.0 : 0.5;
double speed_log = log(tempo) / log(base);
// This is the number of how many 0.5 or 2.0 tempos we need to daisychain
int whole = qFloor(speed_log);
// Set speed_log to the remainder
speed_log -= whole;
for (int i=0;i<=whole;i++) {
double filter_tempo = (i == whole) ? qPow(base, speed_log) : base;
if (qFuzzyCompare(filter_tempo, 1.0)) {
// This filter would do nothing
continue;
}
previous_filter = CreateTempoFilter(filter_graph_,
previous_filter,
filter_tempo);
if (!previous_filter) {
qCritical() << "Failed to create audio tempo filter";
Close();
return false;
}
}
}
// Create conversion filter
if (from.sample_rate() != to.sample_rate() || from.channel_layout() != to.channel_layout() || from.format() != to.format()
|| (to.FormatIsPlanar() && create_tempo)) { // Tempo processor automatically converts to packed,
// so if the desired output is planar, it'll need
// to be converted
snprintf(filter_args, 200, "sample_fmts=%s:sample_rates=%d:channel_layouts=0x%" PRIx64,
av_get_sample_fmt_name(to_fmt_),
to.sample_rate(),
to.channel_layout());
AVFilterContext *c;
r = avfilter_graph_create_filter(&c, avfilter_get_by_name("aformat"), "fmt", filter_args, nullptr, filter_graph_);
if (r < 0) {
qCritical() << "Failed to create format conversion filter:" << r << filter_args;
Close();
return false;
}
r = avfilter_link(previous_filter, 0, c, 0);
if (r < 0) {
qCritical() << "Failed to link filters:" << r;
Close();
return false;
}
previous_filter = c;
}
// Create buffersink (output)
r = avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("abuffersink"), "out", nullptr, nullptr, filter_graph_);
if (r < 0) {
qCritical() << "Failed to create buffersink:" << r;
Close();
return false;
}
r = avfilter_link(previous_filter, 0, buffersink_ctx_, 0);
if (r < 0) {
qCritical() << "Failed to link filters:" << r;
Close();
return false;
}
r = avfilter_graph_config(filter_graph_, nullptr);
if (r < 0) {
qCritical() << "Failed to configure graph:" << r;
Close();
return false;
}
in_frame_ = av_frame_alloc();
if (in_frame_) {
in_frame_->sample_rate = from.sample_rate();
in_frame_->format = from_fmt_;
in_frame_->channel_layout = from.channel_layout();
in_frame_->channels = from.channel_count();
in_frame_->pts = 0;
} else {
qCritical() << "Failed to allocate input frame";
Close();
return false;
}
out_frame_ = av_frame_alloc();
if (!out_frame_) {
qCritical() << "Failed to allocate output frame";
Close();
return false;
}
from_ = from;
to_ = to;
return true;
}
void AudioProcessor::Close()
{
if (filter_graph_) {
avfilter_graph_free(&filter_graph_);
filter_graph_ = nullptr;
buffersrc_ctx_ = nullptr;
buffersink_ctx_ = nullptr;
}
if (in_frame_) {
av_frame_free(&in_frame_);
in_frame_ = nullptr;
}
if (out_frame_) {
av_frame_free(&out_frame_);
out_frame_ = nullptr;
}
}
int AudioProcessor::Convert(float **in, int nb_in_samples, AudioProcessor::Buffer *output)
{
if (!IsOpen()) {
qCritical() << "Tried to convert on closed processor";
return -1;
}
int r = 0;
if (in && nb_in_samples) {
// Set frame parameters
in_frame_->nb_samples = nb_in_samples;
for (int i=0; i<from_.channel_count(); i++) {
in_frame_->data[i] = reinterpret_cast<uint8_t*>(in[i]);
in_frame_->linesize[i] = from_.samples_to_bytes(nb_in_samples);
}
r = av_buffersrc_add_frame_flags(buffersrc_ctx_, in_frame_, AV_BUFFERSRC_FLAG_KEEP_REF);
if (r < 0) {
qCritical() << "Failed to add frame to buffersrc:" << r;
return r;
}
}
if (output) {
int nb_channels = to_.channel_count();
if (to_.FormatIsPacked()) {
nb_channels = 1;
}
AudioProcessor::Buffer &result = *output;
result.resize(nb_channels);
int byte_offset = 0;
while (true) {
av_frame_unref(out_frame_);
r = av_buffersink_get_frame(buffersink_ctx_, out_frame_);
if (r < 0) {
if (r == AVERROR(EAGAIN)) {
r = 0;
} else {
// Handle unexpected error
qCritical() << "Failed to pull from buffersink:" << r;
}
break;
}
int nb_bytes = out_frame_->nb_samples * to_.bytes_per_sample_per_channel();
if (to_.FormatIsPacked()) {
nb_bytes *= to_.channel_count();
}
for (int i=0; i<nb_channels; i++) {
result[i].resize(byte_offset + nb_bytes);
memcpy(result[i].data() + byte_offset, out_frame_->data[i], nb_bytes);
}
byte_offset += nb_bytes;
}
av_frame_unref(out_frame_);
}
return r;
}
void AudioProcessor::Flush()
{
int r = av_buffersrc_add_frame_flags(buffersrc_ctx_, nullptr, AV_BUFFERSRC_FLAG_KEEP_REF);
if (r < 0) {
qCritical() << "Failed to flush:" << r;
}
}
AVFilterContext *AudioProcessor::CreateTempoFilter(AVFilterGraph* graph, AVFilterContext* link, const double &tempo)
{
// Set up tempo param, which is taken as a C string
char speed_param[20];
snprintf(speed_param, 20, "%f", tempo);
AVFilterContext* tempo_ctx = nullptr;
if (avfilter_graph_create_filter(&tempo_ctx, avfilter_get_by_name("atempo"), "atempo", speed_param, nullptr, graph) >= 0
&& avfilter_link(link, 0, tempo_ctx, 0) == 0) {
return tempo_ctx;
}
return nullptr;
}
}
@@ -18,14 +18,8 @@
***/
#ifndef TEMPOPROCESSOR_H
#define TEMPOPROCESSOR_H
#ifdef __MINGW32__
#ifndef __USE_MINGW_ANSI_STDIO
#define __USE_MINGW_ANSI_STDIO
#endif
#endif
#ifndef AUDIOPROCESSOR_H
#define AUDIOPROCESSOR_H
#include <inttypes.h>
@@ -37,28 +31,28 @@ extern "C" {
namespace olive {
class TempoProcessor
class AudioProcessor
{
public:
TempoProcessor();
AudioProcessor();
~TempoProcessor();
~AudioProcessor();
DISABLE_COPY_MOVE(TempoProcessor)
DISABLE_COPY_MOVE(AudioProcessor)
bool IsOpen() const;
bool Open(const AudioParams &from, const AudioParams &to, double tempo = 1.0);
const double& GetSpeed() const;
void Close();
bool Open(const AudioParams& params, const double &speed);
bool IsOpen() const { return filter_graph_; }
void Push(const QByteArray &packed);
using Buffer = QVector<QByteArray>;
int Convert(float **in, int nb_in_samples, AudioProcessor::Buffer *output);
void Flush();
QByteArray Pull();
void Close();
const AudioParams &from() const { return from_; }
const AudioParams &to() const { return to_; }
private:
static AVFilterContext* CreateTempoFilter(AVFilterGraph *graph, AVFilterContext *link, const double& tempo);
@@ -69,17 +63,18 @@ private:
AVFilterContext* buffersink_ctx_;
AudioParams params_;
AudioParams from_;
AVSampleFormat from_fmt_;
int64_t timestamp_;
AudioParams to_;
AVSampleFormat to_fmt_;
double speed_;
AVFrame *in_frame_;
bool open_;
AVFrame *out_frame_;
bool flushed_;
};
}
#endif // TEMPOPROCESSOR_H
#endif // AUDIOPROCESSOR_H
+11 -11
View File
@@ -40,10 +40,10 @@ AudioVisualWaveform::AudioVisualWaveform() :
}
}
void AudioVisualWaveform::OverwriteSamplesFromBuffer(SampleBufferPtr samples, int sample_rate, const rational &start, double target_rate, Sample& data, int &start_index, int &samples_length)
void AudioVisualWaveform::OverwriteSamplesFromBuffer(const SampleBuffer &samples, int sample_rate, const rational &start, double target_rate, Sample& data, int &start_index, int &samples_length)
{
start_index = time_to_samples(start, target_rate);
samples_length = time_to_samples(static_cast<double>(samples->sample_count()) / static_cast<double>(sample_rate), target_rate);
samples_length = time_to_samples(static_cast<double>(samples.sample_count()) / static_cast<double>(sample_rate), target_rate);
int end_index = start_index + samples_length;
if (data.size() < end_index) {
@@ -54,7 +54,7 @@ void AudioVisualWaveform::OverwriteSamplesFromBuffer(SampleBufferPtr samples, in
for (int i=0; i<samples_length; i+=channels_) {
int src_start = qRound((double(i) * chunk_size)) / channels_;
int src_end = qMin(qRound((double(i + channels_) * chunk_size)) / channels_, samples->sample_count());
int src_end = qMin(qRound((double(i + channels_) * chunk_size)) / channels_, samples.sample_count());
Sample summary = SumSamples(samples,
src_start,
@@ -91,7 +91,7 @@ void AudioVisualWaveform::OverwriteSamplesFromMipmap(const AudioVisualWaveform::
input_length = samples_length;
}
void AudioVisualWaveform::OverwriteSamples(SampleBufferPtr samples, int sample_rate, const rational &start)
void AudioVisualWaveform::OverwriteSamples(const SampleBuffer &samples, int sample_rate, const rational &start)
{
if (!channels_) {
qWarning() << "Failed to write samples - channel count is zero";
@@ -125,7 +125,7 @@ void AudioVisualWaveform::OverwriteSamples(SampleBufferPtr samples, int sample_r
current_mipmap->second);
}
rational sample_length(samples->sample_count(), sample_rate);
rational sample_length(samples.sample_count(), sample_rate);
length_ = qMax(length_, start + sample_length);
}
@@ -277,7 +277,7 @@ AudioVisualWaveform::Sample AudioVisualWaveform::GetSummaryFromTime(const ration
return AudioVisualWaveform::Sample(channel_count(), {0, 0});
}
void ExpandMinMaxChannel(float *a, int start, int length, float &min_val, float &max_val)
void ExpandMinMaxChannel(const float *a, int start, int length, float &min_val, float &max_val)
{
#if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM)
// SSE optimized
@@ -321,13 +321,13 @@ void ExpandMinMaxChannel(float *a, int start, int length, float &min_val, float
#endif
}
AudioVisualWaveform::Sample AudioVisualWaveform::SumSamples(SampleBufferPtr samples, int start_index, int length)
AudioVisualWaveform::Sample AudioVisualWaveform::SumSamples(const SampleBuffer &samples, int start_index, int length)
{
int channels = samples->audio_params().channel_count();
int channels = samples.audio_params().channel_count();
AudioVisualWaveform::Sample summed_samples(channels);
for (int channel=0; channel<samples->audio_params().channel_count(); channel++) {
ExpandMinMaxChannel(samples->data(channel), start_index, length, summed_samples[channel].min, summed_samples[channel].max);
for (int channel=0; channel<samples.audio_params().channel_count(); channel++) {
ExpandMinMaxChannel(samples.data(channel), start_index, length, summed_samples[channel].min, summed_samples[channel].max);
}
// for reference: this approximation is n x faster (and less accurate) for a n-tracks clip
@@ -426,7 +426,7 @@ void AudioVisualWaveform::DrawWaveform(QPainter *painter, const QRect& rect, con
int start = qMax(rect.x(), -top_left.x());
int end = qMin(rect.right(), -top_left.x() + viewport.width());
bool rectified = Config::Current()[QStringLiteral("RectifiedWaveforms")].toBool();
bool rectified = OLIVE_CONFIG("RectifiedWaveforms").toBool();
for (int i=start;i<end;i++) {
sample_index = next_sample_index;
+3 -3
View File
@@ -65,7 +65,7 @@ public:
*
* Starting at `start`, writes samples over anything in the buffer, expanding it if necessary.
*/
void OverwriteSamples(SampleBufferPtr samples, int sample_rate, const rational& start = 0);
void OverwriteSamples(const SampleBuffer &samples, int sample_rate, const rational& start = 0);
/**
* @brief Replaces sums at a certain range in this visual waveform
@@ -98,7 +98,7 @@ public:
Sample GetSummaryFromTime(const rational& start, const rational& length) const;
static Sample SumSamples(SampleBufferPtr samples, int start_index, int length);
static Sample SumSamples(const SampleBuffer &samples, int start_index, int length);
static Sample ReSumSamples(const SamplePerChannel *samples, int nb_samples, int nb_channels);
@@ -111,7 +111,7 @@ public:
static const rational kMaximumSampleRate;
private:
void OverwriteSamplesFromBuffer(SampleBufferPtr samples, int sample_rate, const rational& start, double target_rate, Sample &data, int &start_index, int &samples_length);
void OverwriteSamplesFromBuffer(const SampleBuffer &samples, int sample_rate, const rational& start, double target_rate, Sample &data, int &start_index, int &samples_length);
void OverwriteSamplesFromMipmap(const Sample& input, double input_sample_rate, int &input_start, int &input_length, const rational& start, double output_rate, Sample &output_data);
-101
View File
@@ -1,101 +0,0 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 "packedprocessor.h"
#include "common/ffmpegutils.h"
namespace olive {
PackedProcessor::PackedProcessor() :
swr_ctx_(nullptr)
{
}
PackedProcessor::~PackedProcessor()
{
Close();
}
bool PackedProcessor::Open(const AudioParams &params)
{
if (IsOpen()) {
return true;
}
swr_ctx_ = swr_alloc_set_opts(nullptr,
params.channel_layout(),
FFmpegUtils::GetFFmpegSampleFormat(AudioParams::GetPackedEquivalent(params.format())),
params.sample_rate(),
params.channel_layout(),
FFmpegUtils::GetFFmpegSampleFormat(params.format()),
params.sample_rate(),
0,
nullptr);
if (!swr_ctx_) {
qCritical() << "Failed to allocate resample context";
return false;
}
if (swr_init(swr_ctx_) < 0) {
qCritical() << "Failed to init resample context";
swr_free(&swr_ctx_);
return false;
}
return true;
}
QByteArray PackedProcessor::Convert(SampleBufferPtr planar)
{
if (!IsOpen()) {
qCritical() << "Tried to convert while closed";
return QByteArray();
}
int nb_samples = planar->sample_count();
if (nb_samples == 0) {
return QByteArray();
}
QByteArray output(planar->audio_params().samples_to_bytes(nb_samples), Qt::Uninitialized);
uint8_t *output_data = reinterpret_cast<uint8_t*>(output.data());
int ret = swr_convert(swr_ctx_, &output_data, nb_samples,
const_cast<const uint8_t**>(reinterpret_cast<uint8_t**>(planar->to_raw_ptrs())),
nb_samples);
if (ret < 0) {
char buf[200];
av_strerror(ret, buf, 200);
qDebug() << "Packed processor failed with error:" << buf << ret;
}
return output;
}
void PackedProcessor::Close()
{
if (swr_ctx_) {
swr_free(&swr_ctx_);
}
}
}
-104
View File
@@ -1,104 +0,0 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 "planarprocessor.h"
#include "common/ffmpegutils.h"
namespace olive {
PlanarProcessor::PlanarProcessor() :
swr_ctx_(nullptr)
{
}
PlanarProcessor::~PlanarProcessor()
{
Close();
}
bool PlanarProcessor::Open(const AudioParams &params)
{
if (IsOpen()) {
return true;
}
swr_ctx_ = swr_alloc_set_opts(nullptr,
params.channel_layout(),
FFmpegUtils::GetFFmpegSampleFormat(AudioParams::GetPlanarEquivalent(params.format())),
params.sample_rate(),
params.channel_layout(),
FFmpegUtils::GetFFmpegSampleFormat(params.format()),
params.sample_rate(),
0,
nullptr);
if (!swr_ctx_) {
qCritical() << "Failed to allocate resample context";
return false;
}
if (swr_init(swr_ctx_) < 0) {
qCritical() << "Failed to init resample context";
swr_free(&swr_ctx_);
return false;
}
params_ = params;
return true;
}
SampleBufferPtr PlanarProcessor::Convert(const QByteArray &packed)
{
if (!IsOpen()) {
qCritical() << "Tried to convert while closed";
return nullptr;
}
if (packed.isEmpty()) {
return nullptr;
}
int nb_samples_per_channel = params_.bytes_to_samples(packed.size());
SampleBufferPtr output = SampleBuffer::CreateAllocated(params_, nb_samples_per_channel);
const uint8_t *input = reinterpret_cast<const uint8_t*>(packed.constData());
int ret = swr_convert(swr_ctx_,
reinterpret_cast<uint8_t**>(output->to_raw_ptrs()), nb_samples_per_channel,
&input, nb_samples_per_channel);
if (ret < 0) {
char buf[200];
av_strerror(ret, buf, 200);
qDebug() << "Planar processor failed with error:" << buf << ret;
}
return output;
}
void PlanarProcessor::Close()
{
if (swr_ctx_) {
swr_free(&swr_ctx_);
}
}
}
-269
View File
@@ -1,269 +0,0 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 "tempoprocessor.h"
extern "C" {
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/opt.h>
}
#include <QDebug>
#include "common/ffmpegutils.h"
namespace olive {
TempoProcessor::TempoProcessor() :
filter_graph_(nullptr),
buffersrc_ctx_(nullptr),
buffersink_ctx_(nullptr),
open_(false)
{
}
TempoProcessor::~TempoProcessor()
{
Close();
}
bool TempoProcessor::IsOpen() const
{
return open_;
}
const double &TempoProcessor::GetSpeed() const
{
return speed_;
}
bool TempoProcessor::Open(const AudioParams &params, const double& speed)
{
if (open_) {
return true;
}
params_ = params;
speed_ = speed;
// Create AVFilterGraph instance
filter_graph_ = avfilter_graph_alloc();
if (!filter_graph_) {
qCritical() << "Failed to create AVFilterGraph";
Close();
return false;
}
// Set up audio buffer args
char filter_args[200];
snprintf(filter_args, 200, "time_base=%d/%d:sample_rate=%d:sample_fmt=%d:channel_layout=0x%" PRIx64,
1,
params_.sample_rate(),
params_.sample_rate(),
FFmpegUtils::GetFFmpegSampleFormat(params_.format()),
params.channel_layout());
// Create buffer and buffersink
if (avfilter_graph_create_filter(&buffersrc_ctx_, avfilter_get_by_name("abuffer"), "in", filter_args, nullptr, filter_graph_) < 0) {
qCritical() << "Failed to create audio buffer source";
Close();
return false;
}
if (avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("abuffersink"), "out", nullptr, nullptr, filter_graph_) < 0) {
qCritical() << "Failed to create audio buffer sink";
Close();
return false;
}
// Create audio tempo filters: FFmpeg's atempo can only be set between 0.5 and 2.0. If the requested speed is outside
// those boundaries, we need to daisychain more than one together.
double base = (speed_ > 1.0) ? 2.0 : 0.5;
double speed_log = log(speed_) / log(base);
// This is the number of how many 0.5 or 2.0 tempos we need to daisychain
int whole = qFloor(speed_log);
// Set speed_log to the remainder
speed_log -= whole;
AVFilterContext* previous_filter = buffersrc_ctx_;
for (int i=0;i<=whole;i++) {
double filter_tempo = (i == whole) ? qPow(base, speed_log) : base;
if (qFuzzyCompare(filter_tempo, 1.0)) {
// This filter would do nothing
continue;
}
previous_filter = CreateTempoFilter(filter_graph_,
previous_filter,
filter_tempo);
if (!previous_filter) {
qCritical() << "Failed to create audio tempo filter";
Close();
return false;
}
}
// Link the last filter to the buffersink
if (avfilter_link(previous_filter, 0, buffersink_ctx_, 0) != 0) {
qCritical() << "Failed to link final filter and buffer sink";
Close();
return false;
}
// Config graph
if (avfilter_graph_config(filter_graph_, nullptr) < 0) {
qCritical() << "Failed to configure filter graph";
Close();
return false;
}
timestamp_ = 0;
open_ = true;
flushed_ = false;
return true;
}
void TempoProcessor::Push(const QByteArray &packed)
{
if (!IsOpen()) {
qWarning() << "Tried to push to closed TempoProcessor";
return;
}
if (flushed_) {
qWarning() << "Tried to push to flushed TempoProcessor";
return;
}
AVFrame* src_frame = av_frame_alloc();
if (!src_frame) {
qCritical() << "Failed to allocate source frame";
return;
}
// Allocate a buffer for the number of samples we got
src_frame->sample_rate = params_.sample_rate();
src_frame->format = FFmpegUtils::GetFFmpegSampleFormat(params_.format());
src_frame->channel_layout = params_.channel_layout();
src_frame->nb_samples = params_.bytes_to_samples(packed.size());
src_frame->pts = timestamp_;
timestamp_ += src_frame->nb_samples;
if (av_frame_get_buffer(src_frame, 0) < 0) {
qCritical() << "Failed to allocate buffer for source frame";
av_frame_free(&src_frame);
return;
}
// Copy buffer from data array to frame
memcpy(src_frame->data[0], packed, packed.size());
int ret = av_buffersrc_add_frame_flags(buffersrc_ctx_, src_frame, AV_BUFFERSRC_FLAG_KEEP_REF);
if (ret < 0) {
qCritical() << "Failed to feed buffer source" << ret;
}
if (src_frame) {
av_frame_free(&src_frame);
}
}
void TempoProcessor::Flush()
{
if (!flushed_) {
int ret = av_buffersrc_add_frame_flags(buffersrc_ctx_, nullptr, AV_BUFFERSRC_FLAG_KEEP_REF);
if (ret < 0) {
qCritical() << "Failed to feed buffer source" << ret;
}
flushed_ = true;
}
}
QByteArray TempoProcessor::Pull()
{
QByteArray b;
AVFrame *processed_frame = av_frame_alloc();
// Try to pull samples from the buffersink
int ret = av_buffersink_get_frame(buffersink_ctx_, processed_frame);
if (ret < 0) {
// We couldn't pull for some reason, if the error was EAGAIN, we just need to send more samples. Otherwise the
// error might be fatal...
if (ret != AVERROR(EAGAIN)) {
qCritical() << "Failed to pull from buffersink" << ret;
}
av_frame_free(&processed_frame);
return b;
}
b.resize(params_.samples_to_bytes(processed_frame->nb_samples));
// Copy the bytes
memcpy(b.data(), processed_frame->data[0], b.size());
// If the index has reached the limit of this processed frame, we can dispose of the frame now
av_frame_free(&processed_frame);
return b;
}
void TempoProcessor::Close()
{
open_ = false;
if (filter_graph_) {
avfilter_graph_free(&filter_graph_);
filter_graph_ = nullptr;
}
buffersrc_ctx_ = nullptr;
buffersink_ctx_ = nullptr;
}
AVFilterContext *TempoProcessor::CreateTempoFilter(AVFilterGraph* graph, AVFilterContext* link, const double &tempo)
{
// Set up tempo param, which is taken as a C string
char speed_param[20];
snprintf(speed_param, 20, "%f", tempo);
AVFilterContext* tempo_ctx = nullptr;
if (avfilter_graph_create_filter(&tempo_ctx, avfilter_get_by_name("atempo"), "atempo", speed_param, nullptr, graph) >= 0
&& avfilter_link(link, 0, tempo_ctx, 0) == 0) {
return tempo_ctx;
}
return nullptr;
}
}
+6 -6
View File
@@ -109,7 +109,7 @@ FramePtr Decoder::RetrieveVideo(const rational &timecode, const RetrieveVideoPar
return RetrieveVideoInternal(timecode, divider, cancelled);
}
Decoder::RetrieveAudioStatus Decoder::RetrieveAudio(SampleBufferPtr dest, const TimeRange &range, const AudioParams &params, const QString& cache_path, Footage::LoopMode loop_mode, RenderMode::Mode mode)
Decoder::RetrieveAudioStatus Decoder::RetrieveAudio(SampleBuffer &dest, const TimeRange &range, const AudioParams &params, const QString& cache_path, Footage::LoopMode loop_mode, RenderMode::Mode mode)
{
QMutexLocker locker(&mutex_);
@@ -272,14 +272,14 @@ bool Decoder::ConformAudioInternal(const QVector<QString> &filenames, const Audi
return false;
}
bool Decoder::RetrieveAudioFromConform(SampleBufferPtr sample_buffer, const QVector<QString> &conform_filenames, const TimeRange& range, Footage::LoopMode loop_mode, const AudioParams &input_params)
bool Decoder::RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, const TimeRange& range, Footage::LoopMode loop_mode, const AudioParams &input_params)
{
PlanarFileDevice input;
if (input.open(conform_filenames, QFile::ReadOnly)) {
qint64 read_index = input_params.time_to_bytes(range.in()) / input_params.channel_count();
qint64 write_index = 0;
const qint64 buffer_length_in_bytes = sample_buffer->sample_count() * input_params.bytes_per_sample_per_channel();
const qint64 buffer_length_in_bytes = sample_buffer.sample_count() * input_params.bytes_per_sample_per_channel();
while (write_index < buffer_length_in_bytes) {
if (loop_mode == Footage::kLoopModeLoop) {
@@ -297,15 +297,15 @@ bool Decoder::RetrieveAudioFromConform(SampleBufferPtr sample_buffer, const QVec
if (read_index < 0) {
// Reading before 0, write silence here until audio data would actually start
write_count = qMin(-read_index, buffer_length_in_bytes);
sample_buffer->silence_bytes(write_index, write_index + write_count);
sample_buffer.silence_bytes(write_index, write_index + write_count);
} else if (read_index >= input.size()) {
// Reading after data length, write silence until the end of the buffer
write_count = buffer_length_in_bytes - write_index;
sample_buffer->silence_bytes(write_index, write_index + write_count);
sample_buffer.silence_bytes(write_index, write_index + write_count);
} else {
write_count = qMin(input.size() - read_index, buffer_length_in_bytes - write_index);
input.seek(read_index);
input.read(reinterpret_cast<char**>(sample_buffer->to_raw_ptrs()), write_count, write_index);
input.read(reinterpret_cast<char**>(sample_buffer.to_raw_ptrs().data()), write_count, write_index);
}
read_index += write_count;
+2 -2
View File
@@ -201,7 +201,7 @@ public:
*
* This function is thread safe and can only run while the decoder is open. \see Open()
*/
RetrieveAudioStatus RetrieveAudio(SampleBufferPtr dest, const TimeRange& range, const AudioParams& params, const QString &cache_path, Footage::LoopMode loop_mode, RenderMode::Mode mode);
RetrieveAudioStatus RetrieveAudio(SampleBuffer &dest, const TimeRange& range, const AudioParams& params, const QString &cache_path, Footage::LoopMode loop_mode, RenderMode::Mode mode);
/**
* @brief Determine the last time this decoder instance was used in any way
@@ -307,7 +307,7 @@ signals:
private:
void UpdateLastAccessed();
bool RetrieveAudioFromConform(SampleBufferPtr sample_buffer, const QVector<QString> &conform_filenames, const TimeRange &range, Footage::LoopMode loop_mode, const AudioParams &params);
bool RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, const TimeRange &range, Footage::LoopMode loop_mode, const AudioParams &params);
CodecStream stream_;
+1 -1
View File
@@ -183,7 +183,7 @@ public slots:
virtual bool Open() = 0;
virtual bool WriteFrame(olive::FramePtr frame, olive::rational time) = 0;
virtual bool WriteAudio(olive::SampleBufferPtr audio) = 0;
virtual bool WriteAudio(const olive::SampleBuffer &audio) = 0;
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) = 0;
virtual void Close() = 0;
+2 -2
View File
@@ -515,7 +515,7 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, cons
// Resample audio to our destination parameters
nb_samples = swr_convert(resampler,
reinterpret_cast<uint8_t**>(data.to_raw_ptrs()),
reinterpret_cast<uint8_t**>(data.to_raw_ptrs().data()),
nb_samples,
const_cast<const uint8_t**>(frame->data),
frame->nb_samples);
@@ -526,7 +526,7 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, cons
nb_bytes_per_channel = params.samples_to_bytes(nb_samples) / nb_channels;
// Write to files
wave_out.write(const_cast<const char**>(reinterpret_cast<char**>(data.to_raw_ptrs())), nb_bytes_per_channel);
wave_out.write(const_cast<const char**>(reinterpret_cast<char**>(data.to_raw_ptrs().data())), nb_bytes_per_channel);
}
// Free buffer
+9 -9
View File
@@ -266,26 +266,26 @@ fail:
return success;
}
bool FFmpegEncoder::WriteAudio(SampleBufferPtr audio)
bool FFmpegEncoder::WriteAudio(const SampleBuffer &audio)
{
bool result = true;
// Create input buffer
int input_sample_count = 0;
uint8_t** input_data = nullptr;
if (audio) {
input_sample_count = audio->sample_count();
if (audio.is_allocated()) {
input_sample_count = audio.sample_count();
int input_linesize;
av_samples_alloc_array_and_samples(&input_data, &input_linesize, audio->audio_params().channel_count(),
input_sample_count, FFmpegUtils::GetFFmpegSampleFormat(audio->audio_params().format()), 0);
av_samples_alloc_array_and_samples(&input_data, &input_linesize, audio.audio_params().channel_count(),
input_sample_count, FFmpegUtils::GetFFmpegSampleFormat(audio.audio_params().format()), 0);
for (int i=0; i<audio->audio_params().channel_count(); i++) {
memcpy(input_data[i], audio->data(i), input_sample_count * audio->audio_params().bytes_per_sample_per_channel());
for (int i=0; i<audio.audio_params().channel_count(); i++) {
memcpy(input_data[i], audio.data(i), input_sample_count * audio.audio_params().bytes_per_sample_per_channel());
}
}
result = WriteAudioData(audio->audio_params(), const_cast<const uint8_t**>(input_data), input_sample_count);
result = WriteAudioData(audio.audio_params().is_valid() ? audio.audio_params() : params().audio_params(), const_cast<const uint8_t**>(input_data), input_sample_count);
if (input_data) {
av_freep(&input_data[0]);
@@ -774,7 +774,7 @@ void FFmpegEncoder::FlushEncoders()
}
if (audio_codec_ctx_) {
WriteAudio(nullptr);
WriteAudio(SampleBuffer());
FlushCodecCtx(audio_codec_ctx_, audio_stream_);
}
+1 -1
View File
@@ -47,7 +47,7 @@ public:
virtual bool WriteFrame(olive::FramePtr frame, olive::rational time) override;
virtual bool WriteAudio(olive::SampleBufferPtr audio) override;
virtual bool WriteAudio(const olive::SampleBuffer &audio) override;
bool WriteAudioData(const AudioParams &audio_params, const uint8_t **data, int input_sample_count);
+1 -1
View File
@@ -62,7 +62,7 @@ bool OIIOEncoder::WriteFrame(FramePtr frame, rational time)
return true;
}
bool OIIOEncoder::WriteAudio(SampleBufferPtr audio)
bool OIIOEncoder::WriteAudio(const SampleBuffer &audio)
{
// Do nothing
return false;
+1 -1
View File
@@ -35,7 +35,7 @@ public slots:
virtual bool Open() override;
virtual bool WriteFrame(olive::FramePtr frame, olive::rational time) override;
virtual bool WriteAudio(SampleBufferPtr audio) override;
virtual bool WriteAudio(const SampleBuffer &audio) override;
virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override;
virtual void Close() override;
+28 -33
View File
@@ -20,6 +20,8 @@
#include "samplebuffer.h"
#include "common/cpuoptimize.h"
namespace olive {
SampleBuffer::SampleBuffer() :
@@ -27,25 +29,18 @@ SampleBuffer::SampleBuffer() :
{
}
SampleBufferPtr SampleBuffer::Create()
SampleBuffer::SampleBuffer(const AudioParams &audio_params, const rational &length) :
audio_params_(audio_params)
{
return std::make_shared<SampleBuffer>();
sample_count_per_channel_ = audio_params_.time_to_samples(length);
allocate();
}
SampleBufferPtr SampleBuffer::CreateAllocated(const AudioParams &audio_params, const rational &length)
SampleBuffer::SampleBuffer(const AudioParams &audio_params, int samples_per_channel) :
audio_params_(audio_params),
sample_count_per_channel_(samples_per_channel)
{
return CreateAllocated(audio_params, audio_params.time_to_samples(length));
}
SampleBufferPtr SampleBuffer::CreateAllocated(const AudioParams &audio_params, int samples_per_channel)
{
SampleBufferPtr buffer = Create();
buffer->set_audio_params(audio_params);
buffer->set_sample_count(samples_per_channel);
buffer->allocate();
return buffer;
allocate();
}
const AudioParams &SampleBuffer::audio_params() const
@@ -104,14 +99,11 @@ void SampleBuffer::allocate()
for (int i=0; i<audio_params_.channel_count(); i++) {
data_[i].resize(sample_count_per_channel_);
}
update_raw();
}
void SampleBuffer::destroy()
{
data_.clear();
raw_ptrs_.clear();
}
void SampleBuffer::reverse()
@@ -157,29 +149,40 @@ void SampleBuffer::speed(double speed)
}
data_ = output_data;
update_raw();
}
void SampleBuffer::transform_volume(float f)
{
for (int i=0;i<audio_params().channel_count();i++) {
for (int j=0;j<sample_count_per_channel_;j++) {
data_[i][j] *= f;
}
transform_volume_for_channel(i, f);
}
}
void SampleBuffer::transform_volume_for_channel(int channel, float volume)
{
for (int i=0;i<sample_count_per_channel_;i++) {
data_[channel][i] *= volume;
float *cdat = data_[channel].data();
int unopt_start = 0;
#if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM)
__m128 mult = _mm_load1_ps(&volume);
unopt_start = (sample_count_per_channel_ / 4) * 4;
for (int j=0; j<unopt_start; j+=4) {
float *here = cdat + j;
__m128 samples = _mm_loadu_ps(here);
__m128 multiplied = _mm_mul_ps(samples, mult);
_mm_storeu_ps(here, multiplied);
}
#endif
for (int j=unopt_start; j<sample_count_per_channel_; j++) {
cdat[j] *= volume;
}
}
void SampleBuffer::transform_volume_for_sample(int sample_index, float volume)
{
for (int i=0;i<audio_params().channel_count();i++) {
data_[i][sample_index] *= volume;
transform_volume_for_sample_on_channel(sample_index, i, volume);
}
}
@@ -220,12 +223,4 @@ void SampleBuffer::set(int channel, const float *data, int sample_offset, int sa
memcpy(&data_[channel].data()[sample_offset], data, sizeof(float) * sample_length);
}
void SampleBuffer::update_raw()
{
raw_ptrs_.resize(data_.size());
for (int i=0; i<raw_ptrs_.size(); i++) {
raw_ptrs_[i] = data_[i].data();
}
}
}
+9 -15
View File
@@ -27,9 +27,6 @@
namespace olive {
class SampleBuffer;
using SampleBufferPtr = std::shared_ptr<SampleBuffer>;
/**
* @brief A buffer of audio samples
*
@@ -42,12 +39,8 @@ class SampleBuffer
{
public:
SampleBuffer();
static SampleBufferPtr Create();
static SampleBufferPtr CreateAllocated(const AudioParams& audio_params, const rational& length);
static SampleBufferPtr CreateAllocated(const AudioParams& audio_params, int samples_per_channel);
DISABLE_COPY_MOVE(SampleBuffer)
SampleBuffer(const AudioParams& audio_params, const rational& length);
SampleBuffer(const AudioParams& audio_params, int samples_per_channel);
const AudioParams& audio_params() const;
void set_audio_params(const AudioParams& params);
@@ -69,9 +62,13 @@ public:
return data_.at(channel).constData();
}
float **to_raw_ptrs()
QVector<float *> to_raw_ptrs()
{
return raw_ptrs_.data();
QVector<float *> r(data_.size());
for (int i=0; i<r.size(); i++) {
r[i] = data_[i].data();
}
return r;
}
bool is_allocated() const;
@@ -96,19 +93,16 @@ public:
}
private:
void update_raw();
AudioParams audio_params_;
int sample_count_per_channel_;
QVector< QVector<float> > data_;
QVector<float*> raw_ptrs_;
};
}
Q_DECLARE_METATYPE(olive::SampleBufferPtr)
Q_DECLARE_METATYPE(olive::SampleBuffer)
#endif // SAMPLEBUFFER_H
+36
View File
@@ -79,4 +79,40 @@ QString QtUtils::GetFormattedDateTime(const QDateTime &dt)
return dt.toString(Qt::TextDate);
}
QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm, int bounding_width)
{
QStringList list;
QStringList lines = s.split('\n');
// Iterate every line
for (int i=0; i<lines.size(); i++) {
QString this_line = lines.at(i);
while (this_line.size() > 1 && QFontMetricsWidth(fm, this_line) >= bounding_width) {
for (int j=this_line.size()-1; j>=0; j--) {
if (this_line.at(j).isSpace()) {
QString chopped = this_line.left(j);
if (QFontMetricsWidth(fm, chopped) < bounding_width) {
list.append(chopped);
int k = j+1;
while (k < this_line.size() && this_line.at(k).isSpace()) {
k++;
}
this_line.remove(0, k);
break;
}
}
}
}
if (!this_line.isEmpty()) {
list.append(this_line);
}
}
return list;
}
}
+2
View File
@@ -62,6 +62,8 @@ public:
static QString GetFormattedDateTime(const QDateTime &dt);
static QStringList WordWrapString(const QString &s, const QFontMetrics &fm, int bounding_width);
};
}
+4
View File
@@ -121,6 +121,10 @@ void Config::SetDefaults()
SetEntryInternal(QStringLiteral("AudioOutput"), NodeValue::kText, QString());
SetEntryInternal(QStringLiteral("AudioInput"), NodeValue::kText, QString());
SetEntryInternal(QStringLiteral("AudioOutputSampleRate"), NodeValue::kInt, 48000);
SetEntryInternal(QStringLiteral("AudioOutputChannelLayout"), NodeValue::kInt, AV_CH_LAYOUT_STEREO);
SetEntryInternal(QStringLiteral("AudioOutputSampleFormat"), NodeValue::kInt, AudioParams::kFormatSigned16Packed);
SetEntryInternal(QStringLiteral("AudioRecordingFormat"), NodeValue::kInt, ExportFormat::kFormatWAV);
SetEntryInternal(QStringLiteral("AudioRecordingCodec"), NodeValue::kInt, ExportCodec::kCodecPCM);
SetEntryInternal(QStringLiteral("AudioRecordingSampleRate"), NodeValue::kInt, 48000);
+1
View File
@@ -31,6 +31,7 @@
namespace olive {
#define OLIVE_CONFIG(x) Config::Current()[QStringLiteral(x)]
#define OLIVE_CONFIG_STR(x) Config::Current()[x]
class Config {
public:
+7 -26
View File
@@ -108,7 +108,7 @@ void Core::DeclareTypesForQt()
qRegisterMetaType<NodeValueTable>();
qRegisterMetaType<NodeValueDatabase>();
qRegisterMetaType<FramePtr>();
qRegisterMetaType<SampleBufferPtr>();
qRegisterMetaType<SampleBuffer>();
qRegisterMetaType<AudioParams>();
qRegisterMetaType<NodeKeyframe::Type>();
qRegisterMetaType<Decoder::RetrieveState>();
@@ -794,7 +794,7 @@ void Core::StartGUI(bool full_screen)
connect(this, &Core::ProjectClosed, main_window_, &MainWindow::ProjectClose);
// Start autorecovery timer using the config value as its interval
SetAutorecoveryInterval(Config::Current()["AutorecoveryInterval"].toInt());
SetAutorecoveryInterval(OLIVE_CONFIG("AutorecoveryInterval").toInt());
connect(&autorecovery_timer_, &QTimer::timeout, this, &Core::SaveAutorecovery);
autorecovery_timer_.start();
@@ -960,7 +960,7 @@ bool Core::RevertProjectInternal(Project *p, bool by_opening_existing)
void Core::SaveAutorecovery()
{
if (Config::Current()[QStringLiteral("AutorecoveryEnabled")].toBool()) {
if (OLIVE_CONFIG("AutorecoveryEnabled").toBool()) {
foreach (Project* p, open_projects_) {
if (!p->has_autorecovery_been_saved()) {
QDir project_autorecovery_dir(QDir(FileFunctions::GetAutoRecoveryRoot()).filePath(p->GetUuid().toString()));
@@ -986,7 +986,7 @@ void Core::SaveAutorecovery()
realname_file.close();
}
int64_t max_recoveries_per_file = Config::Current()[QStringLiteral("AutorecoveryMaximum")].toLongLong();
int64_t max_recoveries_per_file = OLIVE_CONFIG("AutorecoveryMaximum").toLongLong();
// Since we write an extra file, increment total allowed files by 1
max_recoveries_per_file++;
@@ -1075,12 +1075,12 @@ Folder *Core::GetSelectedFolderInActiveProject() const
Timecode::Display Core::GetTimecodeDisplay() const
{
return static_cast<Timecode::Display>(Config::Current()["TimecodeDisplay"].toInt());
return static_cast<Timecode::Display>(OLIVE_CONFIG("TimecodeDisplay").toInt());
}
void Core::SetTimecodeDisplay(Timecode::Display d)
{
Config::Current()["TimecodeDisplay"] = d;
OLIVE_CONFIG("TimecodeDisplay") = d;
emit TimecodeDisplayChanged(d);
}
@@ -1202,7 +1202,7 @@ void Core::SetStartupLocale()
}
}
QString use_locale = Config::Current()[QStringLiteral("Language")].toString();
QString use_locale = OLIVE_CONFIG("Language").toString();
if (use_locale.isEmpty()) {
// No configured locale, auto-detect the system's locale
@@ -1412,25 +1412,6 @@ int Core::CountFilesInFileList(const QFileInfoList &filenames)
return file_count;
}
QString GetRenderModePreferencePrefix(RenderMode::Mode mode, const QString &preference) {
QString key;
key.append((mode == RenderMode::kOffline) ? QStringLiteral("Offline") : QStringLiteral("Online"));
key.append(preference);
return key;
}
QVariant Core::GetPreferenceForRenderMode(RenderMode::Mode mode, const QString &preference)
{
return Config::Current()[GetRenderModePreferencePrefix(mode, preference)];
}
void Core::SetPreferenceForRenderMode(RenderMode::Mode mode, const QString &preference, const QVariant &value)
{
Config::Current()[GetRenderModePreferencePrefix(mode, preference)] = value;
}
bool Core::LabelNodes(const QVector<Node *> &nodes, MultiUndoCommand *parent)
{
if (nodes.isEmpty()) {
-3
View File
@@ -247,9 +247,6 @@ public:
*/
static int CountFilesInFileList(const QFileInfoList &filenames);
static QVariant GetPreferenceForRenderMode(RenderMode::Mode mode, const QString& preference);
static void SetPreferenceForRenderMode(RenderMode::Mode mode, const QString& preference, const QVariant& value);
/**
* @brief Show a dialog to the user to rename a set of nodes
*/
+1 -1
View File
@@ -132,7 +132,7 @@ AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent) :
void AboutDialog::accept()
{
if (dont_show_again_checkbox_ && dont_show_again_checkbox_->isChecked()) {
Config::Current()[QStringLiteral("ShowWelcomeDialog")] = false;
OLIVE_CONFIG("ShowWelcomeDialog") = false;
}
QDialog::accept();
+2 -2
View File
@@ -38,8 +38,8 @@ public:
int GetValue() const;
static const int kDefaultH264CRF = 23;
static const int kDefaultH265CRF = 28;
static const int kDefaultH264CRF = 18;
static const int kDefaultH265CRF = 23;
private:
static const int kMinimumCRF = 0;
+1 -1
View File
@@ -208,7 +208,7 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
video_tab_->height_slider()->SetDefaultValue(vp.height());
video_tab_->SetSelectedFrameRate(vp.frame_rate());
video_tab_->pixel_aspect_combobox()->SetPixelAspectRatio(vp.pixel_aspect_ratio());
video_tab_->pixel_format_field()->SetPixelFormat(static_cast<VideoParams::Format>(Config::Current()[QStringLiteral("OnlinePixelFormat")].toInt()));
video_tab_->pixel_format_field()->SetPixelFormat(static_cast<VideoParams::Format>(OLIVE_CONFIG("OnlinePixelFormat").toInt()));
video_tab_->interlaced_combobox()->SetInterlaceMode(vp.interlacing());
audio_tab_->sample_rate_combobox()->SetSampleRate(ap.sample_rate());
audio_tab_->sample_format_combobox()->SetAttemptToRestoreFormat(false);
@@ -74,7 +74,7 @@ PreferencesAppearanceTab::PreferencesAppearanceTab()
color_layout->addWidget(new QLabel(cat_name), i, 0);
ColorCodingComboBox* ccc = new ColorCodingComboBox();
ccc->SetColor(Config::Current()[QStringLiteral("CatColor%1").arg(i)].toInt());
ccc->SetColor(OLIVE_CONFIG_STR(QStringLiteral("CatColor%1").arg(i)).toInt());
color_layout->addWidget(ccc, i, 1);
color_btns_.append(ccc);
}
@@ -92,7 +92,7 @@ PreferencesAppearanceTab::PreferencesAppearanceTab()
marker_layout->addWidget(new QLabel("Default Marker Color"), 0, 0);
marker_btn_ = new ColorCodingComboBox();
marker_btn_->SetColor(Config::Current()[QStringLiteral("MarkerColor")].toInt());
marker_btn_->SetColor(OLIVE_CONFIG("MarkerColor").toInt());
marker_layout->addWidget(marker_btn_, 0, 1);
appearance_layout->addWidget(marker_group, row, 0, 1, 2);
@@ -109,14 +109,14 @@ void PreferencesAppearanceTab::Accept(MultiUndoCommand *command)
if (style_path != StyleManager::GetStyle()) {
StyleManager::SetStyle(style_path);
Config::Current()[QStringLiteral("Style")] = style_path;
OLIVE_CONFIG("Style") = style_path;
}
for (int i=0; i<color_btns_.size(); i++) {
Config::Current()[QStringLiteral("CatColor%1").arg(i)] = color_btns_.at(i)->GetSelectedColor();
OLIVE_CONFIG_STR(QStringLiteral("CatColor%1").arg(i)) = color_btns_.at(i)->GetSelectedColor();
}
Config::Current()[QStringLiteral("MarkerColor")] = marker_btn_->GetSelectedColor();
OLIVE_CONFIG("MarkerColor") = marker_btn_->GetSelectedColor();
}
}
@@ -69,6 +69,40 @@ PreferencesAudioTab::PreferencesAudioTab()
audio_output_devices_ = new QComboBox();
output_layout->addWidget(audio_output_devices_, row, 1);
row++;
{
int output_row = 0;
QGroupBox *output_param_group = new QGroupBox(tr("Advanced"));
output_layout->addWidget(output_param_group, row, 0, 1, 2);
QGridLayout *output_param_layout = new QGridLayout(output_param_group);
output_param_layout->addWidget(new QLabel(tr("Sample Rate:")), output_row, 0);
output_rate_combo_ = new SampleRateComboBox();
output_rate_combo_->SetSampleRate(OLIVE_CONFIG("AudioOutputSampleRate").toInt());
output_param_layout->addWidget(output_rate_combo_, output_row, 1);
output_row++;
output_param_layout->addWidget(new QLabel(tr("Channel Layout:")), output_row, 0);
output_ch_layout_combo_ = new ChannelLayoutComboBox();
output_ch_layout_combo_->SetChannelLayout(OLIVE_CONFIG("AudioOutputChannelLayout").toULongLong());
output_param_layout->addWidget(output_ch_layout_combo_, output_row, 1);
output_row++;
output_param_layout->addWidget(new QLabel(tr("Sample Format:")), output_row, 0);
output_fmt_combo_ = new SampleFormatComboBox();
output_fmt_combo_->SetPackedFormats();
output_fmt_combo_->SetSampleFormat(static_cast<AudioParams::Format>(OLIVE_CONFIG("AudioOutputSampleFormat").toInt()));
output_param_layout->addWidget(output_fmt_combo_, output_row, 1);
}
}
row = 0;
@@ -146,12 +180,18 @@ void PreferencesAudioTab::Accept(MultiUndoCommand *command)
AudioManager::instance()->SetOutputDevice(output_device);
AudioManager::instance()->SetInputDevice(input_device);
OLIVE_CONFIG("AudioOutputSampleRate") = output_rate_combo_->GetSampleRate();
OLIVE_CONFIG("AudioOutputChannelLayout") = QVariant::fromValue(output_ch_layout_combo_->GetChannelLayout());
OLIVE_CONFIG("AudioOutputSampleFormat") = output_fmt_combo_->GetSampleFormat();
OLIVE_CONFIG("AudioRecordingFormat") = record_format_combo_->GetFormat();
OLIVE_CONFIG("AudioRecordingCodec") = record_options_->GetCodec();
OLIVE_CONFIG("AudioRecordingSampleRate") = record_options_->sample_rate_combobox()->GetSampleRate();
OLIVE_CONFIG("AudioRecordingChannelLayout") = QVariant::fromValue(record_options_->channel_layout_combobox()->GetChannelLayout());
OLIVE_CONFIG("AudioRecordingBitRate") = QVariant::fromValue(record_options_->bit_rate_slider()->GetValue());
OLIVE_CONFIG("AudioRecordingSampleFormat") = record_options_->sample_format_combobox()->GetSampleFormat();
emit AudioManager::instance()->OutputParamsChanged();
}
void PreferencesAudioTab::RefreshBackends()
@@ -61,6 +61,10 @@ private:
*/
QPushButton* refresh_devices_btn_;
SampleRateComboBox *output_rate_combo_;
ChannelLayoutComboBox *output_ch_layout_combo_;
SampleFormatComboBox *output_fmt_combo_;
ExportFormatComboBox *record_format_combo_;
ExportAudioTab *record_options_;
@@ -108,10 +108,8 @@ void PreferencesBehaviorTab::Accept(MultiUndoCommand *command)
{
Q_UNUSED(command)
QMap<QTreeWidgetItem*, QString>::const_iterator iterator;
for (iterator=config_map_.begin();iterator!=config_map_.end();iterator++) {
Config::Current()[iterator.value()] = (iterator.key()->checkState(0) == Qt::Checked);
for (auto iterator=config_map_.begin();iterator!=config_map_.end();iterator++) {
OLIVE_CONFIG_STR(iterator.value()) = (iterator.key()->checkState(0) == Qt::Checked);
}
}
@@ -119,7 +117,7 @@ QTreeWidgetItem* PreferencesBehaviorTab::AddItem(const QString &text, const QStr
{
QTreeWidgetItem* item = new QTreeWidgetItem({text});
item->setToolTip(0, tooltip);
item->setCheckState(0, Config::Current()[config_key].toBool() ? Qt::Checked : Qt::Unchecked);
item->setCheckState(0, OLIVE_CONFIG_STR(config_key).toBool() ? Qt::Checked : Qt::Unchecked);
config_map_.insert(item, config_key);
@@ -71,7 +71,7 @@ PreferencesDiskTab::PreferencesDiskTab()
cache_ahead_slider_ = new FloatSlider();
cache_ahead_slider_->SetFormat(tr("%1 seconds"));
cache_ahead_slider_->SetMinimum(0);
cache_ahead_slider_->SetValue(Config::Current()["DiskCacheAhead"].value<rational>().toDouble());
cache_ahead_slider_->SetValue(OLIVE_CONFIG("DiskCacheAhead").value<rational>().toDouble());
cache_behavior_layout->addWidget(cache_ahead_slider_, row, 1);
cache_behavior_layout->addWidget(new QLabel(tr("Cache Behind:")), row, 2);
@@ -79,7 +79,7 @@ PreferencesDiskTab::PreferencesDiskTab()
cache_behind_slider_ = new FloatSlider();
cache_behind_slider_->SetMinimum(0);
cache_behind_slider_->SetFormat(tr("%1 seconds"));
cache_behind_slider_->SetValue(Config::Current()["DiskCacheBehind"].value<rational>().toDouble());
cache_behind_slider_->SetValue(OLIVE_CONFIG("DiskCacheBehind").value<rational>().toDouble());
cache_behavior_layout->addWidget(cache_behind_slider_, row, 3);
outer_layout->addStretch();
@@ -115,8 +115,8 @@ void PreferencesDiskTab::Accept(MultiUndoCommand *command)
default_disk_cache_folder_->SetPath(disk_cache_location_->text());
}
Config::Current()["DiskCacheBehind"] = QVariant::fromValue(rational::fromDouble(cache_behind_slider_->GetValue()));
Config::Current()["DiskCacheAhead"] = QVariant::fromValue(rational::fromDouble(cache_ahead_slider_->GetValue()));
OLIVE_CONFIG("DiskCacheBehind") = QVariant::fromValue(rational::fromDouble(cache_behind_slider_->GetValue()));
OLIVE_CONFIG("DiskCacheAhead") = QVariant::fromValue(rational::fromDouble(cache_ahead_slider_->GetValue()));
}
}
@@ -55,7 +55,7 @@ PreferencesGeneralTab::PreferencesGeneralTab()
AddLanguage(l);
}
QString current_language = Config::Current()[QStringLiteral("Language")].toString();
QString current_language = OLIVE_CONFIG("Language").toString();
if (current_language.isEmpty()) {
// No configured language, use system language
current_language = QLocale::system().name();
@@ -86,7 +86,7 @@ PreferencesGeneralTab::PreferencesGeneralTab()
autoscroll_method_->addItem(tr("None"), AutoScroll::kNone);
autoscroll_method_->addItem(tr("Page Scrolling"), AutoScroll::kPage);
autoscroll_method_->addItem(tr("Smooth Scrolling"), AutoScroll::kSmooth);
autoscroll_method_->setCurrentIndex(Config::Current()["Autoscroll"].toInt());
autoscroll_method_->setCurrentIndex(OLIVE_CONFIG("Autoscroll").toInt());
timeline_layout->addWidget(autoscroll_method_, row, 1);
row++;
@@ -94,7 +94,7 @@ PreferencesGeneralTab::PreferencesGeneralTab()
timeline_layout->addWidget(new QLabel(tr("Rectified Waveforms:")), row, 0);
rectified_waveforms_ = new QCheckBox();
rectified_waveforms_->setChecked(Config::Current()["RectifiedWaveforms"].toBool());
rectified_waveforms_->setChecked(OLIVE_CONFIG("RectifiedWaveforms").toBool());
timeline_layout->addWidget(rectified_waveforms_, row, 1);
row++;
@@ -105,7 +105,7 @@ PreferencesGeneralTab::PreferencesGeneralTab()
default_still_length_->SetMinimum(rational(100, 1000));
default_still_length_->SetTimebase(rational(100, 1000));
default_still_length_->SetFormat(tr("%1 seconds"));
default_still_length_->SetValue(Config::Current()["DefaultStillLength"].value<rational>());
default_still_length_->SetValue(OLIVE_CONFIG("DefaultStillLength").value<rational>());
timeline_layout->addWidget(default_still_length_);
}
@@ -119,7 +119,7 @@ PreferencesGeneralTab::PreferencesGeneralTab()
autorecovery_layout->addWidget(new QLabel(tr("Enable Auto-Recovery:")), row, 0);
autorecovery_enabled_ = new QCheckBox();
autorecovery_enabled_->setChecked(Config::Current()[QStringLiteral("AutorecoveryEnabled")].toBool());
autorecovery_enabled_->setChecked(OLIVE_CONFIG("AutorecoveryEnabled").toBool());
autorecovery_layout->addWidget(autorecovery_enabled_, row, 1);
row++;
@@ -130,7 +130,7 @@ PreferencesGeneralTab::PreferencesGeneralTab()
autorecovery_interval_->SetMinimum(1);
autorecovery_interval_->SetMaximum(60);
autorecovery_interval_->SetFormat(QT_TRANSLATE_N_NOOP("olive::SliderBase", "%n minute(s)"), true);
autorecovery_interval_->SetValue(Config::Current()[QStringLiteral("AutorecoveryInterval")].toLongLong());
autorecovery_interval_->SetValue(OLIVE_CONFIG("AutorecoveryInterval").toLongLong());
autorecovery_layout->addWidget(autorecovery_interval_, row, 1);
row++;
@@ -140,7 +140,7 @@ PreferencesGeneralTab::PreferencesGeneralTab()
autorecovery_maximum_ = new IntegerSlider();
autorecovery_maximum_->SetMinimum(1);
autorecovery_maximum_->SetMaximum(1000);
autorecovery_maximum_->SetValue(Config::Current()[QStringLiteral("AutorecoveryMaximum")].toLongLong());
autorecovery_maximum_->SetValue(OLIVE_CONFIG("AutorecoveryMaximum").toLongLong());
autorecovery_layout->addWidget(autorecovery_maximum_, row, 1);
row++;
@@ -157,11 +157,11 @@ void PreferencesGeneralTab::Accept(MultiUndoCommand *command)
{
Q_UNUSED(command)
Config::Current()[QStringLiteral("RectifiedWaveforms")] = rectified_waveforms_->isChecked();
OLIVE_CONFIG("RectifiedWaveforms") = rectified_waveforms_->isChecked();
Config::Current()[QStringLiteral("Autoscroll")] = autoscroll_method_->currentData();
OLIVE_CONFIG("Autoscroll") = autoscroll_method_->currentData();
Config::Current()[QStringLiteral("DefaultStillLength")] = QVariant::fromValue(default_still_length_->GetValue());
OLIVE_CONFIG("DefaultStillLength") = QVariant::fromValue(default_still_length_->GetValue());
QString set_language = language_combobox_->currentData().toString();
if (QLocale::system().name() == set_language) {
@@ -170,14 +170,14 @@ void PreferencesGeneralTab::Accept(MultiUndoCommand *command)
}
// If the language has changed, set it now
if (Config::Current()[QStringLiteral("Language")].toString() != set_language) {
Config::Current()[QStringLiteral("Language")] = set_language;
if (OLIVE_CONFIG("Language").toString() != set_language) {
OLIVE_CONFIG("Language") = set_language;
Core::instance()->SetLanguage(set_language.isEmpty() ? QLocale::system().name() : set_language);
}
Config::Current()[QStringLiteral("AutorecoveryEnabled")] = autorecovery_enabled_->isChecked();
Config::Current()[QStringLiteral("AutorecoveryInterval")] = QVariant::fromValue(autorecovery_interval_->GetValue());
Config::Current()[QStringLiteral("AutorecoveryMaximum")] = QVariant::fromValue(autorecovery_maximum_->GetValue());
OLIVE_CONFIG("AutorecoveryEnabled") = autorecovery_enabled_->isChecked();
OLIVE_CONFIG("AutorecoveryInterval") = QVariant::fromValue(autorecovery_interval_->GetValue());
OLIVE_CONFIG("AutorecoveryMaximum") = QVariant::fromValue(autorecovery_maximum_->GetValue());
Core::instance()->SetAutorecoveryInterval(autorecovery_interval_->GetValue());
}
+8 -8
View File
@@ -148,14 +148,14 @@ void SequenceDialog::SetAsDefaultClicked()
tr("Are you sure you want to set the current parameters as defaults?"),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
// Maybe replace with Preset system
Config::Current()[QStringLiteral("DefaultSequenceWidth")] = parameter_tab_->GetSelectedVideoWidth();
Config::Current()[QStringLiteral("DefaultSequenceHeight")] = parameter_tab_->GetSelectedVideoHeight();
Config::Current()[QStringLiteral("DefaultSequencePixelAspect")] = QVariant::fromValue(parameter_tab_->GetSelectedVideoPixelAspect());
Config::Current()[QStringLiteral("DefaultSequenceFrameRate")] = QVariant::fromValue(parameter_tab_->GetSelectedVideoFrameRate().flipped());
Config::Current()[QStringLiteral("DefaultSequenceInterlacing")] = parameter_tab_->GetSelectedVideoInterlacingMode();
Config::Current()[QStringLiteral("DefaultSequenceAudioFrequency")] = parameter_tab_->GetSelectedAudioSampleRate();
Config::Current()[QStringLiteral("DefaultSequenceAudioLayout")] = QVariant::fromValue(parameter_tab_->GetSelectedAudioChannelLayout());
Config::Current()[QStringLiteral("DefaultSequenceAutoCache")] = QVariant::fromValue(parameter_tab_->GetSelectedPreviewAutoCache());
OLIVE_CONFIG("DefaultSequenceWidth") = parameter_tab_->GetSelectedVideoWidth();
OLIVE_CONFIG("DefaultSequenceHeight") = parameter_tab_->GetSelectedVideoHeight();
OLIVE_CONFIG("DefaultSequencePixelAspect") = QVariant::fromValue(parameter_tab_->GetSelectedVideoPixelAspect());
OLIVE_CONFIG("DefaultSequenceFrameRate") = QVariant::fromValue(parameter_tab_->GetSelectedVideoFrameRate().flipped());
OLIVE_CONFIG("DefaultSequenceInterlacing") = parameter_tab_->GetSelectedVideoInterlacingMode();
OLIVE_CONFIG("DefaultSequenceAudioFrequency") = parameter_tab_->GetSelectedAudioSampleRate();
OLIVE_CONFIG("DefaultSequenceAudioLayout") = QVariant::fromValue(parameter_tab_->GetSelectedAudioChannelLayout());
OLIVE_CONFIG("DefaultSequenceAutoCache") = QVariant::fromValue(parameter_tab_->GetSelectedPreviewAutoCache());
}
}
@@ -100,8 +100,8 @@ QTreeWidgetItem* SequenceDialogPresetTab::CreateFolder(const QString &name)
QTreeWidgetItem *SequenceDialogPresetTab::CreateHDPresetFolder(const QString &name, int width, int height, int divider)
{
const VideoParams::Format default_format = static_cast<VideoParams::Format>(Config::Current()["OfflinePixelFormat"].toInt());
const bool default_autocache = Config::Current()[QStringLiteral("DefaultSequenceAutoCache")].toBool();
const VideoParams::Format default_format = static_cast<VideoParams::Format>(OLIVE_CONFIG("OfflinePixelFormat").toInt());
const bool default_autocache = OLIVE_CONFIG("DefaultSequenceAutoCache").toBool();
QTreeWidgetItem* parent = CreateFolder(name);
AddStandardItem(parent, std::make_shared<SequencePreset>(tr("%1 23.976 FPS").arg(name),
width,
@@ -163,8 +163,8 @@ QTreeWidgetItem *SequenceDialogPresetTab::CreateHDPresetFolder(const QString &na
QTreeWidgetItem *SequenceDialogPresetTab::CreateSDPresetFolder(const QString &name, int width, int height, const rational& frame_rate, const rational &standard_par, const rational &wide_par, int divider)
{
const VideoParams::Format default_format = static_cast<VideoParams::Format>(Config::Current()["OfflinePixelFormat"].toInt());
const bool default_autocache = Config::Current()[QStringLiteral("DefaultSequenceAutoCache")].toBool();
const VideoParams::Format default_format = static_cast<VideoParams::Format>(OLIVE_CONFIG("OfflinePixelFormat").toInt());
const bool default_autocache = OLIVE_CONFIG("DefaultSequenceAutoCache").toBool();
QTreeWidgetItem* parent = CreateFolder(name);
preset_tree_->addTopLevelItem(parent);
AddStandardItem(parent, std::make_shared<SequencePreset>(tr("%1 Standard").arg(name),
+19 -21
View File
@@ -42,11 +42,6 @@ PanNode::PanNode()
SetEffectInput(kSamplesInput);
}
Node *PanNode::copy() const
{
return new PanNode();
}
QString PanNode::Name() const
{
return tr("Pan");
@@ -72,45 +67,48 @@ void PanNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeV
Q_UNUSED(globals)
// Create a sample job
SampleJob job(kSamplesInput, value);
if (job.HasSamples()) {
bool push_job = false;
SampleBuffer samples = value[kSamplesInput].toSamples();
if (samples.is_allocated()) {
bool pushed_job = false;
// This node is only compatible with stereo audio
if (job.samples()->audio_params().channel_count() == 2) {
if (samples.audio_params().channel_count() == 2) {
// If the input is static, we can just do it now which will be faster
if (IsInputStatic(kPanningInput)) {
float pan_volume = job.GetValue(kPanningInput).data().toFloat();
float pan_volume = value[kPanningInput].toDouble();
if (!qIsNull(pan_volume)) {
if (pan_volume > 0) {
job.samples()->transform_volume_for_channel(0, 1.0f - pan_volume);
samples.transform_volume_for_channel(0, 1.0f - pan_volume);
} else {
job.samples()->transform_volume_for_channel(1, 1.0f + pan_volume);
samples.transform_volume_for_channel(1, 1.0f + pan_volume);
}
}
} else {
// Requires job
push_job = true;
pushed_job = true;
table->Push(NodeValue::kSamples, SampleJob(kSamplesInput, value), this);
}
}
table->Push(NodeValue::kSamples, push_job ? QVariant::fromValue(job) : QVariant::fromValue(job.samples()), this);
if (!pushed_job) {
table->Push(value[kSamplesInput]);
}
}
}
void PanNode::ProcessSamples(const NodeValueRow &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const
void PanNode::ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const
{
float pan_val = values[kPanningInput].data().toFloat();
float pan_val = values[kPanningInput].toDouble();
for (int i=0;i<input->audio_params().channel_count();i++) {
output->data(i)[index] = input->data(i)[index];
for (int i=0;i<input.audio_params().channel_count();i++) {
output.data(i)[index] = input.data(i)[index];
}
if (pan_val > 0) {
output->data(0)[index] *= (1.0F - pan_val);
output.data(0)[index] *= (1.0F - pan_val);
} else if (pan_val < 0) {
output->data(1)[index] *= (1.0F - qAbs(pan_val));
output.data(1)[index] *= (1.0F - qAbs(pan_val));
}
}
+2 -4
View File
@@ -31,9 +31,7 @@ class PanNode : public Node
public:
PanNode();
NODE_DEFAULT_DESTRUCTOR(PanNode)
virtual Node* copy() const override;
NODE_DEFAULT_FUNCTIONS(PanNode)
virtual QString Name() const override;
virtual QString id() const override;
@@ -42,7 +40,7 @@ public:
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
virtual void ProcessSamples(const NodeValueRow &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const override;
virtual void ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const override;
virtual void Retranslate() override;
+22 -14
View File
@@ -41,11 +41,6 @@ VolumeNode::VolumeNode()
SetEffectInput(kSamplesInput);
}
Node *VolumeNode::copy() const
{
return new VolumeNode();
}
QString VolumeNode::Name() const
{
return tr("Volume");
@@ -68,17 +63,30 @@ QString VolumeNode::Description() const
void VolumeNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
{
return ValueInternal(kOpMultiply,
kPairSampleNumber,
kSamplesInput,
value[kSamplesInput],
kVolumeInput,
value[kVolumeInput],
globals,
table);
Q_UNUSED(globals)
// Create a sample job
SampleBuffer buffer = value[kSamplesInput].toSamples();
if (buffer.is_allocated()) {
// If the input is static, we can just do it now which will be faster
if (IsInputStatic(kVolumeInput)) {
auto volume = value[kVolumeInput].toDouble();
if (!qFuzzyCompare(volume, 1.0)) {
buffer.transform_volume(volume);
}
table->Push(NodeValue::kSamples, QVariant::fromValue(buffer), this);
} else {
// Requires job
SampleJob job(kSamplesInput, value);
table->Push(NodeValue::kSamples, QVariant::fromValue(job), this);
}
}
}
void VolumeNode::ProcessSamples(const NodeValueRow &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const
void VolumeNode::ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const
{
return ProcessSamplesInternal(values, kOpMultiply, kSamplesInput, kVolumeInput, input, output, index);
}
+2 -4
View File
@@ -31,9 +31,7 @@ class VolumeNode : public MathNodeBase
public:
VolumeNode();
NODE_DEFAULT_DESTRUCTOR(VolumeNode)
virtual Node* copy() const override;
NODE_DEFAULT_FUNCTIONS(VolumeNode)
virtual QString Name() const override;
virtual QString id() const override;
@@ -42,7 +40,7 @@ public:
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
virtual void ProcessSamples(const NodeValueRow &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const override;
virtual void ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const override;
virtual void Retranslate() override;
+2
View File
@@ -45,6 +45,8 @@ Block::Block() :
SetInputProperty(kLengthInput, QStringLiteral("viewlock"), true);
IgnoreHashingFrom(kLengthInput);
SetInputFlags(kEnabledInput, InputFlags(GetInputFlags(kEnabledInput) | kInputFlagNotConnectable | kInputFlagNotKeyframable));
SetFlags(kDontShowInParamView);
}
-2
View File
@@ -37,8 +37,6 @@ class Block : public Node
public:
Block();
NODE_DEFAULT_DESTRUCTOR(Block)
virtual QVector<CategoryID> Category() const override;
const rational& in() const
+1 -5
View File
@@ -61,11 +61,6 @@ ClipBlock::ClipBlock() :
SetEffectInput(kBufferIn);
}
Node *ClipBlock::copy() const
{
return new ClipBlock();
}
QString ClipBlock::Name() const
{
if (track()) {
@@ -289,6 +284,7 @@ void ClipBlock::Retranslate()
SetInputName(kMediaInInput, tr("Media In"));
SetInputName(kSpeedInput, tr("Speed"));
SetInputName(kReverseInput, tr("Reverse"));
SetInputName(kMaintainAudioPitchInput, tr("Maintain Audio Pitch"));
}
void ClipBlock::Hash(QCryptographicHash &hash, const NodeGlobals &globals, const VideoParams &video_params) const
+1 -3
View File
@@ -37,9 +37,7 @@ class ClipBlock : public Block
public:
ClipBlock();
NODE_DEFAULT_DESTRUCTOR(ClipBlock)
virtual Node* copy() const override;
NODE_DEFAULT_FUNCTIONS(ClipBlock)
virtual QString Name() const override;
virtual QString id() const override;
-5
View File
@@ -26,11 +26,6 @@ GapBlock::GapBlock()
{
}
Node *GapBlock::copy() const
{
return new GapBlock();
}
QString GapBlock::Name() const
{
return tr("Gap");
+1 -3
View File
@@ -34,9 +34,7 @@ class GapBlock : public Block
public:
GapBlock();
NODE_DEFAULT_DESTRUCTOR(GapBlock)
virtual Node * copy() const override;
NODE_DEFAULT_FUNCTIONS(GapBlock)
virtual QString Name() const override;
virtual QString id() const override;
+9 -4
View File
@@ -29,11 +29,16 @@ const QString SubtitleBlock::kTextIn = QStringLiteral("text_in");
SubtitleBlock::SubtitleBlock()
{
AddInput(kTextIn, NodeValue::kText, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
}
Node *SubtitleBlock::copy() const
{
return new SubtitleBlock();
SetInputFlags(kBufferIn, InputFlags(GetInputFlags(kBufferIn) | kInputFlagHidden));
SetInputFlags(kLengthInput, InputFlags(GetInputFlags(kLengthInput) | kInputFlagHidden));
SetInputFlags(kMediaInInput, InputFlags(GetInputFlags(kMediaInInput) | kInputFlagHidden));
SetInputFlags(kSpeedInput, InputFlags(GetInputFlags(kSpeedInput) | kInputFlagHidden));
SetInputFlags(kReverseInput, InputFlags(GetInputFlags(kReverseInput) | kInputFlagHidden));
SetInputFlags(kMaintainAudioPitchInput, InputFlags(GetInputFlags(kMaintainAudioPitchInput) | kInputFlagHidden));
// Undo block flag that hides in param view
SetFlags(GetFlags() & ~kDontShowInParamView);
}
QString SubtitleBlock::Name() const
+1 -3
View File
@@ -31,9 +31,7 @@ class SubtitleBlock : public ClipBlock
public:
SubtitleBlock();
NODE_DEFAULT_DESTRUCTOR(SubtitleBlock)
virtual Node* copy() const override;
NODE_DEFAULT_FUNCTIONS(SubtitleBlock)
virtual QString Name() const override;
virtual QString id() const override;
@@ -24,12 +24,6 @@ namespace olive {
CrossDissolveTransition::CrossDissolveTransition()
{
}
Node *CrossDissolveTransition::copy() const
{
return new CrossDissolveTransition();
}
QString CrossDissolveTransition::Name() const
@@ -52,9 +46,9 @@ QString CrossDissolveTransition::Description() const
return tr("Smoothly transition between two clips.");
}
ShaderCode CrossDissolveTransition::GetShaderCode(const QString &shader_id) const
ShaderCode CrossDissolveTransition::GetShaderCode(const ShaderRequest &request) const
{
Q_UNUSED(shader_id)
Q_UNUSED(request)
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/crossdissolve.frag"), QString());
}
@@ -66,27 +60,27 @@ void CrossDissolveTransition::ShaderJobEvent(const NodeValueRow &value, ShaderJo
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
}
void CrossDissolveTransition::SampleJobEvent(SampleBufferPtr from_samples, SampleBufferPtr to_samples, SampleBufferPtr out_samples, double time_in) const
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++) {
double this_sample_time = out_samples->audio_params().samples_to_time(i).toDouble() + time_in;
for (int i=0; i<out_samples.sample_count(); i++) {
double this_sample_time = out_samples.audio_params().samples_to_time(i).toDouble() + time_in;
double progress = GetTotalProgress(this_sample_time);
for (int j=0; j<out_samples->audio_params().channel_count(); j++) {
out_samples->data(j)[i] = 0;
for (int j=0; j<out_samples.audio_params().channel_count(); j++) {
out_samples.data(j)[i] = 0;
if (from_samples) {
if (i < from_samples->sample_count()) {
out_samples->data(j)[i] += from_samples->data(j)[i] * TransformCurve(1.0 - progress);
if (from_samples.is_allocated()) {
if (i < from_samples.sample_count()) {
out_samples.data(j)[i] += from_samples.data(j)[i] * TransformCurve(1.0 - progress);
}
}
if (to_samples) {
if (to_samples.is_allocated()) {
// Offset input samples from the end
int in_index = i - (out_samples->sample_count() - to_samples->sample_count());
int in_index = i - (out_samples.sample_count() - to_samples.sample_count());
if (in_index >= 0) {
out_samples->data(j)[i] += to_samples->data(j)[in_index] * TransformCurve(progress);
out_samples.data(j)[i] += to_samples.data(j)[in_index] * TransformCurve(progress);
}
}
}
@@ -31,9 +31,7 @@ class CrossDissolveTransition : public TransitionBlock
public:
CrossDissolveTransition();
NODE_DEFAULT_DESTRUCTOR(CrossDissolveTransition)
virtual Node* copy() const override;
NODE_DEFAULT_FUNCTIONS(CrossDissolveTransition)
virtual QString Name() const override;
virtual QString id() const override;
@@ -42,12 +40,12 @@ public:
//virtual void Retranslate() override;
virtual ShaderCode GetShaderCode(const QString& shader_id) const override;
virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
protected:
virtual void ShaderJobEvent(const NodeValueRow &value, ShaderJob& job) const override;
virtual void SampleJobEvent(SampleBufferPtr from_samples, SampleBufferPtr to_samples, SampleBufferPtr out_samples, double time_in) const override;
virtual void SampleJobEvent(const SampleBuffer &from_samples, const SampleBuffer &to_samples, SampleBuffer &out_samples, double time_in) const override;
};
@@ -29,11 +29,6 @@ DipToColorTransition::DipToColorTransition()
AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(0, 0, 0)));
}
Node *DipToColorTransition::copy() const
{
return new DipToColorTransition();
}
QString DipToColorTransition::Name() const
{
return tr("Dip To Color");
@@ -54,16 +49,16 @@ QString DipToColorTransition::Description() const
return tr("Transition between clips by dipping to a color.");
}
ShaderCode DipToColorTransition::GetShaderCode(const QString &shader_id) const
ShaderCode DipToColorTransition::GetShaderCode(const ShaderRequest &request) const
{
Q_UNUSED(shader_id)
Q_UNUSED(request)
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/diptoblack.frag"), QString());
}
void DipToColorTransition::ShaderJobEvent(const NodeValueRow &value, ShaderJob &job) const
{
job.InsertValue(kColorInput, value);
job.Insert(kColorInput, value);
}
}
@@ -31,16 +31,14 @@ class DipToColorTransition : public TransitionBlock
public:
DipToColorTransition();
NODE_DEFAULT_DESTRUCTOR(DipToColorTransition)
virtual Node* copy() const override;
NODE_DEFAULT_FUNCTIONS(DipToColorTransition)
virtual QString Name() const override;
virtual QString id() const override;
virtual QVector<CategoryID> Category() const override;
virtual QString Description() const override;
virtual ShaderCode GetShaderCode(const QString& shader_id) const override;
virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
static const QString kColorInput;
+12 -29
View File
@@ -162,15 +162,15 @@ double TransitionBlock::GetInternalTransitionTime(const double &time) const
void TransitionBlock::InsertTransitionTimes(AcceleratedJob *job, const double &time) const
{
// Provides total transition progress from 0.0 (start) - 1.0 (end)
job->InsertValue(QStringLiteral("ove_tprog_all"),
job->Insert(QStringLiteral("ove_tprog_all"),
NodeValue(NodeValue::kFloat, GetTotalProgress(time), this));
// Provides progress of out section from 1.0 (start) - 0.0 (end)
job->InsertValue(QStringLiteral("ove_tprog_out"),
job->Insert(QStringLiteral("ove_tprog_out"),
NodeValue(NodeValue::kFloat, GetOutProgress(time), this));
// Provides progress of in section from 0.0 (start) - 1.0 (end)
job->InsertValue(QStringLiteral("ove_tprog_in"),
job->Insert(QStringLiteral("ove_tprog_in"),
NodeValue(NodeValue::kFloat, GetInProgress(time), this));
}
@@ -188,14 +188,14 @@ void TransitionBlock::Value(const NodeValueRow &value, const NodeGlobals &global
ShaderJob job;
if (out_buffer.type() != NodeValue::kNone) {
job.InsertValue(kOutBlockInput, out_buffer);
job.Insert(kOutBlockInput, out_buffer);
}
if (in_buffer.type() != NodeValue::kNone) {
job.InsertValue(kInBlockInput, in_buffer);
job.Insert(kInBlockInput, in_buffer);
}
job.InsertValue(kCurveInput, value);
job.Insert(kCurveInput, value);
double time = globals.time().in().toDouble();
InsertTransitionTimes(&job, time);
@@ -206,25 +206,22 @@ void TransitionBlock::Value(const NodeValueRow &value, const NodeGlobals &global
push_job = QVariant::fromValue(job);
} else if (data_type == NodeValue::kSamples) {
// This must be an audio transition
SampleBufferPtr from_samples = out_buffer.data().value<SampleBufferPtr>();
SampleBufferPtr to_samples = in_buffer.data().value<SampleBufferPtr>();
SampleBuffer from_samples = out_buffer.toSamples();
SampleBuffer to_samples = in_buffer.toSamples();
if (from_samples || to_samples) {
if (from_samples.is_allocated() || to_samples.is_allocated()) {
double time_in = globals.time().in().toDouble();
double time_out = globals.time().out().toDouble();
const AudioParams& params = (from_samples) ? from_samples->audio_params() : to_samples->audio_params();
const AudioParams& params = (from_samples.is_allocated()) ? from_samples.audio_params() : to_samples.audio_params();
SampleBufferPtr out_samples;
SampleBuffer out_samples;
if (params.is_valid()) {
int nb_samples = params.time_to_samples(time_out - time_in);
out_samples = SampleBuffer::CreateAllocated(params, nb_samples);
out_samples = SampleBuffer(params, nb_samples);
SampleJobEvent(from_samples, to_samples, out_samples, time_in);
} else {
// Create dummy sample buffer
out_samples = SampleBuffer::Create();
}
job_type = NodeValue::kSamples;
@@ -251,20 +248,6 @@ void TransitionBlock::InvalidateCache(const TimeRange &range, const QString &fro
super::InvalidateCache(r, from, element, options);
}
void TransitionBlock::ShaderJobEvent(const NodeValueRow &value, ShaderJob &job) const
{
Q_UNUSED(value)
Q_UNUSED(job)
}
void TransitionBlock::SampleJobEvent(SampleBufferPtr from_samples, SampleBufferPtr to_samples, SampleBufferPtr out_samples, double time_in) const
{
Q_UNUSED(from_samples)
Q_UNUSED(to_samples)
Q_UNUSED(out_samples)
Q_UNUSED(time_in)
}
double TransitionBlock::TransformCurve(double linear) const
{
switch (static_cast<CurveType>(GetStandardValue(kCurveInput).toInt())) {
+2 -4
View File
@@ -33,8 +33,6 @@ class TransitionBlock : public Block
public:
TransitionBlock();
NODE_DEFAULT_DESTRUCTOR(TransitionBlock)
virtual void Retranslate() override;
rational in_offset() const;
@@ -75,9 +73,9 @@ public:
static const QString kCenterInput;
protected:
virtual void ShaderJobEvent(const NodeValueRow &value, ShaderJob& job) const;
virtual void ShaderJobEvent(const NodeValueRow &value, ShaderJob& job) const {}
virtual void SampleJobEvent(SampleBufferPtr from_samples, SampleBufferPtr to_samples, SampleBufferPtr out_samples, double time_in) const;
virtual void SampleJobEvent(const SampleBuffer &from_samples, const SampleBuffer &to_samples, SampleBuffer &out_samples, double time_in) const {}
double TransformCurve(double linear) const;
+3
View File
@@ -15,6 +15,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
add_subdirectory(colormanager)
add_subdirectory(displaytransform)
add_subdirectory(ociobase)
add_subdirectory(ociogradingtransformlinear)
set(OLIVE_SOURCES
${OLIVE_SOURCES}
@@ -273,6 +273,7 @@ void ColorManager::InputValueChangedEvent(const QString &input, int element)
try {
SetConfig(OCIO::Config::CreateFromFile(GetConfigFilename().toUtf8()));
emit ConfigChanged();
} catch (OCIO::Exception&) {}
}
+5 -5
View File
@@ -38,6 +38,8 @@ class ColorManager : public Node
public:
ColorManager();
NODE_DEFAULT_FUNCTIONS(ColorManager)
virtual QString Name() const override
{
return tr("Color Manager");
@@ -58,11 +60,6 @@ public:
return tr("Color management configuration for project.");
}
virtual Node* copy() const override
{
return new ColorManager();
}
OCIO::ConstConfigRcPtr GetConfig() const;
static OCIO::ConstConfigRcPtr CreateConfigFromFile(const QString& filename);
@@ -124,6 +121,9 @@ public:
virtual void Retranslate() override;
signals:
void ConfigChanged();
protected:
virtual void InputValueChangedEvent(const QString &input, int element) override;
@@ -0,0 +1,22 @@
# Olive - Non-Linear Video Editor
# Copyright (C) 2021 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/color/displaytransform/displaytransform.cpp
node/color/displaytransform/displaytransform.h
PARENT_SCOPE
)
@@ -0,0 +1,144 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 "displaytransform.h"
#include "node/color/colormanager/colormanager.h"
namespace olive {
const QString DisplayTransformNode::kDisplayInput = QStringLiteral("display_in");
const QString DisplayTransformNode::kViewInput = QStringLiteral("view_in");
const QString DisplayTransformNode::kDirectionInput = QStringLiteral("dir_in");
#define super OCIOBaseNode
DisplayTransformNode::DisplayTransformNode()
{
AddInput(kDisplayInput, NodeValue::kCombo, 0, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable));
AddInput(kViewInput, NodeValue::kCombo, 0, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable));
AddInput(kDirectionInput, NodeValue::kCombo, 0, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable));
}
QString DisplayTransformNode::Name() const
{
return tr("Display Transform");
}
QString DisplayTransformNode::id() const
{
return QStringLiteral("org.olivevideoeditor.Olive.displaytransform");
}
QVector<Node::CategoryID> DisplayTransformNode::Category() const
{
return {kCategoryColor};
}
QString DisplayTransformNode::Description() const
{
return tr("Converts an image to or from a display color space.");
}
void DisplayTransformNode::Retranslate()
{
super::Retranslate();
SetInputName(kTextureInput, tr("Input"));
SetInputName(kDisplayInput, tr("Display"));
SetInputName(kViewInput, tr("View"));
SetInputName(kDirectionInput, tr("Direction"));
SetComboBoxStrings(kDirectionInput, {tr("Forward"), tr("Inverse")});
}
void DisplayTransformNode::InputValueChangedEvent(const QString &input, int element)
{
Q_UNUSED(element);
if (input == kDisplayInput || input == kDirectionInput || input == kViewInput) {
if (input == kDisplayInput) {
UpdateViews();
}
GenerateProcessor();
}
}
QString DisplayTransformNode::GetDisplay() const
{
if (manager()) {
int index = GetStandardValue(kDisplayInput).toInt();
if (index < manager()->ListAvailableDisplays().size()) {
return manager()->ListAvailableDisplays().at(index);
}
}
return QString();
}
QString DisplayTransformNode::GetView() const
{
if (manager()) {
QString display = GetDisplay();
if (!display.isEmpty()) {
int index = GetStandardValue(kViewInput).toInt();
QStringList views = manager()->ListAvailableViews(display);
if (index < views.size()) {
return views.at(index);
}
}
}
return QString();
}
ColorProcessor::Direction DisplayTransformNode::GetDirection() const
{
return static_cast<ColorProcessor::Direction>(GetStandardValue(kDirectionInput).toInt());;
}
void DisplayTransformNode::UpdateDisplays()
{
if (manager()) {
SetComboBoxStrings(kDisplayInput, manager()->ListAvailableDisplays());
}
}
void DisplayTransformNode::UpdateViews()
{
if (manager()) {
SetComboBoxStrings(kViewInput, manager()->ListAvailableViews(GetDisplay()));
}
}
void DisplayTransformNode::ConfigChanged()
{
UpdateDisplays();
UpdateViews();
GenerateProcessor();
}
void DisplayTransformNode::GenerateProcessor()
{
if (manager()) {
ColorTransform transform(GetDisplay(), GetView(), QString());
set_processor(ColorProcessor::Create(manager(), manager()->GetReferenceColorSpace(), transform, GetDirection()));
}
}
}
@@ -0,0 +1,67 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 DISPLAYTRANSFORMNODE_H
#define DISPLAYTRANSFORMNODE_H
#include "node/color/ociobase/ociobase.h"
#include "render/colorprocessor.h"
namespace olive {
class DisplayTransformNode : public OCIOBaseNode
{
Q_OBJECT
public:
DisplayTransformNode();
NODE_DEFAULT_FUNCTIONS(DisplayTransformNode)
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 InputValueChangedEvent(const QString &input, int element) override;
QString GetDisplay() const;
QString GetView() const;
ColorProcessor::Direction GetDirection() const;
static const QString kDisplayInput;
static const QString kViewInput;
static const QString kDirectionInput;
protected slots:
virtual void ConfigChanged() override;
private:
void GenerateProcessor();
void UpdateDisplays();
void UpdateViews();
};
} // olive
#endif // DISPLAYTRANSFORMNODE_H
+22
View File
@@ -0,0 +1,22 @@
# Olive - Non-Linear Video Editor
# Copyright (C) 2021 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/color/ociobase/ociobase.cpp
node/color/ociobase/ociobase.h
PARENT_SCOPE
)
+69
View File
@@ -0,0 +1,69 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 "ociobase.h"
#include "node/color/colormanager/colormanager.h"
#include "node/project/project.h"
namespace olive {
const QString OCIOBaseNode::kTextureInput = QStringLiteral("tex_in");
OCIOBaseNode::OCIOBaseNode() :
manager_(nullptr),
processor_(nullptr)
{
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
SetEffectInput(kTextureInput);
connect(this, &Node::AddedToGraph, this, &OCIOBaseNode::ParentChanged);
SetFlags(kVideoEffect);
}
void OCIOBaseNode::ParentChanged(NodeGraph *graph)
{
if (manager_) {
disconnect(manager_, &ColorManager::ConfigChanged, this, &OCIOBaseNode::ConfigChanged);
manager_ = nullptr;
}
if (Project *p = dynamic_cast<Project*>(graph)) {
manager_ = p->color_manager();
connect(manager_, &ColorManager::ConfigChanged, this, &OCIOBaseNode::ConfigChanged);
ConfigChanged();
}
}
void OCIOBaseNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
{
if (value[kTextureInput].toTexture() && processor_) {
ColorTransformJob job;
job.SetColorProcessor(processor_);
job.SetInputTexture(value[kTextureInput].toTexture());
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
}
}
}
@@ -18,45 +18,43 @@
***/
#ifndef PLANARPROCESSOR_H
#define PLANARPROCESSOR_H
#ifndef OCIOBASENODE_H
#define OCIOBASENODE_H
extern "C" {
#include <libswresample/swresample.h>
}
#include "codec/samplebuffer.h"
#include "render/audioparams.h"
#include "node/node.h"
#include "render/job/colortransformjob.h"
namespace olive {
class PlanarProcessor
class OCIOBaseNode : public Node
{
Q_OBJECT
public:
PlanarProcessor();
OCIOBaseNode();
~PlanarProcessor();
virtual void Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const override;
DISABLE_COPY_MOVE(PlanarProcessor)
static const QString kTextureInput;
bool Open(const AudioParams &params);
protected slots:
virtual void ConfigChanged() = 0;
SampleBufferPtr Convert(const QByteArray &packed);
protected:
ColorManager *manager() const { return manager_; }
void Close();
bool IsOpen() const
{
return swr_ctx_;
}
ColorProcessorPtr processor() const { return processor_; }
void set_processor(ColorProcessorPtr p) { processor_ = p; }
private:
SwrContext *swr_ctx_;
ColorManager *manager_;
AudioParams params_;
ColorProcessorPtr processor_;
private slots:
void ParentChanged(olive::NodeGraph *graph);
};
}
#endif // PLANARPROCESSOR_H
#endif // OCIOBASENODE_H
@@ -0,0 +1,22 @@
# Olive - Non-Linear Video Editor
# Copyright (C) 2021 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/color/ociogradingtransformlinear/ociogradingtransformlinear.cpp
node/color/ociogradingtransformlinear/ociogradingtransformlinear.h
PARENT_SCOPE
)
@@ -0,0 +1,218 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 "ociogradingtransformlinear.h"
#include <iostream>
#include "common/ocioutils.h"
#include "node/project/project.h"
#include "render/colorprocessor.h"
#include "widget/slider/floatslider.h"
namespace olive {
const QString OCIOGradingTransformLinearNode::kContrastInput = QStringLiteral("ocio_grading_primary_contrast");
const QString OCIOGradingTransformLinearNode::kOffsetInput = QStringLiteral("ocio_grading_primary_offset");
const QString OCIOGradingTransformLinearNode::kExposureInput = QStringLiteral("ocio_grading_primary_exposure");
const QString OCIOGradingTransformLinearNode::kSaturationInput = QStringLiteral("ocio_grading_primary_saturation");
const QString OCIOGradingTransformLinearNode::kPivotInput = QStringLiteral("ocio_grading_primary_pivot");
const QString OCIOGradingTransformLinearNode::kClampBlackEnableInput = QStringLiteral("clamp_black_enable_in");
const QString OCIOGradingTransformLinearNode::kClampBlackInput = QStringLiteral("ocio_grading_primary_clampBlack");
const QString OCIOGradingTransformLinearNode::kClampWhiteEnableInput = QStringLiteral("clamp_white_enable_in");
const QString OCIOGradingTransformLinearNode::kClampWhiteInput = QStringLiteral("ocio_grading_primary_clampWhite");
#define super OCIOBaseNode
OCIOGradingTransformLinearNode::OCIOGradingTransformLinearNode()
{
AddInput(kContrastInput, NodeValue::kVec4, QVector4D{1.0, 1.0, 1.0, 1.0});
// Minimum based on OCIO::GradingPrimary::validate
SetInputProperty(kContrastInput, QStringLiteral("min"), QVector4D{0.01f, 0.01f, 0.01f, 0.01f});
SetInputProperty(kContrastInput, QStringLiteral("base"), 0.01);
SetVec4InputColors(kContrastInput);
AddInput(kOffsetInput, NodeValue::kVec4, QVector4D{0.0, 0.0, 0.0, 0.0});
SetInputProperty(kOffsetInput, QStringLiteral("base"), 0.01);
SetVec4InputColors(kOffsetInput);
AddInput(kExposureInput, NodeValue::kVec4, QVector4D{0.0, 0.0, 0.0, 0.0});
SetInputProperty(kExposureInput, QStringLiteral("base"), 0.01);
SetVec4InputColors(kExposureInput);
AddInput(kSaturationInput, NodeValue::kFloat, 1.0);
SetInputProperty(kSaturationInput, QStringLiteral("view"), FloatSlider::kPercentage);
SetInputProperty(kSaturationInput, QStringLiteral("min"), 0.0);
AddInput(kPivotInput, NodeValue::kFloat, 0.18); // Default listed in OCIO::GradingPrimary
SetInputProperty(kPivotInput, QStringLiteral("base"), 0.01);
AddInput(kClampBlackEnableInput, NodeValue::kBoolean, false);
AddInput(kClampBlackInput, NodeValue::kFloat, 0.0);
SetInputProperty(kClampBlackInput, QStringLiteral("enabled"), GetStandardValue(kClampBlackEnableInput).toBool());
SetInputProperty(kClampBlackInput, QStringLiteral("base"), 0.01);
AddInput(kClampWhiteEnableInput, NodeValue::kBoolean, false);
AddInput(kClampWhiteInput, NodeValue::kFloat, 1.0);
SetInputProperty(kClampWhiteInput, QStringLiteral("enabled"), GetStandardValue(kClampWhiteEnableInput).toBool());
SetInputProperty(kClampWhiteInput, QStringLiteral("base"), 0.01);
// FIXME: Temporarily disabled. This will break if "clamp black" is keyframed or connected to
// something and there's currently no solution to remedy that. If there is in the future,
// we can look into re-enabling this.
//SetInputProperty(kClampWhiteInput, QStringLiteral("min"), GetStandardValue(kClampBlackInput).toDouble() + 0.000001);
}
QString OCIOGradingTransformLinearNode::Name() const
{
return tr("OCIO Color Grading (Linear)");
}
QString OCIOGradingTransformLinearNode::id() const
{
return QStringLiteral("org.olivevideoeditor.Olive.ociogradingtransformlinear");
}
QVector<Node::CategoryID> OCIOGradingTransformLinearNode::Category() const
{
return {kCategoryColor};
}
QString OCIOGradingTransformLinearNode::Description() const
{
return tr("Simple linear color grading using OpenColorIO.");
}
void OCIOGradingTransformLinearNode::Retranslate()
{
super::Retranslate();
SetInputName(kTextureInput, tr("Input"));
SetInputName(kContrastInput, tr("Contrast"));
SetInputName(kOffsetInput, tr("Offset"));
SetInputName(kExposureInput, tr("Exposure"));
SetInputProperty(kExposureInput, QStringLiteral("tooltip"), tr("Exposure increments in stops."));
SetInputName(kSaturationInput, tr("Saturation"));
SetInputName(kPivotInput, tr("Pivot"));
SetInputName(kClampBlackEnableInput, tr("Enable Black Clamp"));
SetInputName(kClampBlackInput, tr("Black Clamp"));
SetInputName(kClampWhiteEnableInput, tr("Enable White Clamp"));
SetInputName(kClampWhiteInput, tr("White Clamp"));
}
void OCIOGradingTransformLinearNode::InputValueChangedEvent(const QString &input, int element)
{
Q_UNUSED(element);
if (input == kClampWhiteEnableInput) {
SetInputProperty(kClampWhiteInput, QStringLiteral("enabled"), GetStandardValue(kClampWhiteEnableInput).toBool());
} else if (input == kClampBlackEnableInput) {
SetInputProperty(kClampBlackInput, QStringLiteral("enabled"), GetStandardValue(kClampBlackEnableInput).toBool());
} else if (input == kClampBlackInput) {
// Ensure the white clamp is always greater than the black clamp as per OCIO::GradingPrimary::validate
// FIXME: Temporarily disabled. This will break if "clamp black" is keyframed or connected to
// something and there's currently no solution to remedy that. If there is in the future,
// we can look into re-enabling this.
//SetInputProperty(kClampWhiteInput, QStringLiteral("min"), GetStandardValue(kClampBlackInput).toDouble() + 0.000001);
}
GenerateProcessor();
}
void OCIOGradingTransformLinearNode::GenerateProcessor()
{
if (manager()) {
OCIO::GradingPrimaryTransformRcPtr gp = OCIO::GradingPrimaryTransform::Create(OCIO::GRADING_LIN);
gp->makeDynamic();
gp->setDirection(OCIO::TransformDirection::TRANSFORM_DIR_FORWARD);
try {
set_processor(ColorProcessor::Create(manager()->GetConfig()->getProcessor(gp)));
} catch (const OCIO::Exception &e) {
std::cerr << std::endl << e.what() << std::endl;
}
}
}
void OCIOGradingTransformLinearNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
{
if (value[kTextureInput].toTexture() && processor()) {
ColorTransformJob job;
job.SetColorProcessor(processor());
job.SetInputTexture(value[kTextureInput].toTexture());
job.Insert(value);
const int MASTER_CHANNEL = 0;
const int RED_CHANNEL = 1;
const int GREEN_CHANNEL = 2;
const int BLUE_CHANNEL = 3;
// Oddly, OCIO uses RGBMs when setting the GradingPrimary on the CPU, but uses vec3s on the GPU.
// Even more oddly, the conversion from RGBM to vec3 does not appear to have a public API.
// Therefore, this code has been duplicated from OCIO here:
// https://github.com/AcademySoftwareFoundation/OpenColorIO/blob/3abbe5b20521169580fcfe3692aca81859859953/src/OpenColorIO/ops/gradingprimary/GradingPrimary.cpp#L157
QVector4D offset = value[kOffsetInput].toVec4();
offset[RED_CHANNEL] += offset[MASTER_CHANNEL];
offset[GREEN_CHANNEL] += offset[MASTER_CHANNEL];
offset[BLUE_CHANNEL] += offset[MASTER_CHANNEL];
job.Insert(kOffsetInput, NodeValue(NodeValue::kVec3, QVector3D(offset[RED_CHANNEL], offset[GREEN_CHANNEL], offset[BLUE_CHANNEL])));
QVector4D exposure = value[kExposureInput].toVec4();
exposure[RED_CHANNEL] = std::pow(2.0f, exposure[MASTER_CHANNEL] + exposure[RED_CHANNEL]);
exposure[GREEN_CHANNEL] = std::pow(2.0f, exposure[MASTER_CHANNEL] + exposure[GREEN_CHANNEL]);
exposure[BLUE_CHANNEL] = std::pow(2.0f, exposure[MASTER_CHANNEL] + exposure[BLUE_CHANNEL]);
job.Insert(kExposureInput, NodeValue(NodeValue::kVec3, QVector3D(exposure[RED_CHANNEL], exposure[GREEN_CHANNEL], exposure[BLUE_CHANNEL])));
QVector4D contrast = value[kContrastInput].toVec4();
contrast[RED_CHANNEL] *= contrast[MASTER_CHANNEL];
contrast[GREEN_CHANNEL] *= contrast[MASTER_CHANNEL];
contrast[BLUE_CHANNEL] *= contrast[MASTER_CHANNEL];
job.Insert(kContrastInput, NodeValue(NodeValue::kVec3, QVector3D(contrast[RED_CHANNEL], contrast[GREEN_CHANNEL], contrast[BLUE_CHANNEL])));
if (!value[kClampBlackEnableInput].toBool()) {
job.Insert(kClampBlackInput, NodeValue(NodeValue::kFloat, OCIO::GradingPrimary::NoClampBlack()));
}
if (!value[kClampWhiteEnableInput].toBool()) {
job.Insert(kClampWhiteInput, NodeValue(NodeValue::kFloat, OCIO::GradingPrimary::NoClampWhite()));
}
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
}
}
void OCIOGradingTransformLinearNode::ConfigChanged()
{
GenerateProcessor();
}
void OCIOGradingTransformLinearNode::SetVec4InputColors(const QString &input)
{
SetInputProperty(input, QStringLiteral("color0"), QColor(192, 192, 192).name());
SetInputProperty(input, QStringLiteral("color1"), QColor(255, 0, 0).name());
SetInputProperty(input, QStringLiteral("color2"), QColor(0, 255, 0).name());
SetInputProperty(input, QStringLiteral("color3"), QColor(0, 0, 255).name());
}
}
@@ -0,0 +1,68 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 OCIOGRADINGTRANSFORMLINEARNODE_H
#define OCIOGRADINGTRANSFORMLINEARNODE_H
#include "node/color/ociobase/ociobase.h"
#include "render/colorprocessor.h"
namespace olive {
class OCIOGradingTransformLinearNode : public OCIOBaseNode
{
Q_OBJECT
public:
OCIOGradingTransformLinearNode();
NODE_DEFAULT_FUNCTIONS(OCIOGradingTransformLinearNode)
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 InputValueChangedEvent(const QString &input, int element) override;
void GenerateProcessor();
virtual void Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const override;
static const QString kContrastInput;
static const QString kOffsetInput;
static const QString kExposureInput;
static const QString kSaturationInput;
static const QString kPivotInput;
static const QString kClampBlackEnableInput;
static const QString kClampBlackInput;
static const QString kClampWhiteEnableInput;
static const QString kClampWhiteInput;
protected slots:
virtual void ConfigChanged() override;
private:
void SetVec4InputColors(const QString &input);
};
} // olive
#endif
+1
View File
@@ -17,6 +17,7 @@
add_subdirectory(cornerpin)
add_subdirectory(crop)
add_subdirectory(flip)
add_subdirectory(mask)
add_subdirectory(transform)
set(OLIVE_SOURCES
@@ -70,9 +70,9 @@ void CornerPinDistortNode::Retranslate()
void CornerPinDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
{
ShaderJob job;
job.InsertValue(value);
job.Insert(value);
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
job.InsertValue(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
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
// vertex coordinates.
@@ -94,23 +94,23 @@ void CornerPinDistortNode::Value(const NodeValueRow &value, const NodeGlobals &g
job.SetVertexCoordinates(adjusted_vertices);
// If no texture do nothing
if (!job.GetValue(kTextureInput).data().isNull()) {
if (job.Get(kTextureInput).toTexture()) {
// In the special case that all sliders are in their default position just
// push the texture.
if (!(job.GetValue(kTopLeftInput).data().value<QVector2D>().isNull()
&& job.GetValue(kTopRightInput).data().value<QVector2D>().isNull() &&
job.GetValue(kBottomRightInput).data().value<QVector2D>().isNull() &&
job.GetValue(kBottomLeftInput).data().value<QVector2D>().isNull())) {
if (!(job.Get(kTopLeftInput).toVec2().isNull()
&& job.Get(kTopRightInput).toVec2().isNull() &&
job.Get(kBottomRightInput).toVec2().isNull() &&
job.Get(kBottomLeftInput).toVec2().isNull())) {
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
} else {
table->Push(NodeValue::kTexture, job.GetValue(kTextureInput).data(), this);
table->Push(job.Get(kTextureInput));
}
}
}
ShaderCode CornerPinDistortNode::GetShaderCode(const QString &shader_id) const
ShaderCode CornerPinDistortNode::GetShaderCode(const ShaderRequest &request) const
{
Q_UNUSED(shader_id)
Q_UNUSED(request)
return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/cornerpin.frag")),
FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/cornerpin.vert")));
@@ -119,25 +119,24 @@ ShaderCode CornerPinDistortNode::GetShaderCode(const QString &shader_id) const
QPointF CornerPinDistortNode::ValueToPixel(int value, const NodeValueRow& row, const QVector2D &resolution) const
{
Q_ASSERT(value >= 0 && value <= 3);
QVector2D v;
switch (value) {
case 0: // Top left
return QPointF(row[kTopLeftInput].data().value<QVector2D>().x(),
row[kTopLeftInput].data().value<QVector2D>().y());
break;
case 1: // Top right
return QPointF(resolution.x() + row[kTopRightInput].data().value<QVector2D>().x(),
row[kTopRightInput].data().value<QVector2D>().y());
break;
case 2: // Bottom right
return QPointF(resolution.x() + row[kBottomRightInput].data().value<QVector2D>().x(),
resolution.y() + row[kBottomRightInput].data().value<QVector2D>().y());
break;
case 3: //Bottom left
return QPointF(row[kBottomLeftInput].data().value<QVector2D>().x(),
row[kBottomLeftInput].data().value<QVector2D>().y() + resolution.y());
break;
default: // We should never get here
return QPointF();
case 0: // Top left
v = row[kTopLeftInput].toVec2();
return QPointF(v.x(), v.y());
case 1: // Top right
v = row[kTopRightInput].toVec2();
return QPointF(resolution.x() + v.x(), v.y());
case 2: // Bottom right
v = row[kBottomRightInput].toVec2();
return QPointF(resolution.x() + v.x(), resolution.y() + v.y());
case 3: //Bottom left
v = row[kBottomLeftInput].toVec2();
return QPointF(v.x(), v.y() + resolution.y());
default: // We should never get here
return QPointF();
}
}
@@ -35,12 +35,7 @@ class CornerPinDistortNode : public Node
public:
CornerPinDistortNode();
NODE_DEFAULT_DESTRUCTOR(CornerPinDistortNode)
virtual Node* copy() const override
{
return new CornerPinDistortNode();
}
NODE_DEFAULT_FUNCTIONS(CornerPinDistortNode)
virtual QString Name() const override
{
@@ -66,7 +61,7 @@ public:
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
+14 -14
View File
@@ -78,25 +78,25 @@ void CropDistortNode::Retranslate()
void CropDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
{
ShaderJob job;
job.InsertValue(value);
job.InsertValue(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
job.Insert(value);
job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
if (!job.GetValue(kTextureInput).data().isNull()) {
if (!qIsNull(job.GetValue(kLeftInput).data().toDouble())
|| !qIsNull(job.GetValue(kRightInput).data().toDouble())
|| !qIsNull(job.GetValue(kTopInput).data().toDouble())
|| !qIsNull(job.GetValue(kBottomInput).data().toDouble())) {
if (job.Get(kTextureInput).toTexture()) {
if (!qIsNull(job.Get(kLeftInput).toDouble())
|| !qIsNull(job.Get(kRightInput).toDouble())
|| !qIsNull(job.Get(kTopInput).toDouble())
|| !qIsNull(job.Get(kBottomInput).toDouble())) {
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
} else {
table->Push(NodeValue::kTexture, job.GetValue(kTextureInput).data(), this);
table->Push(job.Get(kTextureInput));
}
}
}
ShaderCode CropDistortNode::GetShaderCode(const QString &shader_id) const
ShaderCode CropDistortNode::GetShaderCode(const ShaderRequest &request) const
{
Q_UNUSED(shader_id)
Q_UNUSED(request)
return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/crop.frag")));
}
@@ -104,10 +104,10 @@ void CropDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGl
{
const QVector2D &resolution = globals.resolution();
double left_pt = resolution.x() * row[kLeftInput].data().toDouble();
double top_pt = resolution.y() * row[kTopInput].data().toDouble();
double right_pt = resolution.x() * (1.0 - row[kRightInput].data().toDouble());
double bottom_pt = resolution.y() * (1.0 - row[kBottomInput].data().toDouble());
double left_pt = resolution.x() * row[kLeftInput].toDouble();
double top_pt = resolution.y() * row[kTopInput].toDouble();
double right_pt = resolution.x() * (1.0 - row[kRightInput].toDouble());
double bottom_pt = resolution.y() * (1.0 - row[kBottomInput].toDouble());
double center_x_pt = mid(left_pt, right_pt);
double center_y_pt = mid(top_pt, bottom_pt);
+2 -7
View File
@@ -36,12 +36,7 @@ class CropDistortNode : public Node
public:
CropDistortNode();
NODE_DEFAULT_DESTRUCTOR(CropDistortNode)
virtual Node* copy() const override
{
return new CropDistortNode();
}
NODE_DEFAULT_FUNCTIONS(CropDistortNode)
virtual QString Name() const override
{
@@ -67,7 +62,7 @@ public:
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
+6 -11
View File
@@ -40,11 +40,6 @@ FlipDistortNode::FlipDistortNode()
SetEffectInput(kTextureInput);
}
Node* FlipDistortNode::copy() const
{
return new FlipDistortNode();
}
QString FlipDistortNode::Name() const
{
return tr("Flip");
@@ -74,9 +69,9 @@ void FlipDistortNode::Retranslate()
SetInputName(kVerticalInput, tr("Vertical"));
}
ShaderCode FlipDistortNode::GetShaderCode(const QString& shader_id) const
ShaderCode FlipDistortNode::GetShaderCode(const ShaderRequest &request) const
{
Q_UNUSED(shader_id)
Q_UNUSED(request)
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/flip.frag"));
}
@@ -84,16 +79,16 @@ void FlipDistortNode::Value(const NodeValueRow &value, const NodeGlobals &global
{
ShaderJob job;
job.InsertValue(value);
job.Insert(value);
// If there's no texture, no need to run an operation
if (!job.GetValue(kTextureInput).data().isNull()) {
if (job.Get(kTextureInput).toTexture()) {
// Only run shader if at least one of flip or flop are selected
if (job.GetValue(kHorizontalInput).data().toBool() || job.GetValue(kVerticalInput).data().toBool()) {
if (job.Get(kHorizontalInput).toBool() || job.Get(kVerticalInput).toBool()) {
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
} else {
// If we're not flipping or flopping just push the texture
table->Push(job.GetValue(kTextureInput));
table->Push(job.Get(kTextureInput));
}
}
+2 -4
View File
@@ -31,9 +31,7 @@ class FlipDistortNode : public Node
public:
FlipDistortNode();
NODE_DEFAULT_DESTRUCTOR(FlipDistortNode)
virtual Node* copy() const override;
NODE_DEFAULT_FUNCTIONS(FlipDistortNode)
virtual QString Name() const override;
virtual QString id() const override;
@@ -42,7 +40,7 @@ public:
virtual void Retranslate() override;
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
static const QString kTextureInput;
+22
View File
@@ -0,0 +1,22 @@
# Olive - Non-Linear Video Editor
# Copyright (C) 2021 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/distort/mask/mask.cpp
node/distort/mask/mask.h
PARENT_SCOPE
)
+94
View File
@@ -0,0 +1,94 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 "mask.h"
#include "node/filter/blur/blur.h"
namespace olive {
#define super PolygonGenerator
const QString MaskDistortNode::kFeatherInput = QStringLiteral("feather_in");
MaskDistortNode::MaskDistortNode()
{
// Mask should always be (1.0, 1.0, 1.0) for multiply to work correctly
SetInputFlags(kColorInput, InputFlags(GetInputFlags(kColorInput) | kInputFlagHidden));
AddInput(kFeatherInput, NodeValue::kFloat, 0.0);
SetInputProperty(kFeatherInput, QStringLiteral("min"), 0.0);
}
ShaderCode MaskDistortNode::GetShaderCode(const ShaderRequest &request) const
{
if (request.id == QStringLiteral("mrg")) {
return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/multiply.frag")));
} else if (request.id == QStringLiteral("feather")) {
return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/blur.frag")));
} else {
return ShaderCode();
}
}
void MaskDistortNode::Retranslate()
{
super::Retranslate();
SetInputName(kBaseInput, tr("Texture"));
SetInputName(kFeatherInput, tr("Feather"));
}
void MaskDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
{
GenerateJob job = GetGenerateJob(value);
if (value[kBaseInput].toTexture()) {
// Push as merge node
ShaderJob merge;
merge.SetShaderID(QStringLiteral("mrg"));
merge.Insert(QStringLiteral("tex_a"), value[kBaseInput]);
if (value[kFeatherInput].toDouble() > 0.0) {
// Nest a blur shader in there too
ShaderJob feather;
feather.SetShaderID(QStringLiteral("feather"));
feather.Insert(BlurFilterNode::kTextureInput, NodeValue(NodeValue::kTexture, job, this));
feather.Insert(BlurFilterNode::kMethodInput, NodeValue(NodeValue::kInt, int(BlurFilterNode::kGaussian), this));
feather.Insert(BlurFilterNode::kHorizInput, NodeValue(NodeValue::kBoolean, true, this));
feather.Insert(BlurFilterNode::kVertInput, NodeValue(NodeValue::kBoolean, true, this));
feather.Insert(BlurFilterNode::kRepeatEdgePixelsInput, NodeValue(NodeValue::kBoolean, true, this));
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 {
merge.Insert(QStringLiteral("tex_b"), NodeValue(NodeValue::kTexture, job, this));
}
table->Push(NodeValue::kTexture, QVariant::fromValue(merge), this);
}
}
}
+68
View File
@@ -0,0 +1,68 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 MASKDISTORTNODE_H
#define MASKDISTORTNODE_H
#include "node/generator/polygon/polygon.h"
namespace olive {
class MaskDistortNode : public PolygonGenerator
{
Q_OBJECT
public:
MaskDistortNode();
NODE_DEFAULT_FUNCTIONS(MaskDistortNode)
virtual QString Name() const override
{
return tr("Mask");
}
virtual QString id() const override
{
return QStringLiteral("org.olivevideoeditor.Olive.mask");
}
virtual QVector<CategoryID> Category() const override
{
return {kCategoryDistort};
}
virtual QString Description() const override
{
return tr("Apply a polygonal mask.");
}
virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
virtual void Retranslate() override;
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
static const QString kFeatherInput;
};
}
#endif // MASKDISTORTNODE_H
@@ -92,16 +92,16 @@ void TransformDistortNode::Value(const NodeValueRow &value, const NodeGlobals &g
bool pushed_job = false;
// If we have a texture, generate a matrix and make it happen
if (TexturePtr texture = texture_meta.data().value<TexturePtr>()) {
if (TexturePtr texture = texture_meta.toTexture()) {
// Adjust our matrix by the resolutions involved
QMatrix4x4 real_matrix = GenerateAutoScaledMatrix(generated_matrix, value, globals, texture->params());
if (!real_matrix.isIdentity()) {
// The matrix will transform things
ShaderJob job;
job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture), this));
job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, real_matrix, this));
job.SetInterpolation(QStringLiteral("ove_maintex"), static_cast<Texture::Interpolation>(value[kInterpolationInput].data().toInt()));
job.Insert(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture), this));
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.
@@ -119,9 +119,9 @@ void TransformDistortNode::Value(const NodeValueRow &value, const NodeGlobals &g
}
}
ShaderCode TransformDistortNode::GetShaderCode(const QString &shader_id) const
ShaderCode TransformDistortNode::GetShaderCode(const ShaderRequest &request) const
{
Q_UNUSED(shader_id);
Q_UNUSED(request);
// Returns default frag and vert shader
return ShaderCode();
@@ -140,7 +140,7 @@ void TransformDistortNode::Hash(QCryptographicHash &hash, const NodeGlobals &glo
traverser.SetCacheVideoParams(video_params);
NodeValueRow db = traverser.GenerateRow(this, globals.time());
TexturePtr tex = db[kTextureInput].data().value<TexturePtr>();
TexturePtr tex = db[kTextureInput].toTexture();
if (tex) {
VideoParams tex_params = tex->params();
QMatrix4x4 matrix = GenerateMatrix(db, true, false, false, false);
@@ -167,13 +167,13 @@ void TransformDistortNode::GizmoDragStart(const NodeValueRow &row, double x, dou
} else if (IsAScaleGizmo(gizmo)) {
// Dragging scale handle
TexturePtr tex = row[kTextureInput].data().value<TexturePtr>();
TexturePtr tex = row[kTextureInput].toTexture();
if (!tex) {
return;
}
gizmo_scale_uniform_ = row[kUniformScaleInput].data().toBool();
gizmo_anchor_pt_ = (row[kAnchorInput].data().value<QVector2D>() + gizmo->GetGlobals().resolution()/2).toPointF();
gizmo_scale_uniform_ = row[kUniformScaleInput].toBool();
gizmo_anchor_pt_ = (row[kAnchorInput].toVec2() + gizmo->GetGlobals().resolution()/2).toPointF();
if (gizmo == point_gizmo_[kGizmoScaleTopLeft] || gizmo == point_gizmo_[kGizmoScaleTopRight]
|| gizmo == point_gizmo_[kGizmoScaleBottomLeft] || gizmo == point_gizmo_[kGizmoScaleBottomRight]) {
@@ -187,7 +187,7 @@ void TransformDistortNode::GizmoDragStart(const NodeValueRow &row, double x, dou
// Store texture size
VideoParams texture_params = tex->params();
QVector2D texture_sz(texture_params.square_pixel_width(), texture_params.height());
gizmo_scale_anchor_ = row[kAnchorInput].data().value<QVector2D>() + texture_sz/2;
gizmo_scale_anchor_ = row[kAnchorInput].toVec2() + texture_sz/2;
if (gizmo == point_gizmo_[kGizmoScaleTopRight]
|| gizmo == point_gizmo_[kGizmoScaleBottomRight]
@@ -208,7 +208,7 @@ void TransformDistortNode::GizmoDragStart(const NodeValueRow &row, double x, dou
} else if (gizmo == rotation_gizmo_) {
gizmo_anchor_pt_ = (row[kAnchorInput].data().value<QVector2D>() + gizmo->GetGlobals().resolution()/2).toPointF();
gizmo_anchor_pt_ = (row[kAnchorInput].toVec2() + gizmo->GetGlobals().resolution()/2).toPointF();
gizmo_start_angle_ = qAtan2(y - gizmo_anchor_pt_.y(), x - gizmo_anchor_pt_.x());
gizmo_last_angle_ = gizmo_start_angle_;
gizmo_last_alt_angle_ = qAtan2(x - gizmo_anchor_pt_.x(), y - gizmo_anchor_pt_.y());
@@ -368,7 +368,7 @@ QMatrix4x4 TransformDistortNode::AdjustMatrixByResolutions(const QMatrix4x4 &mat
void TransformDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals)
{
TexturePtr tex = row[kTextureInput].data().value<TexturePtr>();
TexturePtr tex = row[kTextureInput].toTexture();
if (!tex) {
return;
}
@@ -384,7 +384,7 @@ void TransformDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const N
QVector2D tex_offset = tex_params.offset();
// Retrieve autoscale value
AutoScaleType autoscale = static_cast<AutoScaleType>(row[kAutoscaleInput].data().toInt());
AutoScaleType autoscale = static_cast<AutoScaleType>(row[kAutoscaleInput].toInt());
// Fold values into a matrix for the rectangle
QMatrix4x4 rectangle_matrix;
@@ -441,7 +441,7 @@ QMatrix4x4 TransformDistortNode::GenerateAutoScaledMatrix(const QMatrix4x4& gene
{
const QVector2D &sequence_res = globals.resolution();
QVector2D texture_res(texture_params.square_pixel_width(), texture_params.height());
AutoScaleType autoscale = static_cast<AutoScaleType>(value[kAutoscaleInput].data().toInt());
AutoScaleType autoscale = static_cast<AutoScaleType>(value[kAutoscaleInput].toInt());
return AdjustMatrixByResolutions(generated_matrix,
sequence_res,
@@ -34,23 +34,13 @@ class TransformDistortNode : public MatrixGenerator
public:
TransformDistortNode();
NODE_DEFAULT_DESTRUCTOR(TransformDistortNode)
virtual Node* copy() const override
{
return new TransformDistortNode();
}
NODE_DEFAULT_FUNCTIONS(TransformDistortNode)
virtual QString Name() const override
{
return tr("Transform");
}
virtual QString ShortName() const override
{
return Node::ShortName();
}
virtual QString id() const override
{
return QStringLiteral("org.olivevideoeditor.Olive.transform");
@@ -70,7 +60,7 @@ public:
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
virtual ShaderCode GetShaderCode(const QString& shader_id) const override;
virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
enum AutoScaleType {
kAutoScaleNone,
+6 -6
View File
@@ -37,9 +37,9 @@ void OpacityEffect::Retranslate()
SetInputName(kValueInput, tr("Opacity"));
}
ShaderCode OpacityEffect::GetShaderCode(const QString &shader_id) const
ShaderCode OpacityEffect::GetShaderCode(const ShaderRequest &request) const
{
Q_UNUSED(shader_id)
Q_UNUSED(request)
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/opacity.frag"));
}
@@ -47,16 +47,16 @@ void OpacityEffect::Value(const NodeValueRow &value, const NodeGlobals &globals,
{
ShaderJob job;
job.InsertValue(value);
job.Insert(value);
// If there's no texture, no need to run an operation
if (!job.GetValue(kTextureInput).data().isNull()) {
if (!qFuzzyCompare(job.GetValue(kValueInput).data().toDouble(), 1.0)) {
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
table->Push(job.GetValue(kTextureInput));
table->Push(job.Get(kTextureInput));
}
}
}
+2 -4
View File
@@ -10,9 +10,7 @@ class OpacityEffect : public Node
public:
OpacityEffect();
NODE_DEFAULT_DESTRUCTOR(OpacityEffect)
NODE_COPY_FUNCTION(OpacityEffect)
NODE_DEFAULT_FUNCTIONS(OpacityEffect)
virtual QString Name() const override
{
@@ -36,7 +34,7 @@ public:
virtual void Retranslate() override;
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
static const QString kTextureInput;
+14 -7
View File
@@ -29,9 +29,12 @@
#include "block/subtitle/subtitle.h"
#include "block/transition/crossdissolve/crossdissolvetransition.h"
#include "block/transition/diptocolor/diptocolortransition.h"
#include "color/displaytransform/displaytransform.h"
#include "color/ociogradingtransformlinear/ociogradingtransformlinear.h"
#include "distort/cornerpin/cornerpindistortnode.h"
#include "distort/crop/cropdistortnode.h"
#include "distort/flip/flipdistortnode.h"
#include "distort/mask/mask.h"
#include "distort/transform/transformdistortnode.h"
#include "effect/opacity/opacityeffect.h"
#include "generator/matrix/matrix.h"
@@ -52,6 +55,7 @@
#include "math/trigonometry/trigonometry.h"
#include "keying/colordifferencekey/colordifferencekey.h"
#include "keying/despill/despill.h"
#include "keying/chromakey/chromakey.h"
#include "output/track/track.h"
#include "output/viewer/viewer.h"
#include "project/folder/folder.h"
@@ -61,8 +65,8 @@
#include "time/timeremap/timeremap.h"
namespace olive {
QList<Node*> NodeFactory::library_;
QVector<int> NodeFactory::hidden_;
void NodeFactory::Initialize()
{
@@ -74,10 +78,6 @@ void NodeFactory::Initialize()
library_.append(created_node);
}
hidden_.append(kTextGeneratorV1);
hidden_.append(kTextGeneratorV2);
hidden_.append(kGroupNode);
}
void NodeFactory::Destroy()
@@ -103,8 +103,7 @@ Menu *NodeFactory::CreateMenu(QWidget* parent, bool create_none_item, Node::Cate
continue;
}
if (hidden_.contains(i)) {
// Skip this node
if (n->GetFlags() & Node::kDontShowInCreateMenu) {
continue;
}
@@ -281,6 +280,14 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id)
return new TimeOffsetNode();
case kCornerPinDistort:
return new CornerPinDistortNode();
case kDisplayTransform:
return new DisplayTransformNode();
case kOCIOGradingTransformLinear:
return new OCIOGradingTransformLinearNode();
case kChromaKey:
return new ChromaKeyNode();
case kMaskDistort:
return new MaskDistortNode();
case kInternalNodeCount:
break;
+4 -2
View File
@@ -70,6 +70,10 @@ public:
kNoiseGenerator,
kTimeOffsetNode,
kCornerPinDistort,
kDisplayTransform,
kOCIOGradingTransformLinear,
kChromaKey,
kMaskDistort,
// Count value
kInternalNodeCount
@@ -96,8 +100,6 @@ public:
private:
static QList<Node*> library_;
static QVector<int> hidden_;
};
}
+114 -22
View File
@@ -29,30 +29,50 @@ const QString BlurFilterNode::kHorizInput = QStringLiteral("horiz_in");
const QString BlurFilterNode::kVertInput = QStringLiteral("vert_in");
const QString BlurFilterNode::kRepeatEdgePixelsInput = QStringLiteral("repeat_edge_pixels_in");
const QString BlurFilterNode::kDirectionalDegreesInput = QStringLiteral("directional_degrees_in");
const QString BlurFilterNode::kRadialCenterInput = QStringLiteral("radial_center_in");
#define super Node
BlurFilterNode::BlurFilterNode()
{
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
AddInput(kMethodInput, NodeValue::kCombo, 1); // Default to gaussian
Method default_method = kGaussian;
AddInput(kMethodInput, NodeValue::kCombo, default_method, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable));
AddInput(kRadiusInput, NodeValue::kFloat, 10.0);
SetInputProperty(kRadiusInput, QStringLiteral("min"), 0.0);
AddInput(kHorizInput, NodeValue::kBoolean, true);
{
// Box and gaussian only
AddInput(kHorizInput, NodeValue::kBoolean, true);
AddInput(kVertInput, NodeValue::kBoolean, true);
}
AddInput(kVertInput, NodeValue::kBoolean, true);
{
// Directional only
AddInput(kDirectionalDegreesInput, NodeValue::kFloat, 0.0);
}
{
// Radial only
AddInput(kRadialCenterInput, NodeValue::kVec2, QVector2D(0, 0));
}
UpdateInputs(default_method);
AddInput(kRepeatEdgePixelsInput, NodeValue::kBoolean, true);
SetFlags(kVideoEffect);
SetEffectInput(kTextureInput);
}
Node *BlurFilterNode::copy() const
{
return new BlurFilterNode();
radial_center_gizmo_ = AddDraggableGizmo<PointGizmo>();
radial_center_gizmo_->SetShape(PointGizmo::kAnchorPoint);
radial_center_gizmo_->AddInput(NodeKeyframeTrackReference(NodeInput(this, kRadialCenterInput), 0));
radial_center_gizmo_->AddInput(NodeKeyframeTrackReference(NodeInput(this, kRadialCenterInput), 1));
}
QString BlurFilterNode::Name() const
@@ -81,16 +101,19 @@ void BlurFilterNode::Retranslate()
SetInputName(kTextureInput, tr("Input"));
SetInputName(kMethodInput, tr("Method"));
SetComboBoxStrings(kMethodInput, { tr("Box"), tr("Gaussian") });
SetComboBoxStrings(kMethodInput, { tr("Box"), tr("Gaussian"), tr("Directional"), tr("Radial") });
SetInputName(kRadiusInput, tr("Radius"));
SetInputName(kHorizInput, tr("Horizontal"));
SetInputName(kVertInput, tr("Vertical"));
SetInputName(kRepeatEdgePixelsInput, tr("Repeat Edge Pixels"));
SetInputName(kDirectionalDegreesInput, tr("Direction"));
SetInputName(kRadialCenterInput, tr("Center"));
}
ShaderCode BlurFilterNode::GetShaderCode(const QString &shader_id) const
ShaderCode BlurFilterNode::GetShaderCode(const ShaderRequest &request) const
{
Q_UNUSED(shader_id)
Q_UNUSED(request)
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/blur.frag"));
}
@@ -98,34 +121,103 @@ void BlurFilterNode::Value(const NodeValueRow &value, const NodeGlobals &globals
{
ShaderJob job;
job.InsertValue(value);
job.InsertValue(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
job.Insert(value);
job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
Method method = static_cast<Method>(job.Get(kMethodInput).toInt());
// If there's no texture, no need to run an operation
if (!job.GetValue(kTextureInput).data().isNull()) {
if (job.Get(kTextureInput).toTexture()) {
// Check if radius > 0, and both "horiz" and/or "vert" are enabled
if ((job.GetValue(kHorizInput).data().toBool() || job.GetValue(kVertInput).data().toBool())
&& job.GetValue(kRadiusInput).data().toDouble() > 0.0) {
bool can_push_job = true;
// Set iteration count to 2 if we're blurring both horizontally and vertically
if (job.GetValue(kHorizInput).data().toBool() && job.GetValue(kVertInput).data().toBool()) {
job.SetIterations(2, kTextureInput);
// Check if radius is > 0
if (job.Get(kRadiusInput).toDouble() > 0.0) {
// Method-specific considerations
switch (method) {
case kBox:
case kGaussian:
{
bool horiz = job.Get(kHorizInput).toBool();
bool vert = job.Get(kVertInput).toBool();
if (!horiz && !vert) {
// Disable job if horiz and vert are unchecked
can_push_job = false;
} else if (horiz && vert) {
// Set iteration count to 2 if we're blurring both horizontally and vertically
job.SetIterations(2, kTextureInput);
}
break;
}
case kDirectional:
case kRadial:
break;
}
} else {
can_push_job = false;
}
if (can_push_job) {
// If we're not repeating pixels, expect an alpha channel to appear
if (!job.GetValue(kRepeatEdgePixelsInput).data().toBool()) {
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
table->Push(job.GetValue(kTextureInput));
table->Push(job.Get(kTextureInput));
}
}
}
void BlurFilterNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals)
{
if (row[kMethodInput].toInt() == kRadial) {
const QVector2D &sequence_res = globals.resolution();
QVector2D sequence_half_res = sequence_res * 0.5;
radial_center_gizmo_->SetVisible(true);
radial_center_gizmo_->SetPoint(sequence_half_res.toPointF() + row[kRadialCenterInput].toVec2().toPointF());
SetInputProperty(kRadialCenterInput, QStringLiteral("offset"), sequence_half_res);
} else{
radial_center_gizmo_->SetVisible(false);
}
}
void BlurFilterNode::GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers)
{
DraggableGizmo *gizmo = static_cast<DraggableGizmo*>(sender());
if (gizmo == radial_center_gizmo_) {
NodeInputDragger &x_drag = gizmo->GetDraggers()[0];
NodeInputDragger &y_drag = gizmo->GetDraggers()[1];
x_drag.Drag(x_drag.GetStartValue().toDouble() + x);
y_drag.Drag(y_drag.GetStartValue().toDouble() + y);
}
}
void BlurFilterNode::InputValueChangedEvent(const QString &input, int element)
{
if (input == kMethodInput) {
UpdateInputs(GetMethod());
}
super::InputValueChangedEvent(input, element);
}
void BlurFilterNode::UpdateInputs(Method method)
{
SetInputFlags(kHorizInput, (method == kBox || method == kGaussian) ? InputFlags() : InputFlags(kInputFlagHidden));
SetInputFlags(kVertInput, (method == kBox || method == kGaussian) ? InputFlags() : InputFlags(kInputFlagHidden));
SetInputFlags(kDirectionalDegreesInput, (method == kDirectional) ? InputFlags() : InputFlags(kInputFlagHidden));
SetInputFlags(kRadialCenterInput, (method == kRadial) ? InputFlags() : InputFlags(kInputFlagHidden));
}
}
+31 -3
View File
@@ -21,6 +21,7 @@
#ifndef BLURFILTERNODE_H
#define BLURFILTERNODE_H
#include "node/gizmo/point.h"
#include "node/node.h"
namespace olive {
@@ -31,9 +32,14 @@ class BlurFilterNode : public Node
public:
BlurFilterNode();
NODE_DEFAULT_DESTRUCTOR(BlurFilterNode)
enum Method {
kBox,
kGaussian,
kDirectional,
kRadial
};
virtual Node* copy() const override;
NODE_DEFAULT_FUNCTIONS(BlurFilterNode)
virtual QString Name() const override;
virtual QString id() const override;
@@ -42,9 +48,16 @@ public:
virtual void Retranslate() override;
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
Method GetMethod() const
{
return static_cast<Method>(GetStandardValue(kMethodInput).toInt());
}
virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override;
static const QString kTextureInput;
static const QString kMethodInput;
static const QString kRadiusInput;
@@ -52,6 +65,21 @@ public:
static const QString kVertInput;
static const QString kRepeatEdgePixelsInput;
static const QString kDirectionalDegreesInput;
static const QString kRadialCenterInput;
protected slots:
virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) override;
protected:
virtual void InputValueChangedEvent(const QString& input, int element) override;
private:
void UpdateInputs(Method method);
PointGizmo *radial_center_gizmo_;
};
}
+8 -8
View File
@@ -55,27 +55,27 @@ void MosaicFilterNode::Value(const NodeValueRow &value, const NodeGlobals &globa
{
ShaderJob job;
job.InsertValue(value);
job.Insert(value);
// Mipmapping makes this look weird, so we just use bilinear for finding the color of each block
job.SetInterpolation(kTextureInput, Texture::kLinear);
if (!job.GetValue(kTextureInput).data().isNull()) {
TexturePtr texture = job.GetValue(kTextureInput).data().value<TexturePtr>();
if (job.Get(kTextureInput).toTexture()) {
TexturePtr texture = job.Get(kTextureInput).toTexture();
if (texture
&& job.GetValue(kHorizInput).data().toInt() != texture->width()
&& job.GetValue(kVertInput).data().toInt() != texture->height()) {
&& job.Get(kHorizInput).toInt() != texture->width()
&& job.Get(kVertInput).toInt() != texture->height()) {
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
} else {
table->Push(job.GetValue(kTextureInput));
table->Push(job.Get(kTextureInput));
}
}
}
ShaderCode MosaicFilterNode::GetShaderCode(const QString &shader_id) const
ShaderCode MosaicFilterNode::GetShaderCode(const ShaderRequest &request) const
{
Q_UNUSED(shader_id)
Q_UNUSED(request)
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/mosaic.frag"));
}
+2 -7
View File
@@ -31,12 +31,7 @@ class MosaicFilterNode : public Node
public:
MosaicFilterNode();
NODE_DEFAULT_DESTRUCTOR(MosaicFilterNode)
virtual Node* copy() const override
{
return new MosaicFilterNode();
}
NODE_DEFAULT_FUNCTIONS(MosaicFilterNode)
virtual QString Name() const override
{
@@ -61,7 +56,7 @@ public:
virtual void Retranslate() override;
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
static const QString kTextureInput;
static const QString kHorizInput;
+8 -13
View File
@@ -53,11 +53,6 @@ StrokeFilterNode::StrokeFilterNode()
SetEffectInput(kTextureInput);
}
Node *StrokeFilterNode::copy() const
{
return new StrokeFilterNode();
}
QString StrokeFilterNode::Name() const
{
return tr("Stroke");
@@ -93,22 +88,22 @@ void StrokeFilterNode::Value(const NodeValueRow &value, const NodeGlobals &globa
{
ShaderJob job;
job.InsertValue(value);
job.InsertValue(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
job.Insert(value);
job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
if (!job.GetValue(kTextureInput).data().isNull()) {
if (job.GetValue(kRadiusInput).data().toDouble() > 0.0
&& job.GetValue(kOpacityInput).data().toDouble() > 0.0) {
if (job.Get(kTextureInput).toTexture()) {
if (job.Get(kRadiusInput).toDouble() > 0.0
&& job.Get(kOpacityInput).toDouble() > 0.0) {
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
} else {
table->Push(job.GetValue(kTextureInput));
table->Push(job.Get(kTextureInput));
}
}
}
ShaderCode StrokeFilterNode::GetShaderCode(const QString &shader_id) const
ShaderCode StrokeFilterNode::GetShaderCode(const ShaderRequest &request) const
{
Q_UNUSED(shader_id)
Q_UNUSED(request)
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/stroke.frag"));
}
+2 -4
View File
@@ -31,9 +31,7 @@ class StrokeFilterNode : public Node
public:
StrokeFilterNode();
NODE_DEFAULT_DESTRUCTOR(StrokeFilterNode)
virtual Node* copy() const override;
NODE_DEFAULT_FUNCTIONS(StrokeFilterNode)
virtual QString Name() const override;
virtual QString id() const override;
@@ -43,7 +41,7 @@ public:
virtual void Retranslate() override;
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
static const QString kTextureInput;
static const QString kColorInput;
+15 -20
View File
@@ -44,18 +44,13 @@ MatrixGenerator::MatrixGenerator()
AddInput(kScaleInput, NodeValue::kVec2, QVector2D(1.0f, 1.0f));
SetInputProperty(kScaleInput, QStringLiteral("min"), QVector2D(0, 0));
SetInputProperty(kScaleInput, QStringLiteral("view"), FloatSlider::kPercentage);
SetInputProperty(kScaleInput, QStringLiteral("disabley"), true);
SetInputProperty(kScaleInput, QStringLiteral("disable1"), true);
AddInput(kUniformScaleInput, NodeValue::kBoolean, true, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
AddInput(kAnchorInput, NodeValue::kVec2, QVector2D(0.0, 0.0));
}
Node *MatrixGenerator::copy() const
{
return new MatrixGenerator();
}
QString MatrixGenerator::Name() const
{
return tr("Orthographic Matrix");
@@ -108,47 +103,47 @@ QMatrix4x4 MatrixGenerator::GenerateMatrix(const NodeValueRow &value, bool take,
if (!ignore_anchor) {
if (take) {
// Take and store
anchor = value[kAnchorInput].data().value<QVector2D>();
anchor = value[kAnchorInput].toVec2();
} else {
// Get and store
anchor = value[kAnchorInput].data().value<QVector2D>();
anchor = value[kAnchorInput].toVec2();
}
} else if (take) {
// Just take
value[kAnchorInput].data().value<QVector2D>();
value[kAnchorInput].toVec2();
}
if (!ignore_scale) {
if (take) {
scale = value[kScaleInput].data().value<QVector2D>();
scale = value[kScaleInput].toVec2();
} else {
scale = value[kScaleInput].data().value<QVector2D>();
scale = value[kScaleInput].toVec2();
}
} else if (take) {
value[kScaleInput].data().value<QVector2D>();
value[kScaleInput].toVec2();
}
if (!ignore_position) {
if (take) {
position = value[kPositionInput].data().value<QVector2D>();
position = value[kPositionInput].toVec2();
} else {
position = value[kPositionInput].data().value<QVector2D>();
position = value[kPositionInput].toVec2();
}
} else if (take) {
value[kPositionInput].data().value<QVector2D>();
value[kPositionInput].toVec2();
}
if (take) {
return GenerateMatrix(position,
value[kRotationInput].data().toFloat(),
value[kRotationInput].toDouble(),
scale,
value[kUniformScaleInput].data().toBool(),
value[kUniformScaleInput].toBool(),
anchor);
} else {
return GenerateMatrix(position,
value[kRotationInput].data().toFloat(),
value[kRotationInput].toDouble(),
scale,
value[kUniformScaleInput].data().toBool(),
value[kUniformScaleInput].toBool(),
anchor);
}
@@ -188,7 +183,7 @@ void MatrixGenerator::InputValueChangedEvent(const QString &input, int element)
Q_UNUSED(element)
if (input == kUniformScaleInput) {
SetInputProperty(kScaleInput, QStringLiteral("disabley"), GetStandardValue(kUniformScaleInput).toBool());
SetInputProperty(kScaleInput, QStringLiteral("disable1"), GetStandardValue(kUniformScaleInput).toBool());
}
}
+1 -3
View File
@@ -34,9 +34,7 @@ class MatrixGenerator : public Node
public:
MatrixGenerator();
NODE_DEFAULT_DESTRUCTOR(MatrixGenerator)
virtual Node* copy() const override;
NODE_DEFAULT_FUNCTIONS(MatrixGenerator)
virtual QString Name() const override;
virtual QString ShortName() const override;
+3 -8
View File
@@ -44,11 +44,6 @@ NoiseGeneratorNode::NoiseGeneratorNode()
SetFlags(kVideoEffect);
}
Node* NoiseGeneratorNode::copy() const
{
return new NoiseGeneratorNode();
}
QString NoiseGeneratorNode::Name() const
{
return tr("Noise");
@@ -78,7 +73,7 @@ void NoiseGeneratorNode::Retranslate()
SetInputName(kColorInput, tr("Color"));
}
ShaderCode NoiseGeneratorNode::GetShaderCode(const QString& shader_id) const
ShaderCode NoiseGeneratorNode::GetShaderCode(const ShaderRequest &request) const
{
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/noise.frag"));
}
@@ -87,8 +82,8 @@ void NoiseGeneratorNode::Value(const NodeValueRow &value, const NodeGlobals &glo
{
ShaderJob job;
job.InsertValue(value);
job.InsertValue(QStringLiteral("time_in"), NodeValue(NodeValue::kFloat, globals.time().in().toDouble(), this));
job.Insert(value);
job.Insert(QStringLiteral("time_in"), NodeValue(NodeValue::kFloat, globals.time().in().toDouble(), this));
table->Push(NodeValue::kTexture, QVariant::fromValue(job), this);
}
+2 -4
View File
@@ -30,9 +30,7 @@ class NoiseGeneratorNode : public Node {
public:
NoiseGeneratorNode();
NODE_DEFAULT_DESTRUCTOR(NoiseGeneratorNode)
virtual Node *copy() const override;
NODE_DEFAULT_FUNCTIONS(NoiseGeneratorNode)
virtual QString Name() const override;
virtual QString id() const override;
@@ -41,7 +39,7 @@ class NoiseGeneratorNode : public Node {
virtual void Retranslate() override;
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
virtual void Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const override;
static const QString kBaseIn;
+16 -14
View File
@@ -61,11 +61,6 @@ PolygonGenerator::PolygonGenerator()
poly_gizmo_ = new PathGizmo(this);
}
Node *PolygonGenerator::copy() const
{
return new PolygonGenerator();
}
QString PolygonGenerator::Name() const
{
return tr("Polygon");
@@ -94,14 +89,21 @@ void PolygonGenerator::Retranslate()
SetInputName(kColorInput, tr("Color"));
}
void PolygonGenerator::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
GenerateJob PolygonGenerator::GetGenerateJob(const NodeValueRow &value) const
{
GenerateJob job;
job.InsertValue(value);
job.Insert(value);
job.SetRequestedFormat(VideoParams::kFormatFloat32);
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
return job;
}
void PolygonGenerator::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const
{
GenerateJob job = GetGenerateJob(value);
PushMergableJob(value, QVariant::fromValue(job), table);
}
@@ -114,7 +116,7 @@ void PolygonGenerator::GenerateFrame(FramePtr frame, const GenerateJob &job) con
QImage img(frame->width(), frame->height(), QImage::Format_Grayscale8);
img.fill(Qt::transparent);
QVector<NodeValue> points = job.GetValue(kPointsInput).data().value< QVector<NodeValue> >();
QVector<NodeValue> points = job.Get(kPointsInput).value< QVector<NodeValue> >();
QPainterPath path = GeneratePath(points);
@@ -128,7 +130,7 @@ void PolygonGenerator::GenerateFrame(FramePtr frame, const GenerateJob &job) con
p.drawPath(path);
// Transplant alpha channel to frame
Color rgba = job.GetValue(kColorInput).data().value<Color>();
Color rgba = job.Get(kColorInput).toColor();
#if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM)
__m128 sse_color = _mm_loadu_ps(rgba.data());
#endif
@@ -194,7 +196,7 @@ void PolygonGenerator::UpdateGizmoPositions(const NodeValueRow &row, const NodeG
{
QPointF half_res(globals.resolution_by_par().x()/2, globals.resolution_by_par().y()/2);
QVector<NodeValue> points = row[kPointsInput].data().value< QVector<NodeValue> >();
QVector<NodeValue> points = row[kPointsInput].value< QVector<NodeValue> >();
int current_pos_sz = gizmo_position_handles_.size();
@@ -221,7 +223,7 @@ void PolygonGenerator::UpdateGizmoPositions(const NodeValueRow &row, const NodeG
if (!points.isEmpty()) {
for (int i=0; i<points.size(); i++) {
const Bezier &pt = points.at(i).data().value<Bezier>();
const Bezier &pt = points.at(i).toBezier();
QPointF main = pt.ToPointF() + half_res;
QPointF cp1 = main + pt.ControlPoint1ToPointF();
@@ -265,14 +267,14 @@ QPainterPath PolygonGenerator::GeneratePath(const QVector<NodeValue> &points)
QPainterPath path;
if (!points.isEmpty()) {
const Bezier &first_pt = points.first().data().value<Bezier>();
const Bezier &first_pt = points.first().toBezier();
path.moveTo(first_pt.ToPointF());
for (int i=1; i<points.size(); i++) {
AddPointToPath(&path, points.at(i-1).data().value<Bezier>(), points.at(i).data().value<Bezier>());
AddPointToPath(&path, points.at(i-1).toBezier(), points.at(i).toBezier());
}
AddPointToPath(&path, points.last().data().value<Bezier>(), first_pt);
AddPointToPath(&path, points.last().toBezier(), first_pt);
}
return path;
+4 -3
View File
@@ -39,9 +39,7 @@ class PolygonGenerator : public GeneratorWithMerge
public:
PolygonGenerator();
NODE_DEFAULT_DESTRUCTOR(PolygonGenerator)
virtual Node* copy() const override;
NODE_DEFAULT_FUNCTIONS(PolygonGenerator)
virtual QString Name() const override;
virtual QString id() const override;
@@ -59,6 +57,9 @@ public:
static const QString kPointsInput;
static const QString kColorInput;
protected:
GenerateJob GetGenerateJob(const NodeValueRow &value) const;
protected slots:
virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) override;
@@ -42,9 +42,9 @@ void GeneratorWithMerge::Retranslate()
SetInputName(kBaseInput, tr("Base"));
}
ShaderCode GeneratorWithMerge::GetShaderCode(const QString &shader_id) const
ShaderCode GeneratorWithMerge::GetShaderCode(const ShaderRequest &request) const
{
if (shader_id == QStringLiteral("mrg")) {
if (request.id == QStringLiteral("mrg")) {
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/alphaover.frag"));
}
@@ -53,13 +53,13 @@ ShaderCode GeneratorWithMerge::GetShaderCode(const QString &shader_id) const
void GeneratorWithMerge::PushMergableJob(const NodeValueRow &value, const QVariant &job, NodeValueTable *table) const
{
if (!value[kBaseInput].data().isNull()) {
if (value[kBaseInput].toTexture()) {
// Push as merge node
ShaderJob merge;
merge.SetShaderID(QStringLiteral("mrg"));
merge.InsertValue(MergeNode::kBaseIn, value[kBaseInput]);
merge.InsertValue(MergeNode::kBlendIn, NodeValue(NodeValue::kTexture, job, this));
merge.Insert(MergeNode::kBaseIn, value[kBaseInput]);
merge.Insert(MergeNode::kBlendIn, NodeValue(NodeValue::kTexture, job, this));
table->Push(NodeValue::kTexture, QVariant::fromValue(merge), this);
} else {
@@ -31,11 +31,9 @@ class GeneratorWithMerge : public Node
public:
GeneratorWithMerge();
NODE_DEFAULT_DESTRUCTOR(GeneratorWithMerge)
virtual void Retranslate() override;
virtual ShaderCode GetShaderCode(const QString &shader_id) const override;
virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
static const QString kBaseInput;
+5 -5
View File
@@ -61,12 +61,12 @@ void ShapeNode::Retranslate()
SetComboBoxStrings(kTypeInput, {tr("Rectangle"), tr("Ellipse")});
}
ShaderCode ShapeNode::GetShaderCode(const QString &shader_id) const
ShaderCode ShapeNode::GetShaderCode(const ShaderRequest &request) const
{
if (shader_id == QStringLiteral("shape")) {
if (request.id == QStringLiteral("shape")) {
return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/shape.frag")));
} else {
return super::GetShaderCode(shader_id);
return super::GetShaderCode(request);
}
}
@@ -74,8 +74,8 @@ void ShapeNode::Value(const NodeValueRow &value, const NodeGlobals &globals, Nod
{
ShaderJob job;
job.InsertValue(value);
job.InsertValue(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
job.Insert(value);
job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, globals.resolution(), this));
job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn);
job.SetShaderID(QStringLiteral("shape"));
+2 -3
View File
@@ -36,8 +36,7 @@ public:
kEllipse
};
NODE_DEFAULT_DESTRUCTOR(ShapeNode)
NODE_COPY_FUNCTION(ShapeNode)
NODE_DEFAULT_FUNCTIONS(ShapeNode)
virtual QString Name() const override;
virtual QString id() const override;
@@ -46,7 +45,7 @@ public:
virtual void Retranslate() override;
virtual ShaderCode GetShaderCode(const QString& shader_id) const override;
virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override;
virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override;
static QString kTypeInput;
+2 -2
View File
@@ -79,8 +79,8 @@ void ShapeNodeBase::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlob
QVector2D center_pt = globals.resolution() * 0.5;
SetInputProperty(kPositionInput, QStringLiteral("offset"), center_pt);
QVector2D pos = row[kPositionInput].data().value<QVector2D>();
QVector2D sz = row[kSizeInput].data().value<QVector2D>();
QVector2D pos = row[kPositionInput].toVec2();
QVector2D sz = row[kSizeInput].toVec2();
QVector2D half_sz = sz * 0.5;
double left_pt = pos.x() + center_pt.x() - half_sz.x();

Some files were not shown because too many files have changed in this diff Show More