From 7acdb5a285983b4454682cd0f4c08ac943e6853c Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 4 Dec 2022 17:45:27 -0800 Subject: [PATCH 01/71] config: default to rectified waveforms on --- app/config/config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/config/config.cpp b/app/config/config.cpp index 192fcdb749..82d358a281 100644 --- a/app/config/config.cpp +++ b/app/config/config.cpp @@ -91,7 +91,7 @@ void Config::SetDefaults() SetEntryInternal(QStringLiteral("Autoscroll"), NodeValue::kInt, AutoScroll::kPage); SetEntryInternal(QStringLiteral("AutoSelectDivider"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean, false); - SetEntryInternal(QStringLiteral("RectifiedWaveforms"), NodeValue::kBoolean, false); + SetEntryInternal(QStringLiteral("RectifiedWaveforms"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("DropWithoutSequenceBehavior"), NodeValue::kInt, ImportTool::kDWSAsk); SetEntryInternal(QStringLiteral("Loop"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("SplitClipsCopyNodes"), NodeValue::kBoolean, true); From bae1951bf22d9f7587cf6256aab9fb74ece56cbf Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 4 Dec 2022 17:47:07 -0800 Subject: [PATCH 02/71] audio: initial implementation of no-conform approach Seems to work reasonably well. Is missing loop modes. Some codecs produce artifacts. --- app/codec/decoder.cpp | 36 +++--- app/codec/decoder.h | 11 +- app/codec/ffmpeg/ffmpegdecoder.cpp | 173 +++++++++++++++++++++++++++-- app/codec/ffmpeg/ffmpegdecoder.h | 18 +++ app/codec/planarfiledevice.h | 3 - app/render/audioparams.cpp | 7 +- app/render/audioparams.h | 1 + app/render/renderprocessor.cpp | 6 +- 8 files changed, 222 insertions(+), 33 deletions(-) diff --git a/app/codec/decoder.cpp b/app/codec/decoder.cpp index 6fb2a2676f..d50404581e 100644 --- a/app/codec/decoder.cpp +++ b/app/codec/decoder.cpp @@ -123,7 +123,7 @@ TexturePtr Decoder::RetrieveVideo(const RetrieveVideoParams &p) return cached_texture_; } -Decoder::RetrieveAudioStatus Decoder::RetrieveAudio(SampleBuffer &dest, const TimeRange &range, const AudioParams ¶ms, const QString& cache_path, LoopMode loop_mode, RenderMode::Mode mode) +Decoder::RetrieveAudioStatus Decoder::RetrieveAudio(SampleBuffer &dest, const rational &time, const QString& cache_path, LoopMode loop_mode, RenderMode::Mode mode) { QMutexLocker locker(&mutex_); @@ -139,18 +139,24 @@ Decoder::RetrieveAudioStatus Decoder::RetrieveAudio(SampleBuffer &dest, const Ti return kInvalid; } - // Get conform state from ConformManager - ConformManager::Conform conform = ConformManager::instance()->GetConformState(id(), cache_path, stream_, params, (mode == RenderMode::kOnline)); - if (conform.state == ConformManager::kConformGenerating) { - // If we need the task, it's available in `conform.task` - return kWaitingForConform; - } + const bool use_conforms = false; + + if (use_conforms) { + // Get conform state from ConformManager + ConformManager::Conform conform = ConformManager::instance()->GetConformState(id(), cache_path, stream_, dest.audio_params(), (mode == RenderMode::kOnline)); + if (conform.state == ConformManager::kConformGenerating) { + // If we need the task, it's available in `conform.task` + return kWaitingForConform; + } - // See if we got the conform - if (RetrieveAudioFromConform(dest, conform.filenames, range, loop_mode, params)) { - return kOK; + // See if we got the conform + if (RetrieveAudioFromConform(dest, conform.filenames, time, loop_mode)) { + return kOK; + } else { + return kUnknownError; + } } else { - return kUnknownError; + return RetrieveAudioInternal(dest, time); } } @@ -280,14 +286,16 @@ bool Decoder::ConformAudioInternal(const QVector &filenames, const Audi return false; } -bool Decoder::RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector &conform_filenames, TimeRange range, LoopMode loop_mode, const AudioParams &input_params) +bool Decoder::RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector &conform_filenames, rational time, LoopMode loop_mode) { + const AudioParams &input_params = sample_buffer.audio_params(); + PlanarFileDevice input; if (input.open(conform_filenames, QFile::ReadOnly)) { // Offset range by audio start offset - range -= GetAudioStartOffset(); + time -= GetAudioStartOffset(); - qint64 read_index = input_params.time_to_bytes(range.in()) / input_params.channel_count(); + qint64 read_index = input_params.time_to_bytes(time) / 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(); diff --git a/app/codec/decoder.h b/app/codec/decoder.h index 471958bc96..2fa03c6eb6 100644 --- a/app/codec/decoder.h +++ b/app/codec/decoder.h @@ -193,7 +193,7 @@ class Decoder : public QObject * * This function is thread safe and can only run while the decoder is open. \see Open() */ - RetrieveAudioStatus RetrieveAudio(SampleBuffer &dest, const TimeRange& range, const AudioParams& params, const QString &cache_path, LoopMode loop_mode, RenderMode::Mode mode); + RetrieveAudioStatus RetrieveAudio(SampleBuffer &dest, const rational& time, const QString &cache_path, LoopMode loop_mode, RenderMode::Mode mode); /** * @brief Determine the last time this decoder instance was used in any way @@ -273,6 +273,11 @@ class Decoder : public QObject */ virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p); + virtual RetrieveAudioStatus RetrieveAudioInternal(SampleBuffer &dest, const rational &time) + { + return kOK; + } + virtual bool ConformAudioInternal(const QVector& filenames, const AudioParams ¶ms, CancelAtom *cancelled); void SignalProcessingProgress(int64_t ts, int64_t duration); @@ -289,6 +294,8 @@ class Decoder : public QObject virtual rational GetAudioStartOffset() const { return 0; } + virtual int GetAudioSampleRate() const { return 0; } + signals: /** * @brief While indexing, this signal will provide progress as a percentage (0-100 inclusive) if @@ -299,7 +306,7 @@ class Decoder : public QObject private: void UpdateLastAccessed(); - bool RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector &conform_filenames, TimeRange range, LoopMode loop_mode, const AudioParams ¶ms); + bool RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector &conform_filenames, rational time, LoopMode loop_mode); CodecStream stream_; diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index 5e2bdddbe9..c754d1b71e 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -53,6 +53,7 @@ QVariant DeinterlaceShader; FFmpegDecoder::FFmpegDecoder() : sws_ctx_(nullptr), + swr_ctx_(nullptr), working_packet_(nullptr), cache_at_zero_(false), cache_at_eof_(false) @@ -209,7 +210,7 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVi // Flip frame rate so it can be used as a timebase frame_rate_tb.flip(); - int64_t req = Timecode::time_to_timestamp(p.time + rational(instance_.fmt_ctx()->start_time, AV_TIME_BASE), frame_rate_tb); + int64_t req = Timecode::time_to_timestamp(p.time, frame_rate_tb) + av_rescale_q(instance_.avstream()->start_time, instance_.avstream()->time_base, frame_rate_tb.toAVRational()); int64_t frm = Timecode::rescale_timestamp(original->pts, instance_.avstream()->time_base, frame_rate_tb); bool first = (req == frm); @@ -271,6 +272,8 @@ void FFmpegDecoder::CloseInternal() ClearFrameCache(); FreeScaler(); + FreeResampler(); + instance_.Close(); } @@ -286,6 +289,11 @@ rational FFmpegDecoder::GetAudioStartOffset() const } } +int FFmpegDecoder::GetAudioSampleRate() const +{ + return instance_.avstream()->codecpar->sample_rate; +} + QString FFmpegDecoder::id() const { return QStringLiteral("ffmpeg"); @@ -432,10 +440,7 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can } else if (avstream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { // Create an audio stream object - uint64_t channel_layout = avstream->codecpar->channel_layout; - if (!channel_layout) { - channel_layout = static_cast(av_get_default_channel_layout(avstream->codecpar->channels)); - } + uint64_t channel_layout = ValidateChannelLayout(avstream); if (avstream->duration == AV_NOPTS_VALUE || duration_guessed_from_bitrate) { // Loop through stream until we get the whole duration @@ -642,6 +647,99 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector &filenames, cons return success; } +Decoder::RetrieveAudioStatus FFmpegDecoder::RetrieveAudioInternal(SampleBuffer &dest, const rational& time) +{ + // Calculate our target sample + const AudioParams ¶ms = dest.audio_params(); + AVRational dst_tb = params.sample_rate_as_time_base().toAVRational(); + + // Calculate stream's timestamp at this time + int64_t ts = Timecode::time_to_timestamp(time, dst_tb, Timecode::kRound); + + // Seek to correct part of the audio if necessary + if (swr_time_ == AV_NOPTS_VALUE + || ts < swr_time_ + || ts >= swr_time_ + params.sample_rate()) { + if (swr_time_ == AV_NOPTS_VALUE) { + qDebug() << "seeking because of no swr time"; + } else { + qDebug() << "seeking" << this->stream().filename() << "because" << ts << "is <" << swr_time_ << "or >=" << (swr_time_ + params.sample_rate()); + } + int64_t seek_to = av_rescale_q(ts, dst_tb, instance_.avstream()->time_base); + seek_to = std::max(int64_t(0), seek_to - second_ts_); + instance_.Seek(seek_to); + FreeResampler(); + } + + // Ensure swr_ctx is initialized + Decoder::RetrieveAudioStatus resampler_status = ValidateResampler(params); + if (resampler_status != kOK) { + return resampler_status; + } + + // Copy data continuously to output, resampling if necessary + AVFramePtr decoded = CreateAVFramePtr(); + int dst_bpc = AudioParams::bytes_per_sample_per_channel(params.format()); + auto dst_ptrs_vector = dest.to_raw_ptrs(); + uint8_t **dst_ptrs = reinterpret_cast(dst_ptrs_vector.data()); + int64_t dst_offset = std::max(int64_t(0), -ts); + for (size_t j = dst_offset; j < dest.sample_count(); ) { + // Read frame, if a frame wasn't allocated by an earlier seek + int ret = instance_.GetFrame(working_packet_, decoded.get()); + uint8_t **in_data = decoded->data; + if (ret < 0) { + if (ret != AVERROR_EOF) { + FFmpegError(ret); + return kUnknownError; + } else { + in_data = nullptr; + } + } + + // Fill in SwrContext's current time if it doesn't yet exist + if (swr_time_ == AV_NOPTS_VALUE) { + swr_time_ = av_rescale_q(decoded->pts, instance_.avstream()->time_base, dst_tb); + } + + // Determine if any samples need to be skipped + int64_t skip = (ts + j) - swr_time_; + if (skip > 0) { + swr_drop_output(swr_ctx_, skip); + swr_time_ += skip; + } + + // Push data to swresample + int copied = swr_convert(swr_ctx_, + dst_ptrs, dest.sample_count() - j, + const_cast(in_data), decoded->nb_samples); + if (copied < 0) { + // Encountered an error, handle it + if (copied != AVERROR_EOF) { + FFmpegError(ret); + return kUnknownError; + } else { + break; + } + } + + // Increment SwrContext sample count + swr_time_ += copied; + + // If reached EOF and SwrContext is fully flushed, exit now + if (ret == AVERROR_EOF && !in_data && copied == 0) { + break; + } + + // Limit + j += copied; + for (size_t i=0; itime_base); - if (instance_.fmt_ctx()->start_time != AV_NOPTS_VALUE) { - target_ts += av_rescale_q(instance_.fmt_ctx()->start_time, {1, AV_TIME_BASE}, instance_.avstream()->time_base); - } - const int64_t min_seek = 0; int64_t seek_ts = std::max(min_seek, target_ts - MaximumQueueSize()); bool still_seeking = false; @@ -948,6 +1042,15 @@ void FFmpegDecoder::FreeScaler() } } +void FFmpegDecoder::FreeResampler() +{ + if (swr_ctx_) { + swr_free(&swr_ctx_); + swr_ctx_ = nullptr; + } + swr_time_ = AV_NOPTS_VALUE; +} + AVFramePtr FFmpegDecoder::GetFrameFromCache(const int64_t &t) const { if (t < cached_frames_.front()->pts) { @@ -989,6 +1092,58 @@ void FFmpegDecoder::RemoveFirstFrame() cache_at_zero_ = false; } +Decoder::RetrieveAudioStatus FFmpegDecoder::ValidateResampler(const AudioParams &output) +{ + auto src_fmt_native = FFmpegUtils::GetNativeSampleFormat(static_cast(instance_.avstream()->codecpar->format)); + + // We need a resampler. Check if one already exists. + int64_t input_ch_layout = ValidateChannelLayout(instance_.avstream()); + + if (swr_ctx_ + && swr_irate_ == instance_.avstream()->codecpar->sample_rate + && swr_ichannels_ == input_ch_layout + && swr_iformat_ == src_fmt_native + && swr_orate_ == output.sample_rate() + && swr_ochannels_ == output.channel_layout() + && swr_oformat_ == output.format()) { + // Resampler is valid, continue + } else { + // Resampler must be (re)created + FreeResampler(); + + int r; + + swr_ctx_ = swr_alloc_set_opts(nullptr, + output.channel_layout(), + FFmpegUtils::GetFFmpegSampleFormat(output.format()), + output.sample_rate(), + input_ch_layout, + static_cast(instance_.avstream()->codecpar->format), + instance_.avstream()->codecpar->sample_rate, + 0, nullptr); + + if (!swr_ctx_) { + qCritical() << "Failed to allocate SwrContext"; + return kUnknownError; + } + + r = swr_init(swr_ctx_); + if (r < 0) { + FFmpegError(r); + return kUnknownError; + } + + swr_irate_ = instance_.avstream()->codecpar->sample_rate; + swr_ichannels_ = input_ch_layout; + swr_iformat_ = src_fmt_native; + swr_orate_ = output.sample_rate(); + swr_ochannels_ = output.channel_layout(); + swr_oformat_ = output.format(); + } + + return kOK; +} + int FFmpegDecoder::MaximumQueueSize() { // Fairly arbitrary size. This used to need to be the number of current threads to ensure any diff --git a/app/codec/ffmpeg/ffmpegdecoder.h b/app/codec/ffmpeg/ffmpegdecoder.h index a695f0b312..8bd463de07 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.h +++ b/app/codec/ffmpeg/ffmpegdecoder.h @@ -65,9 +65,11 @@ class FFmpegDecoder : public Decoder virtual bool OpenInternal() override; virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p) override; virtual bool ConformAudioInternal(const QVector& filenames, const AudioParams ¶ms, CancelAtom *cancelled) override; + virtual RetrieveAudioStatus RetrieveAudioInternal(SampleBuffer &dest, const rational& time) override; virtual void CloseInternal() override; virtual rational GetAudioStartOffset() const override; + virtual int GetAudioSampleRate() const override; private: class Instance @@ -136,6 +138,8 @@ class FFmpegDecoder : public Decoder void FreeScaler(); + void FreeResampler(); + static VideoParams::Format GetNativePixelFormat(AVPixelFormat pix_fmt); static int GetNativeChannelCount(AVPixelFormat pix_fmt); @@ -157,6 +161,11 @@ class FFmpegDecoder : public Decoder void RemoveFirstFrame(); + Decoder::RetrieveAudioStatus ValidateResampler(const AudioParams &output); + + int64_t ts_to_samples(int64_t t) const; + int64_t samples_to_ts(int64_t s) const; + static int MaximumQueueSize(); SwsContext *sws_ctx_; @@ -169,6 +178,15 @@ class FFmpegDecoder : public Decoder AVColorRange sws_colrange_; AVColorSpace sws_colspace_; + SwrContext *swr_ctx_; + int swr_irate_; + int64_t swr_ichannels_; + AudioParams::Format swr_iformat_; + int swr_orate_; + int64_t swr_ochannels_; + AudioParams::Format swr_oformat_; + int64_t swr_time_; + AVPacket *working_packet_; int64_t second_ts_; diff --git a/app/codec/planarfiledevice.h b/app/codec/planarfiledevice.h index 00da3a454b..7905ac507e 100644 --- a/app/codec/planarfiledevice.h +++ b/app/codec/planarfiledevice.h @@ -24,9 +24,6 @@ #include #include -#include "codec/samplebuffer.h" -#include "common/define.h" - namespace olive { class PlanarFileDevice : public QObject diff --git a/app/render/audioparams.cpp b/app/render/audioparams.cpp index 3712f8cdea..1651a5ffc3 100644 --- a/app/render/audioparams.cpp +++ b/app/render/audioparams.cpp @@ -149,7 +149,12 @@ int AudioParams::channel_count() const int AudioParams::bytes_per_sample_per_channel() const { - switch (format_) { + return bytes_per_sample_per_channel(format_); +} + +int AudioParams::bytes_per_sample_per_channel(Format format) +{ + switch (format) { case kFormatUnsigned8Packed: case kFormatUnsigned8Planar: return 1; diff --git a/app/render/audioparams.h b/app/render/audioparams.h index aff2232398..5282501399 100644 --- a/app/render/audioparams.h +++ b/app/render/audioparams.h @@ -220,6 +220,7 @@ class AudioParams { rational bytes_per_channel_to_time(const qint64 &bytes) const; int channel_count() const; int bytes_per_sample_per_channel() const; + static int bytes_per_sample_per_channel(AudioParams::Format format); int bits_per_sample() const; bool is_valid() const; diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index d723d54e79..8778bb6546 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -421,13 +421,11 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJ void RenderProcessor::ProcessAudioFootage(SampleBuffer &destination, const FootageJob *stream, const TimeRange &input_time) { - DecoderPtr decoder = ResolveDecoderFromInput(stream->decoder(), Decoder::CodecStream(stream->filename(), stream->audio_params().stream_index(), nullptr)); + DecoderPtr decoder = ResolveDecoderFromInput(stream->decoder(), Decoder::CodecStream(stream->filename(), stream->audio_params().stream_index(), GetCurrentBlock())); if (decoder) { - const AudioParams& audio_params = GetCacheAudioParams(); - Decoder::RetrieveAudioStatus status = decoder->RetrieveAudio(destination, - input_time, audio_params, + input_time.in(), stream->cache_path(), loop_mode(), static_cast(ticket_->property("mode").toInt())); From fa5d2673ada9c2a572824426fcbfb81c3d86e8af Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Fri, 17 Feb 2023 12:30:27 -0800 Subject: [PATCH 03/71] updated to latest master --- app/codec/ffmpeg/ffmpegdecoder.cpp | 2 +- app/codec/ffmpeg/ffmpegdecoder.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index d40103282d..d6853b9022 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -678,7 +678,7 @@ Decoder::RetrieveAudioStatus FFmpegDecoder::RetrieveAudioInternal(SampleBuffer & // Copy data continuously to output, resampling if necessary AVFramePtr decoded = CreateAVFramePtr(); - int dst_bpc = AudioParams::bytes_per_sample_per_channel(params.format()); + int dst_bpc = params.format().byte_count(); auto dst_ptrs_vector = dest.to_raw_ptrs(); uint8_t **dst_ptrs = reinterpret_cast(dst_ptrs_vector.data()); int64_t dst_offset = std::max(int64_t(0), -ts); diff --git a/app/codec/ffmpeg/ffmpegdecoder.h b/app/codec/ffmpeg/ffmpegdecoder.h index f7b50e9fcd..2676e7e660 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.h +++ b/app/codec/ffmpeg/ffmpegdecoder.h @@ -182,10 +182,10 @@ class FFmpegDecoder : public Decoder SwrContext *swr_ctx_; int swr_irate_; int64_t swr_ichannels_; - AudioParams::Format swr_iformat_; + SampleFormat swr_iformat_; int swr_orate_; int64_t swr_ochannels_; - AudioParams::Format swr_oformat_; + SampleFormat swr_oformat_; int64_t swr_time_; AVPacket *working_packet_; From 51e54d3fbf1d1df42ee5886820ed9217d5f0242e Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 26 Mar 2023 11:34:41 -0700 Subject: [PATCH 04/71] initial traverser removal --- app/node/CMakeLists.txt | 2 - app/node/audio/pan/pan.cpp | 13 +- app/node/audio/pan/pan.h | 2 +- app/node/audio/volume/volume.cpp | 16 +- app/node/audio/volume/volume.h | 2 +- app/node/block/clip/clip.cpp | 12 +- app/node/block/clip/clip.h | 2 +- .../diptocolor/diptocolortransition.cpp | 4 +- .../diptocolor/diptocolortransition.h | 2 +- app/node/block/transition/transition.cpp | 31 +- app/node/block/transition/transition.h | 4 +- app/node/color/ociobase/ociobase.cpp | 8 +- app/node/color/ociobase/ociobase.h | 2 +- .../ociogradingtransformlinear.cpp | 24 +- .../ociogradingtransformlinear.h | 2 +- .../cornerpin/cornerpindistortnode.cpp | 52 +- .../distort/cornerpin/cornerpindistortnode.h | 6 +- app/node/distort/crop/cropdistortnode.cpp | 26 +- app/node/distort/crop/cropdistortnode.h | 4 +- app/node/distort/flip/flipdistortnode.cpp | 15 +- app/node/distort/flip/flipdistortnode.h | 2 +- app/node/distort/mask/mask.cpp | 22 +- app/node/distort/mask/mask.h | 2 +- app/node/distort/ripple/rippledistortnode.cpp | 32 +- app/node/distort/ripple/rippledistortnode.h | 4 +- app/node/distort/swirl/swirldistortnode.cpp | 23 +- app/node/distort/swirl/swirldistortnode.h | 4 +- app/node/distort/tile/tiledistortnode.cpp | 25 +- app/node/distort/tile/tiledistortnode.h | 4 +- .../transform/transformdistortnode.cpp | 63 +-- .../distort/transform/transformdistortnode.h | 10 +- app/node/distort/wave/wavedistortnode.cpp | 14 +- app/node/distort/wave/wavedistortnode.h | 2 +- app/node/effect/opacity/opacityeffect.cpp | 22 +- app/node/effect/opacity/opacityeffect.h | 2 +- app/node/filter/blur/blur.cpp | 33 +- app/node/filter/blur/blur.h | 4 +- .../filter/dropshadow/dropshadowfilter.cpp | 16 +- app/node/filter/dropshadow/dropshadowfilter.h | 2 +- app/node/filter/mosaic/mosaicfilternode.cpp | 18 +- app/node/filter/mosaic/mosaicfilternode.h | 2 +- app/node/filter/stroke/stroke.cpp | 18 +- app/node/filter/stroke/stroke.h | 2 +- app/node/generator/matrix/matrix.cpp | 18 +- app/node/generator/matrix/matrix.h | 4 +- app/node/generator/noise/noise.cpp | 11 +- app/node/generator/noise/noise.h | 2 +- app/node/generator/polygon/polygon.cpp | 22 +- app/node/generator/polygon/polygon.h | 6 +- .../generator/shape/generatorwithmerge.cpp | 12 +- app/node/generator/shape/generatorwithmerge.h | 2 +- app/node/generator/shape/shapenode.cpp | 10 +- app/node/generator/shape/shapenode.h | 2 +- app/node/generator/shape/shapenodebase.cpp | 8 +- app/node/generator/shape/shapenodebase.h | 2 +- app/node/generator/solid/solid.cpp | 4 +- app/node/generator/solid/solid.h | 2 +- app/node/generator/text/textv1.cpp | 8 +- app/node/generator/text/textv1.h | 2 +- app/node/generator/text/textv2.cpp | 12 +- app/node/generator/text/textv2.h | 2 +- app/node/generator/text/textv3.cpp | 28 +- app/node/generator/text/textv3.h | 4 +- app/node/gizmo/draggable.cpp | 4 +- app/node/gizmo/draggable.h | 4 +- app/node/gizmo/gizmo.h | 6 +- app/node/globals.cpp | 7 + app/node/globals.h | 22 +- app/node/input/multicam/multicamnode.cpp | 59 +-- app/node/input/multicam/multicamnode.h | 11 +- app/node/input/time/timeinput.cpp | 12 +- app/node/input/time/timeinput.h | 2 +- app/node/input/value/valuenode.cpp | 6 +- app/node/input/value/valuenode.h | 2 +- app/node/keying/chromakey/chromakey.cpp | 14 +- app/node/keying/chromakey/chromakey.h | 2 +- .../colordifferencekey/colordifferencekey.cpp | 13 +- .../colordifferencekey/colordifferencekey.h | 2 +- app/node/keying/despill/despill.cpp | 29 +- app/node/keying/despill/despill.h | 2 +- app/node/math/math/math.cpp | 25 +- app/node/math/math/math.h | 2 +- app/node/math/math/mathbase.cpp | 61 +-- app/node/math/math/mathbase.h | 4 +- app/node/math/merge/merge.cpp | 18 +- app/node/math/merge/merge.h | 2 +- app/node/math/trigonometry/trigonometry.cpp | 6 +- app/node/math/trigonometry/trigonometry.h | 2 +- app/node/node.cpp | 60 ++- app/node/node.h | 123 ++--- app/node/output/track/track.cpp | 98 ++-- app/node/output/track/track.h | 7 +- app/node/output/viewer/viewer.cpp | 31 +- app/node/output/viewer/viewer.h | 2 +- app/node/project/footage/footage.cpp | 23 +- app/node/project/footage/footage.h | 2 +- app/node/time/timeformat/timeformat.cpp | 10 +- app/node/time/timeformat/timeformat.h | 2 +- app/node/time/timeoffset/timeoffsetnode.cpp | 4 +- app/node/time/timeoffset/timeoffsetnode.h | 2 +- app/node/time/timeremap/timeremap.cpp | 4 +- app/node/time/timeremap/timeremap.h | 2 +- app/node/traverser.cpp | 479 ------------------ app/node/traverser.h | 169 ------ app/panel/CMakeLists.txt | 1 - app/panel/table/CMakeLists.txt | 22 - app/panel/table/table.cpp | 38 -- app/panel/table/table.h | 53 -- app/render/rendermanager.h | 1 - app/render/renderprocessor.cpp | 177 +++++-- app/render/renderprocessor.h | 60 ++- app/widget/CMakeLists.txt | 1 - app/widget/nodetableview/CMakeLists.txt | 24 - app/widget/nodetableview/nodetableview.cpp | 165 ------ app/widget/nodetableview/nodetableview.h | 51 -- app/widget/nodetableview/nodetablewidget.cpp | 38 -- app/widget/nodetableview/nodetablewidget.h | 57 --- app/widget/nodevaluetree/nodevaluetree.cpp | 9 +- app/widget/nodeview/nodeview.cpp | 1 - app/widget/viewer/viewerdisplay.cpp | 61 ++- app/widget/viewer/viewerdisplay.h | 11 +- app/window/mainwindow/mainwindow.h | 1 - 122 files changed, 928 insertions(+), 1894 deletions(-) delete mode 100644 app/node/traverser.cpp delete mode 100644 app/node/traverser.h delete mode 100644 app/panel/table/CMakeLists.txt delete mode 100644 app/panel/table/table.cpp delete mode 100644 app/panel/table/table.h delete mode 100644 app/widget/nodetableview/CMakeLists.txt delete mode 100644 app/widget/nodetableview/nodetableview.cpp delete mode 100644 app/widget/nodetableview/nodetableview.h delete mode 100644 app/widget/nodetableview/nodetablewidget.cpp delete mode 100644 app/widget/nodetableview/nodetablewidget.h diff --git a/app/node/CMakeLists.txt b/app/node/CMakeLists.txt index ef1978c21d..e9eea1b68b 100644 --- a/app/node/CMakeLists.txt +++ b/app/node/CMakeLists.txt @@ -51,8 +51,6 @@ set(OLIVE_SOURCES node/project.cpp node/project.h node/splitvalue.h - node/traverser.cpp - node/traverser.h node/value.cpp node/value.h node/valuedatabase.cpp diff --git a/app/node/audio/pan/pan.cpp b/app/node/audio/pan/pan.cpp index 5f4ecebf1d..572057e646 100644 --- a/app/node/audio/pan/pan.cpp +++ b/app/node/audio/pan/pan.cpp @@ -62,16 +62,17 @@ QString PanNode::Description() const return tr("Adjust the stereo panning of an audio source."); } -void PanNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue PanNode::Value(const ValueParams &p) const { // Create a sample job - SampleBuffer samples = value[kSamplesInput].toSamples(); + NodeValue samples_original = GetInputValue(p, kSamplesInput); + SampleBuffer samples = samples_original.toSamples(); if (samples.is_allocated()) { // This node is only compatible with stereo audio 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 = value[kPanningInput].toDouble(); + float pan_volume = GetInputValue(p, kPanningInput).toDouble(); if (!qIsNull(pan_volume)) { if (pan_volume > 0) { samples.transform_volume_for_channel(0, 1.0f - pan_volume); @@ -80,14 +81,14 @@ void PanNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeV } } - table->Push(NodeValue(NodeValue::kSamples, samples, this)); + return NodeValue(NodeValue::kSamples, samples, this); } else { // Requires job - table->Push(NodeValue::kSamples, SampleJob(globals.time(), kSamplesInput, value), this); + return NodeValue(NodeValue::kSamples, CreateSampleJob(p, kSamplesInput), this); } } else { // Pass right through - table->Push(value[kSamplesInput]); + return samples_original; } } } diff --git a/app/node/audio/pan/pan.h b/app/node/audio/pan/pan.h index bc1be92f9b..183e17915e 100644 --- a/app/node/audio/pan/pan.h +++ b/app/node/audio/pan/pan.h @@ -38,7 +38,7 @@ class PanNode : public Node virtual QVector Category() const override; virtual QString Description() const override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; virtual void ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const override; diff --git a/app/node/audio/volume/volume.cpp b/app/node/audio/volume/volume.cpp index 229295d98e..baaed6d367 100644 --- a/app/node/audio/volume/volume.cpp +++ b/app/node/audio/volume/volume.cpp @@ -61,28 +61,30 @@ QString VolumeNode::Description() const return tr("Adjusts the volume of an audio source."); } -void VolumeNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue VolumeNode::Value(const ValueParams &p) const { // Create a sample job - SampleBuffer buffer = value[kSamplesInput].toSamples(); + SampleBuffer buffer = GetInputValue(p, 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(); + auto volume = GetInputValue(p, kVolumeInput).toDouble(); if (!qFuzzyCompare(volume, 1.0)) { buffer.transform_volume(volume); } - table->Push(NodeValue::kSamples, QVariant::fromValue(buffer), this); + return NodeValue(NodeValue::kSamples, QVariant::fromValue(buffer), this); } else { // Requires job - SampleJob job(globals.time(), kSamplesInput, value); - job.Insert(kVolumeInput, value); - table->Push(NodeValue::kSamples, QVariant::fromValue(job), this); + SampleJob job = CreateSampleJob(p, kSamplesInput); + job.Insert(kVolumeInput, GetInputValue(p, kVolumeInput)); + return NodeValue(NodeValue::kSamples, QVariant::fromValue(job), this); } } + + return NodeValue(); } void VolumeNode::ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const diff --git a/app/node/audio/volume/volume.h b/app/node/audio/volume/volume.h index ba22598214..97e249809e 100644 --- a/app/node/audio/volume/volume.h +++ b/app/node/audio/volume/volume.h @@ -38,7 +38,7 @@ class VolumeNode : public MathNodeBase virtual QVector Category() const override; virtual QString Description() const override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; virtual void ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const override; diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index d30ec6ac2a..48e59be39e 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -484,17 +484,9 @@ TimeRange ClipBlock::OutputTimeAdjustment(const QString& input, int element, con return super::OutputTimeAdjustment(input, element, input_time); } -void ClipBlock::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue ClipBlock::Value(const ValueParams &p) const { - Q_UNUSED(globals) - - // We discard most values here except for the buffer we received - NodeValue data = value[kBufferIn]; - - table->Clear(); - if (data.type() != NodeValue::kNone) { - table->Push(data); - } + return GetInputValue(p, kBufferIn); } void ClipBlock::Retranslate() diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index 19987c7fb7..d766aca4cf 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -72,7 +72,7 @@ class ClipBlock : public Block virtual TimeRange OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; virtual void Retranslate() override; diff --git a/app/node/block/transition/diptocolor/diptocolortransition.cpp b/app/node/block/transition/diptocolor/diptocolortransition.cpp index 42d04dd949..caa0165e70 100644 --- a/app/node/block/transition/diptocolor/diptocolortransition.cpp +++ b/app/node/block/transition/diptocolor/diptocolortransition.cpp @@ -65,9 +65,9 @@ void DipToColorTransition::Retranslate() SetInputName(kColorInput, tr("Color")); } -void DipToColorTransition::ShaderJobEvent(const NodeValueRow &value, ShaderJob *job) const +void DipToColorTransition::ShaderJobEvent(const ValueParams &p, ShaderJob *job) const { - job->Insert(kColorInput, value); + job->Insert(kColorInput, GetInputValue(p, kColorInput)); } } diff --git a/app/node/block/transition/diptocolor/diptocolortransition.h b/app/node/block/transition/diptocolor/diptocolortransition.h index 8c84134ebd..5e0e6f438c 100644 --- a/app/node/block/transition/diptocolor/diptocolortransition.h +++ b/app/node/block/transition/diptocolor/diptocolortransition.h @@ -45,7 +45,7 @@ class DipToColorTransition : public TransitionBlock static const QString kColorInput; protected: - virtual void ShaderJobEvent(const NodeValueRow &value, ShaderJob *job) const override; + virtual void ShaderJobEvent(const ValueParams &p, ShaderJob *job) const override; }; diff --git a/app/node/block/transition/transition.cpp b/app/node/block/transition/transition.cpp index 9209a14001..ea8bce6189 100644 --- a/app/node/block/transition/transition.cpp +++ b/app/node/block/transition/transition.cpp @@ -157,10 +157,20 @@ void TransitionBlock::InsertTransitionTimes(AcceleratedJob *job, const double &t NodeValue(NodeValue::kFloat, GetInProgress(time), this)); } -void TransitionBlock::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue TransitionBlock::Value(const ValueParams &p) const { - NodeValue out_buffer = value[kOutBlockInput]; - NodeValue in_buffer = value[kInBlockInput]; + NodeValue out_buffer, in_buffer; + + if (Block* block = dynamic_cast(GetConnectedOutput(kOutBlockInput))) { + // Retransform time as if it came from the track + out_buffer = GetInputValue(p, kOutBlockInput); + } + + if (Block* block = dynamic_cast(GetConnectedOutput(kInBlockInput))) { + // Retransform time as if it came from the track + in_buffer = GetInputValue(p, kInBlockInput); + } + NodeValue::Type data_type = (out_buffer.type() != NodeValue::kNone) ? out_buffer.type() : in_buffer.type(); NodeValue::Type job_type = NodeValue::kNone; @@ -182,23 +192,23 @@ void TransitionBlock::Value(const NodeValueRow &value, const NodeGlobals &global job.Insert(kInBlockInput, NodeValue(NodeValue::kTexture, nullptr)); } - job.Insert(kCurveInput, value); + job.Insert(kCurveInput, GetInputValue(p, kCurveInput)); - double time = globals.time().in().toDouble(); + double time = p.time().in().toDouble(); InsertTransitionTimes(&job, time); - ShaderJobEvent(value, &job); + ShaderJobEvent(p, &job); job_type = NodeValue::kTexture; - push_job = QVariant::fromValue(Texture::Job(globals.vparams(), job)); + push_job = QVariant::fromValue(Texture::Job(p.vparams(), job)); } else if (data_type == NodeValue::kSamples) { // This must be an audio transition SampleBuffer from_samples = out_buffer.toSamples(); SampleBuffer to_samples = in_buffer.toSamples(); if (from_samples.is_allocated() || to_samples.is_allocated()) { - double time_in = globals.time().in().toDouble(); - double time_out = globals.time().out().toDouble(); + double time_in = p.time().in().toDouble(); + double time_out = p.time().out().toDouble(); const AudioParams& params = (from_samples.is_allocated()) ? from_samples.audio_params() : to_samples.audio_params(); @@ -217,8 +227,9 @@ void TransitionBlock::Value(const NodeValueRow &value, const NodeGlobals &global } if (!push_job.isNull()) { - table->Push(job_type, push_job, this); + return NodeValue(job_type, push_job, this); } + return NodeValue(); } void TransitionBlock::InvalidateCache(const TimeRange &range, const QString &from, int element, InvalidateCacheOptions options) diff --git a/app/node/block/transition/transition.h b/app/node/block/transition/transition.h index d16a195bdc..14b140e277 100644 --- a/app/node/block/transition/transition.h +++ b/app/node/block/transition/transition.h @@ -63,7 +63,7 @@ class TransitionBlock : public Block double GetOutProgress(const double &time) const; double GetInProgress(const double &time) const; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &globals) const override; virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1, InvalidateCacheOptions options = InvalidateCacheOptions()) override; @@ -73,7 +73,7 @@ class TransitionBlock : public Block static const QString kCenterInput; protected: - virtual void ShaderJobEvent(const NodeValueRow &value, ShaderJob *job) const {} + virtual void ShaderJobEvent(const ValueParams &p, ShaderJob *job) const {} virtual void SampleJobEvent(const SampleBuffer &from_samples, const SampleBuffer &to_samples, SampleBuffer &out_samples, double time_in) const {} diff --git a/app/node/color/ociobase/ociobase.cpp b/app/node/color/ociobase/ociobase.cpp index 015f1db979..343fcfd35d 100644 --- a/app/node/color/ociobase/ociobase.cpp +++ b/app/node/color/ociobase/ociobase.cpp @@ -53,9 +53,9 @@ void OCIOBaseNode::RemovedFromGraphEvent(Project *p) } } -void OCIOBaseNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue OCIOBaseNode::Value(const ValueParams &p) const { - auto tex_met = value[kTextureInput]; + auto tex_met = GetInputValue(p, kTextureInput); TexturePtr t = tex_met.toTexture(); if (t && processor_) { ColorTransformJob job; @@ -63,8 +63,10 @@ void OCIOBaseNode::Value(const NodeValueRow &value, const NodeGlobals &globals, job.SetColorProcessor(processor_); job.SetInputTexture(tex_met); - table->Push(NodeValue::kTexture, t->toJob(job), this); + return NodeValue(NodeValue::kTexture, t->toJob(job), this); } + + return tex_met; } } diff --git a/app/node/color/ociobase/ociobase.h b/app/node/color/ociobase/ociobase.h index c476a69fef..ad79f46c4d 100644 --- a/app/node/color/ociobase/ociobase.h +++ b/app/node/color/ociobase/ociobase.h @@ -35,7 +35,7 @@ class OCIOBaseNode : public Node virtual void AddedToGraphEvent(Project *p) override; virtual void RemovedFromGraphEvent(Project *p) override; - virtual void Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; static const QString kTextureInput; diff --git a/app/node/color/ociogradingtransformlinear/ociogradingtransformlinear.cpp b/app/node/color/ociogradingtransformlinear/ociogradingtransformlinear.cpp index 6b88ab7ce1..3778953e94 100644 --- a/app/node/color/ociogradingtransformlinear/ociogradingtransformlinear.cpp +++ b/app/node/color/ociogradingtransformlinear/ociogradingtransformlinear.cpp @@ -153,14 +153,16 @@ void OCIOGradingTransformLinearNode::GenerateProcessor() } } -void OCIOGradingTransformLinearNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue OCIOGradingTransformLinearNode::Value(const ValueParams &p) const { - if (TexturePtr tex = value[kTextureInput].toTexture()) { + NodeValue tex_meta = GetInputValue(p, kTextureInput); + + if (TexturePtr tex = tex_meta.toTexture()) { if (processor()) { - ColorTransformJob job(value); + ColorTransformJob job = CreateJob(p); job.SetColorProcessor(processor()); - job.SetInputTexture(value[kTextureInput]); + job.SetInputTexture(tex_meta); const int MASTER_CHANNEL = 0; const int RED_CHANNEL = 1; @@ -171,35 +173,37 @@ void OCIOGradingTransformLinearNode::Value(const NodeValueRow &value, const Node // 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(); + QVector4D offset = GetInputValue(p, 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(); + QVector4D exposure = GetInputValue(p, 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(); + QVector4D contrast = GetInputValue(p, 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()) { + if (!GetInputValue(p, kClampBlackEnableInput).toBool()) { job.Insert(kClampBlackInput, NodeValue(NodeValue::kFloat, OCIO::GradingPrimary::NoClampBlack())); } - if (!value[kClampWhiteEnableInput].toBool()) { + if (!GetInputValue(p, kClampWhiteEnableInput).toBool()) { job.Insert(kClampWhiteInput, NodeValue(NodeValue::kFloat, OCIO::GradingPrimary::NoClampWhite())); } - table->Push(NodeValue::kTexture, tex->toJob(job), this); + return NodeValue(NodeValue::kTexture, tex->toJob(job), this); } } + + return tex_meta; } void OCIOGradingTransformLinearNode::ConfigChanged() diff --git a/app/node/color/ociogradingtransformlinear/ociogradingtransformlinear.h b/app/node/color/ociogradingtransformlinear/ociogradingtransformlinear.h index 72a7692088..4e0eaa113a 100644 --- a/app/node/color/ociogradingtransformlinear/ociogradingtransformlinear.h +++ b/app/node/color/ociogradingtransformlinear/ociogradingtransformlinear.h @@ -43,7 +43,7 @@ class OCIOGradingTransformLinearNode : public OCIOBaseNode virtual void InputValueChangedEvent(const QString &input, int element) override; void GenerateProcessor(); - virtual void Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; static const QString kContrastInput; static const QString kOffsetInput; diff --git a/app/node/distort/cornerpin/cornerpindistortnode.cpp b/app/node/distort/cornerpin/cornerpindistortnode.cpp index 3f37e39947..68cb0f3c7d 100644 --- a/app/node/distort/cornerpin/cornerpindistortnode.cpp +++ b/app/node/distort/cornerpin/cornerpindistortnode.cpp @@ -67,27 +67,29 @@ void CornerPinDistortNode::Retranslate() SetInputName(kBottomLeftInput, tr("Bottom Left")); } -void CornerPinDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue CornerPinDistortNode::Value(const ValueParams &p) const { // If no texture do nothing - if (TexturePtr tex = value[kTextureInput].toTexture()) { + NodeValue tex_meta = GetInputValue(p, kTextureInput); + + if (TexturePtr tex = tex_meta.toTexture()) { // In the special case that all sliders are in their default position just // push the texture. - if (!(value[kTopLeftInput].toVec2().isNull() - && value[kTopRightInput].toVec2().isNull() && - value[kBottomRightInput].toVec2().isNull() && - value[kBottomLeftInput].toVec2().isNull())) { - ShaderJob job(value); + if (!(GetInputValue(p, kTopLeftInput).toVec2().isNull() + && GetInputValue(p, kTopRightInput).toVec2().isNull() && + GetInputValue(p, kBottomRightInput).toVec2().isNull() && + GetInputValue(p, kBottomLeftInput).toVec2().isNull())) { + ShaderJob job = CreateJob(p); job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, tex->virtual_resolution(), this)); // Convert slider values to their pixel values and then convert to clip space (-1.0 ... 1.0) for overriding the // vertex coordinates. const QVector2D &resolution = tex->virtual_resolution(); QVector2D half_resolution = resolution * 0.5; - QVector2D top_left = QVector2D(ValueToPixel(0, value, resolution)) / half_resolution - QVector2D(1.0, 1.0); - QVector2D top_right = QVector2D(ValueToPixel(1, value, resolution)) / half_resolution - QVector2D(1.0, 1.0); - QVector2D bottom_right = QVector2D(ValueToPixel(2, value, resolution)) / half_resolution - QVector2D(1.0, 1.0); - QVector2D bottom_left = QVector2D(ValueToPixel(3, value, resolution)) / half_resolution - QVector2D(1.0, 1.0); + QVector2D top_left = QVector2D(ValueToPixel(0, p, resolution)) / half_resolution - QVector2D(1.0, 1.0); + QVector2D top_right = QVector2D(ValueToPixel(1, p, resolution)) / half_resolution - QVector2D(1.0, 1.0); + QVector2D bottom_right = QVector2D(ValueToPixel(2, p, resolution)) / half_resolution - QVector2D(1.0, 1.0); + QVector2D bottom_left = QVector2D(ValueToPixel(3, p, resolution)) / half_resolution - QVector2D(1.0, 1.0); // Override default vertex coordinates. QVector adjusted_vertices = {top_left.x(), top_left.y(), 0.0f, @@ -99,11 +101,11 @@ void CornerPinDistortNode::Value(const NodeValueRow &value, const NodeGlobals &g bottom_right.x(), bottom_right.y(), 0.0f}; job.SetVertexCoordinates(adjusted_vertices); - table->Push(NodeValue::kTexture, tex->toJob(job), this); - } else { - table->Push(value[kTextureInput]); + return NodeValue(NodeValue::kTexture, tex->toJob(job), this); } } + + return tex_meta; } ShaderCode CornerPinDistortNode::GetShaderCode(const ShaderRequest &request) const @@ -114,7 +116,7 @@ ShaderCode CornerPinDistortNode::GetShaderCode(const ShaderRequest &request) con FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/cornerpin.vert"))); } -QPointF CornerPinDistortNode::ValueToPixel(int value, const NodeValueRow& row, const QVector2D &resolution) const +QPointF CornerPinDistortNode::ValueToPixel(int value, const ValueParams& p, const QVector2D &resolution) const { Q_ASSERT(value >= 0 && value <= 3); @@ -122,16 +124,16 @@ QPointF CornerPinDistortNode::ValueToPixel(int value, const NodeValueRow& row, c switch (value) { case 0: // Top left - v = row[kTopLeftInput].toVec2(); + v = GetInputValue(p, kTopLeftInput).toVec2(); return QPointF(v.x(), v.y()); case 1: // Top right - v = row[kTopRightInput].toVec2(); + v = GetInputValue(p, kTopRightInput).toVec2(); return QPointF(resolution.x() + v.x(), v.y()); case 2: // Bottom right - v = row[kBottomRightInput].toVec2(); + v = GetInputValue(p, kBottomRightInput).toVec2(); return QPointF(resolution.x() + v.x(), resolution.y() + v.y()); case 3: //Bottom left - v = row[kBottomLeftInput].toVec2(); + v = GetInputValue(p, kBottomLeftInput).toVec2(); return QPointF(v.x(), v.y() + resolution.y()); default: // We should never get here return QPointF(); @@ -148,15 +150,15 @@ void CornerPinDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardM } } -void CornerPinDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) +void CornerPinDistortNode::UpdateGizmoPositions(const ValueParams &p) { - if (TexturePtr tex = row[kTextureInput].toTexture()) { + if (TexturePtr tex = GetInputValue(p, kTextureInput).toTexture()) { const QVector2D &resolution = tex->virtual_resolution(); - QPointF top_left = ValueToPixel(0, row, resolution); - QPointF top_right = ValueToPixel(1, row, resolution); - QPointF bottom_right = ValueToPixel(2, row, resolution); - QPointF bottom_left = ValueToPixel(3, row, resolution); + QPointF top_left = ValueToPixel(0, p, resolution); + QPointF top_right = ValueToPixel(1, p, resolution); + QPointF bottom_right = ValueToPixel(2, p, resolution); + QPointF bottom_left = ValueToPixel(3, p, resolution); // Add the correct offset to each slider SetInputProperty(kTopLeftInput, QStringLiteral("offset"), QVector2D(0.0, 0.0)); diff --git a/app/node/distort/cornerpin/cornerpindistortnode.h b/app/node/distort/cornerpin/cornerpindistortnode.h index 39dda53d88..01387b9fb9 100644 --- a/app/node/distort/cornerpin/cornerpindistortnode.h +++ b/app/node/distort/cornerpin/cornerpindistortnode.h @@ -59,17 +59,17 @@ class CornerPinDistortNode : public Node virtual void Retranslate() override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override; + virtual void UpdateGizmoPositions(const ValueParams &p) override; /** * @brief Convenience function - converts the 2D slider values from being * an offset to the actual pixel value. */ - QPointF ValueToPixel(int value, const NodeValueRow &row, const QVector2D &resolution) const; + QPointF ValueToPixel(int value, const ValueParams &p, const QVector2D &resolution) const; static const QString kTextureInput; static const QString kPerspectiveInput; diff --git a/app/node/distort/crop/cropdistortnode.cpp b/app/node/distort/crop/cropdistortnode.cpp index da278f8800..ce3ff5e50c 100644 --- a/app/node/distort/crop/cropdistortnode.cpp +++ b/app/node/distort/crop/cropdistortnode.cpp @@ -75,23 +75,23 @@ void CropDistortNode::Retranslate() SetInputName(kFeatherInput, tr("Feather")); } -void CropDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue CropDistortNode::Value(const ValueParams &p) const { - ShaderJob job; - job.Insert(value); + NodeValue tex_meta = GetInputValue(p, kTextureInput); - if (TexturePtr texture = job.Get(kTextureInput).toTexture()) { + if (TexturePtr texture = tex_meta.toTexture()) { + ShaderJob job = CreateJob(p); job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, QVector2D(texture->params().width(), texture->params().height()), this)); 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, texture->toJob(job), this); - } else { - table->Push(job.Get(kTextureInput)); + return NodeValue(NodeValue::kTexture, texture->toJob(job), this); } } + + return tex_meta; } ShaderCode CropDistortNode::GetShaderCode(const ShaderRequest &request) const @@ -100,16 +100,16 @@ ShaderCode CropDistortNode::GetShaderCode(const ShaderRequest &request) const return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/crop.frag"))); } -void CropDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) +void CropDistortNode::UpdateGizmoPositions(const ValueParams &p) { - if (TexturePtr tex = row[kTextureInput].toTexture()) { + if (TexturePtr tex = GetInputValue(p, kTextureInput).toTexture()) { const QVector2D &resolution = tex->virtual_resolution(); temp_resolution_ = resolution; - 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 left_pt = resolution.x() * GetInputValue(p, kLeftInput).toDouble(); + double top_pt = resolution.y() * GetInputValue(p, kTopInput).toDouble(); + double right_pt = resolution.x() * (1.0 - GetInputValue(p, kRightInput).toDouble()); + double bottom_pt = resolution.y() * (1.0 - GetInputValue(p, kBottomInput).toDouble()); double center_x_pt = mid(left_pt, right_pt); double center_y_pt = mid(top_pt, bottom_pt); diff --git a/app/node/distort/crop/cropdistortnode.h b/app/node/distort/crop/cropdistortnode.h index ebbab1946e..de0b425f92 100644 --- a/app/node/distort/crop/cropdistortnode.h +++ b/app/node/distort/crop/cropdistortnode.h @@ -60,11 +60,11 @@ class CropDistortNode : public Node virtual void Retranslate() override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override; + virtual void UpdateGizmoPositions(const ValueParams &p) override; static const QString kTextureInput; static const QString kLeftInput; diff --git a/app/node/distort/flip/flipdistortnode.cpp b/app/node/distort/flip/flipdistortnode.cpp index 1d8e360347..1818ef53dc 100644 --- a/app/node/distort/flip/flipdistortnode.cpp +++ b/app/node/distort/flip/flipdistortnode.cpp @@ -75,18 +75,19 @@ ShaderCode FlipDistortNode::GetShaderCode(const ShaderRequest &request) const return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/flip.frag")); } -void FlipDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue FlipDistortNode::Value(const ValueParams &p) const { // If there's no texture, no need to run an operation - if (TexturePtr tex = value[kTextureInput].toTexture()) { + NodeValue v = GetInputValue(p, kTextureInput); + + if (TexturePtr tex = v.toTexture()) { // Only run shader if at least one of flip or flop are selected - if (value[kHorizontalInput].toBool() || value[kVerticalInput].toBool()) { - table->Push(NodeValue::kTexture, tex->toJob(ShaderJob(value)), this); - } else { - // If we're not flipping or flopping just push the texture - table->Push(value[kTextureInput]); + if (GetInputValue(p, kHorizontalInput).toBool() || GetInputValue(p, kVerticalInput).toBool()) { + return NodeValue(NodeValue::kTexture, tex->toJob(CreateJob(p)), this); } } + + return v; } } diff --git a/app/node/distort/flip/flipdistortnode.h b/app/node/distort/flip/flipdistortnode.h index 1c9dcf727c..25d1d17465 100644 --- a/app/node/distort/flip/flipdistortnode.h +++ b/app/node/distort/flip/flipdistortnode.h @@ -41,7 +41,7 @@ class FlipDistortNode : public Node virtual void Retranslate() override; virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &globals) const override; static const QString kTextureInput; static const QString kHorizontalInput; diff --git a/app/node/distort/mask/mask.cpp b/app/node/distort/mask/mask.cpp index 71e789e4e9..e0b43443a5 100644 --- a/app/node/distort/mask/mask.cpp +++ b/app/node/distort/mask/mask.cpp @@ -62,14 +62,14 @@ void MaskDistortNode::Retranslate() SetInputName(kFeatherInput, tr("Feather")); } -void MaskDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue MaskDistortNode::Value(const ValueParams &p) const { - TexturePtr texture = value[kBaseInput].toTexture(); + TexturePtr texture = GetInputValue(p, kBaseInput).toTexture(); - VideoParams job_params = texture ? texture->params() : globals.vparams(); - NodeValue job(NodeValue::kTexture, Texture::Job(job_params, GetGenerateJob(value, job_params)), this); + VideoParams job_params = texture ? texture->params() : p.vparams(); + NodeValue job(NodeValue::kTexture, Texture::Job(job_params, GetGenerateJob(p, job_params)), this); - if (value[kInvertInput].toBool()) { + if (GetInputValue(p, kInvertInput).toBool()) { ShaderJob invert; invert.SetShaderID(QStringLiteral("invert")); invert.Insert(QStringLiteral("tex_in"), job); @@ -81,9 +81,9 @@ void MaskDistortNode::Value(const NodeValueRow &value, const NodeGlobals &global ShaderJob merge; merge.SetShaderID(QStringLiteral("mrg")); - merge.Insert(QStringLiteral("tex_a"), value[kBaseInput]); + merge.Insert(QStringLiteral("tex_a"), GetInputValue(p, kBaseInput)); - if (value[kFeatherInput].toDouble() > 0.0) { + if (GetInputValue(p, kFeatherInput).toDouble() > 0.0) { // Nest a blur shader in there too ShaderJob feather; @@ -93,18 +93,18 @@ void MaskDistortNode::Value(const NodeValueRow &value, const NodeGlobals &global 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.Insert(BlurFilterNode::kRadiusInput, NodeValue(NodeValue::kFloat, GetInputValue(p, kFeatherInput).toDouble(), this)); feather.SetIterations(2, BlurFilterNode::kTextureInput); - feather.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, texture ? texture->virtual_resolution() : globals.square_resolution(), this)); + feather.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, texture ? texture->virtual_resolution() : p.square_resolution(), this)); merge.Insert(QStringLiteral("tex_b"), NodeValue(NodeValue::kTexture, Texture::Job(job_params, feather), this)); } else { merge.Insert(QStringLiteral("tex_b"), job); } - table->Push(NodeValue::kTexture, Texture::Job(job_params, merge), this); + return NodeValue(NodeValue::kTexture, Texture::Job(job_params, merge), this); } else { - table->Push(job); + return job; } } diff --git a/app/node/distort/mask/mask.h b/app/node/distort/mask/mask.h index e5e44c2c99..6fb4a150a8 100644 --- a/app/node/distort/mask/mask.h +++ b/app/node/distort/mask/mask.h @@ -57,7 +57,7 @@ class MaskDistortNode : public PolygonGenerator virtual void Retranslate() override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; static const QString kInvertInput; static const QString kFeatherInput; diff --git a/app/node/distort/ripple/rippledistortnode.cpp b/app/node/distort/ripple/rippledistortnode.cpp index 2e8a1f1b49..dfccc07b2e 100644 --- a/app/node/distort/ripple/rippledistortnode.cpp +++ b/app/node/distort/ripple/rippledistortnode.cpp @@ -92,27 +92,41 @@ ShaderCode RippleDistortNode::GetShaderCode(const ShaderRequest &request) const return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/ripple.frag")); } -void RippleDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue RippleDistortNode::Value(const ValueParams &p) const { // If there's no texture, no need to run an operation - if (TexturePtr tex = value[kTextureInput].toTexture()) { + NodeValue texture = GetInputValue(p, kTextureInput); + + if (TexturePtr tex = texture.toTexture()) { // Only run shader if at least one of flip or flop are selected - if (!qIsNull(value[kIntensityInput].toDouble())) { - ShaderJob job(value); + NodeValue intensity = GetInputValue(p, kIntensityInput); + + if (!qIsNull(intensity.toDouble())) { + ShaderJob job; + + job.Insert(kTextureInput, texture); + job.Insert(kEvolutionInput, GetInputValue(p, kEvolutionInput)); + job.Insert(kIntensityInput, intensity); + job.Insert(kFrequencyInput, GetInputValue(p, kFrequencyInput)); + job.Insert(kPositionInput, GetInputValue(p, kPositionInput)); + job.Insert(kStretchInput, GetInputValue(p, kStretchInput)); job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, tex->virtual_resolution(), this)); - table->Push(NodeValue::kTexture, tex->toJob(job), this); + + return NodeValue(NodeValue::kTexture, tex->toJob(job), this); } else { // If we're not flipping or flopping just push the texture - table->Push(value[kTextureInput]); + return texture; } } + + return NodeValue(); } -void RippleDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) +void RippleDistortNode::UpdateGizmoPositions(const ValueParams &p) { - if (TexturePtr tex = row[kTextureInput].toTexture()) { + if (TexturePtr tex = GetInputValue(p, kTextureInput).toTexture()) { QPointF half_res(tex->virtual_resolution().x()/2, tex->virtual_resolution().y()/2); - gizmo_->SetPoint(half_res + row[kPositionInput].toVec2().toPointF()); + gizmo_->SetPoint(half_res + GetInputValue(p, kPositionInput).toVec2().toPointF()); } } diff --git a/app/node/distort/ripple/rippledistortnode.h b/app/node/distort/ripple/rippledistortnode.h index 9f05e06a89..a2d9ff357a 100644 --- a/app/node/distort/ripple/rippledistortnode.h +++ b/app/node/distort/ripple/rippledistortnode.h @@ -42,9 +42,9 @@ class RippleDistortNode : public Node virtual void Retranslate() override; virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; - virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override; + virtual void UpdateGizmoPositions(const ValueParams &p) override; static const QString kTextureInput; static const QString kEvolutionInput; diff --git a/app/node/distort/swirl/swirldistortnode.cpp b/app/node/distort/swirl/swirldistortnode.cpp index eb91bc27a7..c232bc8042 100644 --- a/app/node/distort/swirl/swirldistortnode.cpp +++ b/app/node/distort/swirl/swirldistortnode.cpp @@ -87,27 +87,28 @@ ShaderCode SwirlDistortNode::GetShaderCode(const ShaderRequest &request) const return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/swirl.frag")); } -void SwirlDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue SwirlDistortNode::Value(const ValueParams &p) const { // If there's no texture, no need to run an operation - if (TexturePtr tex = value[kTextureInput].toTexture()) { + NodeValue tex_meta = GetInputValue(p, kTextureInput); + + if (TexturePtr tex = tex_meta.toTexture()) { // Only run shader if at least one of flip or flop are selected - if (!qIsNull(value[kAngleInput].toDouble()) && !qIsNull(value[kRadiusInput].toDouble())) { - ShaderJob job(value); + if (!qIsNull(GetInputValue(p, kAngleInput).toDouble()) && !qIsNull(GetInputValue(p, kRadiusInput).toDouble())) { + ShaderJob job = CreateJob(p); job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, tex->virtual_resolution(), this)); - table->Push(NodeValue::kTexture, tex->toJob(job), this); - } else { - // If we're not flipping or flopping just push the texture - table->Push(value[kTextureInput]); + return NodeValue(NodeValue::kTexture, tex->toJob(job), this); } } + + return tex_meta; } -void SwirlDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) +void SwirlDistortNode::UpdateGizmoPositions(const ValueParams &p) { - QPointF half_res(globals.square_resolution().x()/2, globals.square_resolution().y()/2); + QPointF half_res(p.square_resolution().x()/2, p.square_resolution().y()/2); - gizmo_->SetPoint(half_res + row[kPositionInput].toVec2().toPointF()); + gizmo_->SetPoint(half_res + GetInputValue(p, kPositionInput).toVec2().toPointF()); } void SwirlDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) diff --git a/app/node/distort/swirl/swirldistortnode.h b/app/node/distort/swirl/swirldistortnode.h index 744704ddc9..764c2aa160 100644 --- a/app/node/distort/swirl/swirldistortnode.h +++ b/app/node/distort/swirl/swirldistortnode.h @@ -42,9 +42,9 @@ class SwirlDistortNode : public Node virtual void Retranslate() override; virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; - virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override; + virtual void UpdateGizmoPositions(const ValueParams &p) override; static const QString kTextureInput; static const QString kRadiusInput; diff --git a/app/node/distort/tile/tiledistortnode.cpp b/app/node/distort/tile/tiledistortnode.cpp index 864428a7ad..5e0f510aa0 100644 --- a/app/node/distort/tile/tiledistortnode.cpp +++ b/app/node/distort/tile/tiledistortnode.cpp @@ -108,31 +108,32 @@ ShaderCode TileDistortNode::GetShaderCode(const ShaderRequest &request) const return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/tile.frag")); } -void TileDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue TileDistortNode::Value(const ValueParams &p) const { // If there's no texture, no need to run an operation - if (TexturePtr tex = value[kTextureInput].toTexture()) { + NodeValue texture = GetInputValue(p, kTextureInput); + + if (TexturePtr tex = texture.toTexture()) { // Only run shader if at least one of flip or flop are selected - if (!qFuzzyCompare(value[kScaleInput].toDouble(), 1.0)) { - ShaderJob job(value); + if (!qFuzzyCompare(GetInputValue(p, kScaleInput).toDouble(), 1.0)) { + ShaderJob job = CreateJob(p); job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, tex->virtual_resolution(), this)); - table->Push(NodeValue::kTexture, tex->toJob(job), this); - } else { - // If we're not flipping or flopping just push the texture - table->Push(value[kTextureInput]); + return NodeValue(NodeValue::kTexture, tex->toJob(job), this); } } + + return texture; } -void TileDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) +void TileDistortNode::UpdateGizmoPositions(const ValueParams &p) { - if (TexturePtr tex = row[kTextureInput].toTexture()) { + if (TexturePtr tex = GetInputValue(p, kTextureInput).toTexture()) { QPointF res = tex->virtual_resolution().toPointF(); - QPointF pos = row[kPositionInput].toVec2().toPointF(); + QPointF pos = GetInputValue(p, kPositionInput).toVec2().toPointF(); qreal x = pos.x(); qreal y = pos.y(); - Anchor a = static_cast(row[kAnchorInput].toInt()); + Anchor a = static_cast(GetInputValue(p, kAnchorInput).toInt()); if (a == kTopLeft || a == kTopCenter || a == kTopRight) { // Do nothing } else if (a == kMiddleLeft || a == kMiddleCenter || a == kMiddleRight) { diff --git a/app/node/distort/tile/tiledistortnode.h b/app/node/distort/tile/tiledistortnode.h index 44d9b9c1e7..87164123fe 100644 --- a/app/node/distort/tile/tiledistortnode.h +++ b/app/node/distort/tile/tiledistortnode.h @@ -42,9 +42,9 @@ class TileDistortNode : public Node virtual void Retranslate() override; virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; - virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override; + virtual void UpdateGizmoPositions(const ValueParams &p) override; static const QString kTextureInput; static const QString kScaleInput; diff --git a/app/node/distort/transform/transformdistortnode.cpp b/app/node/distort/transform/transformdistortnode.cpp index 01d9e1c454..4bf8542e43 100644 --- a/app/node/distort/transform/transformdistortnode.cpp +++ b/app/node/distort/transform/transformdistortnode.cpp @@ -81,40 +81,41 @@ void TransformDistortNode::Retranslate() SetComboBoxStrings(kInterpolationInput, {tr("Nearest Neighbor"), tr("Bilinear"), tr("Mipmapped Bilinear")}); } -void TransformDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue TransformDistortNode::Value(const ValueParams &p) const { // Generate matrix - QMatrix4x4 generated_matrix = GenerateMatrix(value, false, false, false, value[kParentInput].toMatrix()); + QMatrix4x4 generated_matrix = GenerateMatrix(p, false, false, false, GetInputValue(p, kParentInput).toMatrix()); // Pop texture - NodeValue texture_meta = value[kTextureInput]; + NodeValue texture_meta = GetInputValue(p, kTextureInput); TexturePtr job_to_push = nullptr; // If we have a texture, generate a matrix and make it happen if (TexturePtr texture = texture_meta.toTexture()) { // Adjust our matrix by the resolutions involved - QMatrix4x4 real_matrix = GenerateAutoScaledMatrix(generated_matrix, value, globals, texture->params()); + QMatrix4x4 real_matrix = GenerateAutoScaledMatrix(generated_matrix, p, texture->params()); if (!real_matrix.isIdentity()) { // The matrix will transform things ShaderJob job; job.Insert(QStringLiteral("ove_maintex"), texture_meta); job.Insert(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, real_matrix, this)); - job.SetInterpolation(QStringLiteral("ove_maintex"), static_cast(value[kInterpolationInput].toInt())); + job.SetInterpolation(QStringLiteral("ove_maintex"), static_cast(GetInputValue(p, kInterpolationInput).toInt())); // Use global resolution rather than texture resolution because this may result in a size change - job_to_push = Texture::Job(globals.vparams(), job); + job_to_push = Texture::Job(p.vparams(), job); } } - table->Push(NodeValue::kMatrix, QVariant::fromValue(generated_matrix), this); + qDebug() << "Add second output for matrix"; + //table->Push(NodeValue::kMatrix, QVariant::fromValue(generated_matrix), this); if (!job_to_push) { // Re-push whatever value we received - table->Push(texture_meta); + return texture_meta; } else { - table->Push(NodeValue::kTexture, job_to_push, this); + return NodeValue(NodeValue::kTexture, job_to_push, this); } } @@ -126,24 +127,24 @@ ShaderCode TransformDistortNode::GetShaderCode(const ShaderRequest &request) con return ShaderCode(); } -void TransformDistortNode::GizmoDragStart(const NodeValueRow &row, double x, double y, const rational &time) +void TransformDistortNode::GizmoDragStart(const ValueParams &p, double x, double y, const rational &time) { DraggableGizmo *gizmo = static_cast(sender()); if (gizmo == anchor_gizmo_) { - gizmo_inverted_transform_ = GenerateMatrix(row, true, true, false, row[kParentInput].toMatrix()).toTransform().inverted(); + gizmo_inverted_transform_ = GenerateMatrix(p, true, true, false, GetInputValue(p, kParentInput).toMatrix()).toTransform().inverted(); } else if (IsAScaleGizmo(gizmo)) { // Dragging scale handle - TexturePtr tex = row[kTextureInput].toTexture(); + TexturePtr tex = GetInputValue(p, kTextureInput).toTexture(); if (!tex) { return; } - gizmo_scale_uniform_ = row[kUniformScaleInput].toBool(); - gizmo_anchor_pt_ = (row[kAnchorInput].toVec2() + gizmo->GetGlobals().nonsquare_resolution()/2).toPointF(); + gizmo_scale_uniform_ = GetInputValue(p, kUniformScaleInput).toBool(); + gizmo_anchor_pt_ = (GetInputValue(p, kAnchorInput).toVec2() + gizmo->GetGlobals().nonsquare_resolution()/2).toPointF(); if (gizmo == point_gizmo_[kGizmoScaleTopLeft] || gizmo == point_gizmo_[kGizmoScaleTopRight] || gizmo == point_gizmo_[kGizmoScaleBottomLeft] || gizmo == point_gizmo_[kGizmoScaleBottomRight]) { @@ -157,7 +158,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].toVec2() + texture_sz/2; + gizmo_scale_anchor_ = GetInputValue(p, kAnchorInput).toVec2() + texture_sz/2; if (gizmo == point_gizmo_[kGizmoScaleTopRight] || gizmo == point_gizmo_[kGizmoScaleBottomRight] @@ -174,11 +175,11 @@ void TransformDistortNode::GizmoDragStart(const NodeValueRow &row, double x, dou } // Store current matrix - gizmo_inverted_transform_ = GenerateMatrix(row, true, true, true, row[kParentInput].toMatrix()).toTransform().inverted(); + gizmo_inverted_transform_ = GenerateMatrix(p, true, true, true, GetInputValue(p, kParentInput).toMatrix()).toTransform().inverted(); } else if (gizmo == rotation_gizmo_) { - gizmo_anchor_pt_ = (row[kAnchorInput].toVec2() + gizmo->GetGlobals().nonsquare_resolution()/2).toPointF(); + gizmo_anchor_pt_ = (GetInputValue(p, kAnchorInput).toVec2() + gizmo->GetGlobals().nonsquare_resolution()/2).toPointF(); gizmo_start_angle_ = std::atan2(y - gizmo_anchor_pt_.y(), x - gizmo_anchor_pt_.x()); gizmo_last_angle_ = gizmo_start_angle_; gizmo_last_alt_angle_ = std::atan2(x - gizmo_anchor_pt_.x(), y - gizmo_anchor_pt_.y()); @@ -336,15 +337,15 @@ QMatrix4x4 TransformDistortNode::AdjustMatrixByResolutions(const QMatrix4x4 &mat return adjusted_matrix; } -void TransformDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) +void TransformDistortNode::UpdateGizmoPositions(const ValueParams &p) { - TexturePtr tex = row[kTextureInput].toTexture(); + TexturePtr tex = GetInputValue(p, kTextureInput).toTexture(); if (!tex) { return; } // Get the sequence resolution - const QVector2D &sequence_res = globals.nonsquare_resolution(); + const QVector2D &sequence_res = p.nonsquare_resolution(); QVector2D sequence_half_res = sequence_res * 0.5; QPointF sequence_half_res_pt = sequence_half_res.toPointF(); @@ -354,12 +355,12 @@ void TransformDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const N QVector2D tex_offset = tex_params.offset(); // Retrieve autoscale value - AutoScaleType autoscale = static_cast(row[kAutoscaleInput].toInt()); + AutoScaleType autoscale = static_cast(GetInputValue(p, kAutoscaleInput).toInt()); // Fold values into a matrix for the rectangle QMatrix4x4 rectangle_matrix; rectangle_matrix.scale(sequence_half_res.x(), sequence_half_res.y()); - rectangle_matrix *= AdjustMatrixByResolutions(GenerateMatrix(row, false, false, false, row[kParentInput].toMatrix()), + rectangle_matrix *= AdjustMatrixByResolutions(GenerateMatrix(p, false, false, false, GetInputValue(p, kParentInput).toMatrix()), sequence_res, tex_sz, tex_offset, @@ -379,7 +380,7 @@ void TransformDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const N // Draw anchor point QMatrix4x4 anchor_matrix; anchor_matrix.scale(sequence_half_res.x(), sequence_half_res.y()); - anchor_matrix *= AdjustMatrixByResolutions(GenerateMatrix(row, true, false, false, row[kParentInput].toMatrix()), + anchor_matrix *= AdjustMatrixByResolutions(GenerateMatrix(p, true, false, false, GetInputValue(p, kParentInput).toMatrix()), sequence_res, tex_sz, tex_offset, @@ -402,14 +403,14 @@ void TransformDistortNode::UpdateGizmoPositions(const NodeValueRow &row, const N SetInputProperty(kAnchorInput, QStringLiteral("offset"), tex_sz * 0.5); } -QTransform TransformDistortNode::GizmoTransformation(const NodeValueRow &row, const NodeGlobals &globals) const +QTransform TransformDistortNode::GizmoTransformation(const ValueParams &p) const { - if (TexturePtr texture = row[kTextureInput].toTexture()) { + if (TexturePtr texture = GetInputValue(p, kTextureInput).toTexture()) { //auto m = GenerateMatrix(row, false, false, false, row[kParentInput].toMatrix()); - auto m = GenerateMatrix(row, false, false, false, QMatrix4x4()); - return GenerateAutoScaledMatrix(m, row, globals, texture->params()).toTransform(); + auto m = GenerateMatrix(p, false, false, false, QMatrix4x4()); + return GenerateAutoScaledMatrix(m, p, texture->params()).toTransform(); } - return super::GizmoTransformation(row, globals); + return super::GizmoTransformation(p); } QPointF TransformDistortNode::CreateScalePoint(double x, double y, const QPointF &half_res, const QMatrix4x4 &mat) @@ -417,11 +418,11 @@ QPointF TransformDistortNode::CreateScalePoint(double x, double y, const QPointF return mat.map(QPointF(x, y)) + half_res; } -QMatrix4x4 TransformDistortNode::GenerateAutoScaledMatrix(const QMatrix4x4& generated_matrix, const NodeValueRow& value, const NodeGlobals &globals, const VideoParams& texture_params) const +QMatrix4x4 TransformDistortNode::GenerateAutoScaledMatrix(const QMatrix4x4& generated_matrix, const ValueParams &p, const VideoParams& texture_params) const { - const QVector2D &sequence_res = globals.nonsquare_resolution(); + const QVector2D &sequence_res = p.nonsquare_resolution(); QVector2D texture_res(texture_params.square_pixel_width(), texture_params.height()); - AutoScaleType autoscale = static_cast(value[kAutoscaleInput].toInt()); + AutoScaleType autoscale = static_cast(GetInputValue(p, kAutoscaleInput).toInt()); return AdjustMatrixByResolutions(generated_matrix, sequence_res, diff --git a/app/node/distort/transform/transformdistortnode.h b/app/node/distort/transform/transformdistortnode.h index be17de561e..e3481de4ed 100644 --- a/app/node/distort/transform/transformdistortnode.h +++ b/app/node/distort/transform/transformdistortnode.h @@ -64,7 +64,7 @@ class TransformDistortNode : public MatrixGenerator virtual void Retranslate() override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; @@ -81,8 +81,8 @@ class TransformDistortNode : public MatrixGenerator const QVector2D& offset, AutoScaleType autoscale_type = kAutoScaleNone); - virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override; - virtual QTransform GizmoTransformation(const NodeValueRow &row, const NodeGlobals &globals) const override; + virtual void UpdateGizmoPositions(const ValueParams &p) override; + virtual QTransform GizmoTransformation(const ValueParams &p) const override; static const QString kParentInput; static const QString kTextureInput; @@ -90,14 +90,14 @@ class TransformDistortNode : public MatrixGenerator static const QString kInterpolationInput; protected slots: - virtual void GizmoDragStart(const olive::NodeValueRow &row, double x, double y, const olive::rational &time) override; + virtual void GizmoDragStart(const olive::ValueParams &p, double x, double y, const olive::rational &time) override; virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) override; private: static QPointF CreateScalePoint(double x, double y, const QPointF& half_res, const QMatrix4x4& mat); - QMatrix4x4 GenerateAutoScaledMatrix(const QMatrix4x4 &generated_matrix, const NodeValueRow &db, const NodeGlobals &globals, const VideoParams &texture_params) const; + QMatrix4x4 GenerateAutoScaledMatrix(const QMatrix4x4 &generated_matrix, const ValueParams &globals, const VideoParams &texture_params) const; bool IsAScaleGizmo(NodeGizmo *g) const; diff --git a/app/node/distort/wave/wavedistortnode.cpp b/app/node/distort/wave/wavedistortnode.cpp index 88171d6f23..ac1a3b6442 100644 --- a/app/node/distort/wave/wavedistortnode.cpp +++ b/app/node/distort/wave/wavedistortnode.cpp @@ -82,19 +82,19 @@ ShaderCode WaveDistortNode::GetShaderCode(const ShaderRequest &request) const return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/wave.frag")); } -void WaveDistortNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue WaveDistortNode::Value(const ValueParams &p) const { // If there's no texture, no need to run an operation - if (TexturePtr texture = value[kTextureInput].toTexture()) { + NodeValue tex_meta = GetInputValue(p, kTextureInput); + + if (TexturePtr texture = tex_meta.toTexture()) { // Only run shader if at least one of flip or flop are selected - if (!qIsNull(value[kIntensityInput].toDouble())) { - table->Push(NodeValue::kTexture, Texture::Job(texture->params(), ShaderJob(value)), this); - } else { - // If we're not flipping or flopping just push the texture - table->Push(value[kTextureInput]); + if (!qIsNull(GetInputValue(p, kIntensityInput).toDouble())) { + return NodeValue(NodeValue::kTexture, Texture::Job(texture->params(), CreateJob(p)), this); } } + return tex_meta; } } diff --git a/app/node/distort/wave/wavedistortnode.h b/app/node/distort/wave/wavedistortnode.h index aa253dc0e8..b15b4157a4 100644 --- a/app/node/distort/wave/wavedistortnode.h +++ b/app/node/distort/wave/wavedistortnode.h @@ -41,7 +41,7 @@ class WaveDistortNode : public Node virtual void Retranslate() override; virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &globals) const override; static const QString kTextureInput; static const QString kFrequencyInput; diff --git a/app/node/effect/opacity/opacityeffect.cpp b/app/node/effect/opacity/opacityeffect.cpp index 4b7e7ce67d..4441eb2d3f 100644 --- a/app/node/effect/opacity/opacityeffect.cpp +++ b/app/node/effect/opacity/opacityeffect.cpp @@ -46,21 +46,23 @@ ShaderCode OpacityEffect::GetShaderCode(const ShaderRequest &request) const } } -void OpacityEffect::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue OpacityEffect::Value(const ValueParams &p) const { // If there's no texture, no need to run an operation - if (TexturePtr tex = value[kTextureInput].toTexture()) { - if (TexturePtr opacity_tex = value[kValueInput].toTexture()) { - ShaderJob job(value); + NodeValue texture = GetInputValue(p, kTextureInput); + NodeValue value = GetInputValue(p, kValueInput); + + if (TexturePtr tex = texture.toTexture()) { + if (TexturePtr opacity_tex = value.toTexture()) { + ShaderJob job = CreateJob(p); job.SetShaderID(QStringLiteral("rgbmult")); - table->Push(NodeValue::kTexture, tex->toJob(job), this); - } else if (!qFuzzyCompare(value[kValueInput].toDouble(), 1.0)) { - table->Push(NodeValue::kTexture, tex->toJob(ShaderJob(value)), this); - } else { - // 1.0 float is a no-op, so just push the texture - table->Push(value[kTextureInput]); + return NodeValue(NodeValue::kTexture, tex->toJob(job), this); + } else if (!qFuzzyCompare(value.toDouble(), 1.0)) { + return NodeValue(NodeValue::kTexture, tex->toJob(CreateJob(p)), this); } } + + return texture; } } diff --git a/app/node/effect/opacity/opacityeffect.h b/app/node/effect/opacity/opacityeffect.h index 36e284274e..b4f96e48c8 100644 --- a/app/node/effect/opacity/opacityeffect.h +++ b/app/node/effect/opacity/opacityeffect.h @@ -35,7 +35,7 @@ class OpacityEffect : public Node virtual void Retranslate() override; virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; static const QString kTextureInput; static const QString kValueInput; diff --git a/app/node/filter/blur/blur.cpp b/app/node/filter/blur/blur.cpp index 8d0b68bd35..e91db7900b 100644 --- a/app/node/filter/blur/blur.cpp +++ b/app/node/filter/blur/blur.cpp @@ -117,24 +117,25 @@ ShaderCode BlurFilterNode::GetShaderCode(const ShaderRequest &request) const return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/blur.frag")); } -void BlurFilterNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue BlurFilterNode::Value(const ValueParams &p) const { // If there's no texture, no need to run an operation - if (TexturePtr tex = value[kTextureInput].toTexture()) { - Method method = static_cast(value[kMethodInput].toInt()); + NodeValue tex_meta = GetInputValue(p, kTextureInput); + if (TexturePtr tex = tex_meta.toTexture()) { + Method method = static_cast(GetInputValue(p, kMethodInput).toInt()); bool can_push_job = true; int iterations = 1; // Check if radius is > 0 - if (value[kRadiusInput].toDouble() > 0.0) { + if (GetInputValue(p, kRadiusInput).toDouble() > 0.0) { // Method-specific considerations switch (method) { case kBox: case kGaussian: { - bool horiz = value[kHorizInput].toBool(); - bool vert = value[kVertInput].toBool(); + bool horiz = GetInputValue(p, kHorizInput).toBool(); + bool vert = GetInputValue(p, kVertInput).toBool(); if (!horiz && !vert) { // Disable job if horiz and vert are unchecked @@ -154,27 +155,27 @@ void BlurFilterNode::Value(const NodeValueRow &value, const NodeGlobals &globals } if (can_push_job) { - ShaderJob job(value); + ShaderJob job = CreateJob(p); + job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, tex->virtual_resolution(), this)); job.SetIterations(iterations, kTextureInput); - table->Push(NodeValue::kTexture, tex->toJob(job), this); - } else { - // If we're not performing the blur job, just push the texture - table->Push(value[kTextureInput]); - } + return NodeValue(NodeValue::kTexture, tex->toJob(job), this); + } } + + return tex_meta; } -void BlurFilterNode::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) +void BlurFilterNode::UpdateGizmoPositions(const ValueParams &p) { - if (TexturePtr tex = row[kTextureInput].toTexture()) { - if (row[kMethodInput].toInt() == kRadial) { + if (TexturePtr tex = GetInputValue(p, kTextureInput).toTexture()) { + if (GetInputValue(p, kMethodInput).toInt() == kRadial) { const QVector2D &sequence_res = tex->virtual_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()); + radial_center_gizmo_->SetPoint(sequence_half_res.toPointF() + GetInputValue(p, kRadialCenterInput).toVec2().toPointF()); SetInputProperty(kRadialCenterInput, QStringLiteral("offset"), sequence_half_res); } else{ diff --git a/app/node/filter/blur/blur.h b/app/node/filter/blur/blur.h index 11ff669530..0353d3af60 100644 --- a/app/node/filter/blur/blur.h +++ b/app/node/filter/blur/blur.h @@ -49,14 +49,14 @@ class BlurFilterNode : public Node virtual void Retranslate() override; virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &globals) const override; Method GetMethod() const { return static_cast(GetStandardValue(kMethodInput).toInt()); } - virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override; + virtual void UpdateGizmoPositions(const ValueParams &p) override; static const QString kTextureInput; static const QString kMethodInput; diff --git a/app/node/filter/dropshadow/dropshadowfilter.cpp b/app/node/filter/dropshadow/dropshadowfilter.cpp index fead73b096..46ddf488fe 100644 --- a/app/node/filter/dropshadow/dropshadowfilter.cpp +++ b/app/node/filter/dropshadow/dropshadowfilter.cpp @@ -76,22 +76,26 @@ ShaderCode DropShadowFilter::GetShaderCode(const ShaderRequest &request) const return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/dropshadow.frag")); } -void DropShadowFilter::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue DropShadowFilter::Value(const ValueParams &p) const { - if (TexturePtr tex = value[kTextureInput].toTexture()) { - ShaderJob job(value); + NodeValue tex_meta = GetInputValue(p, kTextureInput); + + if (TexturePtr tex = tex_meta.toTexture()) { + ShaderJob job = CreateJob(p); QString iterative = QStringLiteral("previous_iteration_in"); job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, tex->virtual_resolution(), this)); - job.Insert(iterative, value[kTextureInput]); + job.Insert(iterative, tex_meta); - if (!qIsNull(value[kSoftnessInput].toDouble())) { + if (!qIsNull(GetInputValue(p, kSoftnessInput).toDouble())) { job.SetIterations(3, iterative); } - table->Push(NodeValue::kTexture, tex->toJob(job), this); + return NodeValue(NodeValue::kTexture, tex->toJob(job), this); } + + return NodeValue(); } } diff --git a/app/node/filter/dropshadow/dropshadowfilter.h b/app/node/filter/dropshadow/dropshadowfilter.h index 5e820a2809..2fcf682b47 100644 --- a/app/node/filter/dropshadow/dropshadowfilter.h +++ b/app/node/filter/dropshadow/dropshadowfilter.h @@ -41,7 +41,7 @@ class DropShadowFilter : public Node virtual void Retranslate() override; virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; static const QString kTextureInput; static const QString kColorInput; diff --git a/app/node/filter/mosaic/mosaicfilternode.cpp b/app/node/filter/mosaic/mosaicfilternode.cpp index 63966320ef..bc038f012a 100644 --- a/app/node/filter/mosaic/mosaicfilternode.cpp +++ b/app/node/filter/mosaic/mosaicfilternode.cpp @@ -51,22 +51,24 @@ void MosaicFilterNode::Retranslate() SetInputName(kVertInput, tr("Vertical")); } -void MosaicFilterNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue MosaicFilterNode::Value(const ValueParams &p) const { - if (TexturePtr texture = value[kTextureInput].toTexture()) { + NodeValue tex_meta = GetInputValue(p, kTextureInput); + + if (TexturePtr texture = tex_meta.toTexture()) { if (texture - && value[kHorizInput].toInt() != texture->width() - && value[kVertInput].toInt() != texture->height()) { - ShaderJob job(value); + && GetInputValue(p, kHorizInput).toInt() != texture->width() + && GetInputValue(p, kVertInput).toInt() != texture->height()) { + ShaderJob job = CreateJob(p); // Mipmapping makes this look weird, so we just use bilinear for finding the color of each block job.SetInterpolation(kTextureInput, Texture::kLinear); - table->Push(NodeValue::kTexture, texture->toJob(job), this); - } else { - table->Push(value[kTextureInput]); + return NodeValue(NodeValue::kTexture, texture->toJob(job), this); } } + + return tex_meta; } ShaderCode MosaicFilterNode::GetShaderCode(const ShaderRequest &request) const diff --git a/app/node/filter/mosaic/mosaicfilternode.h b/app/node/filter/mosaic/mosaicfilternode.h index 9f962a0cbd..85a8d3c083 100644 --- a/app/node/filter/mosaic/mosaicfilternode.h +++ b/app/node/filter/mosaic/mosaicfilternode.h @@ -55,7 +55,7 @@ class MosaicFilterNode : public Node virtual void Retranslate() override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; static const QString kTextureInput; diff --git a/app/node/filter/stroke/stroke.cpp b/app/node/filter/stroke/stroke.cpp index 3f49cd4c4f..80c7ea9e2b 100644 --- a/app/node/filter/stroke/stroke.cpp +++ b/app/node/filter/stroke/stroke.cpp @@ -83,18 +83,20 @@ void StrokeFilterNode::Retranslate() SetInputName(kInnerInput, tr("Inner")); } -void StrokeFilterNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue StrokeFilterNode::Value(const ValueParams &p) const { - if (TexturePtr tex = value[kTextureInput].toTexture()) { - if (value[kRadiusInput].toDouble() > 0.0 - && value[kOpacityInput].toDouble() > 0.0) { - ShaderJob job(value); + NodeValue tex_meta = GetInputValue(p, kTextureInput); + + if (TexturePtr tex = tex_meta.toTexture()) { + if (GetInputValue(p, kRadiusInput).toDouble() > 0.0 + && GetInputValue(p, kOpacityInput).toDouble() > 0.0) { + ShaderJob job = CreateJob(p); job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, tex->virtual_resolution(), this)); - table->Push(NodeValue::kTexture, tex->toJob(job), this); - } else { - table->Push(value[kTextureInput]); + return NodeValue(NodeValue::kTexture, tex->toJob(job), this); } } + + return tex_meta; } ShaderCode StrokeFilterNode::GetShaderCode(const ShaderRequest &request) const diff --git a/app/node/filter/stroke/stroke.h b/app/node/filter/stroke/stroke.h index 4ec07c0e94..11e4b1c8b6 100644 --- a/app/node/filter/stroke/stroke.h +++ b/app/node/filter/stroke/stroke.h @@ -40,7 +40,7 @@ class StrokeFilterNode : public Node virtual void Retranslate() override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; static const QString kTextureInput; diff --git a/app/node/generator/matrix/matrix.cpp b/app/node/generator/matrix/matrix.cpp index 0d8a112d43..f09f9ef1ad 100644 --- a/app/node/generator/matrix/matrix.cpp +++ b/app/node/generator/matrix/matrix.cpp @@ -87,35 +87,35 @@ void MatrixGenerator::Retranslate() SetInputName(kAnchorInput, tr("Anchor Point")); } -void MatrixGenerator::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue MatrixGenerator::Value(const ValueParams &p) const { // Push matrix output - QMatrix4x4 mat = GenerateMatrix(value, false, false, false, QMatrix4x4()); - table->Push(NodeValue::kMatrix, mat, this); + QMatrix4x4 mat = GenerateMatrix(p, false, false, false, QMatrix4x4()); + return NodeValue(NodeValue::kMatrix, mat, this); } -QMatrix4x4 MatrixGenerator::GenerateMatrix(const NodeValueRow &value, bool ignore_anchor, bool ignore_position, bool ignore_scale, const QMatrix4x4 &mat) const +QMatrix4x4 MatrixGenerator::GenerateMatrix(const ValueParams &p, bool ignore_anchor, bool ignore_position, bool ignore_scale, const QMatrix4x4 &mat) const { QVector2D anchor; QVector2D position; QVector2D scale; if (!ignore_anchor) { - anchor = value[kAnchorInput].toVec2(); + anchor = GetInputValue(p, kAnchorInput).toVec2(); } if (!ignore_scale) { - scale = value[kScaleInput].toVec2(); + scale = GetInputValue(p, kScaleInput).toVec2(); } if (!ignore_position) { - position = value[kPositionInput].toVec2(); + position = GetInputValue(p, kPositionInput).toVec2(); } return GenerateMatrix(position, - value[kRotationInput].toDouble(), + GetInputValue(p, kRotationInput).toDouble(), scale, - value[kUniformScaleInput].toBool(), + GetInputValue(p, kUniformScaleInput).toBool(), anchor, mat); } diff --git a/app/node/generator/matrix/matrix.h b/app/node/generator/matrix/matrix.h index 60ea1a0355..064a4d6426 100644 --- a/app/node/generator/matrix/matrix.h +++ b/app/node/generator/matrix/matrix.h @@ -44,7 +44,7 @@ class MatrixGenerator : public Node virtual void Retranslate() override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; static const QString kPositionInput; static const QString kRotationInput; @@ -53,7 +53,7 @@ class MatrixGenerator : public Node static const QString kAnchorInput; protected: - QMatrix4x4 GenerateMatrix(const NodeValueRow &value, bool ignore_anchor, bool ignore_position, bool ignore_scale, const QMatrix4x4 &mat) const; + QMatrix4x4 GenerateMatrix(const ValueParams &p, bool ignore_anchor, bool ignore_position, bool ignore_scale, const QMatrix4x4 &mat) const; static QMatrix4x4 GenerateMatrix(const QVector2D &pos, const float &rot, const QVector2D &scale, diff --git a/app/node/generator/noise/noise.cpp b/app/node/generator/noise/noise.cpp index 8d9f70c24a..a52aebeb39 100644 --- a/app/node/generator/noise/noise.cpp +++ b/app/node/generator/noise/noise.cpp @@ -78,15 +78,14 @@ ShaderCode NoiseGeneratorNode::GetShaderCode(const ShaderRequest &request) const return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/noise.frag")); } -void NoiseGeneratorNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue NoiseGeneratorNode::Value(const ValueParams &p) const { - ShaderJob job(value); + ShaderJob job = CreateJob(p); - job.Insert(value); - job.Insert(QStringLiteral("time_in"), NodeValue(NodeValue::kFloat, globals.time().in().toDouble(), this)); + job.Insert(QStringLiteral("time_in"), NodeValue(NodeValue::kFloat, p.time().in().toDouble(), this)); - TexturePtr base = value[kBaseIn].toTexture(); + TexturePtr base = GetInputValue(p, kBaseIn).toTexture(); - table->Push(NodeValue::kTexture, Texture::Job(base ? base->params() : globals.vparams(), job), this); + return NodeValue(NodeValue::kTexture, Texture::Job(base ? base->params() : p.vparams(), job), this); } } diff --git a/app/node/generator/noise/noise.h b/app/node/generator/noise/noise.h index c44526ab77..87ef79def0 100644 --- a/app/node/generator/noise/noise.h +++ b/app/node/generator/noise/noise.h @@ -40,7 +40,7 @@ class NoiseGeneratorNode : public Node { virtual void Retranslate() override; virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - virtual void Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; static const QString kBaseIn; static const QString kColorInput; diff --git a/app/node/generator/polygon/polygon.cpp b/app/node/generator/polygon/polygon.cpp index e05c3da14a..66d7d352fe 100644 --- a/app/node/generator/polygon/polygon.cpp +++ b/app/node/generator/polygon/polygon.cpp @@ -87,24 +87,24 @@ void PolygonGenerator::Retranslate() SetInputName(kColorInput, tr("Color")); } -ShaderJob PolygonGenerator::GetGenerateJob(const NodeValueRow &value, const VideoParams ¶ms) const +ShaderJob PolygonGenerator::GetGenerateJob(const ValueParams &p, const VideoParams ¶ms) const { - VideoParams p = params; - p.set_format(PixelFormat::U8); - auto job = Texture::Job(p, GenerateJob(value)); + VideoParams vp = params; + vp.set_format(PixelFormat::U8); + auto job = Texture::Job(vp, CreateJob(p)); // Conversion to RGB ShaderJob rgb; rgb.SetShaderID(QStringLiteral("rgb")); rgb.Insert(QStringLiteral("texture_in"), NodeValue(NodeValue::kTexture, job, this)); - rgb.Insert(QStringLiteral("color_in"), value[kColorInput]); + rgb.Insert(QStringLiteral("color_in"), GetInputValue(p, kColorInput)); return rgb; } -void PolygonGenerator::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue PolygonGenerator::Value(const ValueParams &p) const { - PushMergableJob(value, Texture::Job(globals.vparams(), GetGenerateJob(value, globals.vparams())), table); + return GetMergableJob(p, Texture::Job(p.vparams(), GetGenerateJob(p, p.vparams()))); } void PolygonGenerator::GenerateFrame(FramePtr frame, const GenerateJob &job) const @@ -164,18 +164,18 @@ void PolygonGenerator::ValidateGizmoVectorSize(QVector &vec, int new_sz) } } -void PolygonGenerator::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) +void PolygonGenerator::UpdateGizmoPositions(const ValueParams &p) { QVector2D res; - if (TexturePtr tex = row[kBaseInput].toTexture()) { + if (TexturePtr tex = GetInputValue(p, kBaseInput).toTexture()) { res = tex->virtual_resolution(); } else { - res = globals.square_resolution(); + res = p.square_resolution(); } Imath::V2d half_res(res.x()/2, res.y()/2); - auto points = row[kPointsInput].toArray(); + auto points = GetInputValue(p, kPointsInput).toArray(); int current_pos_sz = gizmo_position_handles_.size(); diff --git a/app/node/generator/polygon/polygon.h b/app/node/generator/polygon/polygon.h index b5c08a52ee..f9106a2592 100644 --- a/app/node/generator/polygon/polygon.h +++ b/app/node/generator/polygon/polygon.h @@ -47,11 +47,11 @@ class PolygonGenerator : public GeneratorWithMerge virtual void Retranslate() override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const override; - virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override; + virtual void UpdateGizmoPositions(const ValueParams &p) override; virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; @@ -59,7 +59,7 @@ class PolygonGenerator : public GeneratorWithMerge static const QString kColorInput; protected: - ShaderJob GetGenerateJob(const NodeValueRow &value, const VideoParams ¶ms) const; + ShaderJob GetGenerateJob(const ValueParams &p, const VideoParams ¶ms) const; protected slots: virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) override; diff --git a/app/node/generator/shape/generatorwithmerge.cpp b/app/node/generator/shape/generatorwithmerge.cpp index 6832e3e6ea..2e087343f0 100644 --- a/app/node/generator/shape/generatorwithmerge.cpp +++ b/app/node/generator/shape/generatorwithmerge.cpp @@ -51,20 +51,22 @@ ShaderCode GeneratorWithMerge::GetShaderCode(const ShaderRequest &request) const return ShaderCode(); } -void GeneratorWithMerge::PushMergableJob(const NodeValueRow &value, TexturePtr job, NodeValueTable *table) const +NodeValue GeneratorWithMerge::GetMergableJob(const ValueParams &p, TexturePtr job) const { - if (TexturePtr base = value[kBaseInput].toTexture()) { + NodeValue tex_meta = GetInputValue(p, kBaseInput); + + if (TexturePtr base = tex_meta.toTexture()) { // Push as merge node ShaderJob merge; merge.SetShaderID(QStringLiteral("mrg")); - merge.Insert(MergeNode::kBaseIn, value[kBaseInput]); + merge.Insert(MergeNode::kBaseIn, tex_meta); merge.Insert(MergeNode::kBlendIn, NodeValue(NodeValue::kTexture, job, this)); - table->Push(NodeValue::kTexture, base->toJob(merge), this); + return NodeValue(NodeValue::kTexture, base->toJob(merge), this); } else { // Just push generate job - table->Push(NodeValue::kTexture, job, this); + return NodeValue(NodeValue::kTexture, job, this); } } diff --git a/app/node/generator/shape/generatorwithmerge.h b/app/node/generator/shape/generatorwithmerge.h index cc462a5597..9b4bfa358b 100644 --- a/app/node/generator/shape/generatorwithmerge.h +++ b/app/node/generator/shape/generatorwithmerge.h @@ -38,7 +38,7 @@ class GeneratorWithMerge : public Node static const QString kBaseInput; protected: - void PushMergableJob(const NodeValueRow &value, TexturePtr job, NodeValueTable *table) const; + NodeValue GetMergableJob(const ValueParams &p, TexturePtr job) const; }; diff --git a/app/node/generator/shape/shapenode.cpp b/app/node/generator/shape/shapenode.cpp index 4c866e2796..fc4a0d5bbd 100644 --- a/app/node/generator/shape/shapenode.cpp +++ b/app/node/generator/shape/shapenode.cpp @@ -75,16 +75,16 @@ ShaderCode ShapeNode::GetShaderCode(const ShaderRequest &request) const } } -void ShapeNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue ShapeNode::Value(const ValueParams &p) const { - TexturePtr base = value[kBaseInput].toTexture(); + TexturePtr base = GetInputValue(p, kBaseInput).toTexture(); - ShaderJob job(value); + ShaderJob job = CreateJob(p); - job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, base ? base->virtual_resolution() : globals.square_resolution(), this)); + job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, base ? base->virtual_resolution() : p.square_resolution(), this)); job.SetShaderID(QStringLiteral("shape")); - PushMergableJob(value, Texture::Job(base ? base->params() : globals.vparams(), job), table); + return GetMergableJob(p, Texture::Job(base ? base->params() : p.vparams(), job)); } void ShapeNode::InputValueChangedEvent(const QString &input, int element) diff --git a/app/node/generator/shape/shapenode.h b/app/node/generator/shape/shapenode.h index 285fc5bb5a..2c67338330 100644 --- a/app/node/generator/shape/shapenode.h +++ b/app/node/generator/shape/shapenode.h @@ -47,7 +47,7 @@ class ShapeNode : public ShapeNodeBase virtual void Retranslate() override; virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; static QString kTypeInput; static QString kRadiusInput; diff --git a/app/node/generator/shape/shapenodebase.cpp b/app/node/generator/shape/shapenodebase.cpp index f57034ca75..bdbf949d68 100644 --- a/app/node/generator/shape/shapenodebase.cpp +++ b/app/node/generator/shape/shapenodebase.cpp @@ -73,15 +73,15 @@ void ShapeNodeBase::Retranslate() } } -void ShapeNodeBase::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) +void ShapeNodeBase::UpdateGizmoPositions(const ValueParams &p) { // Use offsets to make the appearance of values that start in the top left, even though we // really anchor around the center - QVector2D center_pt = globals.square_resolution() * 0.5; + QVector2D center_pt = p.square_resolution() * 0.5; SetInputProperty(kPositionInput, QStringLiteral("offset"), center_pt); - QVector2D pos = row[kPositionInput].toVec2(); - QVector2D sz = row[kSizeInput].toVec2(); + QVector2D pos = GetInputValue(p, kPositionInput).toVec2(); + QVector2D sz = GetInputValue(p, kSizeInput).toVec2(); QVector2D half_sz = sz * 0.5; double left_pt = pos.x() + center_pt.x() - half_sz.x(); diff --git a/app/node/generator/shape/shapenodebase.h b/app/node/generator/shape/shapenodebase.h index fd0f6658a9..a5c2cd9a17 100644 --- a/app/node/generator/shape/shapenodebase.h +++ b/app/node/generator/shape/shapenodebase.h @@ -37,7 +37,7 @@ class ShapeNodeBase : public GeneratorWithMerge virtual void Retranslate() override; - virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override; + virtual void UpdateGizmoPositions(const ValueParams &p) override; void SetRect(QRectF rect, const VideoParams &sequence_res, MultiUndoCommand *command); diff --git a/app/node/generator/solid/solid.cpp b/app/node/generator/solid/solid.cpp index 4674ab8932..2b0cfb0b0d 100644 --- a/app/node/generator/solid/solid.cpp +++ b/app/node/generator/solid/solid.cpp @@ -59,9 +59,9 @@ void SolidGenerator::Retranslate() SetInputName(kColorInput, tr("Color")); } -void SolidGenerator::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue SolidGenerator::Value(const ValueParams &p) const { - table->Push(NodeValue::kTexture, Texture::Job(globals.vparams(), ShaderJob(value)), this); + return NodeValue(NodeValue::kTexture, Texture::Job(p.vparams(), CreateJob(p)), this); } ShaderCode SolidGenerator::GetShaderCode(const ShaderRequest &request) const diff --git a/app/node/generator/solid/solid.h b/app/node/generator/solid/solid.h index 97013a13ab..25f8a59f43 100644 --- a/app/node/generator/solid/solid.h +++ b/app/node/generator/solid/solid.h @@ -40,7 +40,7 @@ class SolidGenerator : public Node virtual void Retranslate() override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; static const QString kColorInput; diff --git a/app/node/generator/text/textv1.cpp b/app/node/generator/text/textv1.cpp index ae08389c60..598d03c856 100644 --- a/app/node/generator/text/textv1.cpp +++ b/app/node/generator/text/textv1.cpp @@ -90,11 +90,13 @@ void TextGeneratorV1::Retranslate() SetComboBoxStrings(kVAlignInput, {tr("Top"), tr("Center"), tr("Bottom")}); } -void TextGeneratorV1::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue TextGeneratorV1::Value(const ValueParams &p) const { - if (!value[kTextInput].toString().isEmpty()) { - table->Push(NodeValue::kTexture, Texture::Job(globals.vparams(), GenerateJob(value)), this); + if (!GetInputValue(p, kTextInput).toString().isEmpty()) { + return NodeValue(NodeValue::kTexture, Texture::Job(p.vparams(), CreateJob(p)), this); } + + return NodeValue(); } void TextGeneratorV1::GenerateFrame(FramePtr frame, const GenerateJob& job) const diff --git a/app/node/generator/text/textv1.h b/app/node/generator/text/textv1.h index e45b9bc07f..b49f85ff5f 100644 --- a/app/node/generator/text/textv1.h +++ b/app/node/generator/text/textv1.h @@ -40,7 +40,7 @@ class TextGeneratorV1 : public Node virtual void Retranslate() override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const override; diff --git a/app/node/generator/text/textv2.cpp b/app/node/generator/text/textv2.cpp index 3472bb6015..0ad628f001 100644 --- a/app/node/generator/text/textv2.cpp +++ b/app/node/generator/text/textv2.cpp @@ -91,14 +91,16 @@ void TextGeneratorV2::Retranslate() SetComboBoxStrings(kVAlignInput, {tr("Top"), tr("Center"), tr("Bottom")}); } -void TextGeneratorV2::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue TextGeneratorV2::Value(const ValueParams &p) const { - if (!value[kTextInput].toString().isEmpty()) { - GenerateJob job(value); - auto text_params = globals.vparams(); + if (!GetInputValue(p, kTextInput).toString().isEmpty()) { + GenerateJob job = CreateJob(p); + auto text_params = p.vparams(); text_params.set_format(PixelFormat::F32); - table->Push(NodeValue::kTexture, Texture::Job(text_params, job), this); + return NodeValue(NodeValue::kTexture, Texture::Job(text_params, job), this); } + + return NodeValue(); } void TextGeneratorV2::GenerateFrame(FramePtr frame, const GenerateJob& job) const diff --git a/app/node/generator/text/textv2.h b/app/node/generator/text/textv2.h index c3879692e0..f2c51c3ee8 100644 --- a/app/node/generator/text/textv2.h +++ b/app/node/generator/text/textv2.h @@ -40,7 +40,7 @@ class TextGeneratorV2 : public ShapeNodeBase virtual void Retranslate() override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const override; diff --git a/app/node/generator/text/textv3.cpp b/app/node/generator/text/textv3.cpp index 986e2f7544..4774deb4f5 100644 --- a/app/node/generator/text/textv3.cpp +++ b/app/node/generator/text/textv3.cpp @@ -96,12 +96,12 @@ void TextGeneratorV3::Retranslate() SetInputName(kArgsInput, tr("Arguments")); } -void TextGeneratorV3::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue TextGeneratorV3::Value(const ValueParams &p) const { - QString text = value[kTextInput].toString(); + QString text = GetInputValue(p, kTextInput).toString(); - if (value[kUseArgsInput].toBool()) { - auto args = value[kArgsInput].toArray(); + if (GetInputValue(p, kUseArgsInput).toBool()) { + auto args = GetInputValue(p, kArgsInput).toArray(); if (!args.empty()) { QStringList list; list.reserve(args.size()); @@ -113,20 +113,22 @@ void TextGeneratorV3::Value(const NodeValueRow &value, const NodeGlobals &global } } + NodeValue base_val = GetInputValue(p, kTextInput); + if (!text.isEmpty()) { - TexturePtr base = value[kTextInput].toTexture(); + TexturePtr base = base_val.toTexture(); - VideoParams text_params = base ? base->params() : globals.vparams(); + VideoParams text_params = base ? base->params() : p.vparams(); text_params.set_format(PixelFormat::U8); text_params.set_colorspace(project()->color_manager()->GetDefaultInputColorSpace()); - GenerateJob job(value); + GenerateJob job = CreateJob(p); job.Insert(kTextInput, NodeValue(NodeValue::kText, text)); - PushMergableJob(value, Texture::Job(text_params, job), table); - } else if (value[kBaseInput].toTexture()) { - table->Push(value[kBaseInput]); + return GetMergableJob(p, Texture::Job(text_params, job)); } + + return base_val; } void TextGeneratorV3::GenerateFrame(FramePtr frame, const GenerateJob& job) const @@ -176,13 +178,13 @@ void TextGeneratorV3::GenerateFrame(FramePtr frame, const GenerateJob& job) cons text_doc.documentLayout()->draw(&p, ctx); } -void TextGeneratorV3::UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) +void TextGeneratorV3::UpdateGizmoPositions(const ValueParams &p) { - super::UpdateGizmoPositions(row, globals); + super::UpdateGizmoPositions(p); QRectF rect = poly_gizmo()->GetPolygon().boundingRect(); text_gizmo_->SetRect(rect); - text_gizmo_->SetHtml(row[kTextInput].toString()); + text_gizmo_->SetHtml(GetInputValue(p, kTextInput).toString()); } Qt::Alignment TextGeneratorV3::GetQtAlignmentFromOurs(VerticalAlignment v) diff --git a/app/node/generator/text/textv3.h b/app/node/generator/text/textv3.h index b7d6a5674f..f6a665d522 100644 --- a/app/node/generator/text/textv3.h +++ b/app/node/generator/text/textv3.h @@ -41,11 +41,11 @@ class TextGeneratorV3 : public ShapeNodeBase virtual void Retranslate() override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const override; - virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals) override; + virtual void UpdateGizmoPositions(const ValueParams &p) override; enum VerticalAlignment { diff --git a/app/node/gizmo/draggable.cpp b/app/node/gizmo/draggable.cpp index 1c988d4d59..1a0a541127 100644 --- a/app/node/gizmo/draggable.cpp +++ b/app/node/gizmo/draggable.cpp @@ -28,13 +28,13 @@ DraggableGizmo::DraggableGizmo(QObject *parent) { } -void DraggableGizmo::DragStart(const NodeValueRow &row, double abs_x, double abs_y, const rational &time) +void DraggableGizmo::DragStart(const ValueParams &p, double abs_x, double abs_y, const rational &time) { for (int i=0; i +#include "render/cancelatom.h" #include "render/loopmode.h" #include "render/videoparams.h" namespace olive { -class NodeGlobals +class ValueParams { public: - NodeGlobals(){} + ValueParams() + { + } - NodeGlobals(const VideoParams &vparam, const AudioParams &aparam, const TimeRange &time, LoopMode loop_mode) : + ValueParams(const VideoParams &vparam, const AudioParams &aparam, const TimeRange &time, LoopMode loop_mode, CancelAtom *cancel) : video_params_(vparam), audio_params_(aparam), time_(time), - loop_mode_(loop_mode) + loop_mode_(loop_mode), + cancel_atom_(cancel) { } - NodeGlobals(const VideoParams &vparam, const AudioParams &aparam, const rational &time, LoopMode loop_mode) : - NodeGlobals(vparam, aparam, TimeRange(time, time + vparam.frame_rate_as_time_base()), loop_mode) + ValueParams(const VideoParams &vparam, const AudioParams &aparam, const rational &time, LoopMode loop_mode, CancelAtom *cancel) : + ValueParams(vparam, aparam, TimeRange(time, time + vparam.frame_rate_as_time_base()), loop_mode, cancel) { } @@ -53,11 +57,17 @@ class NodeGlobals const TimeRange &time() const { return time_; } LoopMode loop_mode() const { return loop_mode_; } + CancelAtom *cancel_atom() const { return cancel_atom_; } + bool is_cancelled() const { return cancel_atom_ && cancel_atom_->IsCancelled(); } + + //ValueParams time_transformed(const TimeRange &time) const; + private: VideoParams video_params_; AudioParams audio_params_; TimeRange time_; LoopMode loop_mode_; + CancelAtom *cancel_atom_; }; diff --git a/app/node/input/multicam/multicamnode.cpp b/app/node/input/multicam/multicamnode.cpp index 1ea80d5ae5..70a2106152 100644 --- a/app/node/input/multicam/multicamnode.cpp +++ b/app/node/input/multicam/multicamnode.cpp @@ -44,28 +44,15 @@ QString MultiCamNode::Description() const return tr("Allows easy switching between multiple sources."); } -Node::ActiveElements MultiCamNode::GetActiveElementsAtTime(const QString &input, const TimeRange &r) const +NodeValue MultiCamNode::Value(const ValueParams &p) const { - if (input == kSourcesInput) { - int src = GetCurrentSource(); - if (src >= 0 && src < GetSourceCount()) { - Node::ActiveElements a; - a.add(src); - return a; - } else { - return ActiveElements::kNoElements; - } - } else { - return super::GetActiveElementsAtTime(input, r); - } -} + int current = GetInputValue(p, kCurrentInput); -void MultiCamNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const -{ - NodeValueArray arr = value[kSourcesInput].toArray(); - if (!arr.empty()) { - table->Push(arr.begin()->second); + if (Node *n = GetSourceNode(current)) { + n->Value(p); } + + return NodeValue(); } void MultiCamNode::IndexToRowCols(int index, int total_rows, int total_cols, int *row, int *col) @@ -76,29 +63,6 @@ void MultiCamNode::IndexToRowCols(int index, int total_rows, int total_cols, int *row = index/total_cols; } -Node *MultiCamNode::GetConnectedRenderOutput(const QString &input, int element) const -{ - if (sequence_ && input == kSourcesInput && element >= 0 && element < GetSourceCount()) { - return GetTrackList()->GetTrackAt(element); - } else { - return Node::GetConnectedRenderOutput(input, element); - } -} - -bool MultiCamNode::IsInputConnectedForRender(const QString &input, int element) const -{ - if (sequence_ && input == kSourcesInput && element >= 0 && element < GetSourceCount()) { - return true; - } else { - return Node::IsInputConnectedForRender(input, element); - } -} - -QVector MultiCamNode::IgnoreInputsForRendering() const -{ - return {kSequenceInput}; -} - void MultiCamNode::InputConnectedEvent(const QString &input, int element, Node *output) { if (input == kSequenceInput) { @@ -117,6 +81,15 @@ void MultiCamNode::InputDisconnectedEvent(const QString &input, int element, Nod } } +Node *MultiCamNode::GetSourceNode(int source) const +{ + if (sequence_) { + return GetTrackList()->GetTrackAt(source); + } else { + return GetConnectedOutput(kSourcesInput, source); + } +} + TrackList *MultiCamNode::GetTrackList() const { return sequence_->track_list(static_cast(GetStandardValue(kSequenceTypeInput).toInt())); @@ -137,7 +110,7 @@ void MultiCamNode::Retranslate() names.reserve(name_count); for (int i=0; iName(); } names.append(tr("%1: %2").arg(QString::number(i+1), src_name)); diff --git a/app/node/input/multicam/multicamnode.h b/app/node/input/multicam/multicamnode.h index ead53c63c2..ffef070c18 100644 --- a/app/node/input/multicam/multicamnode.h +++ b/app/node/input/multicam/multicamnode.h @@ -21,9 +21,7 @@ class MultiCamNode : public Node virtual QVector Category() const override; virtual QString Description() const override; - virtual ActiveElements GetActiveElementsAtTime(const QString &input, const TimeRange &r) const override; - - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; virtual void Retranslate() override; @@ -57,16 +55,13 @@ class MultiCamNode : public Node return col + row * total_cols; } - virtual Node *GetConnectedRenderOutput(const QString& input, int element = -1) const override; - virtual bool IsInputConnectedForRender(const QString& input, int element = -1) const override; - - virtual QVector IgnoreInputsForRendering() const override; - protected: virtual void InputConnectedEvent(const QString &input, int element, Node *output) override; virtual void InputDisconnectedEvent(const QString &input, int element, Node *output) override; private: + Node *GetSourceNode(int source) const; + TrackList *GetTrackList() const; Sequence *sequence_; diff --git a/app/node/input/time/timeinput.cpp b/app/node/input/time/timeinput.cpp index 80624ad1d3..e6f16c0398 100644 --- a/app/node/input/time/timeinput.cpp +++ b/app/node/input/time/timeinput.cpp @@ -48,13 +48,13 @@ QString TimeInput::Description() const return tr("Generates the time (in seconds) at this frame."); } -void TimeInput::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue TimeInput::Value(const ValueParams &p) const { - table->Push(NodeValue::kFloat, - globals.time().in().toDouble(), - this, - false, - QStringLiteral("time")); + return NodeValue(NodeValue::kFloat, + p.time().in().toDouble(), + this, + false, + QStringLiteral("time")); } } diff --git a/app/node/input/time/timeinput.h b/app/node/input/time/timeinput.h index a00d0d2c66..53d64b7147 100644 --- a/app/node/input/time/timeinput.h +++ b/app/node/input/time/timeinput.h @@ -38,7 +38,7 @@ class TimeInput : public Node virtual QVector Category() const override; virtual QString Description() const override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; }; diff --git a/app/node/input/value/valuenode.cpp b/app/node/input/value/valuenode.cpp index 5520d2cf9f..ac62976771 100644 --- a/app/node/input/value/valuenode.cpp +++ b/app/node/input/value/valuenode.cpp @@ -62,12 +62,12 @@ void ValueNode::Retranslate() SetComboBoxStrings(kTypeInput, type_names); } -void ValueNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue ValueNode::Value(const ValueParams &p) const { - Q_UNUSED(globals) + Q_UNUSED(p) // Ensure value is pushed onto the table - table->Push(value[kValueInput]); + return GetInputValue(p, kValueInput); } void ValueNode::InputValueChangedEvent(const QString &input, int element) diff --git a/app/node/input/value/valuenode.h b/app/node/input/value/valuenode.h index f45bf8914f..f436e4fdcd 100644 --- a/app/node/input/value/valuenode.h +++ b/app/node/input/value/valuenode.h @@ -58,7 +58,7 @@ class ValueNode : public Node virtual void Retranslate() override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; protected: virtual void InputValueChangedEvent(const QString &input, int element) override; diff --git a/app/node/keying/chromakey/chromakey.cpp b/app/node/keying/chromakey/chromakey.cpp index cfbea289b8..d203244d7c 100644 --- a/app/node/keying/chromakey/chromakey.cpp +++ b/app/node/keying/chromakey/chromakey.cpp @@ -130,20 +130,24 @@ void ChromaKeyNode::GenerateProcessor() } } -void ChromaKeyNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue ChromaKeyNode::Value(const ValueParams &p) const { - if (TexturePtr tex = value[kTextureInput].toTexture()) { + NodeValue tex_meta = GetInputValue(p, kTextureInput); + + if (TexturePtr tex = tex_meta.toTexture()) { if (processor()) { - ColorTransformJob job(value); + ColorTransformJob job = CreateJob(p); job.SetColorProcessor(processor()); - job.SetInputTexture(value[kTextureInput]); + job.SetInputTexture(GetInputValue(p, kTextureInput)); job.SetNeedsCustomShader(this); job.SetFunctionName(QStringLiteral("SceneLinearToCIEXYZ_d65")); - table->Push(NodeValue::kTexture, tex->toJob(job), this); + return NodeValue(NodeValue::kTexture, tex->toJob(job), this); } } + + return tex_meta; } void ChromaKeyNode::ConfigChanged() diff --git a/app/node/keying/chromakey/chromakey.h b/app/node/keying/chromakey/chromakey.h index 5082dc32f5..1eac20856d 100644 --- a/app/node/keying/chromakey/chromakey.h +++ b/app/node/keying/chromakey/chromakey.h @@ -37,7 +37,7 @@ class ChromaKeyNode : public OCIOBaseNode { virtual void InputValueChangedEvent(const QString& input, int element) override; virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - virtual void Value(const NodeValueRow& value, const NodeGlobals& globals, NodeValueTable* table) const override; + virtual NodeValue Value(const ValueParams &p) const override; virtual void ConfigChanged() override; diff --git a/app/node/keying/colordifferencekey/colordifferencekey.cpp b/app/node/keying/colordifferencekey/colordifferencekey.cpp index 40ff98ccfe..d0f405b11b 100644 --- a/app/node/keying/colordifferencekey/colordifferencekey.cpp +++ b/app/node/keying/colordifferencekey/colordifferencekey.cpp @@ -91,14 +91,17 @@ ShaderCode ColorDifferenceKeyNode::GetShaderCode(const ShaderRequest &request) c return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/colordifferencekey.frag")); } -void ColorDifferenceKeyNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue ColorDifferenceKeyNode::Value(const ValueParams &p) const { // If there's no texture, no need to run an operation - if (TexturePtr tex = value[kTextureInput].toTexture()) { - ShaderJob job; - job.Insert(value); - table->Push(NodeValue::kTexture, tex->toJob(job), this); + NodeValue tex_meta = GetInputValue(p, kTextureInput); + + if (TexturePtr tex = tex_meta.toTexture()) { + ShaderJob job = CreateJob(p); + return NodeValue(NodeValue::kTexture, tex->toJob(job), this); } + + return tex_meta; } } // namespace olive diff --git a/app/node/keying/colordifferencekey/colordifferencekey.h b/app/node/keying/colordifferencekey/colordifferencekey.h index 31cb106a6e..231d6cc4eb 100644 --- a/app/node/keying/colordifferencekey/colordifferencekey.h +++ b/app/node/keying/colordifferencekey/colordifferencekey.h @@ -34,7 +34,7 @@ class ColorDifferenceKeyNode : public Node { virtual void Retranslate() override; virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - virtual void Value(const NodeValueRow& value, const NodeGlobals& globals, NodeValueTable* table) const override; + virtual NodeValue Value(const ValueParams &p) const override; static const QString kTextureInput; static const QString kGarbageMatteInput; diff --git a/app/node/keying/despill/despill.cpp b/app/node/keying/despill/despill.cpp index 267e3d3c42..889db1a6a6 100644 --- a/app/node/keying/despill/despill.cpp +++ b/app/node/keying/despill/despill.cpp @@ -80,20 +80,23 @@ ShaderCode DespillNode::GetShaderCode(const ShaderRequest &request) const { return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/despill.frag")); } -void DespillNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const { - ShaderJob job; - job.Insert(value); - - // Set luma coefficients - double luma_coeffs[3] = {0.0f, 0.0f, 0.0f}; - project()->color_manager()->GetDefaultLumaCoefs(luma_coeffs); - job.Insert(QStringLiteral("luma_coeffs"), - NodeValue(NodeValue::kVec3, QVector3D(luma_coeffs[0], luma_coeffs[1], luma_coeffs[2]))); - - // If there's no texture, no need to run an operation - if (TexturePtr tex = job.Get(kTextureInput).toTexture()) { - table->Push(NodeValue::kTexture, tex->toJob(job), this); +NodeValue DespillNode::Value(const ValueParams &p) const +{ + NodeValue tex_meta = GetInputValue(p, kTextureInput); + + if (TexturePtr tex = tex_meta.toTexture()) { + ShaderJob job = CreateJob(p); + + // Set luma coefficients + double luma_coeffs[3] = {0.0f, 0.0f, 0.0f}; + project()->color_manager()->GetDefaultLumaCoefs(luma_coeffs); + job.Insert(QStringLiteral("luma_coeffs"), + NodeValue(NodeValue::kVec3, QVector3D(luma_coeffs[0], luma_coeffs[1], luma_coeffs[2]))); + + return NodeValue(NodeValue::kTexture, tex->toJob(job), this); } + + return tex_meta; } diff --git a/app/node/keying/despill/despill.h b/app/node/keying/despill/despill.h index 9364024272..2d6844aaa0 100644 --- a/app/node/keying/despill/despill.h +++ b/app/node/keying/despill/despill.h @@ -35,7 +35,7 @@ class DespillNode : public Node { virtual void Retranslate() override; virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - virtual void Value(const NodeValueRow& value, const NodeGlobals& globals, NodeValueTable* table) const override; + virtual NodeValue Value(const ValueParams &globals) const override; static const QString kTextureInput; static const QString kColorInput; diff --git a/app/node/math/math/math.cpp b/app/node/math/math/math.cpp index 82b07e6e87..4441b3c842 100644 --- a/app/node/math/math/math.cpp +++ b/app/node/math/math/math.cpp @@ -93,28 +93,27 @@ ShaderCode MathNode::GetShaderCode(const ShaderRequest &request) const return GetShaderCodeInternal(request.id, kParamAIn, kParamBIn); } -void MathNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue MathNode::Value(const ValueParams &p) const { // Auto-detect what values to operate with // FIXME: Very inefficient NodeValueTable at, bt; - at.Push(value[kParamAIn]); - bt.Push(value[kParamBIn]); + at.Push(GetInputValue(p, kParamAIn)); + bt.Push(GetInputValue(p, kParamBIn)); PairingCalculator calc(at, bt); // Do nothing if no pairing was found - if (!calc.FoundMostLikelyPairing()) { - return; + if (calc.FoundMostLikelyPairing()) { + return ValueInternal(GetOperation(), + calc.GetMostLikelyPairing(), + kParamAIn, + calc.GetMostLikelyValueA(), + kParamBIn, + calc.GetMostLikelyValueB(), + p); } - return ValueInternal(GetOperation(), - calc.GetMostLikelyPairing(), - kParamAIn, - calc.GetMostLikelyValueA(), - kParamBIn, - calc.GetMostLikelyValueB(), - globals, - table); + return NodeValue(); } void MathNode::ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const diff --git a/app/node/math/math/math.h b/app/node/math/math/math.h index 1e2f1ffcc4..325edcdd24 100644 --- a/app/node/math/math/math.h +++ b/app/node/math/math/math.h @@ -52,7 +52,7 @@ class MathNode : public MathNodeBase SetStandardValue(kMethodIn, o); } - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; virtual void ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const override; diff --git a/app/node/math/math/mathbase.cpp b/app/node/math/math/mathbase.cpp index af8ded1e37..7a23ed4d21 100644 --- a/app/node/math/math/mathbase.cpp +++ b/app/node/math/math/mathbase.cpp @@ -148,21 +148,20 @@ QVector4D MathNodeBase::RetrieveVector(const NodeValue &val) } } -void MathNodeBase::PushVector(NodeValueTable *output, olive::NodeValue::Type type, const QVector4D &vec) const +NodeValue MathNodeBase::PushVector(olive::NodeValue::Type type, const QVector4D &vec) const { switch (type) { case NodeValue::kVec2: - output->Push(type, QVector2D(vec), this); - break; + return NodeValue(type, QVector2D(vec), this); case NodeValue::kVec3: - output->Push(type, QVector3D(vec), this); - break; + return NodeValue(type, QVector3D(vec), this); case NodeValue::kVec4: - output->Push(type, vec, this); - break; + return NodeValue(type, vec, this); default: break; } + + return NodeValue(); } QString MathNodeBase::GetOperationName(Operation o) @@ -227,7 +226,7 @@ void MathNodeBase::PerformAllOnFloatBufferSSE(Operation operation, float *a, flo } #endif -void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QString& param_a_in, const NodeValue& val_a, const QString& param_b_in, const NodeValue& val_b, const NodeGlobals &globals, NodeValueTable *output) const +NodeValue MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QString& param_a_in, const NodeValue& val_a, const QString& param_b_in, const NodeValue& val_b, const ValueParams &p) const { switch (pairing) { @@ -235,25 +234,22 @@ void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QSt { if (val_a.type() == NodeValue::kRational && val_b.type() == NodeValue::kRational && operation != kOpPower) { // Preserve rationals - output->Push(NodeValue::kRational, + return NodeValue(NodeValue::kRational, QVariant::fromValue(PerformAddSubMultDiv(operation, val_a.toRational(), val_b.toRational())), this); } else { - output->Push(NodeValue::kFloat, + return NodeValue(NodeValue::kFloat, PerformAll(operation, RetrieveNumber(val_a), RetrieveNumber(val_b)), this); } - break; } case kPairVecVec: { // We convert all vectors to QVector4D just for simplicity and exploit the fact that kVec4 is higher than kVec2 in // the enum to find the largest data type - PushVector(output, - qMax(val_a.type(), val_b.type()), - PerformAddSubMultDiv(operation, RetrieveVector(val_a), RetrieveVector(val_b))); - break; + return PushVector(qMax(val_a.type(), val_b.type()), + PerformAddSubMultDiv(operation, RetrieveVector(val_a), RetrieveVector(val_b))); } case kPairMatrixVec: @@ -262,10 +258,8 @@ void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QSt QVector4D vec = (val_a.type() == NodeValue::kMatrix) ? RetrieveVector(val_b) : RetrieveVector(val_a); // Only valid operation is multiply - PushVector(output, - qMax(val_a.type(), val_b.type()), - PerformMult(operation, vec, matrix)); - break; + return PushVector(qMax(val_a.type(), val_b.type()), + PerformMult(operation, vec, matrix)); } case kPairVecNumber: @@ -274,16 +268,14 @@ void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QSt float number = RetrieveNumber((val_a.type() & NodeValue::kMatrix) ? val_b : val_a); // Only multiply and divide are valid operations - PushVector(output, val_a.type(), PerformMultDiv(operation, vec, number)); - break; + return PushVector(val_a.type(), PerformMultDiv(operation, vec, number)); } case kPairMatrixMatrix: { QMatrix4x4 mat_a = val_a.toMatrix(); QMatrix4x4 mat_b = val_b.toMatrix(); - output->Push(NodeValue::kMatrix, PerformAddSubMult(operation, mat_a, mat_b), this); - break; + return NodeValue(NodeValue::kMatrix, PerformAddSubMult(operation, mat_a, mat_b), this); } case kPairColorColor: @@ -292,8 +284,7 @@ void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QSt Color col_b = val_b.toColor(); // Only add and subtract are valid operations - output->Push(NodeValue::kColor, QVariant::fromValue(PerformAddSub(operation, col_a, col_b)), this); - break; + return NodeValue(NodeValue::kColor, QVariant::fromValue(PerformAddSub(operation, col_a, col_b)), this); } @@ -303,8 +294,7 @@ void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QSt float num = (val_a.type() == NodeValue::kColor) ? val_b.toDouble() : val_a.toDouble(); // Only multiply and divide are valid operations - output->Push(NodeValue::kColor, QVariant::fromValue(PerformMult(operation, col, num)), this); - break; + return NodeValue(NodeValue::kColor, QVariant::fromValue(PerformMult(operation, col, num)), this); } case kPairSampleSample: @@ -337,8 +327,7 @@ void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QSt } } - output->Push(NodeValue::kSamples, QVariant::fromValue(mixed_samples), this); - break; + return NodeValue(NodeValue::kSamples, QVariant::fromValue(mixed_samples), this); } case kPairTextureColor: @@ -369,7 +358,7 @@ void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QSt } } else if (pairing == kPairTextureMatrix) { // Only allow matrix multiplication - const QVector2D &sequence_res = globals.nonsquare_resolution(); + const QVector2D &sequence_res = p.nonsquare_resolution(); QVector2D texture_res(texture->params().width() * texture->pixel_aspect_ratio().toDouble(), texture->params().height()); QMatrix4x4 adjusted_matrix = TransformDistortNode::AdjustMatrixByResolutions(number_val.toMatrix(), @@ -388,10 +377,10 @@ void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QSt if (operation_is_noop) { // Just push texture as-is - output->Push(texture_val); + return texture_val; } else { // Push shader job - output->Push(NodeValue::kTexture, Texture::Job(globals.vparams(), job), this); + return NodeValue(NodeValue::kTexture, Texture::Job(p.vparams(), job), this); } break; } @@ -419,11 +408,11 @@ void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QSt } } - output->Push(NodeValue::kSamples, QVariant::fromValue(buffer), this); + return NodeValue(NodeValue::kSamples, QVariant::fromValue(buffer), this); } else { - SampleJob job(globals.time(), val_a.type() == NodeValue::kSamples ? val_a : val_b); + SampleJob job(p.time(), val_a.type() == NodeValue::kSamples ? val_a : val_b); job.Insert(number_param, NodeValue(NodeValue::kFloat, number, this)); - output->Push(NodeValue::kSamples, QVariant::fromValue(job), this); + return NodeValue(NodeValue::kSamples, QVariant::fromValue(job), this); } } break; @@ -433,6 +422,8 @@ void MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QSt case kPairCount: break; } + + return NodeValue(); } void MathNodeBase::ProcessSamplesInternal(const NodeValueRow &values, MathNodeBase::Operation operation, const QString ¶m_a_in, const QString ¶m_b_in, const olive::SampleBuffer &input, olive::SampleBuffer &output, int index) const diff --git a/app/node/math/math/mathbase.h b/app/node/math/math/mathbase.h index 674f4d85a4..f84d7ebba3 100644 --- a/app/node/math/math/mathbase.h +++ b/app/node/math/math/mathbase.h @@ -119,9 +119,9 @@ class MathNodeBase : public Node ShaderCode GetShaderCodeInternal(const QString &shader_id, const QString ¶m_a_in, const QString ¶m_b_in) const; - void PushVector(NodeValueTable* output, NodeValue::Type type, const QVector4D& vec) const; + NodeValue PushVector(NodeValue::Type type, const QVector4D& vec) const; - void ValueInternal(Operation operation, Pairing pairing, const QString& param_a_in, const NodeValue &val_a, const QString& param_b_in, const NodeValue& val_b, const NodeGlobals &globals, NodeValueTable *output) const; + NodeValue ValueInternal(Operation operation, Pairing pairing, const QString& param_a_in, const NodeValue &val_a, const QString& param_b_in, const NodeValue& val_b, const ValueParams &p) const; void ProcessSamplesInternal(const NodeValueRow &values, Operation operation, const QString& param_a_in, const QString& param_b_in, const SampleBuffer &input, SampleBuffer &output, int index) const; diff --git a/app/node/math/merge/merge.cpp b/app/node/math/merge/merge.cpp index e95e0ff99f..03417c2538 100644 --- a/app/node/math/merge/merge.cpp +++ b/app/node/math/merge/merge.cpp @@ -20,8 +20,6 @@ #include "merge.h" -#include "node/traverser.h" - namespace olive { const QString MergeNode::kBaseIn = QStringLiteral("base_in"); @@ -74,23 +72,27 @@ ShaderCode MergeNode::GetShaderCode(const ShaderRequest &request) const return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/alphaover.frag")); } -void MergeNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue MergeNode::Value(const ValueParams &p) const { + NodeValue base_val = GetInputValue(p, kBaseIn); + NodeValue blend_val = GetInputValue(p, kBlendIn); - TexturePtr base_tex = value[kBaseIn].toTexture(); - TexturePtr blend_tex = value[kBlendIn].toTexture(); + TexturePtr base_tex = base_val.toTexture(); + TexturePtr blend_tex = blend_val.toTexture(); if (base_tex || blend_tex) { if (!base_tex || (blend_tex && blend_tex->channel_count() < VideoParams::kRGBAChannelCount)) { // We only have a blend texture or the blend texture is RGB only, no need to alpha over - table->Push(value[kBlendIn]); + return blend_val; } else if (!blend_tex) { // We only have a base texture, no need to alpha over - table->Push(value[kBaseIn]); + return base_val; } else { - table->Push(NodeValue::kTexture, base_tex->toJob(ShaderJob(value)), this); + return NodeValue(NodeValue::kTexture, base_tex->toJob(CreateJob(p)), this); } } + + return NodeValue(); } } diff --git a/app/node/math/merge/merge.h b/app/node/math/merge/merge.h index 52dd5c097f..5cb3555903 100644 --- a/app/node/math/merge/merge.h +++ b/app/node/math/merge/merge.h @@ -41,7 +41,7 @@ class MergeNode : public Node virtual void Retranslate() override; virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; static const QString kBaseIn; static const QString kBlendIn; diff --git a/app/node/math/trigonometry/trigonometry.cpp b/app/node/math/trigonometry/trigonometry.cpp index eb85571574..ec82f479d2 100644 --- a/app/node/math/trigonometry/trigonometry.cpp +++ b/app/node/math/trigonometry/trigonometry.cpp @@ -77,9 +77,9 @@ void TrigonometryNode::Retranslate() SetInputName(kXIn, tr("Value")); } -void TrigonometryNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue TrigonometryNode::Value(const ValueParams &p) const { - double x = value[kXIn].toDouble(); + double x = GetInputValue(p, kXIn).toDouble(); switch (static_cast(GetStandardValue(kMethodIn).toInt())) { case kOpSine: @@ -111,7 +111,7 @@ void TrigonometryNode::Value(const NodeValueRow &value, const NodeGlobals &globa break; } - table->Push(NodeValue::kFloat, x, this); + return NodeValue(NodeValue::kFloat, x, this); } } diff --git a/app/node/math/trigonometry/trigonometry.h b/app/node/math/trigonometry/trigonometry.h index 21b82a6780..d28c645d61 100644 --- a/app/node/math/trigonometry/trigonometry.h +++ b/app/node/math/trigonometry/trigonometry.h @@ -40,7 +40,7 @@ class TrigonometryNode : public Node virtual void Retranslate() override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; static const QString kMethodIn; static const QString kXIn; diff --git a/app/node/node.cpp b/app/node/node.cpp index 9930aa7e19..2dc9d0cb11 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -864,6 +864,46 @@ int Node::InputArraySize(const QString &id) const } } +NodeValue Node::GetInputValue(const ValueParams &g, const QString &input, int element) const +{ + if (g.is_cancelled()) { + return NodeValue(); + } + + TimeRange adjusted_time = InputTimeAdjustment(input, element, g.time(), true); + + if (Node *output = GetConnectedOutput(input, element)) { + ValueParams adj_param(g.vparams(), g.aparams(), adjusted_time, g.loop_mode(), g.cancel_atom()); + + NodeValue ret; + + while (output) { + if (output->GetStandardValue(kEnabledInput).toBool()) { + ret = output->Value(adj_param); + break; + } else { + output = output->GetConnectedOutput(output->GetEffectInput()); + } + } + + return ret; + } else { + if (element == -1 && InputIsArray(input)) { + NodeValueArray array; + + int sz = InputArraySize(input); + for (int i = 0; i < sz; i++) { + NodeValue ele(GetInputDataType(input), GetValueAtTime(input, adjusted_time.in(), i), this, false); + array[i] = ele; + } + + return NodeValue(GetInputDataType(input), array, this, true); + } else { + return NodeValue(GetInputDataType(input), GetValueAtTime(input, adjusted_time.in(), element), this, false); + } + } +} + void Node::SetValueHintForInput(const QString &input, const ValueHint &hint, int element) { value_hints_.insert({input, element}, hint); @@ -921,12 +961,10 @@ void Node::SetInputFlag(const QString &input, InputFlag f, bool on) } } -void Node::Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue Node::Value(const ValueParams &p) const { - // Do nothing - Q_UNUSED(value) - Q_UNUSED(globals) - Q_UNUSED(table) + Q_UNUSED(p) + return NodeValue(); } void Node::InvalidateCache(const TimeRange &range, const QString &from, int element, InvalidateCacheOptions options) @@ -2395,12 +2433,10 @@ void Node::SetValueAtTime(const NodeInput &input, const rational &time, const QV bool FindPathInternal(std::list &vec, Node *from, Node *to, int &path_index) { - for (auto it=from->output_connections().cbegin(); it!=from->output_connections().cend(); it++) { - const NodeInput &next = it->second; - - vec.push_back(next); + for (auto it = to->input_connections().cbegin(); it != to->input_connections().cend(); it++) { + vec.push_front(it->first); - if (next.node() == to) { + if (it->second == from) { // Found a path! Determine if it's the index we want if (path_index == 0) { // It is! @@ -2411,11 +2447,11 @@ bool FindPathInternal(std::list &vec, Node *from, Node *to, int &path } } - if (FindPathInternal(vec, next.node(), to, path_index)) { + if (FindPathInternal(vec, from, it->second, path_index)) { return true; } - vec.pop_back(); + vec.pop_front(); } return false; diff --git a/app/node/node.h b/app/node/node.h index 6058122dee..8c11f2a142 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -200,79 +200,16 @@ class Node : public QObject virtual QVariant data(const DataType &d) const; - const QVector& inputs() const - { - return input_ids_; - } - - virtual QVector IgnoreInputsForRendering() const - { - return QVector(); - } - - class ActiveElements - { - public: - enum Mode { - kAllElements, - kSpecified, - kNoElements - }; - - ActiveElements(Mode m = kAllElements) - { - mode_ = m; - } - - Mode mode() const { return mode_; } - std::list elements() const { return elements_; } - - void add(int e) - { - elements_.push_back(e); - mode_ = kSpecified; - } - - private: - Mode mode_; - std::list elements_; - - }; - - virtual ActiveElements GetActiveElementsAtTime(const QString &input, const TimeRange &r) const - { - return ActiveElements::kAllElements; - } - - bool HasInputWithID(const QString& id) const - { - return input_ids_.contains(id); - } - - bool HasParamWithID(const QString& id) const - { - return HasInputWithID(id); - } - - FrameHashCache* video_frame_cache() const - { - return video_cache_; - } + const QVector& inputs() const { return input_ids_; } - ThumbnailCache* thumbnail_cache() const - { - return thumbnail_cache_; - } + bool HasInputWithID(const QString& id) const { return input_ids_.contains(id); } + bool HasParamWithID(const QString& id) const { return HasInputWithID(id); } - AudioPlaybackCache* audio_playback_cache() const - { - return audio_cache_; - } - - AudioWaveformCache* waveform_cache() const - { - return waveform_cache_; - } + // Node caches + FrameHashCache* video_frame_cache() const { return video_cache_; } + ThumbnailCache* thumbnail_cache() const { return thumbnail_cache_; } + AudioPlaybackCache* audio_playback_cache() const { return audio_cache_; } + AudioWaveformCache* waveform_cache() const { return waveform_cache_; } virtual TimeRange GetVideoCacheRange() const { return TimeRange(); } virtual TimeRange GetAudioCacheRange() const { return TimeRange(); } @@ -419,15 +356,6 @@ class Node : public QObject return IsInputConnected(input.input(), input.element()); } - virtual bool IsInputConnectedForRender(const QString& input, int element = -1) const - { - return IsInputConnected(input, element); - } - bool IsInputConnectedForRender(const NodeInput& input) const - { - return IsInputConnectedForRender(input.input(), input.element()); - } - bool IsInputStatic(const QString& input, int element = -1) const { return !IsInputConnected(input, element) && !IsInputKeyframing(input, element); @@ -445,16 +373,6 @@ class Node : public QObject return GetConnectedOutput(input.input(), input.element()); } - virtual Node *GetConnectedRenderOutput(const QString& input, int element = -1) const - { - return GetConnectedOutput(input, element); - } - - Node *GetConnectedRenderOutput(const NodeInput& input) const - { - return GetConnectedRenderOutput(input.input(), input.element()); - } - bool IsUsingStandardValue(const QString& input, int track, int element = -1) const; NodeValue::Type GetInputDataType(const QString& id) const; @@ -635,6 +553,8 @@ class Node : public QObject int InputArraySize(const QString& id) const; + NodeValue GetInputValue(const ValueParams &g, const QString &input, int element = -1) const; + NodeInputImmediate* GetImmediate(const QString& input, int element) const; NodeInput GetEffectInput() @@ -906,7 +826,7 @@ class Node : public QObject * corresponding output if it's connected to one. If your node doesn't directly deal with time, the default behavior * of the NodeParam objects will handle everything related to it automatically. */ - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const; + virtual NodeValue Value(const ValueParams &p) const; bool HasGizmos() const { @@ -918,9 +838,9 @@ class Node : public QObject return gizmos_; } - virtual QTransform GizmoTransformation(const NodeValueRow &row, const NodeGlobals &globals) const { return QTransform(); } + virtual QTransform GizmoTransformation(const ValueParams &p) const { return QTransform(); } - virtual void UpdateGizmoPositions(const NodeValueRow &row, const NodeGlobals &globals){} + virtual void UpdateGizmoPositions(const ValueParams &p){} const QString& GetLabel() const; void SetLabel(const QString& s); @@ -1080,8 +1000,23 @@ class Node : public QObject return AddDraggableGizmo(refs, behavior); } + template + T CreateJob(const ValueParams &p) const + { + T job; + for (const QString &input : inputs()) { + job.Insert(input, GetInputValue(p, input)); + } + return job; + } + + SampleJob CreateSampleJob(const ValueParams &p, const QString &sample_input) const + { + return SampleJob(p.time(), GetInputValue(p, sample_input)); + } + protected slots: - virtual void GizmoDragStart(const olive::NodeValueRow &row, double x, double y, const olive::core::rational &time){} + virtual void GizmoDragStart(const olive::ValueParams &p, double x, double y, const olive::core::rational &time){} virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers){} diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index 0126bbc716..30a8022eed 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -99,57 +99,26 @@ QString Track::Description() const "a Sequence."); } -Node::ActiveElements Track::GetActiveElementsAtTime(const QString &input, const TimeRange &r) const +NodeValue Track::Value(const ValueParams &p) const { - if (input == kBlockInput) { - if (IsMuted() || blocks_.empty() || r.in() >= track_length() || r.out() <= 0) { - return ActiveElements::kNoElements; - } else { - int start = GetBlockIndexAtTime(r.in()); - int end = GetBlockIndexAtTime(r.out()); - - if (start == -1) { - start = 0; - } - if (end == -1) { - end = blocks_.size()-1; - } - - if (blocks_.at(end)->in() == r.out()) { - end--; - } + if (!IsMuted() && !blocks_.empty() && p.time().in() < track_length() && p.time().out() > 0) { + if (this->type() == Track::kVideo) { + // Just pass straight through + int index = GetBlockIndexAtTime(p.time().in()); + if (index != -1) { + Block *b = blocks_.at(index); - ActiveElements a; - for (int i=start; i<=end; i++) { - Block *b = blocks_.at(i); if (b->is_enabled() && (dynamic_cast(b) || dynamic_cast(b))) { - a.add(GetArrayIndexFromCacheIndex(i)); + return GetInputValue(p, kBlockInput, GetArrayIndexFromCacheIndex(index)); } } - - if (a.elements().empty()) { - return ActiveElements::kNoElements; - } else { - return a; - } + } else if (this->type() == Track::kAudio) { + // Audio + return ProcessAudioTrack(p); } - } else { - return super::GetActiveElementsAtTime(input, r); } -} -void Track::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const -{ - if (this->type() == Track::kVideo) { - // Just pass straight through - NodeValueArray a = value[kBlockInput].toArray(); - if (!a.empty()) { - table->Push(a.begin()->second); - } - } else if (this->type() == Track::kAudio) { - // Audio - ProcessAudioTrack(value, globals, table); - } + return NodeValue(); } TimeRange Track::InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time, bool clamp) const @@ -625,29 +594,48 @@ int Track::GetBlockIndexAtTime(const rational &time) const return -1; } -void Track::ProcessAudioTrack(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue Track::ProcessAudioTrack(const ValueParams &p) const { - const TimeRange &range = globals.time(); + const TimeRange &range = p.time(); + + int start = GetBlockIndexAtTime(range.in()); + int end = GetBlockIndexAtTime(range.out()); + + if (start == -1) { + start = 0; + } + if (end == -1) { + end = blocks_.size()-1; + } + + if (blocks_.at(end)->in() == range.out()) { + end--; + } + + QVector blocks; + for (int i=start; i<=end; i++) { + Block *b = blocks_.at(i); + if (b->is_enabled() && (dynamic_cast(b) || dynamic_cast(b))) { + blocks.append(i); + } + } // All these blocks will need to output to a buffer so we create one here - SampleBuffer block_range_buffer(globals.aparams(), range.length()); + SampleBuffer block_range_buffer(p.aparams(), range.length()); block_range_buffer.silence(); - // Loop through active blocks retrieving their audio - NodeValueArray arr = value[kBlockInput].toArray(); - - for (auto it=arr.cbegin(); it!=arr.cend(); it++) { - Block *b = blocks_.at(GetCacheIndexFromArrayIndex(it->first)); + for (int block_index : blocks) { + Block *b = blocks_.at(block_index); TimeRange range_for_block(qMax(b->in(), range.in()), qMin(b->out(), range.out())); qint64 source_offset = 0; - qint64 destination_offset = globals.aparams().time_to_samples(range_for_block.in() - range.in()); - qint64 max_dest_sz = globals.aparams().time_to_samples(range_for_block.length()); + qint64 destination_offset = p.aparams().time_to_samples(range_for_block.in() - range.in()); + qint64 max_dest_sz = p.aparams().time_to_samples(range_for_block.length()); // Destination buffer - SampleBuffer samples_from_this_block = it->second.toSamples(); + SampleBuffer samples_from_this_block = GetInputValue(p, kBlockInput, GetArrayIndexFromCacheIndex(block_index)).toSamples(); if (samples_from_this_block.is_allocated()) { // If this is a clip, we might have extra speed/reverse information @@ -715,7 +703,7 @@ void Track::ProcessAudioTrack(const NodeValueRow &value, const NodeGlobals &glob } } - table->Push(NodeValue::kSamples, QVariant::fromValue(block_range_buffer), this); + return NodeValue(NodeValue::kSamples, QVariant::fromValue(block_range_buffer), this); } int Track::ConnectBlock(Block *b) diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index 4d00b485b2..d6eb2379e0 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -54,8 +54,7 @@ class Track : public Node virtual QVector Category() const override; virtual QString Description() const override; - virtual ActiveElements GetActiveElementsAtTime(const QString &input, const TimeRange &r) const override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time, bool clamp) const override; @@ -460,12 +459,14 @@ public slots: int GetBlockIndexAtTime(const rational &time) const; - void ProcessAudioTrack(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const; + NodeValue ProcessAudioTrack(const ValueParams &p) const; int ConnectBlock(Block *b); void UpdateArrayMap(); + TimeRange TransformTimeForBlock(const TimeRange &input_time, int index); + TimeRangeList block_length_pending_invalidations_; QVector blocks_; diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index adada2aa50..42c220bb92 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -22,7 +22,6 @@ #include "config/config.h" #include "core.h" -#include "node/traverser.h" namespace olive { @@ -353,25 +352,15 @@ void ViewerOutput::InputDisconnectedEvent(const QString &input, int element, Nod rational ViewerOutput::VerifyLengthInternal(Track::Type type) const { - NodeTraverser traverser; - switch (type) { case Track::kVideo: - if (IsInputConnected(kTextureInput)) { - NodeValueTable t = traverser.GenerateTable(GetConnectedOutput(kTextureInput), TimeRange(0, 0)); - rational r = t.Get(NodeValue::kRational, QStringLiteral("length")).toRational(); - if (!r.isNaN()) { - return r; - } + if (ViewerOutput *v = dynamic_cast(GetConnectedOutput(kTextureInput))) { + return v->GetVideoLength(); } break; case Track::kAudio: - if (IsInputConnected(kSamplesInput)) { - NodeValueTable t = traverser.GenerateTable(GetConnectedOutput(kSamplesInput), TimeRange(0, 0)); - rational r = t.Get(NodeValue::kRational, QStringLiteral("length")).toRational(); - if (!r.isNaN()) { - return r; - } + if (ViewerOutput *v = dynamic_cast(GetConnectedOutput(kSamplesInput))) { + return v->GetAudioLength(); } break; case Track::kNone: @@ -416,18 +405,20 @@ void ViewerOutput::SetWaveformEnabled(bool e) } } -void ViewerOutput::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue ViewerOutput::Value(const ValueParams &p) const { if (HasInputWithID(kTextureInput)) { - NodeValue repush = value[kTextureInput]; + NodeValue repush = GetInputValue(p, kTextureInput); repush.set_tag(Track::Reference(Track::kVideo, 0).ToString()); - table->Push(repush); + return repush; } if (HasInputWithID(kSamplesInput)) { - NodeValue repush = value[kSamplesInput]; + NodeValue repush = GetInputValue(p, kSamplesInput); repush.set_tag(Track::Reference(Track::kAudio, 0).ToString()); - table->Push(value[kSamplesInput]); + return repush; } + + return NodeValue(); } bool ViewerOutput::LoadCustom(QXmlStreamReader *reader, SerializedData *data) diff --git a/app/node/output/viewer/viewer.h b/app/node/output/viewer/viewer.h index 0ad696614d..e6f294f4ba 100644 --- a/app/node/output/viewer/viewer.h +++ b/app/node/output/viewer/viewer.h @@ -186,7 +186,7 @@ class ViewerOutput : public Node bool IsVideoAutoCacheEnabled() const { qDebug() << "sequence ac is a stub"; return false; } void SetVideoAutoCacheEnabled(bool e) { qDebug() << "sequence ac is a stub"; } - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; const EncodingParams &GetLastUsedEncodingParams() const { return last_used_encoding_params_; } void SetLastUsedEncodingParams(const EncodingParams &p) { last_used_encoding_params_ = p; } diff --git a/app/node/project/footage/footage.cpp b/app/node/project/footage/footage.cpp index 1ff8cdce04..d7fc1a6b00 100644 --- a/app/node/project/footage/footage.cpp +++ b/app/node/project/footage/footage.cpp @@ -231,22 +231,17 @@ QString Footage::DescribeSubtitleStream(const SubtitleParams ¶ms) .arg(QString::number(params.stream_index())); } -void Footage::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue Footage::Value(const ValueParams &p) const { - Q_UNUSED(globals) - // Pop filename from table - QString file = value[kFilenameInput].toString(); + QString file = GetInputValue(p, kFilenameInput).toString(); // If the file exists and the reference is valid, push a footage job to the renderer if (QFileInfo::exists(file)) { - // Push length - table->Push(NodeValue::kRational, QVariant::fromValue(GetLength()), this, QStringLiteral("length")); - // Push each stream as a footage job for (int i=0; i 1) { + if (p.vparams().divider() > 1) { // Use a divider appropriate for this target resolution - int calculated = VideoParams::GetDividerForTargetResolution(vp.width(), vp.height(), globals.vparams().effective_width(), globals.vparams().effective_height()); - vp.set_divider(std::min(calculated, globals.vparams().divider())); + int calculated = VideoParams::GetDividerForTargetResolution(vp.width(), vp.height(), p.vparams().effective_width(), p.vparams().effective_height()); + vp.set_divider(std::min(calculated, p.vparams().divider())); } else { // Render everything at full res vp.set_divider(1); @@ -266,16 +261,18 @@ void Footage::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeV job.set_video_params(vp); - table->Push(NodeValue::kTexture, Texture::Job(vp, job), this, ref.ToString()); + return NodeValue(NodeValue::kTexture, Texture::Job(vp, job), this, ref.ToString()); } else if (ref.type() == Track::kAudio) { AudioParams ap = GetAudioParams(ref.index()); job.set_audio_params(ap); job.set_cache_path(project()->cache_path()); - table->Push(NodeValue::kSamples, QVariant::fromValue(job), this, ref.ToString()); + return NodeValue(NodeValue::kSamples, QVariant::fromValue(job), this, ref.ToString()); } } } + + return NodeValue(); } QString Footage::GetStreamTypeName(Track::Type type) diff --git a/app/node/project/footage/footage.h b/app/node/project/footage/footage.h index 39d5c1f586..ff3f7bdeaa 100644 --- a/app/node/project/footage/footage.h +++ b/app/node/project/footage/footage.h @@ -157,7 +157,7 @@ class Footage : public ViewerOutput static QString DescribeAudioStream(const AudioParams& params); static QString DescribeSubtitleStream(const SubtitleParams& params); - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; static QString GetStreamTypeName(Track::Type type); diff --git a/app/node/time/timeformat/timeformat.cpp b/app/node/time/timeformat/timeformat.cpp index 50a870a86a..a0f6fa49bf 100644 --- a/app/node/time/timeformat/timeformat.cpp +++ b/app/node/time/timeformat/timeformat.cpp @@ -66,14 +66,14 @@ void TimeFormatNode::Retranslate() SetInputName(kLocalTimeInput, tr("Interpret time as local time")); } -void TimeFormatNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue TimeFormatNode::Value(const ValueParams &p) const { - qint64 ms_since_epoch = value[kTimeInput].toDouble()*1000; - bool time_is_local = value[kLocalTimeInput].toBool(); + qint64 ms_since_epoch = GetInputValue(p, kTimeInput).toDouble()*1000; + bool time_is_local = GetInputValue(p, kLocalTimeInput).toBool(); QDateTime dt = QDateTime::fromMSecsSinceEpoch(ms_since_epoch, time_is_local ? Qt::LocalTime : Qt::UTC); - QString format = value[kFormatInput].toString(); + QString format = GetInputValue(p, kFormatInput).toString(); QString output = dt.toString(format); - table->Push(NodeValue(NodeValue::kText, output, this)); + return NodeValue(NodeValue::kText, output, this); } } diff --git a/app/node/time/timeformat/timeformat.h b/app/node/time/timeformat/timeformat.h index ddc7ac8c1a..ba112f376a 100644 --- a/app/node/time/timeformat/timeformat.h +++ b/app/node/time/timeformat/timeformat.h @@ -40,7 +40,7 @@ class TimeFormatNode : public Node virtual void Retranslate() override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &globals) const override; static const QString kTimeInput; static const QString kFormatInput; diff --git a/app/node/time/timeoffset/timeoffsetnode.cpp b/app/node/time/timeoffset/timeoffsetnode.cpp index 1a6201b5e8..c4d6f9c275 100644 --- a/app/node/time/timeoffset/timeoffsetnode.cpp +++ b/app/node/time/timeoffset/timeoffsetnode.cpp @@ -67,9 +67,9 @@ TimeRange TimeOffsetNode::OutputTimeAdjustment(const QString &input, int element return super::OutputTimeAdjustment(input, element, input_time); } -void TimeOffsetNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue TimeOffsetNode::Value(const ValueParams &p) const { - table->Push(value[kInputInput]); + return GetInputValue(p, kInputInput); } rational TimeOffsetNode::GetRemappedTime(const rational &input) const diff --git a/app/node/time/timeoffset/timeoffsetnode.h b/app/node/time/timeoffset/timeoffsetnode.h index f1890924f4..1aead05170 100644 --- a/app/node/time/timeoffset/timeoffsetnode.h +++ b/app/node/time/timeoffset/timeoffsetnode.h @@ -57,7 +57,7 @@ class TimeOffsetNode : public Node virtual void Retranslate() override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; static const QString kTimeInput; static const QString kInputInput; diff --git a/app/node/time/timeremap/timeremap.cpp b/app/node/time/timeremap/timeremap.cpp index d3035bb3da..6737e10d05 100644 --- a/app/node/time/timeremap/timeremap.cpp +++ b/app/node/time/timeremap/timeremap.cpp @@ -87,9 +87,9 @@ void TimeRemapNode::Retranslate() SetInputName(kInputInput, QStringLiteral("Input")); } -void TimeRemapNode::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +NodeValue TimeRemapNode::Value(const ValueParams &p) const { - table->Push(value[kInputInput]); + return GetInputValue(p, kInputInput); } rational TimeRemapNode::GetRemappedTime(const rational &input) const diff --git a/app/node/time/timeremap/timeremap.h b/app/node/time/timeremap/timeremap.h index 3ba9cd8e13..6f6f77bf3a 100644 --- a/app/node/time/timeremap/timeremap.h +++ b/app/node/time/timeremap/timeremap.h @@ -43,7 +43,7 @@ class TimeRemapNode : public Node virtual void Retranslate() override; - virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + virtual NodeValue Value(const ValueParams &p) const override; static const QString kTimeInput; static const QString kInputInput; diff --git a/app/node/traverser.cpp b/app/node/traverser.cpp deleted file mode 100644 index 86e91ea2d7..0000000000 --- a/app/node/traverser.cpp +++ /dev/null @@ -1,479 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2022 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#include "traverser.h" - -#include "node.h" -#include "node/block/clip/clip.h" -#include "render/job/footagejob.h" -#include "render/rendermanager.h" - -namespace olive { - -NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const TimeRange &range) -{ - NodeValueDatabase database; - - // HACK: Pick up loop mode from clips - LoopMode old_loop_mode = loop_mode_; - if (const ClipBlock *clip = dynamic_cast(node)) { - loop_mode_ = clip->loop_mode(); - } - - // We need to insert tables into the database for each input - auto ignore = node->IgnoreInputsForRendering(); - foreach (const QString& input, node->inputs()) { - if (IsCancelled()) { - return NodeValueDatabase(); - } - - if (ignore.contains(input)) { - continue; - } - - database.Insert(input, ProcessInput(node, input, range)); - } - - loop_mode_ = old_loop_mode; - - return database; -} - -NodeValueRow NodeTraverser::GenerateRow(NodeValueDatabase *database, const Node *node, const TimeRange &range) -{ - // Generate row - NodeValueRow row; - for (auto it=database->begin(); it!=database->end(); it++) { - // Get hint for which value should be pulled - NodeValue value = GenerateRowValue(node, it.key(), &it.value(), range); - row.insert(it.key(), value); - } - - // TEMP: Audio needs to be refactored to work with new job system. But refactoring hasn't been - // done yet, so we emulate old behavior here JUST FOR AUDIO. - for (auto it=row.begin(); it!=row.end(); it++) { - NodeValue &val = it.value(); - if (val.type() == NodeValue::kSamples) { - ResolveJobs(val); - } - } - // END TEMP - - return row; -} - -NodeValueRow NodeTraverser::GenerateRow(const Node *node, const TimeRange &range) -{ - // Generate database of input values of node - NodeValueDatabase database = GenerateDatabase(node, range); - - return GenerateRow(&database, node, range); -} - -NodeValue NodeTraverser::GenerateRowValue(const Node *node, const QString &input, NodeValueTable *table, const TimeRange &time) -{ - NodeValue value = GenerateRowValueElement(node, input, -1, table, time); - - if (value.array()) { - // Resolve each element of array - NodeValueTableArray tables = value.value(); - NodeValueArray output; - - for (auto it=tables.begin(); it!=tables.end(); it++) { - output[it->first] = GenerateRowValueElement(node, input, it->first, &it->second, time); - } - - value = NodeValue(value.type(), QVariant::fromValue(output), value.source(), value.array(), value.tag()); - } - - return value; -} - -NodeValue NodeTraverser::GenerateRowValueElement(const Node *node, const QString &input, int element, NodeValueTable *table, const TimeRange &time) -{ - int value_index = GenerateRowValueElementIndex(node->GetValueHintForInput(input, element), node->GetInputDataType(input), table); - - if (value_index == -1) { - // If value was -1, try getting the last value - value_index = table->Count() - 1; - } - - if (value_index == -1) { - // If value is still -1, assume the table is empty and return nothing - return NodeValue(); - } - - NodeValue value = table->TakeAt(value_index); - - if (value.type() == NodeValue::kTexture && UseCache()) { - if (TexturePtr tex = value.toTexture()) { - QMutexLocker locker(node->video_frame_cache()->mutex()); - - node->video_frame_cache()->LoadState(); - - QString cache = node->video_frame_cache()->GetValidCacheFilename(time.in()); - if (!cache.isEmpty()) { - value.set_value(tex->toJob(CacheJob(cache, value))); - } - } - } - - return value; -} - -int NodeTraverser::GenerateRowValueElementIndex(const Node::ValueHint &hint, NodeValue::Type preferred_type, const NodeValueTable *table) -{ - QVector types = hint.types(); - - if (types.isEmpty()) { - types.append(preferred_type); - } - - if (hint.index() == -1) { - // Get most recent value with this type and tag - return table->GetValueIndex(types, hint.tag()); - } else { - // Try to find value at this index - int index = table->Count() - 1 - hint.index(); - int diff = 0; - - while (index + diff < table->Count() && index - diff >= 0) { - if (index + diff < table->Count() && types.contains(table->at(index + diff).type())) { - return index + diff; - } - if (index - diff >= 0 && types.contains(table->at(index - diff).type())) { - return index - diff; - } - diff++; - } - - return -1; - } -} - -int NodeTraverser::GenerateRowValueElementIndex(const Node *node, const QString &input, int element, const NodeValueTable *table) -{ - return GenerateRowValueElementIndex(node->GetValueHintForInput(input, element), node->GetInputDataType(input), table); -} - -void NodeTraverser::Transform(QTransform *transform, const Node *start, const Node *end, const TimeRange &range) -{ - transform_ = transform; - transform_start_ = start; - transform_now_ = nullptr; - - GenerateTable(end, range); - - transform_ = nullptr; -} - -NodeValueTable NodeTraverser::ProcessInput(const Node* node, const QString& input, const TimeRange& range) -{ - // If input is connected, retrieve value directly - if (node->IsInputConnectedForRender(input)) { - - TimeRange adjusted_range = node->InputTimeAdjustment(input, -1, range, true); - - // Value will equal something from the connected node, follow it - Node *output = node->GetConnectedRenderOutput(input); - NodeValueTable table = GenerateTable(output, adjusted_range, node); - return table; - - } else { - - // Store node - QVariant return_val; - bool is_array = node->InputIsArray(input); - - if (is_array) { - - // Value is an array, we will return a list of NodeValueTables - NodeValueTableArray array_tbl; - - Node::ActiveElements a = node->GetActiveElementsAtTime(input, range); - if (a.mode() == Node::ActiveElements::kAllElements) { - int sz = node->InputArraySize(input); - for (int i=0; iInputTimeAdjustment(input, -1, range, true); - - return_val = node->GetValueAtTime(input, adjusted_range.in()); - - } - - NodeValueTable return_table; - return_table.Push(node->GetInputDataType(input), return_val, node, is_array); - return return_table; - - } -} - -void NodeTraverser::ProcessInputElement(NodeValueTableArray &array_tbl, const Node *node, const QString &input, int element, const TimeRange &range) -{ - NodeValueTable& sub_tbl = array_tbl[element]; - TimeRange adjusted_range = node->InputTimeAdjustment(input, element, range, true); - - if (node->IsInputConnectedForRender(input, element)) { - Node *output = node->GetConnectedRenderOutput(input, element); - sub_tbl = GenerateTable(output, adjusted_range, node); - } else { - QVariant input_value = node->GetValueAtTime(input, adjusted_range.in(), element); - sub_tbl.Push(node->GetInputDataType(input), input_value, node); - } -} - -NodeTraverser::NodeTraverser() : - cancel_(nullptr), - transform_(nullptr), - loop_mode_(LoopMode::kLoopModeOff) -{ -} - -class GTTTime -{ -public: - GTTTime(const Node *n) { t = QDateTime::currentMSecsSinceEpoch(); node = n; } - - ~GTTTime() { qDebug() << "GT for" << node << "took" << (QDateTime::currentMSecsSinceEpoch() - t); } - - qint64 t; - const Node *node; - -}; - -NodeValueTable NodeTraverser::GenerateTable(const Node *n, const TimeRange& range, const Node *next_node) -{ - // NOTE: Times how long a node takes to process, useful for profiling. - //GTTTime gtt(n);Q_UNUSED(gtt); - - // Use table cache to skip processing where available - if (value_cache_.contains(n)) { - QHash &node_value_map = value_cache_[n]; - if (node_value_map.contains(range)) { - return node_value_map.value(range); - } - } - - // Generate row for node - NodeValueDatabase database = GenerateDatabase(n, range); - - // Check for bypass - bool is_enabled; - if (!database[Node::kEnabledInput].Has(NodeValue::kBoolean)) { - // Fallback if we couldn't find a bool value - is_enabled = true; - } else { - is_enabled = database[Node::kEnabledInput].Get(NodeValue::kBoolean).toBool(); - } - - NodeValueTable table; - - if (is_enabled) { - NodeValueRow row = GenerateRow(&database, n, range); - - // Generate output table - table = database.Merge(); - - // By this point, the node should have all the inputs it needs to render correctly - NodeGlobals globals(video_params_, audio_params_, range, loop_mode_); - n->Value(row, globals, &table); - - // `transform_now_` is the next node in the path that needs to be traversed. It only ever goes - // "down" the graph so that any traversing going back up doesn't unnecessarily transform - // from unrelated nodes or the same node twice - if (transform_) { - if (transform_now_ == n || transform_start_ == n) { - if (transform_now_ == n) { - QTransform t = n->GizmoTransformation(row, globals); - if (!t.isIdentity()) { - (*transform_) *= t; - } - } - - transform_now_ = next_node; - } - } - } else { - // If this node has an effect input, ensure that is pushed last - NodeValueTable primary; - if (!n->GetEffectInputID().isEmpty()) { - primary = database.Take(n->GetEffectInputID()); - } - - table = database.Merge(); - table.Push(primary); - } - - value_cache_[n][range] = table; - - return table; -} - -TexturePtr NodeTraverser::ProcessVideoCacheJob(const CacheJob *val) -{ - return nullptr; -} - -QVector2D NodeTraverser::GenerateResolution() const -{ - return QVector2D(video_params_.square_pixel_width(), video_params_.height()); -} - -void NodeTraverser::ResolveJobs(NodeValue &val) -{ - if (val.type() == NodeValue::kTexture) { - - if (TexturePtr job_tex = val.toTexture()) { - if (AcceleratedJob *base_job = job_tex->job()) { - - if (resolved_texture_cache_.contains(job_tex.get())) { - val.set_value(resolved_texture_cache_.value(job_tex.get())); - } else { - // Resolve any sub-jobs - for (auto it=base_job->GetValues().begin(); it!=base_job->GetValues().end(); it++) { - // Jobs will almost always be submitted with one of these types - NodeValue &subval = it.value(); - ResolveJobs(subval); - } - - if (CacheJob *cj = dynamic_cast(base_job)) { - TexturePtr tex = ProcessVideoCacheJob(cj); - if (tex) { - val.set_value(tex); - } else { - val.set_value(cj->GetFallback()); - } - - } else if (ColorTransformJob *ctj = dynamic_cast(base_job)) { - - VideoParams ctj_params = job_tex->params(); - - ctj_params.set_format(GetCacheVideoParams().format()); - - TexturePtr dest = CreateTexture(ctj_params); - - // Resolve input texture - NodeValue v = ctj->GetInputTexture(); - ResolveJobs(v); - ctj->SetInputTexture(v); - - ProcessColorTransform(dest, val.source(), ctj); - - val.set_value(dest); - - } else if (ShaderJob *sj = dynamic_cast(base_job)) { - - VideoParams tex_params = job_tex->params(); - - TexturePtr tex = CreateTexture(tex_params); - - ProcessShader(tex, val.source(), sj); - - val.set_value(tex); - - } else if (GenerateJob *gj = dynamic_cast(base_job)) { - - VideoParams tex_params = job_tex->params(); - - TexturePtr tex = CreateTexture(tex_params); - - ProcessFrameGeneration(tex, val.source(), gj); - - // Convert to reference space - const QString &colorspace = tex_params.colorspace(); - if (!colorspace.isEmpty()) { - // Set format to primary format - tex_params.set_format(GetCacheVideoParams().format()); - - TexturePtr dest = CreateTexture(tex_params); - - ConvertToReferenceSpace(dest, tex, colorspace); - - tex = dest; - } - - val.set_value(tex); - - } else if (FootageJob *fj = dynamic_cast(base_job)) { - - rational footage_time = Footage::AdjustTimeByLoopMode(fj->time().in(), fj->loop_mode(), fj->length(), fj->video_params().video_type(), fj->video_params().frame_rate_as_time_base()); - - TexturePtr tex; - - if (footage_time.isNaN()) { - // Push dummy texture - tex = CreateDummyTexture(fj->video_params()); - } else { - VideoParams managed_params = fj->video_params(); - managed_params.set_format(GetCacheVideoParams().format()); - - tex = CreateTexture(managed_params); - ProcessVideoFootage(tex, fj, footage_time); - } - - val.set_value(tex); - - } - - // Cache resolved value - resolved_texture_cache_.insert(job_tex.get(), val.toTexture()); - } - } - } - - } else if (val.type() == NodeValue::kSamples) { - - if (val.canConvert()) { - - SampleJob job = val.value(); - SampleBuffer output_buffer = CreateSampleBuffer(job.samples().audio_params(), job.samples().sample_count()); - ProcessSamples(output_buffer, val.source(), job.time(), job); - val.set_value(QVariant::fromValue(output_buffer)); - - } else if (val.canConvert()) { - - FootageJob job = val.value(); - SampleBuffer buffer = CreateSampleBuffer(GetCacheAudioParams(), job.time().length()); - ProcessAudioFootage(buffer, &job, job.time()); - val.set_value(buffer); - - } - - } -} - -TexturePtr NodeTraverser::CreateDummyTexture(const VideoParams &p) -{ - return std::make_shared(p); -} - -} diff --git a/app/node/traverser.h b/app/node/traverser.h deleted file mode 100644 index 84284a0d80..0000000000 --- a/app/node/traverser.h +++ /dev/null @@ -1,169 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2022 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#ifndef NODETRAVERSER_H -#define NODETRAVERSER_H - -#include - -#include "codec/decoder.h" -#include "common/cancelableobject.h" -#include "node/output/track/track.h" -#include "render/job/cachejob.h" -#include "render/cancelatom.h" -#include "render/job/footagejob.h" -#include "render/job/colortransformjob.h" -#include "render/job/footagejob.h" -#include "value.h" - -namespace olive { - -class NodeTraverser -{ -public: - NodeTraverser(); - - NodeValueTable GenerateTable(const Node *n, const TimeRange &range, const Node *next_node = nullptr); - - virtual NodeValueDatabase GenerateDatabase(const Node *node, const TimeRange &range); - - NodeValueRow GenerateRow(NodeValueDatabase *database, const Node *node, const TimeRange &range); - NodeValueRow GenerateRow(const Node *node, const TimeRange &range); - - NodeValue GenerateRowValue(const Node *node, const QString &input, NodeValueTable *table, const TimeRange &time); - NodeValue GenerateRowValueElement(const Node *node, const QString &input, int element, NodeValueTable *table, const TimeRange &time); - int GenerateRowValueElementIndex(const Node::ValueHint &hint, NodeValue::Type preferred_type, const NodeValueTable *table); - int GenerateRowValueElementIndex(const Node *node, const QString &input, int element, const NodeValueTable *table); - - void Transform(QTransform *transform, const Node *start, const Node *end, const TimeRange &range); - - const VideoParams& GetCacheVideoParams() const - { - return video_params_; - } - - void SetCacheVideoParams(const VideoParams& params) - { - video_params_ = params; - } - - const AudioParams& GetCacheAudioParams() const - { - return audio_params_; - } - - void SetCacheAudioParams(const AudioParams& params) - { - audio_params_ = params; - } - -protected: - NodeValueTable ProcessInput(const Node *node, const QString &input, const TimeRange &range); - - void ProcessInputElement(NodeValueTableArray &array_tbl, const Node *node, const QString &input, int element, const TimeRange &range); - - virtual void ProcessVideoFootage(TexturePtr destination, const FootageJob *stream, const rational &input_time){} - - virtual void ProcessAudioFootage(SampleBuffer &destination, const FootageJob *stream, const TimeRange &input_time){} - - virtual void ProcessShader(TexturePtr destination, const Node *node, const ShaderJob *job){} - - virtual void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob *job){} - - virtual void ProcessSamples(SampleBuffer &destination, const Node *node, const TimeRange &range, const SampleJob &job){} - - virtual void ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob *job){} - - virtual void ConvertToReferenceSpace(TexturePtr destination, TexturePtr source, const QString &input_cs){} - - virtual TexturePtr ProcessVideoCacheJob(const CacheJob *val); - - virtual TexturePtr CreateTexture(const VideoParams &p) - { - return CreateDummyTexture(p); - } - - virtual SampleBuffer CreateSampleBuffer(const AudioParams ¶ms, int sample_count) - { - // Return dummy by default - return SampleBuffer(); - } - - SampleBuffer CreateSampleBuffer(const AudioParams ¶ms, const rational &length) - { - if (params.is_valid()) { - return CreateSampleBuffer(params, params.time_to_samples(length)); - } else { - return SampleBuffer(); - } - } - - QVector2D GenerateResolution() const; - - bool IsCancelled() - { - return cancel_ && cancel_->IsCancelled(); - } - - bool HeardCancel() const - { - return cancel_ && cancel_->HeardCancel(); - } - - CancelAtom *GetCancelPointer() const { return cancel_; } - void SetCancelPointer(CancelAtom *cancel) { cancel_ = cancel; } - - void ResolveJobs(NodeValue &value); - void ResolveAudioJobs(NodeValue &value); - - Block *GetCurrentBlock() const - { - return block_stack_.empty() ? nullptr : block_stack_.back(); - } - - LoopMode loop_mode() const { return loop_mode_; } - - virtual bool UseCache() const { return false; } - -private: - TexturePtr CreateDummyTexture(const VideoParams &p); - - VideoParams video_params_; - - AudioParams audio_params_; - - CancelAtom *cancel_; - - const Node *transform_start_; - const Node *transform_now_; - QTransform *transform_; - - std::list block_stack_; - - LoopMode loop_mode_; - - QHash > value_cache_; - QHash resolved_texture_cache_; - -}; - -} - -#endif // NODETRAVERSER_H diff --git a/app/panel/CMakeLists.txt b/app/panel/CMakeLists.txt index 0f4c9348fa..9744e69f8f 100644 --- a/app/panel/CMakeLists.txt +++ b/app/panel/CMakeLists.txt @@ -25,7 +25,6 @@ add_subdirectory(pixelsampler) add_subdirectory(project) add_subdirectory(scope) add_subdirectory(sequenceviewer) -add_subdirectory(table) add_subdirectory(taskmanager) add_subdirectory(timebased) add_subdirectory(timeline) diff --git a/app/panel/table/CMakeLists.txt b/app/panel/table/CMakeLists.txt deleted file mode 100644 index 9afcbf2faf..0000000000 --- a/app/panel/table/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# Olive - Non-Linear Video Editor -# Copyright (C) 2022 Olive Team -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -set(OLIVE_SOURCES - ${OLIVE_SOURCES} - panel/table/table.h - panel/table/table.cpp - PARENT_SCOPE -) diff --git a/app/panel/table/table.cpp b/app/panel/table/table.cpp deleted file mode 100644 index b2a82bd2b2..0000000000 --- a/app/panel/table/table.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2022 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#include "table.h" - -namespace olive { - -NodeTablePanel::NodeTablePanel() : - TimeBasedPanel(QStringLiteral("NodeTablePanel")) -{ - SetTimeBasedWidget(new NodeTableWidget(this)); - - Retranslate(); -} - -void NodeTablePanel::Retranslate() -{ - SetTitle(tr("Table View")); -} - -} diff --git a/app/panel/table/table.h b/app/panel/table/table.h deleted file mode 100644 index cbdc2da737..0000000000 --- a/app/panel/table/table.h +++ /dev/null @@ -1,53 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2022 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#ifndef NODETABLEPANEL_H -#define NODETABLEPANEL_H - -#include "panel/timebased/timebased.h" -#include "widget/nodetableview/nodetablewidget.h" - -namespace olive { - -class NodeTablePanel : public TimeBasedPanel -{ - Q_OBJECT -public: - NodeTablePanel(); - -public slots: - void SelectNodes(const QVector& nodes) - { - static_cast(GetTimeBasedWidget())->SelectNodes(nodes); - } - - void DeselectNodes(const QVector& nodes) - { - static_cast(GetTimeBasedWidget())->DeselectNodes(nodes); - } - -private: - virtual void Retranslate() override; - -}; - -} - -#endif // NODETABLEPANEL_H diff --git a/app/render/rendermanager.h b/app/render/rendermanager.h index 558478c471..9e682b1b0a 100644 --- a/app/render/rendermanager.h +++ b/app/render/rendermanager.h @@ -28,7 +28,6 @@ #include "dialog/rendercancel/rendercancel.h" #include "node/output/viewer/viewer.h" #include "node/project.h" -#include "node/traverser.h" #include "render/previewautocacher.h" #include "render/renderer.h" #include "render/renderticket.h" diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 05a79b8c86..8bbe6473c6 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -33,8 +33,6 @@ namespace olive { -#define super NodeTraverser - RenderProcessor::RenderProcessor(RenderTicketPtr ticket, Renderer *render_ctx, DecoderCache* decoder_cache, ShaderCache *shader_cache) : ticket_(ticket), render_ctx_(render_ctx), @@ -48,12 +46,12 @@ TexturePtr RenderProcessor::GenerateTexture(const rational &time, const rational TimeRange range = TimeRange(time, time + frame_length); NodeValueTable table; + NodeValue tex_val; if (Node* node = QtUtils::ValueToPtr(ticket_->property("node"))) { - table = GenerateTable(node, range); + ValueParams vp(GetCacheVideoParams(), GetCacheAudioParams(), range, LoopMode::kLoopModeOff, GetCancelPointer()); + tex_val = node->Value(vp); } - NodeValue tex_val = table.Get(NodeValue::kTexture); - ResolveJobs(tex_val); return tex_val.toTexture(); @@ -219,13 +217,12 @@ void RenderProcessor::Run() { TimeRange time = ticket_->property("time").value(); - NodeValueTable table; + NodeValue sample_val; if (Node* node = QtUtils::ValueToPtr(ticket_->property("node"))) { - table = GenerateTable(node, time); + ValueParams vp(GetCacheVideoParams(), GetCacheAudioParams(), time, LoopMode::kLoopModeOff, GetCancelPointer()); + sample_val = node->Value(vp); } - NodeValue sample_val = table.Get(NodeValue::kSamples); - ResolveJobs(sample_val); SampleBuffer samples = sample_val.toSamples(); @@ -294,28 +291,6 @@ DecoderPtr RenderProcessor::ResolveDecoderFromInput(const QString& decoder_id, c return dec; } -NodeValueDatabase RenderProcessor::GenerateDatabase(const Node *node, const TimeRange &range) -{ - NodeValueDatabase db = super::GenerateDatabase(node, range); - - if (const MultiCamNode *multicam = dynamic_cast(node)) { - if (QtUtils::ValueToPtr(ticket_->property("multicam")) == multicam) { - int sz = multicam->GetSourceCount(); - QVector multicam_tex(sz); - for (int i=0; iGetConnectedRenderOutput(multicam->kSourcesInput, i), range, multicam); - NodeValue val = GenerateRowValueElement(multicam, multicam->kSourcesInput, i, &t, range); - ResolveJobs(val); - - multicam_tex[i] = val.toTexture(); - } - ticket_->setProperty("multicam_output", QVariant::fromValue(multicam_tex)); - } - } - - return db; -} - void RenderProcessor::Process(RenderTicketPtr ticket, Renderer *render_ctx, DecoderCache *decoder_cache, ShaderCache *shader_cache) { RenderProcessor p(ticket, render_ctx, decoder_cache, shader_cache); @@ -343,7 +318,7 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJ qWarning() << "HAVEN'T GOTTEN DEFAULT INPUT COLORSPACE"; } - Decoder::CodecStream default_codec_stream(stream->filename(), stream_data.stream_index(), GetCurrentBlock()); + Decoder::CodecStream default_codec_stream(stream->filename(), stream_data.stream_index(), nullptr); QString decoder_id = stream->decoder(); @@ -426,10 +401,11 @@ void RenderProcessor::ProcessAudioFootage(SampleBuffer &destination, const Foota if (decoder) { const AudioParams& audio_params = GetCacheAudioParams(); + qDebug() << "audio loop modes are stubbed"; Decoder::RetrieveAudioStatus status = decoder->RetrieveAudio(destination, input_time, audio_params, stream->cache_path(), - loop_mode(), + LoopMode::kLoopModeOff, //loop_mode(), static_cast(ticket_->property("mode").toInt())); if (status == Decoder::kWaitingForConform) { @@ -470,6 +446,7 @@ void RenderProcessor::ProcessShader(TexturePtr destination, const Node *node, co void RenderProcessor::ProcessSamples(SampleBuffer &destination, const Node *node, const TimeRange &range, const SampleJob &job) { + /* if (!job.samples().is_allocated()) { return; } @@ -497,6 +474,8 @@ void RenderProcessor::ProcessSamples(SampleBuffer &destination, const Node *node destination, i); } + */ + qDebug() << "processing samples is stubbed"; } void RenderProcessor::ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob *job) @@ -545,12 +524,19 @@ TexturePtr RenderProcessor::ProcessVideoCacheJob(const CacheJob *val) TexturePtr RenderProcessor::CreateTexture(const VideoParams &p) { if (render_ctx_) { + // Create real texture with render context return render_ctx_->CreateTexture(p); } else { - return super::CreateTexture(p); + // Create dummy texture + return CreateDummyTexture(p); } } +TexturePtr RenderProcessor::CreateDummyTexture(const VideoParams &p) +{ + return std::make_shared(p); +} + void RenderProcessor::ConvertToReferenceSpace(TexturePtr destination, TexturePtr source, const QString &input_cs) { if (!render_ctx_) { @@ -574,4 +560,127 @@ bool RenderProcessor::UseCache() const return static_cast(ticket_->property("mode").toInt()) == RenderMode::kOffline; } +void RenderProcessor::ResolveJobs(NodeValue &val) +{ + if (val.type() == NodeValue::kTexture) { + + if (TexturePtr job_tex = val.toTexture()) { + if (AcceleratedJob *base_job = job_tex->job()) { + + if (resolved_texture_cache_.contains(job_tex.get())) { + val.set_value(resolved_texture_cache_.value(job_tex.get())); + } else { + // Resolve any sub-jobs + for (auto it=base_job->GetValues().begin(); it!=base_job->GetValues().end(); it++) { + // Jobs will almost always be submitted with one of these types + NodeValue &subval = it.value(); + ResolveJobs(subval); + } + + if (CacheJob *cj = dynamic_cast(base_job)) { + TexturePtr tex = ProcessVideoCacheJob(cj); + if (tex) { + val.set_value(tex); + } else { + val.set_value(cj->GetFallback()); + } + + } else if (ColorTransformJob *ctj = dynamic_cast(base_job)) { + + VideoParams ctj_params = job_tex->params(); + + ctj_params.set_format(GetCacheVideoParams().format()); + + TexturePtr dest = CreateTexture(ctj_params); + + // Resolve input texture + NodeValue v = ctj->GetInputTexture(); + ResolveJobs(v); + ctj->SetInputTexture(v); + + ProcessColorTransform(dest, val.source(), ctj); + + val.set_value(dest); + + } else if (ShaderJob *sj = dynamic_cast(base_job)) { + + VideoParams tex_params = job_tex->params(); + + TexturePtr tex = CreateTexture(tex_params); + + ProcessShader(tex, val.source(), sj); + + val.set_value(tex); + + } else if (GenerateJob *gj = dynamic_cast(base_job)) { + + VideoParams tex_params = job_tex->params(); + + TexturePtr tex = CreateTexture(tex_params); + + ProcessFrameGeneration(tex, val.source(), gj); + + // Convert to reference space + const QString &colorspace = tex_params.colorspace(); + if (!colorspace.isEmpty()) { + // Set format to primary format + tex_params.set_format(GetCacheVideoParams().format()); + + TexturePtr dest = CreateTexture(tex_params); + + ConvertToReferenceSpace(dest, tex, colorspace); + + tex = dest; + } + + val.set_value(tex); + + } else if (FootageJob *fj = dynamic_cast(base_job)) { + + rational footage_time = Footage::AdjustTimeByLoopMode(fj->time().in(), fj->loop_mode(), fj->length(), fj->video_params().video_type(), fj->video_params().frame_rate_as_time_base()); + + TexturePtr tex; + + if (footage_time.isNaN()) { + // Push dummy texture + tex = CreateDummyTexture(fj->video_params()); + } else { + VideoParams managed_params = fj->video_params(); + managed_params.set_format(GetCacheVideoParams().format()); + + tex = CreateTexture(managed_params); + ProcessVideoFootage(tex, fj, footage_time); + } + + val.set_value(tex); + + } + + // Cache resolved value + resolved_texture_cache_.insert(job_tex.get(), val.toTexture()); + } + } + } + + } else if (val.type() == NodeValue::kSamples) { + + if (val.canConvert()) { + + SampleJob job = val.value(); + SampleBuffer output_buffer = CreateSampleBuffer(job.samples().audio_params(), job.samples().sample_count()); + ProcessSamples(output_buffer, val.source(), job.time(), job); + val.set_value(QVariant::fromValue(output_buffer)); + + } else if (val.canConvert()) { + + FootageJob job = val.value(); + SampleBuffer buffer = CreateSampleBuffer(GetCacheAudioParams(), job.time().length()); + ProcessAudioFootage(buffer, &job, job.time()); + val.set_value(buffer); + + } + + } +} + } diff --git a/app/render/renderprocessor.h b/app/render/renderprocessor.h index b3dd29f591..32ad5440e7 100644 --- a/app/render/renderprocessor.h +++ b/app/render/renderprocessor.h @@ -22,18 +22,20 @@ #define RENDERPROCESSOR_H #include "node/block/clip/clip.h" -#include "node/traverser.h" +#include "render/job/cachejob.h" +#include "render/job/colortransformjob.h" +#include "render/job/footagejob.h" +#include "render/job/samplejob.h" +#include "render/job/shaderjob.h" #include "render/renderer.h" #include "rendercache.h" #include "renderticket.h" namespace olive { -class RenderProcessor : public NodeTraverser +class RenderProcessor { public: - virtual NodeValueDatabase GenerateDatabase(const Node *node, const TimeRange &range) override; - static void Process(RenderTicketPtr ticket, Renderer* render_ctx, DecoderCache* decoder_cache, ShaderCache* shader_cache); struct RenderedWaveform { @@ -44,30 +46,40 @@ class RenderProcessor : public NodeTraverser }; protected: - virtual void ProcessVideoFootage(TexturePtr destination, const FootageJob *stream, const rational &input_time) override; + void ProcessVideoFootage(TexturePtr destination, const FootageJob *stream, const rational &input_time); - virtual void ProcessAudioFootage(SampleBuffer &destination, const FootageJob *stream, const TimeRange &input_time) override; + void ProcessAudioFootage(SampleBuffer &destination, const FootageJob *stream, const TimeRange &input_time); - virtual void ProcessShader(TexturePtr destination, const Node *node, const ShaderJob *job) override; + void ProcessShader(TexturePtr destination, const Node *node, const ShaderJob *job); - virtual void ProcessSamples(SampleBuffer &destination, const Node *node, const TimeRange &range, const SampleJob &job) override; + void ProcessSamples(SampleBuffer &destination, const Node *node, const TimeRange &range, const SampleJob &job); - virtual void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob *job) override; + void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob *job); - virtual void ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob *job) override; + void ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob *job); - virtual TexturePtr ProcessVideoCacheJob(const CacheJob *val) override; + TexturePtr ProcessVideoCacheJob(const CacheJob *val); - virtual TexturePtr CreateTexture(const VideoParams &p) override; + TexturePtr CreateTexture(const VideoParams &p); + TexturePtr CreateDummyTexture(const VideoParams &p); - virtual SampleBuffer CreateSampleBuffer(const AudioParams ¶ms, int sample_count) override + SampleBuffer CreateSampleBuffer(const AudioParams ¶ms, int sample_count) { return SampleBuffer(params, sample_count); } - virtual void ConvertToReferenceSpace(TexturePtr destination, TexturePtr source, const QString &input_cs) override; + SampleBuffer CreateSampleBuffer(const AudioParams ¶ms, const rational &length) + { + if (params.is_valid()) { + return CreateSampleBuffer(params, params.time_to_samples(length)); + } else { + return SampleBuffer(); + } + } + + void ConvertToReferenceSpace(TexturePtr destination, TexturePtr source, const QString &input_cs); - virtual bool UseCache() const override; + bool UseCache() const; private: RenderProcessor(RenderTicketPtr ticket, Renderer* render_ctx, DecoderCache* decoder_cache, ShaderCache* shader_cache); @@ -80,6 +92,18 @@ class RenderProcessor : public NodeTraverser DecoderPtr ResolveDecoderFromInput(const QString &decoder_id, const Decoder::CodecStream& stream); + void ResolveJobs(NodeValue &value); + + const VideoParams &GetCacheVideoParams() const { return vparam_; } + const AudioParams &GetCacheAudioParams() const { return aparam_; } + void SetCacheVideoParams(const VideoParams &vparam) { vparam_ = vparam; } + void SetCacheAudioParams(const AudioParams &aparam) { aparam_ = aparam; } + bool IsCancelled() const { return cancel_atom_ && cancel_atom_->IsCancelled(); } + bool HeardCancel() const { return cancel_atom_ && cancel_atom_->HeardCancel(); } + CancelAtom *GetCancelPointer() const { return cancel_atom_; } + void SetCancelPointer(CancelAtom *p) { cancel_atom_ = p; } + Block *GetCurrentBlock() const { return nullptr; } + RenderTicketPtr ticket_; Renderer* render_ctx_; @@ -88,6 +112,12 @@ class RenderProcessor : public NodeTraverser ShaderCache* shader_cache_; + VideoParams vparam_; + AudioParams aparam_; + CancelAtom *cancel_atom_; + + QHash resolved_texture_cache_; + }; } diff --git a/app/widget/CMakeLists.txt b/app/widget/CMakeLists.txt index 26f2b10e8c..c171e9192c 100644 --- a/app/widget/CMakeLists.txt +++ b/app/widget/CMakeLists.txt @@ -34,7 +34,6 @@ add_subdirectory(menu) add_subdirectory(multicam) add_subdirectory(nodecombobox) add_subdirectory(nodeparamview) -add_subdirectory(nodetableview) add_subdirectory(nodetreeview) add_subdirectory(nodevaluetree) add_subdirectory(nodeview) diff --git a/app/widget/nodetableview/CMakeLists.txt b/app/widget/nodetableview/CMakeLists.txt deleted file mode 100644 index 2132ccd6a9..0000000000 --- a/app/widget/nodetableview/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -# Olive - Non-Linear Video Editor -# Copyright (C) 2022 Olive Team -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -set(OLIVE_SOURCES - ${OLIVE_SOURCES} - widget/nodetableview/nodetableview.cpp - widget/nodetableview/nodetableview.h - widget/nodetableview/nodetablewidget.cpp - widget/nodetableview/nodetablewidget.h - PARENT_SCOPE -) diff --git a/app/widget/nodetableview/nodetableview.cpp b/app/widget/nodetableview/nodetableview.cpp deleted file mode 100644 index f191f4b64a..0000000000 --- a/app/widget/nodetableview/nodetableview.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2022 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#include "nodetableview.h" - -#include -#include - -#include "node/traverser.h" - -namespace olive { - -NodeTableView::NodeTableView(QWidget* parent) : - QTreeWidget(parent) -{ - setColumnCount(3); - setHeaderLabels({tr("Type"), - tr("Source"), - tr("R/X"), - tr("G/Y"), - tr("B/Z"), - tr("A/W")}); -} - -void NodeTableView::SelectNodes(const QVector &nodes) -{ - foreach (Node* n, nodes) { - QTreeWidgetItem* top_item = new QTreeWidgetItem(); - top_item->setText(0, n->GetLabelAndName()); - top_item->setFirstColumnSpanned(true); - this->addTopLevelItem(top_item); - top_level_item_map_.insert(n, top_item); - } - - SetTime(last_time_); -} - -void NodeTableView::DeselectNodes(const QVector &nodes) -{ - foreach (Node* n, nodes) { - delete top_level_item_map_.take(n); - } -} - -void NodeTableView::SetTime(const rational &time) -{ - last_time_ = time; - - NodeTraverser traverser; - - for (auto i=top_level_item_map_.constBegin(); i!=top_level_item_map_.constEnd(); i++) { - Node* node = i.key(); - QTreeWidgetItem* item = i.value(); - - // Generate a value database for this node at this time - NodeValueDatabase db = traverser.GenerateDatabase(node, TimeRange(time, time)); - - // Delete any children of this item that aren't in this database - for (int j=0; jchildCount(); j++) { - if (!db.contains(item->child(j)->data(0, Qt::UserRole).toString())) { - delete item->takeChild(j); - j--; - } - } - - // Update all inputs - for (auto l=db.begin(); l!=db.end(); l++) { - const NodeValueTable& table = l.value(); - - if (!node->HasInputWithID(l.key())) { - // Filters out table entries that aren't inputs (like "global") - continue; - } - - QTreeWidgetItem* input_item = nullptr; - - for (int j=0; jchildCount(); j++) { - QTreeWidgetItem* compare = item->child(j); - - if (compare->data(0, Qt::UserRole).toString() == l.key()) { - input_item = compare; - break; - } - } - - if (!input_item) { - input_item = new QTreeWidgetItem(); - input_item->setText(0, node->GetInputName(l.key())); - input_item->setData(0, Qt::UserRole, l.key()); - input_item->setFirstColumnSpanned(true); - item->addChild(input_item); - } - - // Create children if necessary - while (input_item->childCount() < table.Count()) { - input_item->addChild(new QTreeWidgetItem()); - } - - // Remove children if necessary - while (input_item->childCount() > table.Count()) { - delete input_item->takeChild(input_item->childCount() - 1); - } - - for (int j=0;jchild(j); - - // Set data type name - sub_item->setText(0, NodeValue::GetPrettyDataTypeName(value.type())); - - // Determine source - QString source_name; - if (value.source()) { - source_name = value.source()->GetLabelAndName(); - } else { - source_name = tr("(unknown)"); - } - sub_item->setText(1, source_name); - - switch (value.type()) { - case NodeValue::kVideoParams: - case NodeValue::kAudioParams: - // These types have no string representation - break; - case NodeValue::kTexture: - { - // NodeTraverser puts video params in here - for (int k=0;ksetItemWidget(sub_item, 2 + k, new QCheckBox()); - } - break; - } - default: - { - QVector split_values = value.to_split_value(); - for (int k=0;ksetText(2 + k, NodeValue::ValueToString(value.type(), split_values.at(k), true)); - } - } - } - } - } - } -} - -} diff --git a/app/widget/nodetableview/nodetableview.h b/app/widget/nodetableview/nodetableview.h deleted file mode 100644 index 3c4cf87ca0..0000000000 --- a/app/widget/nodetableview/nodetableview.h +++ /dev/null @@ -1,51 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2022 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#ifndef NODETABLEVIEW_H -#define NODETABLEVIEW_H - -#include - -#include "node/node.h" - -namespace olive { - -class NodeTableView : public QTreeWidget -{ - Q_OBJECT -public: - NodeTableView(QWidget* parent = nullptr); - - void SelectNodes(const QVector &nodes); - - void DeselectNodes(const QVector& nodes); - - void SetTime(const rational& time); - -private: - QMap top_level_item_map_; - - rational last_time_; - -}; - -} - -#endif // NODETABLEVIEW_H diff --git a/app/widget/nodetableview/nodetablewidget.cpp b/app/widget/nodetableview/nodetablewidget.cpp deleted file mode 100644 index 2ebb1eac8f..0000000000 --- a/app/widget/nodetableview/nodetablewidget.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2022 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#include "nodetablewidget.h" - -#include - -namespace olive { - -NodeTableWidget::NodeTableWidget(QWidget* parent) : - TimeBasedWidget(parent) -{ - QVBoxLayout* layout = new QVBoxLayout(this); - layout->setSpacing(0); - layout->setContentsMargins(0, 0, 0, 0); - - view_ = new NodeTableView(); - layout->addWidget(view_); -} - -} diff --git a/app/widget/nodetableview/nodetablewidget.h b/app/widget/nodetableview/nodetablewidget.h deleted file mode 100644 index 24907553f4..0000000000 --- a/app/widget/nodetableview/nodetablewidget.h +++ /dev/null @@ -1,57 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2022 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#ifndef NODETABLEWIDGET_H -#define NODETABLEWIDGET_H - -#include "nodetableview.h" -#include "widget/timebased/timebasedwidget.h" - -namespace olive { - -class NodeTableWidget : public TimeBasedWidget -{ -public: - NodeTableWidget(QWidget* parent = nullptr); - - void SelectNodes(const QVector& nodes) - { - view_->SelectNodes(nodes); - } - - void DeselectNodes(const QVector& nodes) - { - view_->DeselectNodes(nodes); - } - -protected: - virtual void TimeChangedEvent(const rational &time) override - { - view_->SetTime(time); - } - -private: - NodeTableView* view_; - -}; - -} - -#endif // NODETABLEWIDGET_H diff --git a/app/widget/nodevaluetree/nodevaluetree.cpp b/app/widget/nodevaluetree/nodevaluetree.cpp index d980d57a1b..5e4c13405a 100644 --- a/app/widget/nodevaluetree/nodevaluetree.cpp +++ b/app/widget/nodevaluetree/nodevaluetree.cpp @@ -2,8 +2,6 @@ #include -#include "node/traverser.h" - namespace olive { #define super QTreeWidget @@ -26,13 +24,13 @@ NodeValueTree::NodeValueTree(QWidget *parent) : void NodeValueTree::SetNode(const NodeInput &input, const rational &time) { + /* clear(); - NodeTraverser traverser; - Node *connected_node = input.GetConnectedOutput(); - NodeValueTable table = traverser.GenerateTable(connected_node, TimeRange(time, time)); + ValueParams p(VideoParams(), AudioParams(), time, LoopMode::kLoopModeOff); + NodeValue v = connected_node->Value(p); int index = traverser.GenerateRowValueElementIndex(input.node(), input.input(), input.element(), &table); @@ -55,6 +53,7 @@ void NodeValueTree::SetNode(const NodeInput &input, const rational &time) item->setText(2, NodeValue::ValueToString(value, false)); item->setText(3, value.source()->GetLabelAndName()); } + */ } void NodeValueTree::changeEvent(QEvent *event) diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index da2c3199fd..778695949f 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -34,7 +34,6 @@ #include "node/group/group.h" #include "node/nodeundo.h" #include "node/project/serializer/serializer.h" -#include "node/traverser.h" #include "ui/icons/icons.h" #include "widget/menu/menushared.h" #include "widget/timebased/timebasedview.h" diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index 776399c75e..76cb713a7e 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -451,7 +451,7 @@ void ViewerDisplayWidget::OnPaint() p.setWorldTransform(gizmo_last_draw_transform_); - gizmos_->UpdateGizmoPositions(gizmo_db_, NodeGlobals(gizmo_params_, gizmo_audio_params_, gizmo_draw_time_, LoopMode::kLoopModeOff)); + gizmos_->UpdateGizmoPositions(ValueParams(gizmo_params_, gizmo_audio_params_, gizmo_draw_time_, LoopMode::kLoopModeOff, nullptr)); foreach (NodeGizmo *gizmo, gizmos_->GetGizmos()) { if (gizmo->IsVisible()) { gizmo->Draw(&p); @@ -602,6 +602,12 @@ rational ViewerDisplayWidget::GetGizmoTime() return GetAdjustedTime(GetTimeTarget(), gizmos_, time_, Node::kTransformTowardsInput); } +TimeRange ViewerDisplayWidget::GetGizmoTimeRange() +{ + rational gizmo_time = GetGizmoTime(); + return TimeRange(gizmo_time, gizmo_time + gizmo_params_.frame_rate_as_time_base()); +} + bool ViewerDisplayWidget::IsHandDrag(QMouseEvent *event) const { return event->button() == Qt::MiddleButton || Core::instance()->tool() == Tool::kHand; @@ -649,7 +655,7 @@ QTransform ViewerDisplayWidget::GenerateDisplayTransform() return gizmo_transform; } -QTransform ViewerDisplayWidget::GenerateGizmoTransform(NodeTraverser >, const TimeRange &range) +QTransform ViewerDisplayWidget::GenerateGizmoTransform(const TimeRange &range) { QTransform t = GenerateDisplayTransform(); if (GetTimeTarget()) { @@ -661,7 +667,27 @@ QTransform ViewerDisplayWidget::GenerateGizmoTransform(NodeTraverser >, const } QTransform nt; - gt.Transform(&nt, gizmos_, target, range); + + auto list = target->FindPath(gizmos_, target, 0); + + // Get time manually rather than using time target so we can iterate through them per node + std::list ranges; + ranges.push_front(TimeRange(time_, time_ + gizmo_params_.frame_rate_as_time_base())); + for (auto it = list.crbegin(); it != list.crend(); it++) { + // Transform time up the list + TimeRange r = it->node()->InputTimeAdjustment(it->input(), it->element(), ranges.front(), false); + ranges.push_front(r); + } + + for (auto it = list.cbegin(); it != list.cend(); it++) { + // Transform matrix down the list + ValueParams vp(gizmo_params_, gizmo_audio_params_, ranges.front(), LoopMode::kLoopModeOff, nullptr); + QTransform this_transform = (*it).node()->GizmoTransformation(vp); + if (!this_transform.isIdentity()) { + nt *= this_transform; + } + ranges.pop_front(); + } t.translate(gizmo_params_.width()*0.5, gizmo_params_.height()*0.5); t.scale(gizmo_params_.width(), gizmo_params_.height()); @@ -675,7 +701,7 @@ QTransform ViewerDisplayWidget::GenerateGizmoTransform(NodeTraverser >, const return t; } -NodeGizmo *ViewerDisplayWidget::TryGizmoPress(const NodeValueRow &row, const QPointF &p) +NodeGizmo *ViewerDisplayWidget::TryGizmoPress(const QPointF &p) { if (!gizmos_) { return nullptr; @@ -709,7 +735,7 @@ NodeGizmo *ViewerDisplayWidget::TryGizmoPress(const NodeValueRow &row, const QPo void ViewerDisplayWidget::OpenTextGizmo(TextGizmo *text, QMouseEvent *event) { GenerateGizmoTransforms(); - gizmos_->UpdateGizmoPositions(gizmo_db_, NodeGlobals(gizmo_params_, gizmo_audio_params_, gizmo_draw_time_, LoopMode::kLoopModeOff)); + gizmos_->UpdateGizmoPositions(ValueParams(gizmo_params_, gizmo_audio_params_, gizmo_draw_time_, LoopMode::kLoopModeOff, nullptr)); active_text_gizmo_ = text; connect(active_text_gizmo_, &TextGizmo::RectChanged, this, &ViewerDisplayWidget::UpdateActiveTextGizmoSize); @@ -827,12 +853,12 @@ bool ViewerDisplayWidget::OnMousePress(QMouseEvent *event) add_band_end_ = add_band_start_; add_band_ = true; - } else if ((current_gizmo_ = TryGizmoPress(gizmo_db_, gizmo_last_draw_transform_inverted_.map(event->pos())))) { + } else if ((current_gizmo_ = TryGizmoPress(gizmo_last_draw_transform_inverted_.map(event->pos())))) { // Handle gizmo click gizmo_start_drag_ = event->pos(); gizmo_last_drag_ = gizmo_start_drag_; - current_gizmo_->SetGlobals(NodeGlobals(gizmo_params_, gizmo_audio_params_, GenerateGizmoTime(), LoopMode::kLoopModeOff)); + current_gizmo_->SetGlobals(ValueParams(gizmo_params_, gizmo_audio_params_, GenerateGizmoTime(), LoopMode::kLoopModeOff, nullptr)); } else { @@ -878,13 +904,10 @@ bool ViewerDisplayWidget::OnMouseMove(QMouseEvent *event) if (!gizmo_drag_started_) { QPointF start = ScreenToScenePoint(gizmo_start_drag_); - rational gizmo_time = GetGizmoTime(); - NodeTraverser t; - t.SetCacheVideoParams(gizmo_params_); - t.SetCacheAudioParams(gizmo_audio_params_); - NodeValueRow row = t.GenerateRow(gizmos_, TimeRange(gizmo_time, gizmo_time + gizmo_params_.frame_rate_as_time_base())); + TimeRange gizmo_time = GetGizmoTimeRange(); + ValueParams p(gizmo_params_, gizmo_audio_params_, gizmo_time, LoopMode::kLoopModeOff, nullptr); - draggable->DragStart(row, start.x(), start.y(), gizmo_time); + draggable->DragStart(p, start.x(), start.y(), gizmo_time.in()); gizmo_drag_started_ = true; } @@ -1163,7 +1186,7 @@ bool ViewerDisplayWidget::ForwardMouseEventToTextEdit(QMouseEvent *event, bool c if (check_if_outside) { if (local_pos.x() < 0 || local_pos.x() >= text_edit_->width() || local_pos.y() < 0 || local_pos.y() >= text_edit_->height()) { // Allow clicking other gizmos so the user can resize while the text editor is active - if ((current_gizmo_ = TryGizmoPress(gizmo_db_, gizmo_last_draw_transform_inverted_.map(event->pos())))) { + if ((current_gizmo_ = TryGizmoPress(gizmo_last_draw_transform_inverted_.map(event->pos())))) { return false; } else { CloseTextEditor(); @@ -1216,17 +1239,11 @@ void ViewerDisplayWidget::CloseTextEditor() void ViewerDisplayWidget::GenerateGizmoTransforms() { - NodeTraverser gt; - gt.SetCacheVideoParams(gizmo_params_); - gt.SetCacheAudioParams(gizmo_audio_params_); - gizmo_draw_time_ = GenerateGizmoTime(); - if (gizmos_) { - gizmo_db_ = gt.GenerateRow(gizmos_, gizmo_draw_time_); - } + ValueParams p(gizmo_params_, gizmo_audio_params_, gizmo_draw_time_, LoopMode::kLoopModeOff, nullptr); - gizmo_last_draw_transform_ = GenerateGizmoTransform(gt, gizmo_draw_time_); + gizmo_last_draw_transform_ = GenerateGizmoTransform(gizmo_draw_time_); gizmo_last_draw_transform_inverted_ = gizmo_last_draw_transform_.inverted(); } diff --git a/app/widget/viewer/viewerdisplay.h b/app/widget/viewer/viewerdisplay.h index 5ffcd2c4f1..c0e5cebc3b 100644 --- a/app/widget/viewer/viewerdisplay.h +++ b/app/widget/viewer/viewerdisplay.h @@ -28,7 +28,6 @@ #include "node/gizmo/text.h" #include "node/node.h" #include "node/output/track/tracklist.h" -#include "node/traverser.h" #include "tool/tool.h" #include "viewerplaybacktimer.h" #include "viewerqueue.h" @@ -230,12 +229,10 @@ public slots: QTransform GenerateDisplayTransform(); - QTransform GenerateGizmoTransform(NodeTraverser >, const TimeRange &range); + QTransform GenerateGizmoTransform(const TimeRange &range); QTransform GenerateGizmoTransform() { - NodeTraverser t; - t.SetCacheVideoParams(gizmo_params_); - return GenerateGizmoTransform(t, GenerateGizmoTime()); + return GenerateGizmoTransform(GenerateGizmoTime()); } TimeRange GenerateGizmoTime() @@ -267,12 +264,13 @@ protected slots: static void DrawTextWithCrudeShadow(QPainter* painter, const QRect& rect, const QString& text, const QTextOption &opt = QTextOption()); rational GetGizmoTime(); + TimeRange GetGizmoTimeRange(); bool IsHandDrag(QMouseEvent* event) const; void UpdateMatrix(); - NodeGizmo *TryGizmoPress(const NodeValueRow &row, const QPointF &p); + NodeGizmo *TryGizmoPress(const QPointF &p); void OpenTextGizmo(TextGizmo *text, QMouseEvent *event = nullptr); @@ -353,7 +351,6 @@ protected slots: ViewerSafeMarginInfo safe_margin_; Node* gizmos_; - NodeValueRow gizmo_db_; VideoParams gizmo_params_; AudioParams gizmo_audio_params_; QPoint gizmo_start_drag_; diff --git a/app/window/mainwindow/mainwindow.h b/app/window/mainwindow/mainwindow.h index 7fbb81fc47..a591f880a0 100644 --- a/app/window/mainwindow/mainwindow.h +++ b/app/window/mainwindow/mainwindow.h @@ -35,7 +35,6 @@ #include "panel/param/param.h" #include "panel/project/project.h" #include "panel/scope/scope.h" -#include "panel/table/table.h" #include "panel/taskmanager/taskmanager.h" #include "panel/timeline/timeline.h" #include "panel/tool/tool.h" From 83c50bb461ede7126b70339ac84fda8f1c5e9e9f Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 26 Mar 2023 12:09:08 -0700 Subject: [PATCH 05/71] ensure volume and pan adhere to guidelines --- app/node/audio/pan/pan.cpp | 3 +++ app/node/audio/volume/volume.cpp | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/node/audio/pan/pan.cpp b/app/node/audio/pan/pan.cpp index 572057e646..68a13b081a 100644 --- a/app/node/audio/pan/pan.cpp +++ b/app/node/audio/pan/pan.cpp @@ -66,6 +66,7 @@ NodeValue PanNode::Value(const ValueParams &p) const { // Create a sample job NodeValue samples_original = GetInputValue(p, kSamplesInput); + SampleBuffer samples = samples_original.toSamples(); if (samples.is_allocated()) { // This node is only compatible with stereo audio @@ -91,6 +92,8 @@ NodeValue PanNode::Value(const ValueParams &p) const return samples_original; } } + + return samples_original; } void PanNode::ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const diff --git a/app/node/audio/volume/volume.cpp b/app/node/audio/volume/volume.cpp index baaed6d367..31c360cd1b 100644 --- a/app/node/audio/volume/volume.cpp +++ b/app/node/audio/volume/volume.cpp @@ -64,8 +64,9 @@ QString VolumeNode::Description() const NodeValue VolumeNode::Value(const ValueParams &p) const { // Create a sample job - SampleBuffer buffer = GetInputValue(p, kSamplesInput).toSamples(); + NodeValue meta = GetInputValue(p, kSamplesInput); + SampleBuffer buffer = meta.toSamples(); if (buffer.is_allocated()) { // If the input is static, we can just do it now which will be faster if (IsInputStatic(kVolumeInput)) { @@ -84,7 +85,7 @@ NodeValue VolumeNode::Value(const ValueParams &p) const } } - return NodeValue(); + return meta; } void VolumeNode::ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const From 7f79fef6d8ce914bd78144c63432697969d01642 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 26 Mar 2023 12:13:05 -0700 Subject: [PATCH 06/71] node: add convenience functions for enabled --- app/node/block/block.cpp | 12 ------------ app/node/block/block.h | 3 --- app/node/node.cpp | 2 +- app/node/node.h | 3 +++ app/widget/viewer/viewerdisplay.cpp | 9 ++++++--- 5 files changed, 10 insertions(+), 19 deletions(-) diff --git a/app/node/block/block.cpp b/app/node/block/block.cpp index 98c1bedd36..483887df58 100644 --- a/app/node/block/block.cpp +++ b/app/node/block/block.cpp @@ -78,18 +78,6 @@ void Block::set_length_and_media_in(const rational &length) set_length_internal(length); } -bool Block::is_enabled() const -{ - return GetStandardValue(kEnabledInput).toBool(); -} - -void Block::set_enabled(bool e) -{ - SetStandardValue(kEnabledInput, e); - - emit EnabledChanged(); -} - void Block::InputValueChangedEvent(const QString &input, int element) { super::InputValueChangedEvent(input, element); diff --git a/app/node/block/block.h b/app/node/block/block.h index 3b0c7e4e9f..375831f2c9 100644 --- a/app/node/block/block.h +++ b/app/node/block/block.h @@ -99,9 +99,6 @@ class Block : public Node emit TrackChanged(track_); } - bool is_enabled() const; - void set_enabled(bool e); - virtual void Retranslate() override; virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1, InvalidateCacheOptions options = InvalidateCacheOptions()) override; diff --git a/app/node/node.cpp b/app/node/node.cpp index 2dc9d0cb11..6d703f4e18 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -878,7 +878,7 @@ NodeValue Node::GetInputValue(const ValueParams &g, const QString &input, int el NodeValue ret; while (output) { - if (output->GetStandardValue(kEnabledInput).toBool()) { + if (output->is_enabled()) { ret = output->Value(adj_param); break; } else { diff --git a/app/node/node.h b/app/node/node.h index 8c11f2a142..7c79b7fb06 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -373,6 +373,9 @@ class Node : public QObject return GetConnectedOutput(input.input(), input.element()); } + bool is_enabled() const { return GetStandardValue(kEnabledInput).toBool(); } + void set_enabled(bool e) { SetStandardValue(kEnabledInput, e); } + bool IsUsingStandardValue(const QString& input, int track, int element = -1) const; NodeValue::Type GetInputDataType(const QString& id) const; diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index 76cb713a7e..ad49902d29 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -682,9 +682,12 @@ QTransform ViewerDisplayWidget::GenerateGizmoTransform(const TimeRange &range) for (auto it = list.cbegin(); it != list.cend(); it++) { // Transform matrix down the list ValueParams vp(gizmo_params_, gizmo_audio_params_, ranges.front(), LoopMode::kLoopModeOff, nullptr); - QTransform this_transform = (*it).node()->GizmoTransformation(vp); - if (!this_transform.isIdentity()) { - nt *= this_transform; + Node *n = (*it).node(); + if (n->is_enabled()) { + QTransform this_transform = n->GizmoTransformation(vp); + if (!this_transform.isIdentity()) { + nt *= this_transform; + } } ranges.pop_front(); } From 1989effd1ae3d245e217db6e8454efb58522ac69 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 26 Mar 2023 17:58:03 -0700 Subject: [PATCH 07/71] updated audio jobs to work with new system --- app/node/audio/pan/pan.cpp | 73 +++++++++-------- app/node/audio/pan/pan.h | 2 +- app/node/audio/volume/volume.cpp | 38 ++++----- app/node/audio/volume/volume.h | 2 +- app/node/math/math/math.cpp | 25 +++++- app/node/math/math/math.h | 2 +- app/node/math/math/mathbase.cpp | 115 +++++++++++++------------- app/node/math/math/mathbase.h | 3 +- app/node/node.cpp | 2 +- app/node/node.h | 7 +- app/node/output/track/track.cpp | 136 +++++++++++++++---------------- app/node/output/track/track.h | 2 + app/render/job/acceleratedjob.h | 12 +++ app/render/job/samplejob.h | 32 +++----- app/render/job/shaderjob.h | 12 --- app/render/renderprocessor.cpp | 44 +++------- app/render/renderprocessor.h | 2 +- 17 files changed, 251 insertions(+), 258 deletions(-) diff --git a/app/node/audio/pan/pan.cpp b/app/node/audio/pan/pan.cpp index 68a13b081a..5f4e216602 100644 --- a/app/node/audio/pan/pan.cpp +++ b/app/node/audio/pan/pan.cpp @@ -67,47 +67,52 @@ NodeValue PanNode::Value(const ValueParams &p) const // Create a sample job NodeValue samples_original = GetInputValue(p, kSamplesInput); - SampleBuffer samples = samples_original.toSamples(); - if (samples.is_allocated()) { - // This node is only compatible with stereo audio - 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 = GetInputValue(p, kPanningInput).toDouble(); - if (!qIsNull(pan_volume)) { - if (pan_volume > 0) { - samples.transform_volume_for_channel(0, 1.0f - pan_volume); - } else { - samples.transform_volume_for_channel(1, 1.0f + pan_volume); - } - } + if (!IsInputStatic(kPanningInput) || !qIsNull(GetInputValue(p, kPanningInput).toDouble())) { + // Requires sample job + SampleJob job(p); - return NodeValue(NodeValue::kSamples, samples, this); - } else { - // Requires job - return NodeValue(NodeValue::kSamples, CreateSampleJob(p, kSamplesInput), this); - } - } else { - // Pass right through - return samples_original; - } + job.Insert(kSamplesInput, GetInputValue(p, kSamplesInput)); + + return NodeValue(NodeValue::kSamples, SampleJob(job), this); } return samples_original; } -void PanNode::ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const +void PanNode::ProcessSamples(const SampleJob &job, SampleBuffer &output) const { - float pan_val = values[kPanningInput].toDouble(); - - for (int i=0;i 0) { - output.data(0)[index] *= (1.0F - pan_val); - } else if (pan_val < 0) { - output.data(1)[index] *= (1.0F - qAbs(pan_val)); + const ValueParams &p = job.value_params(); + + SampleBuffer input = job.Get(kSamplesInput).toSamples(); + + // This node is only compatible with stereo audio + if (job.audio_params().channel_count() == 2) { + if (IsInputStatic(kPanningInput)) { + float pan_volume = GetInputValue(p, kPanningInput).toDouble(); + if (!qIsNull(pan_volume)) { + if (pan_volume > 0) { + SampleBuffer::transform_volume_for_channel(0, 1.0f - pan_volume, &input, &output); + output.set(1, input.data(1), input.sample_count()); + } else { + output.set(0, input.data(0), input.sample_count()); + SampleBuffer::transform_volume_for_channel(1, 1.0f + pan_volume, &input, &output); + } + } + } else { + for (size_t index = 0; index < output.sample_count(); index++) { + rational this_sample_time = p.time().in() + rational(index, job.audio_params().sample_rate()); + ValueParams this_sample_p(p.vparams(), p.aparams(), TimeRange(this_sample_time, this_sample_time + job.audio_params().sample_rate_as_time_base()), p.loop_mode(), p.cancel_atom()); + auto pan_val = GetInputValue(this_sample_p, kPanningInput).toDouble(); + + if (pan_val > 0) { + output.data(0)[index] = input.data(0)[index] * (1.0F - pan_val); + output.data(1)[index] = input.data(1)[index]; + } else if (pan_val < 0) { + output.data(0)[index] = input.data(0)[index]; + output.data(1)[index] = input.data(1)[index] * (1.0F - qAbs(pan_val)); + } + } + } } } diff --git a/app/node/audio/pan/pan.h b/app/node/audio/pan/pan.h index 183e17915e..050dcb75f5 100644 --- a/app/node/audio/pan/pan.h +++ b/app/node/audio/pan/pan.h @@ -40,7 +40,7 @@ class PanNode : public Node virtual NodeValue Value(const ValueParams &p) const override; - virtual void ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const override; + virtual void ProcessSamples(const SampleJob &job, SampleBuffer &output) const override; virtual void Retranslate() override; diff --git a/app/node/audio/volume/volume.cpp b/app/node/audio/volume/volume.cpp index 31c360cd1b..2014f08a15 100644 --- a/app/node/audio/volume/volume.cpp +++ b/app/node/audio/volume/volume.cpp @@ -66,31 +66,31 @@ NodeValue VolumeNode::Value(const ValueParams &p) const // Create a sample job NodeValue meta = GetInputValue(p, kSamplesInput); - SampleBuffer buffer = meta.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 = GetInputValue(p, kVolumeInput).toDouble(); - - if (!qFuzzyCompare(volume, 1.0)) { - buffer.transform_volume(volume); - } - - return NodeValue(NodeValue::kSamples, QVariant::fromValue(buffer), this); - } else { - // Requires job - SampleJob job = CreateSampleJob(p, kSamplesInput); - job.Insert(kVolumeInput, GetInputValue(p, kVolumeInput)); - return NodeValue(NodeValue::kSamples, QVariant::fromValue(job), this); - } + if (!IsInputStatic(kVolumeInput) || !qFuzzyCompare(GetInputValue(p, kVolumeInput).toDouble(), 1.0)) { + SampleJob job(p); + + job.Insert(kSamplesInput, GetInputValue(p, kSamplesInput)); + + return NodeValue(NodeValue::kSamples, job, this); } return meta; } -void VolumeNode::ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const +void VolumeNode::ProcessSamples(const SampleJob &job, SampleBuffer &output) const { - return ProcessSamplesInternal(values, kOpMultiply, kSamplesInput, kVolumeInput, input, output, index); + SampleBuffer buffer = job.Get(kSamplesInput).toSamples(); + const ValueParams &p = job.value_params(); + + if (IsInputStatic(kVolumeInput)) { + auto volume = GetInputValue(p, kVolumeInput).toDouble(); + + if (!qFuzzyCompare(volume, 1.0)) { + SampleBuffer::transform_volume(volume, &buffer, &output); + } + } else { + return ProcessSamplesNumberInternal(p, kOpMultiply, kVolumeInput, buffer, output); + } } void VolumeNode::Retranslate() diff --git a/app/node/audio/volume/volume.h b/app/node/audio/volume/volume.h index 97e249809e..9f4ec71b8e 100644 --- a/app/node/audio/volume/volume.h +++ b/app/node/audio/volume/volume.h @@ -40,7 +40,7 @@ class VolumeNode : public MathNodeBase virtual NodeValue Value(const ValueParams &p) const override; - virtual void ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const override; + virtual void ProcessSamples(const SampleJob &job, SampleBuffer &output) const override; virtual void Retranslate() override; diff --git a/app/node/math/math/math.cpp b/app/node/math/math/math.cpp index 4441b3c842..c639ddd76e 100644 --- a/app/node/math/math/math.cpp +++ b/app/node/math/math/math.cpp @@ -98,8 +98,18 @@ NodeValue MathNode::Value(const ValueParams &p) const // Auto-detect what values to operate with // FIXME: Very inefficient NodeValueTable at, bt; - at.Push(GetInputValue(p, kParamAIn)); - bt.Push(GetInputValue(p, kParamBIn)); + auto aval = GetInputValue(p, kParamAIn); + auto bval = GetInputValue(p, kParamBIn); + + if (!bval.data().isValid()) { + return aval; + } + if (!aval.data().isValid()) { + return bval; + } + + at.Push(aval); + bt.Push(bval); PairingCalculator calc(at, bt); // Do nothing if no pairing was found @@ -116,9 +126,16 @@ NodeValue MathNode::Value(const ValueParams &p) const return NodeValue(); } -void MathNode::ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const +void MathNode::ProcessSamples(const SampleJob &job, SampleBuffer &output) const { - return ProcessSamplesInternal(values, GetOperation(), kParamAIn, kParamBIn, input, output, index); + const ValueParams &p = job.value_params(); + int pairing = job.Get(QStringLiteral("pairing")).toInt(); + + if (pairing == kPairSampleNumber) { + return ProcessSamplesNumberInternal(p, GetOperation(), job.Get(QStringLiteral("number")).toString(), job.Get(QStringLiteral("samples")).toSamples(), output); + } else { + return ProcessSamplesSamplesInternal(p, GetOperation(), job.Get(QStringLiteral("a")).toSamples(), job.Get(QStringLiteral("b")).toSamples(), output); + } } } diff --git a/app/node/math/math/math.h b/app/node/math/math/math.h index 325edcdd24..7cd0dc0980 100644 --- a/app/node/math/math/math.h +++ b/app/node/math/math/math.h @@ -54,7 +54,7 @@ class MathNode : public MathNodeBase virtual NodeValue Value(const ValueParams &p) const override; - virtual void ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const override; + virtual void ProcessSamples(const SampleJob &job, SampleBuffer &output) const override; static const QString kMethodIn; static const QString kParamAIn; diff --git a/app/node/math/math/mathbase.cpp b/app/node/math/math/mathbase.cpp index 7a23ed4d21..643acad470 100644 --- a/app/node/math/math/mathbase.cpp +++ b/app/node/math/math/mathbase.cpp @@ -228,6 +228,8 @@ void MathNodeBase::PerformAllOnFloatBufferSSE(Operation operation, float *a, flo NodeValue MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QString& param_a_in, const NodeValue& val_a, const QString& param_b_in, const NodeValue& val_b, const ValueParams &p) const { + qDebug() << pairing; + switch (pairing) { case kPairNumberNumber: @@ -299,35 +301,13 @@ NodeValue MathNodeBase::ValueInternal(Operation operation, Pairing pairing, cons case kPairSampleSample: { - SampleBuffer samples_a = val_a.toSamples(); - SampleBuffer samples_b = val_b.toSamples(); - - size_t max_samples = qMax(samples_a.sample_count(), samples_b.sample_count()); - size_t min_samples = qMin(samples_a.sample_count(), samples_b.sample_count()); + SampleJob job(p); - SampleBuffer mixed_samples = SampleBuffer(samples_a.audio_params(), max_samples); + job.Insert(QStringLiteral("a"), val_a); + job.Insert(QStringLiteral("b"), val_b); + job.Insert(QStringLiteral("pairing"), NodeValue(NodeValue::kInt, int(pairing))); - for (int i=0;i(operation, samples_a.data(i)[j], samples_b.data(i)[j]); - } - } - - if (max_samples > min_samples) { - // Fill in remainder space with 0s - size_t remainder = max_samples - min_samples; - - const SampleBuffer &larger_buffer = (max_samples == samples_a.sample_count()) ? samples_a : samples_b; - - for (int i=0;i(operation, samples_a.data(i)[j], samples_b.data(i)[j]); + } + } - if (number_val.type() == NodeValue::kNone) { - number_val = values[param_b_in]; + if (max_samples > min_samples) { + size_t remainder = max_samples - min_samples; - if (number_val.type() == NodeValue::kNone) { - return; + const SampleBuffer &larger_buffer = (max_samples == samples_a.sample_count()) ? samples_a : samples_b; + + for (int i=0;i(operation, input.data(i)[index], number_flt); + for (int i=0;i(operation, input.data(i)[j], v); + } + } } } diff --git a/app/node/math/math/mathbase.h b/app/node/math/math/mathbase.h index f84d7ebba3..e8e1e8306c 100644 --- a/app/node/math/math/mathbase.h +++ b/app/node/math/math/mathbase.h @@ -123,7 +123,8 @@ class MathNodeBase : public Node NodeValue ValueInternal(Operation operation, Pairing pairing, const QString& param_a_in, const NodeValue &val_a, const QString& param_b_in, const NodeValue& val_b, const ValueParams &p) const; - void ProcessSamplesInternal(const NodeValueRow &values, Operation operation, const QString& param_a_in, const QString& param_b_in, const SampleBuffer &input, SampleBuffer &output, int index) const; + void ProcessSamplesSamplesInternal(const ValueParams &p, Operation operation, const SampleBuffer& a, const SampleBuffer &b, SampleBuffer &output) const; + void ProcessSamplesNumberInternal(const ValueParams &p, Operation operation, const QString& number_in, const SampleBuffer &input, SampleBuffer &output) const; }; diff --git a/app/node/node.cpp b/app/node/node.cpp index 6d703f4e18..df3a959d1d 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -2059,7 +2059,7 @@ ShaderCode Node::GetShaderCode(const ShaderRequest &request) const return ShaderCode(QString(), QString()); } -void Node::ProcessSamples(const NodeValueRow &, const SampleBuffer &, SampleBuffer &, int) const +void Node::ProcessSamples(const SampleJob &, SampleBuffer &) const { } diff --git a/app/node/node.h b/app/node/node.h index 7c79b7fb06..20c476d2b9 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -699,7 +699,7 @@ class Node : public QObject /** * @brief If Value() pushes a ShaderJob, this is the function that will process them. */ - virtual void ProcessSamples(const NodeValueRow &values, const SampleBuffer &input, SampleBuffer &output, int index) const; + virtual void ProcessSamples(const SampleJob &job, SampleBuffer &output) const; /** * @brief If Value() pushes a GenerateJob, override this function for the image to create @@ -1013,11 +1013,6 @@ class Node : public QObject return job; } - SampleJob CreateSampleJob(const ValueParams &p, const QString &sample_input) const - { - return SampleJob(p.time(), GetInputValue(p, sample_input)); - } - protected slots: virtual void GizmoDragStart(const olive::ValueParams &p, double x, double y, const olive::core::rational &time){} diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index 30a8022eed..96edae3bb2 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -95,8 +95,7 @@ QVector Track::Category() const QString Track::Description() const { - return tr("Node for representing and processing a single array of Blocks sorted by time. Also represents the end of " - "a Sequence."); + return tr("Node for representing and processing a single array of Blocks sorted by time. Also represents the end of a Sequence."); } NodeValue Track::Value(const ValueParams &p) const @@ -114,7 +113,32 @@ NodeValue Track::Value(const ValueParams &p) const } } else if (this->type() == Track::kAudio) { // Audio - return ProcessAudioTrack(p); + const TimeRange &range = p.time(); + + int start = GetBlockIndexAtTime(range.in()); + int end = GetBlockIndexAtTime(range.out()); + + if (start == -1) { + start = 0; + } + if (end == -1) { + end = blocks_.size()-1; + } + + if (blocks_.at(end)->in() == range.out()) { + end--; + } + + SampleJob job(p); + + for (int i=start; i<=end; i++) { + Block *b = blocks_.at(i); + if (b->is_enabled() && (dynamic_cast(b) || dynamic_cast(b))) { + job.Insert(QString::number(i), GetInputValue(p, kBlockInput, GetArrayIndexFromCacheIndex(i))); + } + } + + return NodeValue(NodeValue::kSamples, job, this); } } @@ -559,73 +583,16 @@ int Track::GetArrayIndexFromBlock(Block *block) const return block_array_indexes_.at(blocks_.indexOf(block)); } -int Track::GetArrayIndexFromCacheIndex(int index) const -{ - return block_array_indexes_.at(index); -} - -int Track::GetCacheIndexFromArrayIndex(int index) const -{ - return block_array_indexes_.indexOf(index); -} - -int Track::GetBlockIndexAtTime(const rational &time) const -{ - if (time < 0 || time >= track_length()) { - return -1; - } - - // Use binary search to find block at time - int low = 0; - int high = blocks_.size() - 1; - while (low <= high) { - int mid = low + (high - low) / 2; - - Block* block = blocks_.at(mid); - if (block->in() <= time && block->out() > time) { - return mid; - } else if (block->out() <= time) { - low = mid + 1; - } else { - high = mid - 1; - } - } - - return -1; -} - -NodeValue Track::ProcessAudioTrack(const ValueParams &p) const +void Track::ProcessSamples(const SampleJob &job, SampleBuffer &block_range_buffer) const { - const TimeRange &range = p.time(); - - int start = GetBlockIndexAtTime(range.in()); - int end = GetBlockIndexAtTime(range.out()); - - if (start == -1) { - start = 0; - } - if (end == -1) { - end = blocks_.size()-1; - } - - if (blocks_.at(end)->in() == range.out()) { - end--; - } - - QVector blocks; - for (int i=start; i<=end; i++) { - Block *b = blocks_.at(i); - if (b->is_enabled() && (dynamic_cast(b) || dynamic_cast(b))) { - blocks.append(i); - } - } + const ValueParams &p = job.value_params(); + const TimeRange &range = job.value_params().time(); // All these blocks will need to output to a buffer so we create one here - SampleBuffer block_range_buffer(p.aparams(), range.length()); block_range_buffer.silence(); - for (int block_index : blocks) { - Block *b = blocks_.at(block_index); + for (auto it = job.GetValues().cbegin(); it != job.GetValues().cend(); it++) { + Block *b = blocks_.at(it.key().toInt()); TimeRange range_for_block(qMax(b->in(), range.in()), qMin(b->out(), range.out())); @@ -635,7 +602,7 @@ NodeValue Track::ProcessAudioTrack(const ValueParams &p) const qint64 max_dest_sz = p.aparams().time_to_samples(range_for_block.length()); // Destination buffer - SampleBuffer samples_from_this_block = GetInputValue(p, kBlockInput, GetArrayIndexFromCacheIndex(block_index)).toSamples(); + SampleBuffer samples_from_this_block = it.value().toSamples(); if (samples_from_this_block.is_allocated()) { // If this is a clip, we might have extra speed/reverse information @@ -702,8 +669,41 @@ NodeValue Track::ProcessAudioTrack(const ValueParams &p) const } } } +} - return NodeValue(NodeValue::kSamples, QVariant::fromValue(block_range_buffer), this); +int Track::GetArrayIndexFromCacheIndex(int index) const +{ + return block_array_indexes_.at(index); +} + +int Track::GetCacheIndexFromArrayIndex(int index) const +{ + return block_array_indexes_.indexOf(index); +} + +int Track::GetBlockIndexAtTime(const rational &time) const +{ + if (time < 0 || time >= track_length()) { + return -1; + } + + // Use binary search to find block at time + int low = 0; + int high = blocks_.size() - 1; + while (low <= high) { + int mid = low + (high - low) / 2; + + Block* block = blocks_.at(mid); + if (block->in() <= time && block->out() > time) { + return mid; + } else if (block->out() <= time) { + low = mid + 1; + } else { + high = mid - 1; + } + } + + return -1; } int Track::ConnectBlock(Block *b) @@ -736,7 +736,7 @@ void Track::RefreshBlockCacheFromArrayMap() } // Disconnecting any existing blocks - for (Block *b : blocks_) { + for (Block *b : qAsConst(blocks_)) { Q_ASSERT(b->track() == this); b->set_track(nullptr); b->set_previous(nullptr); diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index d6eb2379e0..c4d304baae 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -387,6 +387,8 @@ class Track : public Node int GetArrayIndexFromBlock(Block* block) const; + virtual void ProcessSamples(const SampleJob &job, SampleBuffer &output) const override; + Sequence *sequence() const { return sequence_; diff --git a/app/render/job/acceleratedjob.h b/app/render/job/acceleratedjob.h index 7373d52c1e..13b0d4da72 100644 --- a/app/render/job/acceleratedjob.h +++ b/app/render/job/acceleratedjob.h @@ -33,6 +33,16 @@ class AcceleratedJob virtual ~AcceleratedJob(){} + const QString& GetShaderID() const + { + return id_; + } + + void SetShaderID(const QString& id) + { + id_ = id; + } + NodeValue Get(const QString& input) const { return value_map_.value(input); @@ -63,6 +73,8 @@ class AcceleratedJob NodeValueRow &GetValues() { return value_map_; } private: + QString id_; + NodeValueRow value_map_; }; diff --git a/app/render/job/samplejob.h b/app/render/job/samplejob.h index 8d13baf893..2f3f4ca270 100644 --- a/app/render/job/samplejob.h +++ b/app/render/job/samplejob.h @@ -22,6 +22,7 @@ #define SAMPLEJOB_H #include "acceleratedjob.h" +#include "node/globals.h" namespace olive { @@ -30,36 +31,27 @@ class SampleJob : public AcceleratedJob public: SampleJob() { + sample_count_ = 0; } - SampleJob(const TimeRange &time, const NodeValue& value) + SampleJob(const ValueParams &p, size_t sample_count) { - samples_ = value.toSamples(); - time_ = time; + value_params_ = p; + sample_count_ = sample_count; } - SampleJob(const TimeRange &time, const QString& from, const NodeValueRow& row) + SampleJob(const ValueParams &p) : + SampleJob(p, p.aparams().time_to_samples(p.time().length())) { - samples_ = row[from].toSamples(); - time_ = time; } - const SampleBuffer &samples() const - { - return samples_; - } - - bool HasSamples() const - { - return samples_.is_allocated(); - } - - const TimeRange &time() const { return time_; } + const ValueParams &value_params() const { return value_params_; } + const AudioParams &audio_params() const { return value_params_.aparams(); } + size_t sample_count() const { return sample_count_; } private: - SampleBuffer samples_; - - TimeRange time_; + ValueParams value_params_; + size_t sample_count_; }; diff --git a/app/render/job/shaderjob.h b/app/render/job/shaderjob.h index 4cba60e7a2..ace65cfe6a 100644 --- a/app/render/job/shaderjob.h +++ b/app/render/job/shaderjob.h @@ -44,16 +44,6 @@ class ShaderJob : public AcceleratedJob Insert(row); } - const QString& GetShaderID() const - { - return shader_id_; - } - - void SetShaderID(const QString& id) - { - shader_id_ = id; - } - void SetIterations(int iterations, const NodeInput& iterative_input) { SetIterations(iterations, iterative_input.input()); @@ -106,8 +96,6 @@ class ShaderJob : public AcceleratedJob } private: - QString shader_id_; - int iterations_; QString iterative_input_; diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 8bbe6473c6..0bee9e7015 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -444,38 +444,9 @@ void RenderProcessor::ProcessShader(TexturePtr destination, const Node *node, co render_ctx_->BlitToTexture(shader, *job, destination.get()); } -void RenderProcessor::ProcessSamples(SampleBuffer &destination, const Node *node, const TimeRange &range, const SampleJob &job) +void RenderProcessor::ProcessSamples(SampleBuffer &destination, const Node *node, const SampleJob &job) { - /* - if (!job.samples().is_allocated()) { - return; - } - - NodeValueRow value_db; - - const AudioParams& audio_params = GetCacheAudioParams(); - - for (size_t i=0;i(i) / static_cast(audio_params.sample_rate()); - - rational this_sample_time = rational::fromDouble(range.in().toDouble() + sample_to_second); - - // Update all non-sample and non-footage inputs - for (auto j=job.GetValues().constBegin(); j!=job.GetValues().constEnd(); j++) { - TimeRange r = TimeRange(this_sample_time, this_sample_time); - NodeValueTable value = ProcessInput(node, j.key(), r); - - value_db.insert(j.key(), GenerateRowValue(node, j.key(), &value, r)); - } - - node->ProcessSamples(value_db, - job.samples(), - destination, - i); - } - */ - qDebug() << "processing samples is stubbed"; + node->ProcessSamples(job, destination); } void RenderProcessor::ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob *job) @@ -667,8 +638,15 @@ void RenderProcessor::ResolveJobs(NodeValue &val) if (val.canConvert()) { SampleJob job = val.value(); - SampleBuffer output_buffer = CreateSampleBuffer(job.samples().audio_params(), job.samples().sample_count()); - ProcessSamples(output_buffer, val.source(), job.time(), job); + + for (auto it=job.GetValues().begin(); it!=job.GetValues().end(); it++) { + // Jobs will almost always be submitted with one of these types + NodeValue &subval = it.value(); + ResolveJobs(subval); + } + + SampleBuffer output_buffer = CreateSampleBuffer(job.audio_params(), job.sample_count()); + ProcessSamples(output_buffer, val.source(), job); val.set_value(QVariant::fromValue(output_buffer)); } else if (val.canConvert()) { diff --git a/app/render/renderprocessor.h b/app/render/renderprocessor.h index 32ad5440e7..ed9d9d8322 100644 --- a/app/render/renderprocessor.h +++ b/app/render/renderprocessor.h @@ -52,7 +52,7 @@ class RenderProcessor void ProcessShader(TexturePtr destination, const Node *node, const ShaderJob *job); - void ProcessSamples(SampleBuffer &destination, const Node *node, const TimeRange &range, const SampleJob &job); + void ProcessSamples(SampleBuffer &destination, const Node *node, const SampleJob &job); void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob *job); From 04e02c86b02fd4a9db8ad46083dd137a131a6ae4 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 26 Mar 2023 17:59:39 -0700 Subject: [PATCH 08/71] use newer version of core --- ext/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/core b/ext/core index 2777928248..60b9d4590f 160000 --- a/ext/core +++ b/ext/core @@ -1 +1 @@ -Subproject commit 277792824801495e868580ca86f6e7a1b53e4779 +Subproject commit 60b9d4590f385e1f1f3feaad8f6f4ddb4746c4d3 From 12f045c09781f9f22dca37d4ed8d44b023808d3a Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 26 Mar 2023 18:02:26 -0700 Subject: [PATCH 09/71] excise nodevaluedatabase --- app/core.cpp | 1 - app/node/CMakeLists.txt | 2 - app/node/valuedatabase.cpp | 35 ------------- app/node/valuedatabase.h | 89 --------------------------------- app/render/job/acceleratedjob.h | 1 - 5 files changed, 128 deletions(-) delete mode 100644 app/node/valuedatabase.cpp delete mode 100644 app/node/valuedatabase.h diff --git a/app/core.cpp b/app/core.cpp index bdd794e73d..4bb60cef6b 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -110,7 +110,6 @@ void Core::DeclareTypesForQt() qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); - qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); diff --git a/app/node/CMakeLists.txt b/app/node/CMakeLists.txt index e9eea1b68b..38c1a53167 100644 --- a/app/node/CMakeLists.txt +++ b/app/node/CMakeLists.txt @@ -53,7 +53,5 @@ set(OLIVE_SOURCES node/splitvalue.h node/value.cpp node/value.h - node/valuedatabase.cpp - node/valuedatabase.h PARENT_SCOPE ) diff --git a/app/node/valuedatabase.cpp b/app/node/valuedatabase.cpp deleted file mode 100644 index f83e2a7f86..0000000000 --- a/app/node/valuedatabase.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2022 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#include "valuedatabase.h" - -namespace olive { - -NodeValueTable NodeValueDatabase::Merge() const -{ - QHash copy = tables_; - - // Kinda hacky, but we don't need this table to slipstream - copy.remove(QStringLiteral("global")); - - return NodeValueTable::Merge(copy.values()); -} - -} diff --git a/app/node/valuedatabase.h b/app/node/valuedatabase.h deleted file mode 100644 index 27745ee27e..0000000000 --- a/app/node/valuedatabase.h +++ /dev/null @@ -1,89 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2022 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#ifndef NODEVALUEDATABASE_H -#define NODEVALUEDATABASE_H - -#include "param.h" -#include "value.h" - -namespace olive { - -class NodeValueDatabase -{ -public: - NodeValueDatabase() = default; - - NodeValueTable& operator[](const QString& input_id) - { - return tables_[input_id]; - } - - void Insert(const QString& key, const NodeValueTable &value) - { - tables_.insert(key, value); - } - - NodeValueTable Take(const QString &key) - { - return tables_.take(key); - } - - NodeValueTable Merge() const; - - using Tables = QHash; - using const_iterator = Tables::const_iterator; - using iterator = Tables::iterator; - - inline const_iterator cbegin() const - { - return tables_.cbegin(); - } - - inline const_iterator cend() const - { - return tables_.cend(); - } - - inline iterator begin() - { - return tables_.begin(); - } - - inline iterator end() - { - return tables_.end(); - } - - inline bool contains(const QString& s) const - { - return tables_.contains(s); - } - -private: - Tables tables_; - -}; - -} - -Q_DECLARE_METATYPE(olive::NodeValueDatabase) - -#endif // NODEVALUEDATABASE_H diff --git a/app/render/job/acceleratedjob.h b/app/render/job/acceleratedjob.h index 13b0d4da72..07a350f45a 100644 --- a/app/render/job/acceleratedjob.h +++ b/app/render/job/acceleratedjob.h @@ -22,7 +22,6 @@ #define ACCELERATEDJOB_H #include "node/param.h" -#include "node/valuedatabase.h" namespace olive { From 18c7396594908ac6624e7c919c78f9e420c57fb5 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 26 Mar 2023 18:14:47 -0700 Subject: [PATCH 10/71] excised nodevaluetable --- app/core.cpp | 1 - app/node/math/math/math.cpp | 5 +- app/node/math/math/mathbase.cpp | 74 +++++++++++------------ app/node/math/math/mathbase.h | 7 ++- app/node/value.cpp | 102 -------------------------------- app/node/value.h | 99 ------------------------------- app/render/renderprocessor.cpp | 1 - 7 files changed, 41 insertions(+), 248 deletions(-) diff --git a/app/core.cpp b/app/core.cpp index 4bb60cef6b..5030a54b1c 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -109,7 +109,6 @@ void Core::DeclareTypesForQt() { qRegisterMetaType(); qRegisterMetaType(); - qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); diff --git a/app/node/math/math/math.cpp b/app/node/math/math/math.cpp index c639ddd76e..378ab90d1e 100644 --- a/app/node/math/math/math.cpp +++ b/app/node/math/math/math.cpp @@ -97,7 +97,6 @@ NodeValue MathNode::Value(const ValueParams &p) const { // Auto-detect what values to operate with // FIXME: Very inefficient - NodeValueTable at, bt; auto aval = GetInputValue(p, kParamAIn); auto bval = GetInputValue(p, kParamBIn); @@ -108,9 +107,7 @@ NodeValue MathNode::Value(const ValueParams &p) const return bval; } - at.Push(aval); - bt.Push(bval); - PairingCalculator calc(at, bt); + PairingCalculator calc(aval, bval); // Do nothing if no pairing was found if (calc.FoundMostLikelyPairing()) { diff --git a/app/node/math/math/mathbase.cpp b/app/node/math/math/mathbase.cpp index 643acad470..d965ce41e9 100644 --- a/app/node/math/math/mathbase.cpp +++ b/app/node/math/math/mathbase.cpp @@ -479,13 +479,13 @@ bool MathNodeBase::NumberIsNoOp(const MathNodeBase::Operation &op, const float & return false; } -MathNodeBase::PairingCalculator::PairingCalculator(const NodeValueTable &table_a, const NodeValueTable &table_b) +MathNodeBase::PairingCalculator::PairingCalculator(const NodeValue &table_a, const NodeValue &table_b) { QVector pair_likelihood_a = GetPairLikelihood(table_a); QVector pair_likelihood_b = GetPairLikelihood(table_b); - int weight_a = qMax(0, table_b.Count() - table_a.Count()); - int weight_b = qMax(0, table_a.Count() - table_b.Count()); + int weight_a = 0; + int weight_b = 0; QVector likelihoods(kPairCount); @@ -509,47 +509,45 @@ MathNodeBase::PairingCalculator::PairingCalculator(const NodeValueTable &table_a } if (most_likely_pairing_ != kPairNone) { - most_likely_value_a_ = table_a.at(pair_likelihood_a.at(most_likely_pairing_)); - most_likely_value_b_ = table_b.at(pair_likelihood_b.at(most_likely_pairing_)); + most_likely_value_a_ = table_a; + most_likely_value_b_ = table_b; } } -QVector MathNodeBase::PairingCalculator::GetPairLikelihood(const NodeValueTable &table) +QVector MathNodeBase::PairingCalculator::GetPairLikelihood(const NodeValue &table) { QVector likelihood(kPairCount, -1); - for (int i=0;i GetPairLikelihood(const NodeValueTable& table); + static QVector GetPairLikelihood(const NodeValue &table); Pairing most_likely_pairing_; diff --git a/app/node/value.cpp b/app/node/value.cpp index dab17809ed..3931fe8a15 100644 --- a/app/node/value.cpp +++ b/app/node/value.cpp @@ -376,106 +376,4 @@ NodeValue::Type NodeValue::GetDataTypeFromName(const QString &n) return NodeValue::kNone; } -NodeValue NodeValueTable::Get(const QVector &type, const QString &tag) const -{ - int value_index = GetValueIndex(type, tag); - - if (value_index >= 0) { - return values_.at(value_index); - } - - return NodeValue(); -} - -NodeValue NodeValueTable::Take(const QVector &type, const QString &tag) -{ - int value_index = GetValueIndex(type, tag); - - if (value_index >= 0) { - return values_.takeAt(value_index); - } - - return NodeValue(); -} - -bool NodeValueTable::Has(NodeValue::Type type) const -{ - for (int i=values_.size() - 1;i>=0;i--) { - const NodeValue& v = values_.at(i); - - if (v.type() & type) { - return true; - } - } - - return false; -} - -void NodeValueTable::Remove(const NodeValue &v) -{ - for (int i=values_.size() - 1;i>=0;i--) { - const NodeValue& compare = values_.at(i); - - if (compare == v) { - values_.removeAt(i); - return; - } - } -} - -NodeValueTable NodeValueTable::Merge(QList tables) -{ - if (tables.size() == 1) { - return tables.first(); - } - - int row = 0; - - NodeValueTable merged_table; - - // Slipstreams all tables together - while (true) { - bool all_merged = true; - - foreach (const NodeValueTable& t, tables) { - if (row < t.Count()) { - all_merged = false; - } else { - continue; - } - - int row_index = t.Count() - 1 - row; - - merged_table.Prepend(t.at(row_index)); - } - - row++; - - if (all_merged) { - break; - } - } - - return merged_table; -} - -int NodeValueTable::GetValueIndex(const QVector& types, const QString &tag) const -{ - int index = -1; - - for (int i=values_.size() - 1;i>=0;i--) { - const NodeValue& v = values_.at(i); - - if (types.contains(v.type())) { - index = i; - - if (tag.isEmpty() || tag == v.tag()) { - break; - } - } - } - - return index; -} - } diff --git a/app/node/value.h b/app/node/value.h index d42ba9a598..92ea86695b 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -34,10 +34,8 @@ namespace olive { class Node; class NodeValue; -class NodeValueTable; using NodeValueArray = std::map; -using NodeValueTableArray = std::map; class NodeValue { @@ -362,107 +360,10 @@ class NodeValue }; -class NodeValueTable -{ -public: - NodeValueTable() = default; - - NodeValue Get(NodeValue::Type type, const QString& tag = QString()) const - { - QVector types = {type}; - return Get(types, tag); - } - - NodeValue Get(const QVector& type, const QString& tag = QString()) const; - - NodeValue Take(NodeValue::Type type, const QString& tag = QString()) - { - QVector types = {type}; - return Take(types, tag); - } - - NodeValue Take(const QVector& type, const QString& tag = QString()); - - void Push(const NodeValue& value) - { - values_.append(value); - } - - void Push(const NodeValueTable& value) - { - values_.append(value.values_); - } - - template - void Push(NodeValue::Type type, const T& data, const Node *from, bool array = false, const QString& tag = QString()) - { - Push(NodeValue(type, data, from, array, tag)); - } - - template - void Push(NodeValue::Type type, const T& data, const Node *from, const QString& tag) - { - Push(NodeValue(type, data, from, false, tag)); - } - - void Prepend(const NodeValue& value) - { - values_.prepend(value); - } - - template - void Prepend(NodeValue::Type type, const T& data, const Node *from, bool array = false, const QString& tag = QString()) - { - Prepend(NodeValue(type, data, from, array, tag)); - } - - template - void Prepend(NodeValue::Type type, const T& data, const Node *from, const QString& tag) - { - Prepend(NodeValue(type, data, from, false, tag)); - } - - const NodeValue& at(int index) const - { - return values_.at(index); - } - NodeValue TakeAt(int index) - { - return values_.takeAt(index); - } - - int Count() const - { - return values_.size(); - } - - bool Has(NodeValue::Type type) const; - void Remove(const NodeValue& v); - - void Clear() - { - values_.clear(); - } - - bool isEmpty() const - { - return values_.isEmpty(); - } - - int GetValueIndex(const QVector &type, const QString& tag) const; - - static NodeValueTable Merge(QList tables); - -private: - QVector values_; - -}; - using NodeValueRow = QHash; } Q_DECLARE_METATYPE(olive::NodeValue) -Q_DECLARE_METATYPE(olive::NodeValueTable) #endif // NODEVALUE_H diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 0bee9e7015..53c128605b 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -45,7 +45,6 @@ TexturePtr RenderProcessor::GenerateTexture(const rational &time, const rational { TimeRange range = TimeRange(time, time + frame_length); - NodeValueTable table; NodeValue tex_val; if (Node* node = QtUtils::ValueToPtr(ticket_->property("node"))) { ValueParams vp(GetCacheVideoParams(), GetCacheAudioParams(), range, LoopMode::kLoopModeOff, GetCancelPointer()); From d26a14284e2d048ed79d6c382ed444f1d070394a Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 26 Mar 2023 18:35:51 -0700 Subject: [PATCH 11/71] nerfed valuehints --- app/node/audio/pan/pan.cpp | 4 +-- app/node/globals.cpp | 11 +++++-- app/node/globals.h | 14 +++++---- app/node/math/math/mathbase.cpp | 6 ++-- app/node/node.cpp | 30 ++----------------- app/node/node.h | 18 ++--------- app/node/project/footage/footage.cpp | 21 +++++++++---- .../project/serializer/serializer210528.cpp | 16 +--------- .../project/serializer/serializer210907.cpp | 16 +--------- .../project/serializer/serializer211228.cpp | 16 +--------- .../project/serializer/serializer220403.cpp | 16 +--------- app/render/renderprocessor.cpp | 4 +-- app/widget/viewer/viewerdisplay.cpp | 12 ++++---- 13 files changed, 55 insertions(+), 129 deletions(-) diff --git a/app/node/audio/pan/pan.cpp b/app/node/audio/pan/pan.cpp index 5f4e216602..223c4245ca 100644 --- a/app/node/audio/pan/pan.cpp +++ b/app/node/audio/pan/pan.cpp @@ -101,8 +101,8 @@ void PanNode::ProcessSamples(const SampleJob &job, SampleBuffer &output) const } else { for (size_t index = 0; index < output.sample_count(); index++) { rational this_sample_time = p.time().in() + rational(index, job.audio_params().sample_rate()); - ValueParams this_sample_p(p.vparams(), p.aparams(), TimeRange(this_sample_time, this_sample_time + job.audio_params().sample_rate_as_time_base()), p.loop_mode(), p.cancel_atom()); - auto pan_val = GetInputValue(this_sample_p, kPanningInput).toDouble(); + TimeRange this_sample_range(this_sample_time, this_sample_time + job.audio_params().sample_rate_as_time_base()); + auto pan_val = GetInputValue(p.time_transformed(this_sample_range), kPanningInput).toDouble(); if (pan_val > 0) { output.data(0)[index] = input.data(0)[index] * (1.0F - pan_val); diff --git a/app/node/globals.cpp b/app/node/globals.cpp index 52c57619c1..70aaa5bbb0 100644 --- a/app/node/globals.cpp +++ b/app/node/globals.cpp @@ -22,11 +22,18 @@ namespace olive { -/*ValueParams ValueParams::time_transformed(const TimeRange &time) const +ValueParams ValueParams::time_transformed(const TimeRange &time) const { ValueParams g = *this; g.time_ = time; return g; -}*/ +} + +ValueParams ValueParams::output_edited(const QString &output) const +{ + ValueParams g = *this; + g.output_ = output; + return g; +} } diff --git a/app/node/globals.h b/app/node/globals.h index 064d9e8ec9..0eac4a921e 100644 --- a/app/node/globals.h +++ b/app/node/globals.h @@ -36,17 +36,18 @@ class ValueParams { } - ValueParams(const VideoParams &vparam, const AudioParams &aparam, const TimeRange &time, LoopMode loop_mode, CancelAtom *cancel) : + ValueParams(const VideoParams &vparam, const AudioParams &aparam, const TimeRange &time, const QString &output, LoopMode loop_mode, CancelAtom *cancel) : video_params_(vparam), audio_params_(aparam), time_(time), loop_mode_(loop_mode), - cancel_atom_(cancel) + cancel_atom_(cancel), + output_(output) { } - ValueParams(const VideoParams &vparam, const AudioParams &aparam, const rational &time, LoopMode loop_mode, CancelAtom *cancel) : - ValueParams(vparam, aparam, TimeRange(time, time + vparam.frame_rate_as_time_base()), loop_mode, cancel) + ValueParams(const VideoParams &vparam, const AudioParams &aparam, const rational &time, const QString &output, LoopMode loop_mode, CancelAtom *cancel) : + ValueParams(vparam, aparam, TimeRange(time, time + vparam.frame_rate_as_time_base()), output, loop_mode, cancel) { } @@ -56,11 +57,13 @@ class ValueParams const VideoParams &vparams() const { return video_params_; } const TimeRange &time() const { return time_; } LoopMode loop_mode() const { return loop_mode_; } + const QString &output() const { return output_; } CancelAtom *cancel_atom() const { return cancel_atom_; } bool is_cancelled() const { return cancel_atom_ && cancel_atom_->IsCancelled(); } - //ValueParams time_transformed(const TimeRange &time) const; + ValueParams time_transformed(const TimeRange &time) const; + ValueParams output_edited(const QString &output) const; private: VideoParams video_params_; @@ -68,6 +71,7 @@ class ValueParams TimeRange time_; LoopMode loop_mode_; CancelAtom *cancel_atom_; + QString output_; }; diff --git a/app/node/math/math/mathbase.cpp b/app/node/math/math/mathbase.cpp index d965ce41e9..9218a45657 100644 --- a/app/node/math/math/mathbase.cpp +++ b/app/node/math/math/mathbase.cpp @@ -228,8 +228,6 @@ void MathNodeBase::PerformAllOnFloatBufferSSE(Operation operation, float *a, flo NodeValue MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QString& param_a_in, const NodeValue& val_a, const QString& param_b_in, const NodeValue& val_b, const ValueParams &p) const { - qDebug() << pairing; - switch (pairing) { case kPairNumberNumber: @@ -439,8 +437,8 @@ void MathNodeBase::ProcessSamplesNumberInternal(const ValueParams &p, MathNodeBa } else { for (size_t j = 0; j < input.sample_count(); j++) { rational this_sample_time = p.time().in() + rational(j, input.audio_params().sample_rate()); - ValueParams this_sample_p(p.vparams(), p.aparams(), TimeRange(this_sample_time, this_sample_time + input.audio_params().sample_rate_as_time_base()), p.loop_mode(), p.cancel_atom()); - auto v = GetInputValue(this_sample_p, number_in).toDouble(); + TimeRange this_sample_range(this_sample_time, this_sample_time + input.audio_params().sample_rate_as_time_base()); + auto v = GetInputValue(p.time_transformed(this_sample_range), number_in).toDouble(); for (int i=0;i(operation, input.data(i)[j], v); diff --git a/app/node/node.cpp b/app/node/node.cpp index df3a959d1d..6a2cd81e3b 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -873,13 +873,11 @@ NodeValue Node::GetInputValue(const ValueParams &g, const QString &input, int el TimeRange adjusted_time = InputTimeAdjustment(input, element, g.time(), true); if (Node *output = GetConnectedOutput(input, element)) { - ValueParams adj_param(g.vparams(), g.aparams(), adjusted_time, g.loop_mode(), g.cancel_atom()); - NodeValue ret; while (output) { if (output->is_enabled()) { - ret = output->Value(adj_param); + ret = output->Value(g.time_transformed(adjusted_time).output_edited(this->GetValueHintForInput(input, element).tag())); break; } else { output = output->GetConnectedOutput(output->GetEffectInput()); @@ -2476,19 +2474,7 @@ bool Node::ValueHint::load(QXmlStreamReader *reader) Q_UNUSED(version) while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("types")) { - QVector types; - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("type")) { - types.append(static_cast(reader->readElementText().toInt())); - } else { - reader->skipCurrentElement(); - } - } - this->set_type(types); - } else if (reader->name() == QStringLiteral("index")) { - this->set_index(reader->readElementText().toInt()); - } else if (reader->name() == QStringLiteral("tag")) { + if (reader->name() == QStringLiteral("tag")) { this->set_tag(reader->readElementText()); } else { reader->skipCurrentElement(); @@ -2500,17 +2486,7 @@ bool Node::ValueHint::load(QXmlStreamReader *reader) void Node::ValueHint::save(QXmlStreamWriter *writer) const { - writer->writeAttribute(QStringLiteral("version"), QString::number(1)); - - writer->writeStartElement(QStringLiteral("types")); - - for (auto it=this->types().cbegin(); it!=this->types().cend(); it++) { - writer->writeTextElement(QStringLiteral("type"), QString::number(*it)); - } - - writer->writeEndElement(); // types - - writer->writeTextElement(QStringLiteral("index"), QString::number(this->index())); + writer->writeAttribute(QStringLiteral("version"), QString::number(2)); writer->writeTextElement(QStringLiteral("tag"), this->tag()); } diff --git a/app/node/node.h b/app/node/node.h index 20c476d2b9..a5134860c6 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -570,47 +570,35 @@ class Node : public QObject return effect_input_; } - class ValueHint { + class ValueHint + { public: explicit ValueHint(const QVector &types = QVector(), int index = -1, const QString &tag = QString()) : - type_(types), - index_(index), tag_(tag) { } explicit ValueHint(const QVector &types, const QString &tag) : - type_(types), - index_(-1), tag_(tag) { } - explicit ValueHint(int index) : - index_(index) + explicit ValueHint(int index) { } explicit ValueHint(const QString &tag) : - index_(-1), tag_(tag) { } - const QVector &types() const { return type_; } - const int &index() const { return index_; } const QString& tag() const { return tag_; } - - void set_type(const QVector &type) { type_ = type; } - void set_index(const int &index) { index_ = index; } void set_tag(const QString &tag) { tag_ = tag; } bool load(QXmlStreamReader *reader); void save(QXmlStreamWriter *writer) const; private: - QVector type_; - int index_; QString tag_; }; diff --git a/app/node/project/footage/footage.cpp b/app/node/project/footage/footage.cpp index d7fc1a6b00..e3ca49d804 100644 --- a/app/node/project/footage/footage.cpp +++ b/app/node/project/footage/footage.cpp @@ -233,17 +233,18 @@ QString Footage::DescribeSubtitleStream(const SubtitleParams ¶ms) NodeValue Footage::Value(const ValueParams &p) const { + Track::Reference ref = Track::Reference::FromString(p.output()); + // Pop filename from table QString file = GetInputValue(p, kFilenameInput).toString(); // If the file exists and the reference is valid, push a footage job to the renderer if (QFileInfo::exists(file)) { - // Push each stream as a footage job - for (int i=0; i= 0 && ref.index() < GetVideoStreamCount()) { VideoParams vp = GetVideoParams(ref.index()); // Ensure the colorspace is valid and not empty @@ -262,13 +263,21 @@ NodeValue Footage::Value(const ValueParams &p) const job.set_video_params(vp); return NodeValue(NodeValue::kTexture, Texture::Job(vp, job), this, ref.ToString()); - } else if (ref.type() == Track::kAudio) { + } + break; + case Track::kAudio: + if (ref.index() >= 0 && ref.index() < GetAudioStreamCount()) { AudioParams ap = GetAudioParams(ref.index()); job.set_audio_params(ap); job.set_cache_path(project()->cache_path()); return NodeValue(NodeValue::kSamples, QVariant::fromValue(job), this, ref.ToString()); } + break; + case Track::kSubtitle: + case Track::kNone: + case Track::kCount: + break; } } diff --git a/app/node/project/serializer/serializer210528.cpp b/app/node/project/serializer/serializer210528.cpp index a4a3854503..5a62daa694 100644 --- a/app/node/project/serializer/serializer210528.cpp +++ b/app/node/project/serializer/serializer210528.cpp @@ -756,27 +756,13 @@ void ProjectSerializer210528::LoadMarkerList(QXmlStreamReader *reader, TimelineM void ProjectSerializer210528::LoadValueHint(Node::ValueHint *hint, QXmlStreamReader *reader) const { - QVector types; - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("types")) { - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("type")) { - types.append(static_cast(reader->readElementText().toInt())); - } else { - reader->skipCurrentElement(); - } - } - } else if (reader->name() == QStringLiteral("index")) { - hint->set_index(reader->readElementText().toInt()); - } else if (reader->name() == QStringLiteral("tag")) { + if (reader->name() == QStringLiteral("tag")) { hint->set_tag(reader->readElementText()); } else { reader->skipCurrentElement(); } } - - hint->set_type(types); } } diff --git a/app/node/project/serializer/serializer210907.cpp b/app/node/project/serializer/serializer210907.cpp index 197f97b769..a699d6d9e0 100644 --- a/app/node/project/serializer/serializer210907.cpp +++ b/app/node/project/serializer/serializer210907.cpp @@ -748,27 +748,13 @@ void ProjectSerializer210907::LoadMarkerList(QXmlStreamReader *reader, TimelineM void ProjectSerializer210907::LoadValueHint(Node::ValueHint *hint, QXmlStreamReader *reader) const { - QVector types; - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("types")) { - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("type")) { - types.append(static_cast(reader->readElementText().toInt())); - } else { - reader->skipCurrentElement(); - } - } - } else if (reader->name() == QStringLiteral("index")) { - hint->set_index(reader->readElementText().toInt()); - } else if (reader->name() == QStringLiteral("tag")) { + if (reader->name() == QStringLiteral("tag")) { hint->set_tag(reader->readElementText()); } else { reader->skipCurrentElement(); } } - - hint->set_type(types); } } diff --git a/app/node/project/serializer/serializer211228.cpp b/app/node/project/serializer/serializer211228.cpp index a9ccf3d0a0..4e8a638c34 100644 --- a/app/node/project/serializer/serializer211228.cpp +++ b/app/node/project/serializer/serializer211228.cpp @@ -798,27 +798,13 @@ void ProjectSerializer211228::LoadMarkerList(QXmlStreamReader *reader, TimelineM void ProjectSerializer211228::LoadValueHint(Node::ValueHint *hint, QXmlStreamReader *reader) const { - QVector types; - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("types")) { - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("type")) { - types.append(static_cast(reader->readElementText().toInt())); - } else { - reader->skipCurrentElement(); - } - } - } else if (reader->name() == QStringLiteral("index")) { - hint->set_index(reader->readElementText().toInt()); - } else if (reader->name() == QStringLiteral("tag")) { + if (reader->name() == QStringLiteral("tag")) { hint->set_tag(reader->readElementText()); } else { reader->skipCurrentElement(); } } - - hint->set_type(types); } } diff --git a/app/node/project/serializer/serializer220403.cpp b/app/node/project/serializer/serializer220403.cpp index 0ee37bc434..1630aba359 100644 --- a/app/node/project/serializer/serializer220403.cpp +++ b/app/node/project/serializer/serializer220403.cpp @@ -1027,27 +1027,13 @@ void ProjectSerializer220403::LoadMarkerList(QXmlStreamReader *reader, TimelineM void ProjectSerializer220403::LoadValueHint(Node::ValueHint *hint, QXmlStreamReader *reader) const { - QVector types; - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("types")) { - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("type")) { - types.append(static_cast(reader->readElementText().toInt())); - } else { - reader->skipCurrentElement(); - } - } - } else if (reader->name() == QStringLiteral("index")) { - hint->set_index(reader->readElementText().toInt()); - } else if (reader->name() == QStringLiteral("tag")) { + if (reader->name() == QStringLiteral("tag")) { hint->set_tag(reader->readElementText()); } else { reader->skipCurrentElement(); } } - - hint->set_type(types); } } diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 53c128605b..3e3d148288 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -47,7 +47,7 @@ TexturePtr RenderProcessor::GenerateTexture(const rational &time, const rational NodeValue tex_val; if (Node* node = QtUtils::ValueToPtr(ticket_->property("node"))) { - ValueParams vp(GetCacheVideoParams(), GetCacheAudioParams(), range, LoopMode::kLoopModeOff, GetCancelPointer()); + ValueParams vp(GetCacheVideoParams(), GetCacheAudioParams(), range, QString(), LoopMode::kLoopModeOff, GetCancelPointer()); tex_val = node->Value(vp); } @@ -218,7 +218,7 @@ void RenderProcessor::Run() NodeValue sample_val; if (Node* node = QtUtils::ValueToPtr(ticket_->property("node"))) { - ValueParams vp(GetCacheVideoParams(), GetCacheAudioParams(), time, LoopMode::kLoopModeOff, GetCancelPointer()); + ValueParams vp(GetCacheVideoParams(), GetCacheAudioParams(), time, QString(), LoopMode::kLoopModeOff, GetCancelPointer()); sample_val = node->Value(vp); } diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index ad49902d29..26c076bba2 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -451,7 +451,7 @@ void ViewerDisplayWidget::OnPaint() p.setWorldTransform(gizmo_last_draw_transform_); - gizmos_->UpdateGizmoPositions(ValueParams(gizmo_params_, gizmo_audio_params_, gizmo_draw_time_, LoopMode::kLoopModeOff, nullptr)); + gizmos_->UpdateGizmoPositions(ValueParams(gizmo_params_, gizmo_audio_params_, gizmo_draw_time_, QString(), LoopMode::kLoopModeOff, nullptr)); foreach (NodeGizmo *gizmo, gizmos_->GetGizmos()) { if (gizmo->IsVisible()) { gizmo->Draw(&p); @@ -681,7 +681,7 @@ QTransform ViewerDisplayWidget::GenerateGizmoTransform(const TimeRange &range) for (auto it = list.cbegin(); it != list.cend(); it++) { // Transform matrix down the list - ValueParams vp(gizmo_params_, gizmo_audio_params_, ranges.front(), LoopMode::kLoopModeOff, nullptr); + ValueParams vp(gizmo_params_, gizmo_audio_params_, ranges.front(), QString(), LoopMode::kLoopModeOff, nullptr); Node *n = (*it).node(); if (n->is_enabled()) { QTransform this_transform = n->GizmoTransformation(vp); @@ -738,7 +738,7 @@ NodeGizmo *ViewerDisplayWidget::TryGizmoPress(const QPointF &p) void ViewerDisplayWidget::OpenTextGizmo(TextGizmo *text, QMouseEvent *event) { GenerateGizmoTransforms(); - gizmos_->UpdateGizmoPositions(ValueParams(gizmo_params_, gizmo_audio_params_, gizmo_draw_time_, LoopMode::kLoopModeOff, nullptr)); + gizmos_->UpdateGizmoPositions(ValueParams(gizmo_params_, gizmo_audio_params_, gizmo_draw_time_, QString(), LoopMode::kLoopModeOff, nullptr)); active_text_gizmo_ = text; connect(active_text_gizmo_, &TextGizmo::RectChanged, this, &ViewerDisplayWidget::UpdateActiveTextGizmoSize); @@ -861,7 +861,7 @@ bool ViewerDisplayWidget::OnMousePress(QMouseEvent *event) // Handle gizmo click gizmo_start_drag_ = event->pos(); gizmo_last_drag_ = gizmo_start_drag_; - current_gizmo_->SetGlobals(ValueParams(gizmo_params_, gizmo_audio_params_, GenerateGizmoTime(), LoopMode::kLoopModeOff, nullptr)); + current_gizmo_->SetGlobals(ValueParams(gizmo_params_, gizmo_audio_params_, GenerateGizmoTime(), QString(), LoopMode::kLoopModeOff, nullptr)); } else { @@ -908,7 +908,7 @@ bool ViewerDisplayWidget::OnMouseMove(QMouseEvent *event) QPointF start = ScreenToScenePoint(gizmo_start_drag_); TimeRange gizmo_time = GetGizmoTimeRange(); - ValueParams p(gizmo_params_, gizmo_audio_params_, gizmo_time, LoopMode::kLoopModeOff, nullptr); + ValueParams p(gizmo_params_, gizmo_audio_params_, gizmo_time, QString(), LoopMode::kLoopModeOff, nullptr); draggable->DragStart(p, start.x(), start.y(), gizmo_time.in()); gizmo_drag_started_ = true; @@ -1244,7 +1244,7 @@ void ViewerDisplayWidget::GenerateGizmoTransforms() { gizmo_draw_time_ = GenerateGizmoTime(); - ValueParams p(gizmo_params_, gizmo_audio_params_, gizmo_draw_time_, LoopMode::kLoopModeOff, nullptr); + ValueParams p(gizmo_params_, gizmo_audio_params_, gizmo_draw_time_, QString(), LoopMode::kLoopModeOff, nullptr); gizmo_last_draw_transform_ = GenerateGizmoTransform(gizmo_draw_time_); gizmo_last_draw_transform_inverted_ = gizmo_last_draw_transform_.inverted(); From 457b5a0d235b18629dc23c73c1696a428d620749 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 26 Mar 2023 18:50:25 -0700 Subject: [PATCH 12/71] shifted nodevaluerow to accelerated jobs - the only place that uses them now --- app/node/value.h | 2 -- app/render/job/acceleratedjob.h | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/node/value.h b/app/node/value.h index 92ea86695b..da1521abb7 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -360,8 +360,6 @@ class NodeValue }; -using NodeValueRow = QHash; - } Q_DECLARE_METATYPE(olive::NodeValue) diff --git a/app/render/job/acceleratedjob.h b/app/render/job/acceleratedjob.h index 07a350f45a..cc1a221ca7 100644 --- a/app/render/job/acceleratedjob.h +++ b/app/render/job/acceleratedjob.h @@ -28,6 +28,8 @@ namespace olive { class AcceleratedJob { public: + using NodeValueRow = QHash; + AcceleratedJob() = default; virtual ~AcceleratedJob(){} From 79c1ab7e422226b0eb2bc665e02e0f12d1dfd69d Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 26 Mar 2023 18:50:40 -0700 Subject: [PATCH 13/71] make node value arrays into actual arrays --- app/node/node.cpp | 8 +++----- app/node/value.h | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/node/node.cpp b/app/node/node.cpp index 6a2cd81e3b..4ce4b4d79e 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -887,12 +887,10 @@ NodeValue Node::GetInputValue(const ValueParams &g, const QString &input, int el return ret; } else { if (element == -1 && InputIsArray(input)) { - NodeValueArray array; + NodeValueArray array(InputArraySize(input)); - int sz = InputArraySize(input); - for (int i = 0; i < sz; i++) { - NodeValue ele(GetInputDataType(input), GetValueAtTime(input, adjusted_time.in(), i), this, false); - array[i] = ele; + for (size_t i = 0; i < array.size(); i++) { + array[i] = NodeValue(GetInputDataType(input), GetValueAtTime(input, adjusted_time.in(), i), this, false); } return NodeValue(GetInputDataType(input), array, this, true); diff --git a/app/node/value.h b/app/node/value.h index da1521abb7..5240a52395 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -35,7 +35,7 @@ namespace olive { class Node; class NodeValue; -using NodeValueArray = std::map; +using NodeValueArray = std::vector; class NodeValue { From 90a55989d60a46ad59f20d17ae90627ede193308 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Mon, 27 Mar 2023 15:48:43 -0700 Subject: [PATCH 14/71] reimplemented loop mode --- app/node/block/clip/clip.cpp | 2 +- app/node/globals.cpp | 7 +++++++ app/node/globals.h | 1 + app/render/renderprocessor.cpp | 3 +-- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index 48e59be39e..01da6131b4 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -486,7 +486,7 @@ TimeRange ClipBlock::OutputTimeAdjustment(const QString& input, int element, con NodeValue ClipBlock::Value(const ValueParams &p) const { - return GetInputValue(p, kBufferIn); + return GetInputValue(p.loop_mode_edited(this->loop_mode()), kBufferIn); } void ClipBlock::Retranslate() diff --git a/app/node/globals.cpp b/app/node/globals.cpp index 70aaa5bbb0..f572b92392 100644 --- a/app/node/globals.cpp +++ b/app/node/globals.cpp @@ -36,4 +36,11 @@ ValueParams ValueParams::output_edited(const QString &output) const return g; } +ValueParams ValueParams::loop_mode_edited(const LoopMode &lm) const +{ + ValueParams g = *this; + g.loop_mode_ = lm; + return g; +} + } diff --git a/app/node/globals.h b/app/node/globals.h index 0eac4a921e..eb89dd7394 100644 --- a/app/node/globals.h +++ b/app/node/globals.h @@ -64,6 +64,7 @@ class ValueParams ValueParams time_transformed(const TimeRange &time) const; ValueParams output_edited(const QString &output) const; + ValueParams loop_mode_edited(const LoopMode &lm) const; private: VideoParams video_params_; diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 3e3d148288..49c2512caf 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -400,11 +400,10 @@ void RenderProcessor::ProcessAudioFootage(SampleBuffer &destination, const Foota if (decoder) { const AudioParams& audio_params = GetCacheAudioParams(); - qDebug() << "audio loop modes are stubbed"; Decoder::RetrieveAudioStatus status = decoder->RetrieveAudio(destination, input_time, audio_params, stream->cache_path(), - LoopMode::kLoopModeOff, //loop_mode(), + stream->loop_mode(), static_cast(ticket_->property("mode").toInt())); if (status == Decoder::kWaitingForConform) { From 46bc5c37e33f9e50a4243cadec969b37807c06b4 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Mon, 27 Mar 2023 18:57:46 -0700 Subject: [PATCH 15/71] transform: remove note --- app/node/distort/transform/transformdistortnode.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/app/node/distort/transform/transformdistortnode.cpp b/app/node/distort/transform/transformdistortnode.cpp index 4bf8542e43..e3bd477a6d 100644 --- a/app/node/distort/transform/transformdistortnode.cpp +++ b/app/node/distort/transform/transformdistortnode.cpp @@ -108,7 +108,6 @@ NodeValue TransformDistortNode::Value(const ValueParams &p) const } } - qDebug() << "Add second output for matrix"; //table->Push(NodeValue::kMatrix, QVariant::fromValue(generated_matrix), this); if (!job_to_push) { From 6c438578cf96d12f6c4cabdc9e60034db62f1323 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 28 Mar 2023 12:34:46 -0700 Subject: [PATCH 16/71] multicam: fixed all view --- app/node/input/multicam/multicamnode.cpp | 103 ++++++++++++- app/node/input/multicam/multicamnode.h | 4 + app/node/node.cpp | 35 +++-- app/node/node.h | 1 + app/panel/viewer/viewerbase.h | 5 - app/render/previewautocacher.cpp | 13 +- app/render/previewautocacher.h | 4 - app/render/rendermanager.cpp | 2 +- app/render/rendermanager.h | 3 +- app/render/renderprocessor.cpp | 4 +- app/widget/multicam/multicamdisplay.cpp | 111 +------------- app/widget/multicam/multicamdisplay.h | 10 -- app/widget/multicam/multicamwidget.h | 1 + app/widget/viewer/viewer.cpp | 180 ++++++++++++----------- app/widget/viewer/viewer.h | 14 +- app/window/mainwindow/mainwindow.cpp | 3 +- 16 files changed, 238 insertions(+), 255 deletions(-) diff --git a/app/node/input/multicam/multicamnode.cpp b/app/node/input/multicam/multicamnode.cpp index 70a2106152..6f3672488b 100644 --- a/app/node/input/multicam/multicamnode.cpp +++ b/app/node/input/multicam/multicamnode.cpp @@ -44,12 +44,109 @@ QString MultiCamNode::Description() const return tr("Allows easy switching between multiple sources."); } +ShaderCode MultiCamNode::GetShaderCode(const ShaderRequest &request) const +{ + auto l = request.id.split(':'); + int rows = l.at(0).toInt(); + int cols = l.at(1).toInt(); + + return ShaderCode(GenerateShaderCode(rows, cols)); +} + +QString dblToGlsl(double d) +{ + return QString::number(d, 'f'); +} + +QString MultiCamNode::GenerateShaderCode(int rows, int cols) +{ + int multiplier = std::max(cols, rows); + + QStringList shader; + + shader.append(QStringLiteral("in vec2 ove_texcoord;")); + shader.append(QStringLiteral("out vec4 frag_color;")); + + for (int x=0;x 0) { + shader.append(QStringLiteral(" else")); + } + if (x == cols-1) { + shader.append(QStringLiteral(" {")); + } else { + shader.append(QStringLiteral(" if (ove_texcoord.x < %1) {").arg(dblToGlsl(double(x+1)/double(multiplier)))); + } + + for (int y=0;y 0) { + shader.append(QStringLiteral(" else")); + } + if (y == rows-1) { + shader.append(QStringLiteral(" {")); + } else { + shader.append(QStringLiteral(" if (ove_texcoord.y < %1) {").arg(dblToGlsl(double(y+1)/double(multiplier)))); + } + QString input = QStringLiteral("tex_%1_%2").arg(QString::number(y), QString::number(x)); + shader.append(QStringLiteral(" vec2 coord = vec2((ove_texcoord.x+%1)*%2, (ove_texcoord.y+%3)*%4);").arg( + dblToGlsl( - double(x)/double(multiplier)), + dblToGlsl(multiplier), + dblToGlsl( - double(y)/double(multiplier)), + dblToGlsl(multiplier) + )); + shader.append(QStringLiteral(" if (%1_enabled && coord.x >= 0.0 && coord.x < 1.0 && coord.y >= 0.0 && coord.y < 1.0) {").arg(input)); + shader.append(QStringLiteral(" frag_color = texture(%1, coord);").arg(input)); + shader.append(QStringLiteral(" } else {")); + shader.append(QStringLiteral(" discard;")); + shader.append(QStringLiteral(" }")); + shader.append(QStringLiteral(" }")); + } + + shader.append(QStringLiteral(" }")); + } + + shader.append(QStringLiteral("}")); + + return shader.join('\n'); +} + NodeValue MultiCamNode::Value(const ValueParams &p) const { - int current = GetInputValue(p, kCurrentInput); + if (p.output() == QStringLiteral("all")) { + // Switcher mode: output a collage of all sources + int sources = GetSourceCount(); + int rows, cols; + MultiCamNode::GetRowsAndColumns(sources, &rows, &cols); + + ShaderJob job; + job.SetShaderID(QStringLiteral("%1:%2").arg(QString::number(rows), QString::number(cols))); + + for (int i=0; iValue(p); + return NodeValue(NodeValue::kTexture, Texture::Job(p.vparams(), job), this); + } else { + // Default behavior: output currently selected source + int current = GetInputValue(p, kCurrentInput).toInt(); + + if (Node *n = GetSourceNode(current)) { + return GetFakeConnectedValue(p, n, kSourcesInput, current); + } } return NodeValue(); diff --git a/app/node/input/multicam/multicamnode.h b/app/node/input/multicam/multicamnode.h index ffef070c18..a1ea92267b 100644 --- a/app/node/input/multicam/multicamnode.h +++ b/app/node/input/multicam/multicamnode.h @@ -21,6 +21,8 @@ class MultiCamNode : public Node virtual QVector Category() const override; virtual QString Description() const override; + virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; + virtual NodeValue Value(const ValueParams &p) const override; virtual void Retranslate() override; @@ -60,6 +62,8 @@ class MultiCamNode : public Node virtual void InputDisconnectedEvent(const QString &input, int element, Node *output) override; private: + static QString GenerateShaderCode(int rows, int cols); + Node *GetSourceNode(int source) const; TrackList *GetTrackList() const; diff --git a/app/node/node.cpp b/app/node/node.cpp index 4ce4b4d79e..30c94991e8 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -870,22 +870,12 @@ NodeValue Node::GetInputValue(const ValueParams &g, const QString &input, int el return NodeValue(); } - TimeRange adjusted_time = InputTimeAdjustment(input, element, g.time(), true); - if (Node *output = GetConnectedOutput(input, element)) { - NodeValue ret; + return GetFakeConnectedValue(g, output, input, element); + } else { - while (output) { - if (output->is_enabled()) { - ret = output->Value(g.time_transformed(adjusted_time).output_edited(this->GetValueHintForInput(input, element).tag())); - break; - } else { - output = output->GetConnectedOutput(output->GetEffectInput()); - } - } + TimeRange adjusted_time = InputTimeAdjustment(input, element, g.time(), true); - return ret; - } else { if (element == -1 && InputIsArray(input)) { NodeValueArray array(InputArraySize(input)); @@ -900,6 +890,25 @@ NodeValue Node::GetInputValue(const ValueParams &g, const QString &input, int el } } +NodeValue Node::GetFakeConnectedValue(const ValueParams &g, Node *output, const QString &input, int element) const +{ + if (g.is_cancelled()) { + return NodeValue(); + } + + TimeRange adjusted_time = InputTimeAdjustment(input, element, g.time(), true); + + while (output) { + if (output->is_enabled()) { + return output->Value(g.time_transformed(adjusted_time).output_edited(this->GetValueHintForInput(input, element).tag())); + } else { + output = output->GetConnectedOutput(output->GetEffectInput()); + } + } + + return NodeValue(); +} + void Node::SetValueHintForInput(const QString &input, const ValueHint &hint, int element) { value_hints_.insert({input, element}, hint); diff --git a/app/node/node.h b/app/node/node.h index a5134860c6..6257e58931 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -557,6 +557,7 @@ class Node : public QObject int InputArraySize(const QString& id) const; NodeValue GetInputValue(const ValueParams &g, const QString &input, int element = -1) const; + NodeValue GetFakeConnectedValue(const ValueParams &g, Node *node, const QString &input, int element = -1) const; NodeInputImmediate* GetImmediate(const QString& input, int element) const; diff --git a/app/panel/viewer/viewerbase.h b/app/panel/viewer/viewerbase.h index 28f7bf2ec8..d4b8cfd5e1 100644 --- a/app/panel/viewer/viewerbase.h +++ b/app/panel/viewer/viewerbase.h @@ -67,11 +67,6 @@ class ViewerPanelBase : public TimeBasedPanel GetViewerWidget()->UpdateTextureFromNode(); } - void AddPlaybackDevice(ViewerDisplayWidget *vw) - { - GetViewerWidget()->AddPlaybackDevice(vw); - } - void SetTimelineSelectedBlocks(const QVector &b) { GetViewerWidget()->SetTimelineSelectedBlocks(b); diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index 9bf78ce102..47ad6340f4 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -24,7 +24,6 @@ #include #include "codec/conformmanager.h" -#include "node/input/multicam/multicamnode.h" #include "node/inputdragger.h" #include "node/project.h" #include "render/diskmanager.h" @@ -40,7 +39,6 @@ PreviewAutoCacher::PreviewAutoCacher(QObject *parent) : pause_thumbnails_(false), single_frame_render_(nullptr), display_color_processor_(nullptr), - multicam_(nullptr), ignore_cache_requests_(false) { copier_ = new ProjectCopier(this); @@ -235,7 +233,6 @@ void PreviewAutoCacher::VideoRendered() QVector tickets = video_immediate_passthroughs_.take(watcher); foreach (RenderTicketPtr t, tickets) { if (watcher->HasResult()) { - t->setProperty("multicam_output", watcher->GetTicket()->property("multicam_output")); t->Finish(watcher->Get()); } else { t->Finish(); @@ -614,14 +611,15 @@ RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, ViewerOutput *co rvp.AddCache(frame_cache); } + if (dynamic_cast(node)) { + rvp.node_output = QStringLiteral("all"); + } + rvp.return_type = dry ? RenderManager::kNull : RenderManager::kTexture; // Allow using cached images for this render job rvp.use_cache = true; - // Multicam - rvp.multicam = copier_->GetCopy(multicam_); - watcher->SetTicket(RenderManager::instance()->RenderFrame(rvp)); return watcher; @@ -727,9 +725,6 @@ void PreviewAutoCacher::SetProject(Project *project) // Ensure all cache data is cleared video_cache_data_.clear(); audio_cache_data_.clear(); - - // Clear multicam reference - multicam_ = nullptr; } project_ = project; diff --git a/app/render/previewautocacher.h b/app/render/previewautocacher.h index c88f85695e..881af7dd21 100644 --- a/app/render/previewautocacher.h +++ b/app/render/previewautocacher.h @@ -91,8 +91,6 @@ class PreviewAutoCacher : public QObject void SetRendersPaused(bool e); void SetThumbnailsPaused(bool e); - void SetMulticamNode(MultiCamNode *n) { multicam_ = n; } - void SetIgnoreCacheRequests(bool e) { ignore_cache_requests_ = e; } public slots: @@ -181,8 +179,6 @@ public slots: ColorProcessorPtr display_color_processor_; - MultiCamNode *multicam_; - bool ignore_cache_requests_; private slots: diff --git a/app/render/rendermanager.cpp b/app/render/rendermanager.cpp index ab91b0280c..1d223bb6dd 100644 --- a/app/render/rendermanager.cpp +++ b/app/render/rendermanager.cpp @@ -117,7 +117,7 @@ RenderTicketPtr RenderManager::RenderFrame(const RenderVideoParams ¶ms) ticket->setProperty("cache", params.cache_dir); ticket->setProperty("cachetimebase", QVariant::fromValue(params.cache_timebase)); ticket->setProperty("cacheid", QVariant::fromValue(params.cache_id)); - ticket->setProperty("multicam", QtUtils::PtrToValue(params.multicam)); + ticket->setProperty("output", params.node_output); if (params.return_type == ReturnType::kNull) { dry_run_thread_->AddTicket(ticket); diff --git a/app/render/rendermanager.h b/app/render/rendermanager.h index 9e682b1b0a..a1f0a12b5b 100644 --- a/app/render/rendermanager.h +++ b/app/render/rendermanager.h @@ -117,7 +117,6 @@ class RenderManager : public QObject force_size = QSize(0, 0); force_channel_count = 0; mode = m; - multicam = nullptr; } void AddCache(FrameHashCache *cache) @@ -135,7 +134,7 @@ class RenderManager : public QObject bool use_cache; ReturnType return_type; RenderMode::Mode mode; - MultiCamNode *multicam; + QString node_output; QString cache_dir; rational cache_timebase; diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 49c2512caf..d55f050704 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -47,7 +47,7 @@ TexturePtr RenderProcessor::GenerateTexture(const rational &time, const rational NodeValue tex_val; if (Node* node = QtUtils::ValueToPtr(ticket_->property("node"))) { - ValueParams vp(GetCacheVideoParams(), GetCacheAudioParams(), range, QString(), LoopMode::kLoopModeOff, GetCancelPointer()); + ValueParams vp(GetCacheVideoParams(), GetCacheAudioParams(), range, ticket_->property("output").toString(), LoopMode::kLoopModeOff, GetCancelPointer()); tex_val = node->Value(vp); } @@ -218,7 +218,7 @@ void RenderProcessor::Run() NodeValue sample_val; if (Node* node = QtUtils::ValueToPtr(ticket_->property("node"))) { - ValueParams vp(GetCacheVideoParams(), GetCacheAudioParams(), time, QString(), LoopMode::kLoopModeOff, GetCancelPointer()); + ValueParams vp(GetCacheVideoParams(), GetCacheAudioParams(), time, ticket_->property("output").toString(), LoopMode::kLoopModeOff, GetCancelPointer()); sample_val = node->Value(vp); } diff --git a/app/widget/multicam/multicamdisplay.cpp b/app/widget/multicam/multicamdisplay.cpp index 9fce75e68d..a5b79a67bf 100644 --- a/app/widget/multicam/multicamdisplay.cpp +++ b/app/widget/multicam/multicamdisplay.cpp @@ -26,9 +26,7 @@ namespace olive { MulticamDisplay::MulticamDisplay(QWidget *parent) : super(parent), - node_(nullptr), - rows_(0), - cols_(0) + node_(nullptr) { } @@ -57,113 +55,6 @@ void MulticamDisplay::OnPaint() } } -void MulticamDisplay::OnDestroy() -{ - shader_ = QVariant(); -} - -TexturePtr MulticamDisplay::LoadCustomTextureFromFrame(const QVariant &v) -{ - if (v.canConvert >()) { - QVector tex = v.value >(); - - TexturePtr main = renderer()->CreateTexture(this->GetViewportParams()); - - int rows, cols; - MultiCamNode::GetRowsAndColumns(tex.size(), &rows, &cols); - - if (shader_.isNull() || rows_ != rows || cols_ != cols) { - if (!shader_.isNull()) { - renderer()->DestroyNativeShader(shader_); - } - - shader_ = renderer()->CreateNativeShader(ShaderCode(GenerateShaderCode(rows, cols))); - - rows_ = rows; - cols_ = cols; - } - - ShaderJob job; - - for (int i=0; iBlitToTexture(shader_, job, main.get()); - - return main; - } else { - return super::LoadCustomTextureFromFrame(v); - } -} - -QString dblToGlsl(double d) -{ - return QString::number(d, 'f'); -} - -QString MulticamDisplay::GenerateShaderCode(int rows, int cols) -{ - int multiplier = std::max(cols, rows); - - QStringList shader; - - shader.append(QStringLiteral("in vec2 ove_texcoord;")); - shader.append(QStringLiteral("out vec4 frag_color;")); - - for (int x=0;x 0) { - shader.append(QStringLiteral(" else")); - } - if (x == cols-1) { - shader.append(QStringLiteral(" {")); - } else { - shader.append(QStringLiteral(" if (ove_texcoord.x < %1) {").arg(dblToGlsl(double(x+1)/double(multiplier)))); - } - - for (int y=0;y 0) { - shader.append(QStringLiteral(" else")); - } - if (y == rows-1) { - shader.append(QStringLiteral(" {")); - } else { - shader.append(QStringLiteral(" if (ove_texcoord.y < %1) {").arg(dblToGlsl(double(y+1)/double(multiplier)))); - } - QString input = QStringLiteral("tex_%1_%2").arg(QString::number(y), QString::number(x)); - shader.append(QStringLiteral(" vec2 coord = vec2((ove_texcoord.x+%1)*%2, (ove_texcoord.y+%3)*%4);").arg( - dblToGlsl( - double(x)/double(multiplier)), - dblToGlsl(multiplier), - dblToGlsl( - double(y)/double(multiplier)), - dblToGlsl(multiplier) - )); - shader.append(QStringLiteral(" if (%1_enabled && coord.x >= 0.0 && coord.x < 1.0 && coord.y >= 0.0 && coord.y < 1.0) {").arg(input)); - shader.append(QStringLiteral(" frag_color = texture(%1, coord);").arg(input)); - shader.append(QStringLiteral(" } else {")); - shader.append(QStringLiteral(" discard;")); - shader.append(QStringLiteral(" }")); - shader.append(QStringLiteral(" }")); - } - - shader.append(QStringLiteral(" }")); - } - - shader.append(QStringLiteral("}")); - - return shader.join('\n'); -} - void MulticamDisplay::SetMulticamNode(MultiCamNode *n) { node_ = n; diff --git a/app/widget/multicam/multicamdisplay.h b/app/widget/multicam/multicamdisplay.h index 4d926f5f17..2898142d1d 100644 --- a/app/widget/multicam/multicamdisplay.h +++ b/app/widget/multicam/multicamdisplay.h @@ -37,19 +37,9 @@ class MulticamDisplay : public ViewerDisplayWidget protected: virtual void OnPaint() override; - virtual void OnDestroy() override; - - virtual TexturePtr LoadCustomTextureFromFrame(const QVariant &v) override; - private: - static QString GenerateShaderCode(int rows, int cols); - MultiCamNode *node_; - QVariant shader_; - int rows_; - int cols_; - }; } diff --git a/app/widget/multicam/multicamwidget.h b/app/widget/multicam/multicamwidget.h index a6e5f311a2..0addf74986 100644 --- a/app/widget/multicam/multicamwidget.h +++ b/app/widget/multicam/multicamwidget.h @@ -35,6 +35,7 @@ class MulticamWidget : public TimeBasedWidget MulticamDisplay *GetDisplayWidget() const { return display_; } + MultiCamNode *GetMultiCamNode() const { return node_; } void SetMulticamNode(ViewerOutput *viewer, MultiCamNode *n, ClipBlock *clip, const rational &time); protected: diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 61d4c30285..fea0ad0fad 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -430,12 +430,14 @@ void ViewerWidget::ConnectMulticamWidget(MulticamWidget *p) { if (multicam_panel_) { disconnect(multicam_panel_, &MulticamWidget::Switched, this, &ViewerWidget::DetectMulticamNodeNow); + playback_devices_.removeOne(multicam_panel_->GetDisplayWidget()); } multicam_panel_ = p; if (multicam_panel_) { connect(multicam_panel_, &MulticamWidget::Switched, this, &ViewerWidget::DetectMulticamNodeNow); + playback_devices_.push_back(multicam_panel_->GetDisplayWidget()); } } @@ -636,55 +638,57 @@ void ViewerWidget::SetWaveformMode(WaveformMode wf) UpdateWaveformViewFromMode(); } -void ViewerWidget::DetectMulticamNode(const rational &time) +MultiCamNode *ViewerWidget::DetectMulticamNode(const rational &time) { // Look for multicam node MultiCamNode *multicam = nullptr; ClipBlock *clip = nullptr; // Faster way to do this - if (multicam_panel_ && multicam_panel_->isVisible()) { - if (Sequence *s = dynamic_cast(GetConnectedNode())) { - // Prefer selected nodes - for (Node *n : qAsConst(node_view_selected_)) { - if ((multicam = dynamic_cast(n))) { - // Found multicam, now try to find corresponding clip from selected timeline blocks - for (Block *b : qAsConst(timeline_selected_blocks_)) { - if (ClipBlock *c = dynamic_cast(b)) { - if (c->range().Contains(time) && c->ContextContainsNode(multicam)) { - clip = c; - break; + if (multicam_panel_) { + if (multicam_panel_->isVisible()) { + if (Sequence *s = dynamic_cast(GetConnectedNode())) { + // Prefer selected nodes + for (Node *n : qAsConst(node_view_selected_)) { + if ((multicam = dynamic_cast(n))) { + // Found multicam, now try to find corresponding clip from selected timeline blocks + for (Block *b : qAsConst(timeline_selected_blocks_)) { + if (ClipBlock *c = dynamic_cast(b)) { + if (c->range().Contains(time) && c->ContextContainsNode(multicam)) { + clip = c; + break; + } } } + break; } - break; } - } - // Next, prefer multicam from selected block - if (!multicam) { - for (Block *b : qAsConst(timeline_selected_blocks_)) { - if (b->range().Contains(time)) { - if ((clip = dynamic_cast(b))) { - if ((multicam = clip->FindMulticam())) { - break; + // Next, prefer multicam from selected block + if (!multicam) { + for (Block *b : qAsConst(timeline_selected_blocks_)) { + if (b->range().Contains(time)) { + if ((clip = dynamic_cast(b))) { + if ((multicam = clip->FindMulticam())) { + break; + } } } } } - } - if (!multicam) { - const QVector &tracks = s->GetTracks(); - for (Track *t : tracks) { - if (t->IsLocked()) { - continue; - } + if (!multicam) { + const QVector &tracks = s->GetTracks(); + for (Track *t : tracks) { + if (t->IsLocked()) { + continue; + } - Block *b = t->NearestBlockBeforeOrAt(time); - if ((clip = dynamic_cast(b))) { - if ((multicam = clip->FindMulticam())) { - break; + Block *b = t->NearestBlockBeforeOrAt(time); + if ((clip = dynamic_cast(b))) { + if ((multicam = clip->FindMulticam())) { + break; + } } } } @@ -693,17 +697,12 @@ void ViewerWidget::DetectMulticamNode(const rational &time) } if (multicam) { - if (multicam_panel_) { - multicam_panel_->SetMulticamNode(GetConnectedNode(), multicam, clip, time); - } - // FIXME: Really dirty - RenderManager::instance()->GetCacher()->SetMulticamNode(multicam); + multicam_panel_->SetMulticamNode(GetConnectedNode(), multicam, clip, time); } else { - RenderManager::instance()->GetCacher()->SetMulticamNode(nullptr); - if (multicam_panel_) { - multicam_panel_->SetMulticamNode(nullptr, nullptr, nullptr, time); - } + multicam_panel_->SetMulticamNode(nullptr, nullptr, nullptr, time); } + + return multicam; } bool ViewerWidget::IsVideoVisible() const @@ -897,17 +896,24 @@ void ViewerWidget::UpdateTextureFromNode() watcher->setProperty("start", QDateTime::currentMSecsSinceEpoch()); watcher->setProperty("time", QVariant::fromValue(time)); connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::RendererGeneratedFrame); - nonqueue_watchers_.append(watcher); + nonqueue_watchers_.push_back(watcher); // Clear queue because we want this frame more than any others RenderManager::instance()->GetCacher()->ClearSingleFrameRendersThatArentRunning(); - DetectMulticamNode(time); + watcher->SetTicket(GetSingleFrame(time)); - watcher->SetTicket(GetFrame(time)); + if (MultiCamNode *m = DetectMulticamNode(time)) { + // Render multicam frame too + RenderTicketWatcher *mcw = new RenderTicketWatcher(); + connect(mcw, &RenderTicketWatcher::Finished, this, &ViewerWidget::RendererGeneratedMultiCamFrame); + nonqueue_multicam_watchers_.push_back(mcw); + mcw->SetTicket(RenderManager::instance()->GetCacher()->GetSingleFrame(m, this->GetConnectedNode(), time, false)); + } } else { // There is definitely no frame here, we can immediately flip to showing nothing nonqueue_watchers_.clear(); + nonqueue_multicam_watchers_.clear(); SetEmptyImage(); return; } @@ -1113,15 +1119,9 @@ bool ViewerWidget::ViewerMightBeAStill() void ViewerWidget::SetDisplayImage(RenderTicketPtr ticket) { foreach (ViewerDisplayWidget *dw, playback_devices_) { - QVariant push; - if (ticket) { - if (dynamic_cast(dw)) { - push = ticket->property("multicam_output"); - } else { - push = ticket->Get(); - } + if (!dynamic_cast(dw)) { + dw->SetImage(ticket ? ticket->Get() : QVariant()); } - dw->SetImage(push); } } @@ -1129,8 +1129,7 @@ RenderTicketWatcher *ViewerWidget::RequestNextFrameForQueue(bool increment) { RenderTicketWatcher *watcher = nullptr; - rational next_time = Timecode::timestamp_to_time(playback_queue_next_frame_, - timebase()); + rational next_time = Timecode::timestamp_to_time(playback_queue_next_frame_, timebase()); if (FrameExistsAtTime(next_time) || ViewerMightBeAStill()) { if (increment) { @@ -1139,31 +1138,23 @@ RenderTicketWatcher *ViewerWidget::RequestNextFrameForQueue(bool increment) watcher = new RenderTicketWatcher(); watcher->setProperty("time", QVariant::fromValue(next_time)); - DetectMulticamNode(next_time); connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::RendererGeneratedFrameForQueue); queue_watchers_.append(watcher); - watcher->SetTicket(GetFrame(next_time)); + watcher->SetTicket(GetSingleFrame(next_time)); + + if (auto m = DetectMulticamNode(next_time)) { + RenderTicketWatcher *mcw = new RenderTicketWatcher(); + connect(mcw, &RenderTicketWatcher::Finished, this, &ViewerWidget::RendererGeneratedFrameForQueue); + mcw->setProperty("time", QVariant::fromValue(next_time)); + mcw->setProperty("multicam", true); + queue_watchers_.push_back(mcw); + mcw->SetTicket(RenderManager::instance()->GetCacher()->GetSingleFrame(m, this->GetConnectedNode(), next_time, false)); + } } return watcher; } -RenderTicketPtr ViewerWidget::GetFrame(const rational &t) -{ - QString cache_fn = GetConnectedNode()->video_frame_cache()->GetValidCacheFilename(t); - - if (!QFileInfo::exists(cache_fn)) { - // Frame hasn't been cached, start render job - return GetSingleFrame(t); - } else { - // Frame has been cached, grab the frame - RenderTicketPtr ticket = std::make_shared(); - ticket->setProperty("time", QVariant::fromValue(t)); - QtConcurrent::run(static_cast(ViewerWidget::DecodeCachedImage), ticket, GetConnectedNode()->video_frame_cache()->GetCacheDirectory(), GetConnectedNode()->video_frame_cache()->GetUuid(), Timecode::time_to_timestamp(t, timebase(), Timecode::kFloor)); - return ticket; - } -} - void ViewerWidget::FinishPlayPreprocess() { // Check if we're still waiting for video or audio respectively @@ -1273,15 +1264,37 @@ void ViewerWidget::RendererGeneratedFrame() RenderTicketWatcher* ticket = static_cast(sender()); if (ticket->HasResult()) { - if (nonqueue_watchers_.contains(ticket)) { - while (!nonqueue_watchers_.isEmpty()) { - // Pop frames that are "old" - if (nonqueue_watchers_.takeFirst() == ticket) { - break; - } + while (!nonqueue_watchers_.empty()) { + // Pop frames that are "old" + auto f = nonqueue_watchers_.front(); + nonqueue_watchers_.pop_front(); + if (f == ticket) { + break; + } + } + + SetDisplayImage(ticket->GetTicket()); + } + + delete ticket; +} + +void ViewerWidget::RendererGeneratedMultiCamFrame() +{ + RenderTicketWatcher* ticket = static_cast(sender()); + + if (ticket->HasResult()) { + while (!nonqueue_multicam_watchers_.empty()) { + // Pop frames that are "old" + auto f = nonqueue_multicam_watchers_.front(); + nonqueue_multicam_watchers_.pop_front(); + if (f == ticket) { + break; } + } - SetDisplayImage(ticket->GetTicket()); + if (multicam_panel_) { + multicam_panel_->GetDisplayWidget()->SetImage(ticket->GetTicket()->Get()); } } @@ -1303,14 +1316,9 @@ void ViewerWidget::RendererGeneratedFrameForQueue() rational ts = watcher->property("time").value(); foreach (ViewerDisplayWidget *dw, playback_devices_) { - QVariant push; - if (dynamic_cast(dw)) { - push = watcher->GetTicket()->property("multicam_output"); - } else { - push = frame; + if (watcher->property("multicam").toBool() == bool(dynamic_cast(dw))) { + dw->queue()->AppendTimewise({ts, frame}, playback_speed_); } - - dw->queue()->AppendTimewise({ts, push}, playback_speed_); } if (prequeuing_video_) { diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index e75c36dbdd..b9a6e9c817 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -103,11 +103,6 @@ class ViewerWidget : public TimeBasedWidget enable_audio_scrubbing_ = e; } - void AddPlaybackDevice(ViewerDisplayWidget *vw) - { - playback_devices_.push_back(vw); - } - void SetTimelineSelectedBlocks(const QVector &b) { timeline_selected_blocks_ = b; @@ -248,8 +243,6 @@ public slots: RenderTicketWatcher *RequestNextFrameForQueue(bool increment = true); - RenderTicketPtr GetFrame(const rational& t); - void FinishPlayPreprocess(); int DeterminePlaybackQueueSize(); @@ -272,7 +265,7 @@ public slots: void CloseAudioProcessor(); - void DetectMulticamNode(const rational &time); + MultiCamNode *DetectMulticamNode(const rational &time); bool IsVideoVisible() const; @@ -305,7 +298,8 @@ public slots: bool prequeuing_video_; int prequeuing_audio_; - QList nonqueue_watchers_; + std::list nonqueue_watchers_; + std::list nonqueue_multicam_watchers_; rational last_length_; @@ -378,6 +372,8 @@ private slots: void RendererGeneratedFrame(); + void RendererGeneratedMultiCamFrame(); + void RendererGeneratedFrameForQueue(); void ViewerInvalidatedVideoRange(const olive::TimeRange &range); diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index 7b9927344c..e2bb7d48cd 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -113,7 +113,6 @@ MainWindow::MainWindow(QWidget *parent) : connect(PanelManager::instance(), &PanelManager::FocusedPanelChanged, this, &MainWindow::FocusedPanelChanged); - sequence_viewer_panel_->AddPlaybackDevice(multicam_panel_->GetMulticamWidget()->GetDisplayWidget()); sequence_viewer_panel_->ConnectMulticamWidget(multicam_panel_->GetMulticamWidget()); scope_panel_->SetViewerPanel(sequence_viewer_panel_); @@ -452,6 +451,8 @@ void MainWindow::closeEvent(QCloseEvent *e) scope_panel_->SetViewerPanel(nullptr); + sequence_viewer_panel_->ConnectMulticamWidget(multicam_panel_->GetMulticamWidget()); + PanelManager::instance()->DeleteAllPanels(); SaveCustomShortcuts(); From 254ab66da05e9de6937c51757525de3063d6ae6b Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 29 Mar 2023 15:24:13 -0700 Subject: [PATCH 17/71] further simplified nodes --- app/codec/conformmanager.cpp | 1 + app/codec/encoder.cpp | 1 + .../autorecovery/autorecoverydialog.cpp | 1 + app/dialog/export/export.cpp | 1 + app/node/audio/pan/pan.cpp | 46 ++--- app/node/audio/pan/pan.h | 4 +- app/node/audio/volume/volume.cpp | 29 +-- app/node/audio/volume/volume.h | 2 - .../crossdissolve/crossdissolvetransition.cpp | 28 ++- .../crossdissolve/crossdissolvetransition.h | 10 +- .../diptocolor/diptocolortransition.cpp | 7 +- .../diptocolor/diptocolortransition.h | 5 +- app/node/block/transition/transition.cpp | 54 ++---- app/node/block/transition/transition.h | 6 +- app/node/color/ociobase/ociobase.cpp | 2 +- .../ociogradingtransformlinear.cpp | 14 +- .../cornerpin/cornerpindistortnode.cpp | 20 +- .../distort/cornerpin/cornerpindistortnode.h | 4 +- app/node/distort/crop/cropdistortnode.cpp | 17 +- app/node/distort/crop/cropdistortnode.h | 4 +- app/node/distort/flip/flipdistortnode.cpp | 5 +- app/node/distort/flip/flipdistortnode.h | 4 +- app/node/distort/mask/mask.cpp | 48 ++--- app/node/distort/mask/mask.h | 2 - app/node/distort/ripple/rippledistortnode.cpp | 8 +- app/node/distort/ripple/rippledistortnode.h | 3 +- app/node/distort/swirl/swirldistortnode.cpp | 9 +- app/node/distort/swirl/swirldistortnode.h | 3 +- app/node/distort/tile/tiledistortnode.cpp | 9 +- app/node/distort/tile/tiledistortnode.h | 3 +- .../transform/transformdistortnode.cpp | 19 +- .../distort/transform/transformdistortnode.h | 4 +- app/node/distort/wave/wavedistortnode.cpp | 5 +- app/node/distort/wave/wavedistortnode.h | 4 +- app/node/effect/opacity/opacityeffect.cpp | 19 +- app/node/effect/opacity/opacityeffect.h | 1 - app/node/filter/blur/blur.cpp | 9 +- app/node/filter/blur/blur.h | 3 +- .../filter/dropshadow/dropshadowfilter.cpp | 9 +- app/node/filter/dropshadow/dropshadowfilter.h | 4 +- app/node/filter/mosaic/mosaicfilternode.cpp | 16 +- app/node/filter/mosaic/mosaicfilternode.h | 4 +- app/node/filter/stroke/stroke.cpp | 18 +- app/node/filter/stroke/stroke.h | 4 +- app/node/generator/matrix/matrix.cpp | 3 +- app/node/generator/noise/noise.cpp | 8 +- app/node/generator/noise/noise.h | 4 +- app/node/generator/polygon/polygon.cpp | 58 +++--- app/node/generator/polygon/polygon.h | 8 +- .../generator/shape/generatorwithmerge.cpp | 15 +- app/node/generator/shape/generatorwithmerge.h | 5 +- app/node/generator/shape/shapenode.cpp | 13 +- app/node/generator/shape/shapenode.h | 4 +- app/node/generator/solid/solid.cpp | 10 +- app/node/generator/solid/solid.h | 4 +- app/node/generator/text/textv1.cpp | 20 +- app/node/generator/text/textv1.h | 5 +- app/node/generator/text/textv2.cpp | 26 +-- app/node/generator/text/textv2.h | 5 +- app/node/generator/text/textv3.cpp | 80 ++++---- app/node/generator/text/textv3.h | 4 +- app/node/input/multicam/multicamnode.cpp | 23 +-- app/node/input/multicam/multicamnode.h | 4 +- app/node/input/time/timeinput.cpp | 6 +- app/node/keying/chromakey/chromakey.cpp | 10 +- app/node/keying/chromakey/chromakey.h | 1 - .../colordifferencekey/colordifferencekey.cpp | 7 +- .../colordifferencekey/colordifferencekey.h | 4 +- app/node/keying/despill/despill.cpp | 8 +- app/node/keying/despill/despill.h | 3 +- app/node/math/math/math.cpp | 17 -- app/node/math/math/math.h | 4 - app/node/math/math/mathbase.cpp | 155 ++++++++------- app/node/math/math/mathbase.h | 13 +- app/node/math/merge/merge.cpp | 6 +- app/node/math/merge/merge.h | 3 +- app/node/math/trigonometry/trigonometry.cpp | 2 +- app/node/node.cpp | 71 +++---- app/node/node.h | 73 +++---- app/node/output/track/track.cpp | 182 +++++++++--------- app/node/output/track/track.h | 2 - app/node/output/viewer/viewer.cpp | 16 +- app/node/project/footage/footage.cpp | 4 +- app/node/time/timeformat/timeformat.cpp | 3 +- app/node/value.cpp | 6 + app/node/value.h | 110 ++++++++--- app/render/CMakeLists.txt | 1 - app/render/job/acceleratedjob.h | 2 +- app/render/job/colortransformjob.h | 15 +- app/render/job/generatejob.h | 23 ++- app/render/job/samplejob.h | 22 +++ app/render/job/shaderjob.h | 44 ++++- app/render/renderer.cpp | 4 +- app/render/renderprocessor.cpp | 28 +-- app/render/renderprocessor.h | 8 +- app/render/shadercode.h | 51 ----- app/widget/scope/histogram/histogram.cpp | 6 +- 97 files changed, 822 insertions(+), 856 deletions(-) delete mode 100644 app/render/shadercode.h diff --git a/app/codec/conformmanager.cpp b/app/codec/conformmanager.cpp index 70d012a941..760b82daed 100644 --- a/app/codec/conformmanager.cpp +++ b/app/codec/conformmanager.cpp @@ -2,6 +2,7 @@ #include +#include "common/filefunctions.h" #include "task/taskmanager.h" namespace olive { diff --git a/app/codec/encoder.cpp b/app/codec/encoder.cpp index 810cdcd1bb..6583577a47 100644 --- a/app/codec/encoder.cpp +++ b/app/codec/encoder.cpp @@ -22,6 +22,7 @@ #include +#include "common/filefunctions.h" #include "common/xmlutils.h" #include "ffmpeg/ffmpegencoder.h" #include "oiio/oiioencoder.h" diff --git a/app/dialog/autorecovery/autorecoverydialog.cpp b/app/dialog/autorecovery/autorecoverydialog.cpp index 1e4354ed1c..b656bbf826 100644 --- a/app/dialog/autorecovery/autorecoverydialog.cpp +++ b/app/dialog/autorecovery/autorecoverydialog.cpp @@ -27,6 +27,7 @@ #include #include +#include "common/filefunctions.h" #include "core.h" namespace olive { diff --git a/app/dialog/export/export.cpp b/app/dialog/export/export.cpp index c5cfa2a170..bb73972238 100644 --- a/app/dialog/export/export.cpp +++ b/app/dialog/export/export.cpp @@ -31,6 +31,7 @@ #include #include "common/digit.h" +#include "common/filefunctions.h" #include "common/qtutils.h" #include "dialog/task/task.h" #include "exportsavepresetdialog.h" diff --git a/app/node/audio/pan/pan.cpp b/app/node/audio/pan/pan.cpp index 223c4245ca..6b71c33cea 100644 --- a/app/node/audio/pan/pan.cpp +++ b/app/node/audio/pan/pan.cpp @@ -62,33 +62,17 @@ QString PanNode::Description() const return tr("Adjust the stereo panning of an audio source."); } -NodeValue PanNode::Value(const ValueParams &p) const -{ - // Create a sample job - NodeValue samples_original = GetInputValue(p, kSamplesInput); - - if (!IsInputStatic(kPanningInput) || !qIsNull(GetInputValue(p, kPanningInput).toDouble())) { - // Requires sample job - SampleJob job(p); - - job.Insert(kSamplesInput, GetInputValue(p, kSamplesInput)); - - return NodeValue(NodeValue::kSamples, SampleJob(job), this); - } - - return samples_original; -} - -void PanNode::ProcessSamples(const SampleJob &job, SampleBuffer &output) const +void PanNode::ProcessSamples(const void *context, const SampleJob &job, SampleBuffer &output) { + const PanNode *n = static_cast(context); const ValueParams &p = job.value_params(); - SampleBuffer input = job.Get(kSamplesInput).toSamples(); + SampleBuffer input = job.Get(PanNode::kSamplesInput).toSamples(); // This node is only compatible with stereo audio if (job.audio_params().channel_count() == 2) { - if (IsInputStatic(kPanningInput)) { - float pan_volume = GetInputValue(p, kPanningInput).toDouble(); + if (n->IsInputStatic(kPanningInput)) { + float pan_volume = n->GetInputValue(p, kPanningInput).toDouble(); if (!qIsNull(pan_volume)) { if (pan_volume > 0) { SampleBuffer::transform_volume_for_channel(0, 1.0f - pan_volume, &input, &output); @@ -102,7 +86,7 @@ void PanNode::ProcessSamples(const SampleJob &job, SampleBuffer &output) const for (size_t index = 0; index < output.sample_count(); index++) { rational this_sample_time = p.time().in() + rational(index, job.audio_params().sample_rate()); TimeRange this_sample_range(this_sample_time, this_sample_time + job.audio_params().sample_rate_as_time_base()); - auto pan_val = GetInputValue(p.time_transformed(this_sample_range), kPanningInput).toDouble(); + auto pan_val = n->GetInputValue(p.time_transformed(this_sample_range), kPanningInput).toDouble(); if (pan_val > 0) { output.data(0)[index] = input.data(0)[index] * (1.0F - pan_val); @@ -116,6 +100,24 @@ void PanNode::ProcessSamples(const SampleJob &job, SampleBuffer &output) const } } +NodeValue PanNode::Value(const ValueParams &p) const +{ + // Create a sample job + NodeValue samples_original = GetInputValue(p, kSamplesInput); + + if (!IsInputStatic(kPanningInput) || !qIsNull(GetInputValue(p, kPanningInput).toDouble())) { + // Requires sample job + SampleJob job(p); + + job.Insert(kSamplesInput, GetInputValue(p, kSamplesInput)); + job.set_function(ProcessSamples, this); + + return job; + } + + return samples_original; +} + void PanNode::Retranslate() { super::Retranslate(); diff --git a/app/node/audio/pan/pan.h b/app/node/audio/pan/pan.h index 050dcb75f5..0d5686bb06 100644 --- a/app/node/audio/pan/pan.h +++ b/app/node/audio/pan/pan.h @@ -40,14 +40,14 @@ class PanNode : public Node virtual NodeValue Value(const ValueParams &p) const override; - virtual void ProcessSamples(const SampleJob &job, SampleBuffer &output) const override; - virtual void Retranslate() override; static const QString kSamplesInput; static const QString kPanningInput; private: + static void ProcessSamples(const void *context, const SampleJob &job, SampleBuffer &output); + NodeInput* samples_input_; NodeInput* panning_input_; diff --git a/app/node/audio/volume/volume.cpp b/app/node/audio/volume/volume.cpp index 2014f08a15..162e388740 100644 --- a/app/node/audio/volume/volume.cpp +++ b/app/node/audio/volume/volume.cpp @@ -63,34 +63,7 @@ QString VolumeNode::Description() const NodeValue VolumeNode::Value(const ValueParams &p) const { - // Create a sample job - NodeValue meta = GetInputValue(p, kSamplesInput); - - if (!IsInputStatic(kVolumeInput) || !qFuzzyCompare(GetInputValue(p, kVolumeInput).toDouble(), 1.0)) { - SampleJob job(p); - - job.Insert(kSamplesInput, GetInputValue(p, kSamplesInput)); - - return NodeValue(NodeValue::kSamples, job, this); - } - - return meta; -} - -void VolumeNode::ProcessSamples(const SampleJob &job, SampleBuffer &output) const -{ - SampleBuffer buffer = job.Get(kSamplesInput).toSamples(); - const ValueParams &p = job.value_params(); - - if (IsInputStatic(kVolumeInput)) { - auto volume = GetInputValue(p, kVolumeInput).toDouble(); - - if (!qFuzzyCompare(volume, 1.0)) { - SampleBuffer::transform_volume(volume, &buffer, &output); - } - } else { - return ProcessSamplesNumberInternal(p, kOpMultiply, kVolumeInput, buffer, output); - } + return ValueInternal(kOpMultiply, kPairSampleNumber, kSamplesInput, GetInputValue(p, kSamplesInput), kVolumeInput, GetInputValue(p, kVolumeInput), p); } void VolumeNode::Retranslate() diff --git a/app/node/audio/volume/volume.h b/app/node/audio/volume/volume.h index 9f4ec71b8e..4677b3b546 100644 --- a/app/node/audio/volume/volume.h +++ b/app/node/audio/volume/volume.h @@ -40,8 +40,6 @@ class VolumeNode : public MathNodeBase virtual NodeValue Value(const ValueParams &p) const override; - virtual void ProcessSamples(const SampleJob &job, SampleBuffer &output) const override; - virtual void Retranslate() override; static const QString kSamplesInput; diff --git a/app/node/block/transition/crossdissolve/crossdissolvetransition.cpp b/app/node/block/transition/crossdissolve/crossdissolvetransition.cpp index 28264a4469..23e47ddd3c 100644 --- a/app/node/block/transition/crossdissolve/crossdissolvetransition.cpp +++ b/app/node/block/transition/crossdissolve/crossdissolvetransition.cpp @@ -46,25 +46,34 @@ QString CrossDissolveTransition::Description() const return tr("Smoothly transition between two clips."); } -ShaderCode CrossDissolveTransition::GetShaderCode(const ShaderRequest &request) const +ShaderCode CrossDissolveTransition::GetShaderCode(const QString &id) { - Q_UNUSED(request) + return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/crossdissolve.frag")); +} - return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/crossdissolve.frag"), QString()); +void CrossDissolveTransition::ShaderJobEvent(const ValueParams &p, ShaderJob *job) const +{ + job->set_function(GetShaderCode); } -void CrossDissolveTransition::SampleJobEvent(const SampleBuffer &from_samples, const SampleBuffer &to_samples, SampleBuffer &out_samples, double time_in) const +void CrossDissolveTransition::ProcessSamples(const void *context, const SampleJob &job, SampleBuffer &out_samples) { + const CrossDissolveTransition *t = static_cast(context); + SampleBuffer from_samples = job.Get(TransitionBlock::kOutBlockInput).toSamples(); + SampleBuffer to_samples = job.Get(TransitionBlock::kInBlockInput).toSamples(); + + double time_in = job.value_params().time().in().toDouble(); + for (size_t i=0; iGetTotalProgress(this_sample_time); for (int j=0; jTransformCurve(1.0 - progress); } } @@ -73,11 +82,16 @@ void CrossDissolveTransition::SampleJobEvent(const SampleBuffer &from_samples, c size_t remain = (out_samples.sample_count() - to_samples.sample_count()); if (i >= remain) { qint64 in_index = i - remain; - out_samples.data(j)[i] += to_samples.data(j)[in_index] * TransformCurve(progress); + out_samples.data(j)[i] += to_samples.data(j)[in_index] * t->TransformCurve(progress); } } } } } +void CrossDissolveTransition::SampleJobEvent(const ValueParams &p, SampleJob *job) const +{ + job->set_function(ProcessSamples, this); +} + } diff --git a/app/node/block/transition/crossdissolve/crossdissolvetransition.h b/app/node/block/transition/crossdissolve/crossdissolvetransition.h index 7b35970023..90106b271e 100644 --- a/app/node/block/transition/crossdissolve/crossdissolvetransition.h +++ b/app/node/block/transition/crossdissolve/crossdissolvetransition.h @@ -38,12 +38,14 @@ class CrossDissolveTransition : public TransitionBlock virtual QVector Category() const override; virtual QString Description() const override; - //virtual void Retranslate() override; +protected: + virtual void ShaderJobEvent(const ValueParams &p, ShaderJob *job) const override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; + virtual void SampleJobEvent(const ValueParams &p, SampleJob *job) const override; -protected: - virtual void SampleJobEvent(const SampleBuffer &from_samples, const SampleBuffer &to_samples, SampleBuffer &out_samples, double time_in) const override; +private: + static ShaderCode GetShaderCode(const QString &id); + static void ProcessSamples(const void *context, const SampleJob &job, SampleBuffer &out_samples); }; diff --git a/app/node/block/transition/diptocolor/diptocolortransition.cpp b/app/node/block/transition/diptocolor/diptocolortransition.cpp index caa0165e70..cfeee782f1 100644 --- a/app/node/block/transition/diptocolor/diptocolortransition.cpp +++ b/app/node/block/transition/diptocolor/diptocolortransition.cpp @@ -51,11 +51,9 @@ QString DipToColorTransition::Description() const return tr("Transition between clips by dipping to a color."); } -ShaderCode DipToColorTransition::GetShaderCode(const ShaderRequest &request) const +ShaderCode DipToColorTransition::GetShaderCode(const QString &id) { - Q_UNUSED(request) - - return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/diptoblack.frag"), QString()); + return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/diptoblack.frag")); } void DipToColorTransition::Retranslate() @@ -68,6 +66,7 @@ void DipToColorTransition::Retranslate() void DipToColorTransition::ShaderJobEvent(const ValueParams &p, ShaderJob *job) const { job->Insert(kColorInput, GetInputValue(p, kColorInput)); + job->set_function(GetShaderCode); } } diff --git a/app/node/block/transition/diptocolor/diptocolortransition.h b/app/node/block/transition/diptocolor/diptocolortransition.h index 5e0e6f438c..af15840f29 100644 --- a/app/node/block/transition/diptocolor/diptocolortransition.h +++ b/app/node/block/transition/diptocolor/diptocolortransition.h @@ -38,8 +38,6 @@ class DipToColorTransition : public TransitionBlock virtual QVector Category() const override; virtual QString Description() const override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - virtual void Retranslate() override; static const QString kColorInput; @@ -47,6 +45,9 @@ class DipToColorTransition : public TransitionBlock protected: virtual void ShaderJobEvent(const ValueParams &p, ShaderJob *job) const override; +private: + static ShaderCode GetShaderCode(const QString &id); + }; } diff --git a/app/node/block/transition/transition.cpp b/app/node/block/transition/transition.cpp index ea8bce6189..5d631ddc59 100644 --- a/app/node/block/transition/transition.cpp +++ b/app/node/block/transition/transition.cpp @@ -145,37 +145,22 @@ 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->Insert(QStringLiteral("ove_tprog_all"), - NodeValue(NodeValue::kFloat, GetTotalProgress(time), this)); + job->Insert(QStringLiteral("ove_tprog_all"), GetTotalProgress(time)); // Provides progress of out section from 1.0 (start) - 0.0 (end) - job->Insert(QStringLiteral("ove_tprog_out"), - NodeValue(NodeValue::kFloat, GetOutProgress(time), this)); + job->Insert(QStringLiteral("ove_tprog_out"), GetOutProgress(time)); // Provides progress of in section from 0.0 (start) - 1.0 (end) - job->Insert(QStringLiteral("ove_tprog_in"), - NodeValue(NodeValue::kFloat, GetInProgress(time), this)); + job->Insert(QStringLiteral("ove_tprog_in"), GetInProgress(time)); } NodeValue TransitionBlock::Value(const ValueParams &p) const { - NodeValue out_buffer, in_buffer; - - if (Block* block = dynamic_cast(GetConnectedOutput(kOutBlockInput))) { - // Retransform time as if it came from the track - out_buffer = GetInputValue(p, kOutBlockInput); - } - - if (Block* block = dynamic_cast(GetConnectedOutput(kInBlockInput))) { - // Retransform time as if it came from the track - in_buffer = GetInputValue(p, kInBlockInput); - } + NodeValue out_buffer = GetInputValue(p, kOutBlockInput); + NodeValue in_buffer = GetInputValue(p, kInBlockInput); NodeValue::Type data_type = (out_buffer.type() != NodeValue::kNone) ? out_buffer.type() : in_buffer.type(); - NodeValue::Type job_type = NodeValue::kNone; - QVariant push_job; - if (data_type == NodeValue::kTexture) { // This must be a visual transition ShaderJob job; @@ -199,36 +184,19 @@ NodeValue TransitionBlock::Value(const ValueParams &p) const ShaderJobEvent(p, &job); - job_type = NodeValue::kTexture; - push_job = QVariant::fromValue(Texture::Job(p.vparams(), job)); + return Texture::Job(p.vparams(), job); } else if (data_type == NodeValue::kSamples) { // This must be an audio transition - SampleBuffer from_samples = out_buffer.toSamples(); - SampleBuffer to_samples = in_buffer.toSamples(); + SampleJob job(p); - if (from_samples.is_allocated() || to_samples.is_allocated()) { - double time_in = p.time().in().toDouble(); - double time_out = p.time().out().toDouble(); + job.Insert(kOutBlockInput, out_buffer); + job.Insert(kInBlockInput, in_buffer); - const AudioParams& params = (from_samples.is_allocated()) ? from_samples.audio_params() : to_samples.audio_params(); + SampleJobEvent(p, &job); - SampleBuffer out_samples; - - if (params.is_valid()) { - int nb_samples = params.time_to_samples(time_out - time_in); - - out_samples = SampleBuffer(params, nb_samples); - SampleJobEvent(from_samples, to_samples, out_samples, time_in); - } - - job_type = NodeValue::kSamples; - push_job = QVariant::fromValue(out_samples); - } + return job; } - if (!push_job.isNull()) { - return NodeValue(job_type, push_job, this); - } return NodeValue(); } diff --git a/app/node/block/transition/transition.h b/app/node/block/transition/transition.h index 14b140e277..e56b349d63 100644 --- a/app/node/block/transition/transition.h +++ b/app/node/block/transition/transition.h @@ -63,6 +63,8 @@ class TransitionBlock : public Block double GetOutProgress(const double &time) const; double GetInProgress(const double &time) const; + double TransformCurve(double linear) const; + virtual NodeValue Value(const ValueParams &globals) const override; virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1, InvalidateCacheOptions options = InvalidateCacheOptions()) override; @@ -75,9 +77,7 @@ class TransitionBlock : public Block protected: virtual void ShaderJobEvent(const ValueParams &p, ShaderJob *job) const {} - virtual void SampleJobEvent(const SampleBuffer &from_samples, const SampleBuffer &to_samples, SampleBuffer &out_samples, double time_in) const {} - - double TransformCurve(double linear) const; + virtual void SampleJobEvent(const ValueParams &p, SampleJob *job) const {} virtual void InputConnectedEvent(const QString& input, int element, Node *output) override; diff --git a/app/node/color/ociobase/ociobase.cpp b/app/node/color/ociobase/ociobase.cpp index 343fcfd35d..6abbcaf9a2 100644 --- a/app/node/color/ociobase/ociobase.cpp +++ b/app/node/color/ociobase/ociobase.cpp @@ -63,7 +63,7 @@ NodeValue OCIOBaseNode::Value(const ValueParams &p) const job.SetColorProcessor(processor_); job.SetInputTexture(tex_met); - return NodeValue(NodeValue::kTexture, t->toJob(job), this); + return t->toJob(job); } return tex_met; diff --git a/app/node/color/ociogradingtransformlinear/ociogradingtransformlinear.cpp b/app/node/color/ociogradingtransformlinear/ociogradingtransformlinear.cpp index 3778953e94..5c73b6e5c1 100644 --- a/app/node/color/ociogradingtransformlinear/ociogradingtransformlinear.cpp +++ b/app/node/color/ociogradingtransformlinear/ociogradingtransformlinear.cpp @@ -159,7 +159,7 @@ NodeValue OCIOGradingTransformLinearNode::Value(const ValueParams &p) const if (TexturePtr tex = tex_meta.toTexture()) { if (processor()) { - ColorTransformJob job = CreateJob(p); + ColorTransformJob job = CreateColorTransformJob(p); job.SetColorProcessor(processor()); job.SetInputTexture(tex_meta); @@ -177,29 +177,29 @@ NodeValue OCIOGradingTransformLinearNode::Value(const ValueParams &p) const 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]))); + job.Insert(kOffsetInput, QVector3D(offset[RED_CHANNEL], offset[GREEN_CHANNEL], offset[BLUE_CHANNEL])); QVector4D exposure = GetInputValue(p, 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]))); + job.Insert(kExposureInput, QVector3D(exposure[RED_CHANNEL], exposure[GREEN_CHANNEL], exposure[BLUE_CHANNEL])); QVector4D contrast = GetInputValue(p, 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]))); + job.Insert(kContrastInput, QVector3D(contrast[RED_CHANNEL], contrast[GREEN_CHANNEL], contrast[BLUE_CHANNEL])); if (!GetInputValue(p, kClampBlackEnableInput).toBool()) { - job.Insert(kClampBlackInput, NodeValue(NodeValue::kFloat, OCIO::GradingPrimary::NoClampBlack())); + job.Insert(kClampBlackInput, OCIO::GradingPrimary::NoClampBlack()); } if (!GetInputValue(p, kClampWhiteEnableInput).toBool()) { - job.Insert(kClampWhiteInput, NodeValue(NodeValue::kFloat, OCIO::GradingPrimary::NoClampWhite())); + job.Insert(kClampWhiteInput, OCIO::GradingPrimary::NoClampWhite()); } - return NodeValue(NodeValue::kTexture, tex->toJob(job), this); + return tex->toJob(job); } } diff --git a/app/node/distort/cornerpin/cornerpindistortnode.cpp b/app/node/distort/cornerpin/cornerpindistortnode.cpp index 68cb0f3c7d..7b2ed0ca03 100644 --- a/app/node/distort/cornerpin/cornerpindistortnode.cpp +++ b/app/node/distort/cornerpin/cornerpindistortnode.cpp @@ -67,6 +67,12 @@ void CornerPinDistortNode::Retranslate() SetInputName(kBottomLeftInput, tr("Bottom Left")); } +ShaderCode CornerPinDistortNode::GetShaderCode(const QString &id) +{ + return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/cornerpin.frag")), + FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/cornerpin.vert"))); +} + NodeValue CornerPinDistortNode::Value(const ValueParams &p) const { // If no texture do nothing @@ -79,8 +85,8 @@ NodeValue CornerPinDistortNode::Value(const ValueParams &p) const && GetInputValue(p, kTopRightInput).toVec2().isNull() && GetInputValue(p, kBottomRightInput).toVec2().isNull() && GetInputValue(p, kBottomLeftInput).toVec2().isNull())) { - ShaderJob job = CreateJob(p); - job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, tex->virtual_resolution(), this)); + ShaderJob job = CreateShaderJob(p, GetShaderCode); + job.Insert(QStringLiteral("resolution_in"), tex->virtual_resolution()); // Convert slider values to their pixel values and then convert to clip space (-1.0 ... 1.0) for overriding the // vertex coordinates. @@ -101,21 +107,13 @@ NodeValue CornerPinDistortNode::Value(const ValueParams &p) const bottom_right.x(), bottom_right.y(), 0.0f}; job.SetVertexCoordinates(adjusted_vertices); - return NodeValue(NodeValue::kTexture, tex->toJob(job), this); + return tex->toJob(job); } } return tex_meta; } -ShaderCode CornerPinDistortNode::GetShaderCode(const ShaderRequest &request) const -{ - Q_UNUSED(request) - - return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/cornerpin.frag")), - FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/cornerpin.vert"))); -} - QPointF CornerPinDistortNode::ValueToPixel(int value, const ValueParams& p, const QVector2D &resolution) const { Q_ASSERT(value >= 0 && value <= 3); diff --git a/app/node/distort/cornerpin/cornerpindistortnode.h b/app/node/distort/cornerpin/cornerpindistortnode.h index 01387b9fb9..35124c9d70 100644 --- a/app/node/distort/cornerpin/cornerpindistortnode.h +++ b/app/node/distort/cornerpin/cornerpindistortnode.h @@ -61,8 +61,6 @@ class CornerPinDistortNode : public Node virtual NodeValue Value(const ValueParams &p) const override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - virtual void UpdateGizmoPositions(const ValueParams &p) override; /** @@ -82,6 +80,8 @@ protected slots: virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) override; private: + static ShaderCode GetShaderCode(const QString &id); + // Gizmo variables static const int kGizmoCornerCount = 4; PointGizmo *gizmo_resize_handle_[kGizmoCornerCount]; diff --git a/app/node/distort/crop/cropdistortnode.cpp b/app/node/distort/crop/cropdistortnode.cpp index ce3ff5e50c..e52d3ea56b 100644 --- a/app/node/distort/crop/cropdistortnode.cpp +++ b/app/node/distort/crop/cropdistortnode.cpp @@ -75,31 +75,30 @@ void CropDistortNode::Retranslate() SetInputName(kFeatherInput, tr("Feather")); } +ShaderCode CropDistortNode::GetShaderCode(const QString &id) +{ + return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/crop.frag"))); +} + NodeValue CropDistortNode::Value(const ValueParams &p) const { NodeValue tex_meta = GetInputValue(p, kTextureInput); if (TexturePtr texture = tex_meta.toTexture()) { - ShaderJob job = CreateJob(p); - job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, QVector2D(texture->params().width(), texture->params().height()), this)); + ShaderJob job = CreateShaderJob(p, GetShaderCode); + job.Insert(QStringLiteral("resolution_in"), QVector2D(texture->params().width(), texture->params().height())); if (!qIsNull(job.Get(kLeftInput).toDouble()) || !qIsNull(job.Get(kRightInput).toDouble()) || !qIsNull(job.Get(kTopInput).toDouble()) || !qIsNull(job.Get(kBottomInput).toDouble())) { - return NodeValue(NodeValue::kTexture, texture->toJob(job), this); + return texture->toJob(job); } } return tex_meta; } -ShaderCode CropDistortNode::GetShaderCode(const ShaderRequest &request) const -{ - Q_UNUSED(request) - return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/crop.frag"))); -} - void CropDistortNode::UpdateGizmoPositions(const ValueParams &p) { if (TexturePtr tex = GetInputValue(p, kTextureInput).toTexture()) { diff --git a/app/node/distort/crop/cropdistortnode.h b/app/node/distort/crop/cropdistortnode.h index de0b425f92..4d758ed888 100644 --- a/app/node/distort/crop/cropdistortnode.h +++ b/app/node/distort/crop/cropdistortnode.h @@ -62,8 +62,6 @@ class CropDistortNode : public Node virtual NodeValue Value(const ValueParams &p) const override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - virtual void UpdateGizmoPositions(const ValueParams &p) override; static const QString kTextureInput; @@ -77,6 +75,8 @@ protected slots: virtual void GizmoDragMove(double delta_x, double delta_y, const Qt::KeyboardModifiers &modifiers) override; private: + static ShaderCode GetShaderCode(const QString &id); + void CreateCropSideInput(const QString& id); // Gizmo variables diff --git a/app/node/distort/flip/flipdistortnode.cpp b/app/node/distort/flip/flipdistortnode.cpp index 1818ef53dc..8cbb6d7d57 100644 --- a/app/node/distort/flip/flipdistortnode.cpp +++ b/app/node/distort/flip/flipdistortnode.cpp @@ -69,9 +69,8 @@ void FlipDistortNode::Retranslate() SetInputName(kVerticalInput, tr("Vertical")); } -ShaderCode FlipDistortNode::GetShaderCode(const ShaderRequest &request) const +ShaderCode FlipDistortNode::GetShaderCode(const QString &id) { - Q_UNUSED(request) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/flip.frag")); } @@ -83,7 +82,7 @@ NodeValue FlipDistortNode::Value(const ValueParams &p) const if (TexturePtr tex = v.toTexture()) { // Only run shader if at least one of flip or flop are selected if (GetInputValue(p, kHorizontalInput).toBool() || GetInputValue(p, kVerticalInput).toBool()) { - return NodeValue(NodeValue::kTexture, tex->toJob(CreateJob(p)), this); + return tex->toJob(CreateShaderJob(p, GetShaderCode)); } } diff --git a/app/node/distort/flip/flipdistortnode.h b/app/node/distort/flip/flipdistortnode.h index 25d1d17465..e0980cb1b6 100644 --- a/app/node/distort/flip/flipdistortnode.h +++ b/app/node/distort/flip/flipdistortnode.h @@ -40,13 +40,15 @@ class FlipDistortNode : public Node virtual void Retranslate() override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; virtual NodeValue Value(const ValueParams &globals) const override; static const QString kTextureInput; static const QString kHorizontalInput; static const QString kVerticalInput; +private: + static ShaderCode GetShaderCode(const QString &id); + }; } diff --git a/app/node/distort/mask/mask.cpp b/app/node/distort/mask/mask.cpp index e0b43443a5..1341641738 100644 --- a/app/node/distort/mask/mask.cpp +++ b/app/node/distort/mask/mask.cpp @@ -40,17 +40,19 @@ MaskDistortNode::MaskDistortNode() SetInputProperty(kFeatherInput, QStringLiteral("min"), 0.0); } -ShaderCode MaskDistortNode::GetShaderCode(const ShaderRequest &request) const +ShaderCode GetInvertShader(const QString &id) { - 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 if (request.id == QStringLiteral("invert")) { - return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/invertrgba.frag"))); - } else { - return super::GetShaderCode(request); - } + return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/invertrgba.frag"))); +} + +ShaderCode GetFeatherShader(const QString &id) +{ + return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/blur.frag"))); +} + +ShaderCode GetMergeShader(const QString &id) +{ + return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/multiply.frag"))); } void MaskDistortNode::Retranslate() @@ -67,42 +69,42 @@ NodeValue MaskDistortNode::Value(const ValueParams &p) const TexturePtr texture = GetInputValue(p, kBaseInput).toTexture(); VideoParams job_params = texture ? texture->params() : p.vparams(); - NodeValue job(NodeValue::kTexture, Texture::Job(job_params, GetGenerateJob(p, job_params)), this); + NodeValue job = Texture::Job(job_params, GetGenerateJob(p, job_params)); if (GetInputValue(p, kInvertInput).toBool()) { ShaderJob invert; - invert.SetShaderID(QStringLiteral("invert")); + invert.set_function(GetInvertShader); invert.Insert(QStringLiteral("tex_in"), job); - job.set_value(Texture::Job(job_params, invert)); + job = Texture::Job(job_params, invert); } if (texture) { // Push as merge node ShaderJob merge; - merge.SetShaderID(QStringLiteral("mrg")); + merge.set_function(GetMergeShader); merge.Insert(QStringLiteral("tex_a"), GetInputValue(p, kBaseInput)); if (GetInputValue(p, kFeatherInput).toDouble() > 0.0) { // Nest a blur shader in there too ShaderJob feather; - feather.SetShaderID(QStringLiteral("feather")); + feather.set_function(GetFeatherShader); feather.Insert(BlurFilterNode::kTextureInput, job); - 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, GetInputValue(p, kFeatherInput).toDouble(), this)); + feather.Insert(BlurFilterNode::kMethodInput, int(BlurFilterNode::kGaussian)); + feather.Insert(BlurFilterNode::kHorizInput, true); + feather.Insert(BlurFilterNode::kVertInput, true); + feather.Insert(BlurFilterNode::kRepeatEdgePixelsInput, true); + feather.Insert(BlurFilterNode::kRadiusInput, GetInputValue(p, kFeatherInput).toDouble()); feather.SetIterations(2, BlurFilterNode::kTextureInput); - feather.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, texture ? texture->virtual_resolution() : p.square_resolution(), this)); + feather.Insert(QStringLiteral("resolution_in"), texture ? texture->virtual_resolution() : p.square_resolution()); - merge.Insert(QStringLiteral("tex_b"), NodeValue(NodeValue::kTexture, Texture::Job(job_params, feather), this)); + merge.Insert(QStringLiteral("tex_b"), Texture::Job(job_params, feather)); } else { merge.Insert(QStringLiteral("tex_b"), job); } - return NodeValue(NodeValue::kTexture, Texture::Job(job_params, merge), this); + return NodeValue(NodeValue::kTexture, Texture::Job(job_params, merge)); } else { return job; } diff --git a/app/node/distort/mask/mask.h b/app/node/distort/mask/mask.h index 6fb4a150a8..38f31ab63a 100644 --- a/app/node/distort/mask/mask.h +++ b/app/node/distort/mask/mask.h @@ -53,8 +53,6 @@ class MaskDistortNode : public PolygonGenerator return tr("Apply a polygonal mask."); } - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - virtual void Retranslate() override; virtual NodeValue Value(const ValueParams &p) const override; diff --git a/app/node/distort/ripple/rippledistortnode.cpp b/app/node/distort/ripple/rippledistortnode.cpp index dfccc07b2e..112e0cd6db 100644 --- a/app/node/distort/ripple/rippledistortnode.cpp +++ b/app/node/distort/ripple/rippledistortnode.cpp @@ -86,9 +86,8 @@ void RippleDistortNode::Retranslate() SetInputName(kStretchInput, tr("Stretch")); } -ShaderCode RippleDistortNode::GetShaderCode(const ShaderRequest &request) const +ShaderCode RippleDistortNode::GetShaderCode(const QString &id) { - Q_UNUSED(request) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/ripple.frag")); } @@ -104,15 +103,16 @@ NodeValue RippleDistortNode::Value(const ValueParams &p) const if (!qIsNull(intensity.toDouble())) { ShaderJob job; + job.set_function(GetShaderCode); job.Insert(kTextureInput, texture); job.Insert(kEvolutionInput, GetInputValue(p, kEvolutionInput)); job.Insert(kIntensityInput, intensity); job.Insert(kFrequencyInput, GetInputValue(p, kFrequencyInput)); job.Insert(kPositionInput, GetInputValue(p, kPositionInput)); job.Insert(kStretchInput, GetInputValue(p, kStretchInput)); - job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, tex->virtual_resolution(), this)); + job.Insert(QStringLiteral("resolution_in"), tex->virtual_resolution()); - return NodeValue(NodeValue::kTexture, tex->toJob(job), this); + return tex->toJob(job); } else { // If we're not flipping or flopping just push the texture return texture; diff --git a/app/node/distort/ripple/rippledistortnode.h b/app/node/distort/ripple/rippledistortnode.h index a2d9ff357a..81ae9c40ef 100644 --- a/app/node/distort/ripple/rippledistortnode.h +++ b/app/node/distort/ripple/rippledistortnode.h @@ -41,7 +41,6 @@ class RippleDistortNode : public Node virtual void Retranslate() override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; virtual NodeValue Value(const ValueParams &p) const override; virtual void UpdateGizmoPositions(const ValueParams &p) override; @@ -57,6 +56,8 @@ protected slots: virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) override; private: + static ShaderCode GetShaderCode(const QString &id); + PointGizmo *gizmo_; }; diff --git a/app/node/distort/swirl/swirldistortnode.cpp b/app/node/distort/swirl/swirldistortnode.cpp index c232bc8042..0a3d290f78 100644 --- a/app/node/distort/swirl/swirldistortnode.cpp +++ b/app/node/distort/swirl/swirldistortnode.cpp @@ -81,9 +81,8 @@ void SwirlDistortNode::Retranslate() SetInputName(kPositionInput, tr("Position")); } -ShaderCode SwirlDistortNode::GetShaderCode(const ShaderRequest &request) const +ShaderCode SwirlDistortNode::GetShaderCode(const QString &id) { - Q_UNUSED(request) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/swirl.frag")); } @@ -95,9 +94,9 @@ NodeValue SwirlDistortNode::Value(const ValueParams &p) const if (TexturePtr tex = tex_meta.toTexture()) { // Only run shader if at least one of flip or flop are selected if (!qIsNull(GetInputValue(p, kAngleInput).toDouble()) && !qIsNull(GetInputValue(p, kRadiusInput).toDouble())) { - ShaderJob job = CreateJob(p); - job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, tex->virtual_resolution(), this)); - return NodeValue(NodeValue::kTexture, tex->toJob(job), this); + ShaderJob job = CreateShaderJob(p, GetShaderCode); + job.Insert(QStringLiteral("resolution_in"), tex->virtual_resolution()); + return tex->toJob(job); } } diff --git a/app/node/distort/swirl/swirldistortnode.h b/app/node/distort/swirl/swirldistortnode.h index 764c2aa160..27577cd384 100644 --- a/app/node/distort/swirl/swirldistortnode.h +++ b/app/node/distort/swirl/swirldistortnode.h @@ -41,7 +41,6 @@ class SwirlDistortNode : public Node virtual void Retranslate() override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; virtual NodeValue Value(const ValueParams &p) const override; virtual void UpdateGizmoPositions(const ValueParams &p) override; @@ -55,6 +54,8 @@ protected slots: virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) override; private: + static ShaderCode GetShaderCode(const QString &id); + PointGizmo *gizmo_; }; diff --git a/app/node/distort/tile/tiledistortnode.cpp b/app/node/distort/tile/tiledistortnode.cpp index 5e0f510aa0..63b1e67c3c 100644 --- a/app/node/distort/tile/tiledistortnode.cpp +++ b/app/node/distort/tile/tiledistortnode.cpp @@ -102,9 +102,8 @@ void TileDistortNode::Retranslate() }); } -ShaderCode TileDistortNode::GetShaderCode(const ShaderRequest &request) const +ShaderCode TileDistortNode::GetShaderCode(const QString &id) { - Q_UNUSED(request) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/tile.frag")); } @@ -116,9 +115,9 @@ NodeValue TileDistortNode::Value(const ValueParams &p) const if (TexturePtr tex = texture.toTexture()) { // Only run shader if at least one of flip or flop are selected if (!qFuzzyCompare(GetInputValue(p, kScaleInput).toDouble(), 1.0)) { - ShaderJob job = CreateJob(p); - job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, tex->virtual_resolution(), this)); - return NodeValue(NodeValue::kTexture, tex->toJob(job), this); + ShaderJob job = CreateShaderJob(p, GetShaderCode); + job.Insert(QStringLiteral("resolution_in"), tex->virtual_resolution()); + return tex->toJob(job); } } diff --git a/app/node/distort/tile/tiledistortnode.h b/app/node/distort/tile/tiledistortnode.h index 87164123fe..62f0db962d 100644 --- a/app/node/distort/tile/tiledistortnode.h +++ b/app/node/distort/tile/tiledistortnode.h @@ -41,7 +41,6 @@ class TileDistortNode : public Node virtual void Retranslate() override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; virtual NodeValue Value(const ValueParams &p) const override; virtual void UpdateGizmoPositions(const ValueParams &p) override; @@ -57,6 +56,8 @@ protected slots: virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) override; private: + static ShaderCode GetShaderCode(const QString &id); + enum Anchor { kTopLeft, kTopCenter, diff --git a/app/node/distort/transform/transformdistortnode.cpp b/app/node/distort/transform/transformdistortnode.cpp index e3bd477a6d..377edda061 100644 --- a/app/node/distort/transform/transformdistortnode.cpp +++ b/app/node/distort/transform/transformdistortnode.cpp @@ -81,6 +81,12 @@ void TransformDistortNode::Retranslate() SetComboBoxStrings(kInterpolationInput, {tr("Nearest Neighbor"), tr("Bilinear"), tr("Mipmapped Bilinear")}); } +ShaderCode TransformDistortNode::GetShaderCode(const QString &id) +{ + // Returns default frag and vert shader + return ShaderCode(); +} + NodeValue TransformDistortNode::Value(const ValueParams &p) const { // Generate matrix @@ -100,8 +106,9 @@ NodeValue TransformDistortNode::Value(const ValueParams &p) const // The matrix will transform things ShaderJob job; job.Insert(QStringLiteral("ove_maintex"), texture_meta); - job.Insert(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, real_matrix, this)); + job.Insert(QStringLiteral("ove_mvpmat"), real_matrix); job.SetInterpolation(QStringLiteral("ove_maintex"), static_cast(GetInputValue(p, kInterpolationInput).toInt())); + job.set_function(GetShaderCode); // Use global resolution rather than texture resolution because this may result in a size change job_to_push = Texture::Job(p.vparams(), job); @@ -114,18 +121,10 @@ NodeValue TransformDistortNode::Value(const ValueParams &p) const // Re-push whatever value we received return texture_meta; } else { - return NodeValue(NodeValue::kTexture, job_to_push, this); + return job_to_push; } } -ShaderCode TransformDistortNode::GetShaderCode(const ShaderRequest &request) const -{ - Q_UNUSED(request); - - // Returns default frag and vert shader - return ShaderCode(); -} - void TransformDistortNode::GizmoDragStart(const ValueParams &p, double x, double y, const rational &time) { DraggableGizmo *gizmo = static_cast(sender()); diff --git a/app/node/distort/transform/transformdistortnode.h b/app/node/distort/transform/transformdistortnode.h index e3481de4ed..7f55f6e066 100644 --- a/app/node/distort/transform/transformdistortnode.h +++ b/app/node/distort/transform/transformdistortnode.h @@ -66,8 +66,6 @@ class TransformDistortNode : public MatrixGenerator virtual NodeValue Value(const ValueParams &p) const override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - enum AutoScaleType { kAutoScaleNone, kAutoScaleFit, @@ -97,6 +95,8 @@ protected slots: private: static QPointF CreateScalePoint(double x, double y, const QPointF& half_res, const QMatrix4x4& mat); + static ShaderCode GetShaderCode(const QString &id); + QMatrix4x4 GenerateAutoScaledMatrix(const QMatrix4x4 &generated_matrix, const ValueParams &globals, const VideoParams &texture_params) const; bool IsAScaleGizmo(NodeGizmo *g) const; diff --git a/app/node/distort/wave/wavedistortnode.cpp b/app/node/distort/wave/wavedistortnode.cpp index ac1a3b6442..b9e3e519a4 100644 --- a/app/node/distort/wave/wavedistortnode.cpp +++ b/app/node/distort/wave/wavedistortnode.cpp @@ -76,9 +76,8 @@ void WaveDistortNode::Retranslate() SetComboBoxStrings(kVerticalInput, {tr("Horizontal"), tr("Vertical")}); } -ShaderCode WaveDistortNode::GetShaderCode(const ShaderRequest &request) const +ShaderCode WaveDistortNode::GetShaderCode(const QString &id) { - Q_UNUSED(request) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/wave.frag")); } @@ -90,7 +89,7 @@ NodeValue WaveDistortNode::Value(const ValueParams &p) const if (TexturePtr texture = tex_meta.toTexture()) { // Only run shader if at least one of flip or flop are selected if (!qIsNull(GetInputValue(p, kIntensityInput).toDouble())) { - return NodeValue(NodeValue::kTexture, Texture::Job(texture->params(), CreateJob(p)), this); + return Texture::Job(texture->params(), CreateShaderJob(p, GetShaderCode)); } } diff --git a/app/node/distort/wave/wavedistortnode.h b/app/node/distort/wave/wavedistortnode.h index b15b4157a4..cf2d89abaa 100644 --- a/app/node/distort/wave/wavedistortnode.h +++ b/app/node/distort/wave/wavedistortnode.h @@ -40,7 +40,6 @@ class WaveDistortNode : public Node virtual void Retranslate() override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; virtual NodeValue Value(const ValueParams &globals) const override; static const QString kTextureInput; @@ -49,6 +48,9 @@ class WaveDistortNode : public Node static const QString kEvolutionInput; static const QString kVerticalInput; +private: + static ShaderCode GetShaderCode(const QString &id); + }; } diff --git a/app/node/effect/opacity/opacityeffect.cpp b/app/node/effect/opacity/opacityeffect.cpp index 4441eb2d3f..81fbb8c8b4 100644 --- a/app/node/effect/opacity/opacityeffect.cpp +++ b/app/node/effect/opacity/opacityeffect.cpp @@ -37,13 +37,14 @@ void OpacityEffect::Retranslate() SetInputName(kValueInput, tr("Opacity")); } -ShaderCode OpacityEffect::GetShaderCode(const ShaderRequest &request) const +ShaderCode GetRGBShaderCode(const QString &id) { - if (request.id == QStringLiteral("rgbmult")) { - return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/opacity_rgb.frag")); - } else { - return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/opacity.frag")); - } + return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/opacity_rgb.frag")); +} + +ShaderCode GetNumberShaderCode(const QString &id) +{ + return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/opacity.frag")); } NodeValue OpacityEffect::Value(const ValueParams &p) const @@ -54,11 +55,11 @@ NodeValue OpacityEffect::Value(const ValueParams &p) const if (TexturePtr tex = texture.toTexture()) { if (TexturePtr opacity_tex = value.toTexture()) { - ShaderJob job = CreateJob(p); + ShaderJob job = CreateShaderJob(p, GetRGBShaderCode); job.SetShaderID(QStringLiteral("rgbmult")); - return NodeValue(NodeValue::kTexture, tex->toJob(job), this); + return tex->toJob(job); } else if (!qFuzzyCompare(value.toDouble(), 1.0)) { - return NodeValue(NodeValue::kTexture, tex->toJob(CreateJob(p)), this); + return tex->toJob(CreateShaderJob(p, GetNumberShaderCode)); } } diff --git a/app/node/effect/opacity/opacityeffect.h b/app/node/effect/opacity/opacityeffect.h index b4f96e48c8..94ec474c01 100644 --- a/app/node/effect/opacity/opacityeffect.h +++ b/app/node/effect/opacity/opacityeffect.h @@ -34,7 +34,6 @@ class OpacityEffect : public Node virtual void Retranslate() override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; virtual NodeValue Value(const ValueParams &p) const override; static const QString kTextureInput; diff --git a/app/node/filter/blur/blur.cpp b/app/node/filter/blur/blur.cpp index e91db7900b..4e61946147 100644 --- a/app/node/filter/blur/blur.cpp +++ b/app/node/filter/blur/blur.cpp @@ -111,9 +111,8 @@ void BlurFilterNode::Retranslate() SetInputName(kRadialCenterInput, tr("Center")); } -ShaderCode BlurFilterNode::GetShaderCode(const ShaderRequest &request) const +ShaderCode BlurFilterNode::GetShaderCode(const QString &id) { - Q_UNUSED(request) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/blur.frag")); } @@ -155,12 +154,12 @@ NodeValue BlurFilterNode::Value(const ValueParams &p) const } if (can_push_job) { - ShaderJob job = CreateJob(p); + ShaderJob job = CreateShaderJob(p, GetShaderCode); - job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, tex->virtual_resolution(), this)); + job.Insert(QStringLiteral("resolution_in"), tex->virtual_resolution()); job.SetIterations(iterations, kTextureInput); - return NodeValue(NodeValue::kTexture, tex->toJob(job), this); + return tex->toJob(job); } } diff --git a/app/node/filter/blur/blur.h b/app/node/filter/blur/blur.h index 0353d3af60..5b45a82081 100644 --- a/app/node/filter/blur/blur.h +++ b/app/node/filter/blur/blur.h @@ -48,7 +48,6 @@ class BlurFilterNode : public Node virtual void Retranslate() override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; virtual NodeValue Value(const ValueParams &globals) const override; Method GetMethod() const @@ -76,6 +75,8 @@ protected slots: virtual void InputValueChangedEvent(const QString& input, int element) override; private: + static ShaderCode GetShaderCode(const QString &id); + void UpdateInputs(Method method); PointGizmo *radial_center_gizmo_; diff --git a/app/node/filter/dropshadow/dropshadowfilter.cpp b/app/node/filter/dropshadow/dropshadowfilter.cpp index 46ddf488fe..e7cade7cba 100644 --- a/app/node/filter/dropshadow/dropshadowfilter.cpp +++ b/app/node/filter/dropshadow/dropshadowfilter.cpp @@ -70,9 +70,8 @@ void DropShadowFilter::Retranslate() SetInputName(kFastInput, tr("Faster (Lower Quality)")); } -ShaderCode DropShadowFilter::GetShaderCode(const ShaderRequest &request) const +ShaderCode DropShadowFilter::GetShaderCode(const QString &id) { - Q_UNUSED(request) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/dropshadow.frag")); } @@ -81,18 +80,18 @@ NodeValue DropShadowFilter::Value(const ValueParams &p) const NodeValue tex_meta = GetInputValue(p, kTextureInput); if (TexturePtr tex = tex_meta.toTexture()) { - ShaderJob job = CreateJob(p); + ShaderJob job = CreateShaderJob(p, GetShaderCode); QString iterative = QStringLiteral("previous_iteration_in"); - job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, tex->virtual_resolution(), this)); + job.Insert(QStringLiteral("resolution_in"), tex->virtual_resolution()); job.Insert(iterative, tex_meta); if (!qIsNull(GetInputValue(p, kSoftnessInput).toDouble())) { job.SetIterations(3, iterative); } - return NodeValue(NodeValue::kTexture, tex->toJob(job), this); + return tex->toJob(job); } return NodeValue(); diff --git a/app/node/filter/dropshadow/dropshadowfilter.h b/app/node/filter/dropshadow/dropshadowfilter.h index 2fcf682b47..1d8e639cac 100644 --- a/app/node/filter/dropshadow/dropshadowfilter.h +++ b/app/node/filter/dropshadow/dropshadowfilter.h @@ -40,7 +40,6 @@ class DropShadowFilter : public Node virtual void Retranslate() override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; virtual NodeValue Value(const ValueParams &p) const override; static const QString kTextureInput; @@ -51,6 +50,9 @@ class DropShadowFilter : public Node static const QString kOpacityInput; static const QString kFastInput; +private: + static ShaderCode GetShaderCode(const QString &id); + }; } diff --git a/app/node/filter/mosaic/mosaicfilternode.cpp b/app/node/filter/mosaic/mosaicfilternode.cpp index bc038f012a..3b6800ae4a 100644 --- a/app/node/filter/mosaic/mosaicfilternode.cpp +++ b/app/node/filter/mosaic/mosaicfilternode.cpp @@ -51,6 +51,11 @@ void MosaicFilterNode::Retranslate() SetInputName(kVertInput, tr("Vertical")); } +ShaderCode MosaicFilterNode::GetShaderCode(const QString &id) +{ + return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/mosaic.frag")); +} + NodeValue MosaicFilterNode::Value(const ValueParams &p) const { NodeValue tex_meta = GetInputValue(p, kTextureInput); @@ -59,23 +64,16 @@ NodeValue MosaicFilterNode::Value(const ValueParams &p) const if (texture && GetInputValue(p, kHorizInput).toInt() != texture->width() && GetInputValue(p, kVertInput).toInt() != texture->height()) { - ShaderJob job = CreateJob(p); + ShaderJob job = CreateShaderJob(p, GetShaderCode); // Mipmapping makes this look weird, so we just use bilinear for finding the color of each block job.SetInterpolation(kTextureInput, Texture::kLinear); - return NodeValue(NodeValue::kTexture, texture->toJob(job), this); + return texture->toJob(job); } } return tex_meta; } -ShaderCode MosaicFilterNode::GetShaderCode(const ShaderRequest &request) const -{ - Q_UNUSED(request) - - return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/mosaic.frag")); -} - } diff --git a/app/node/filter/mosaic/mosaicfilternode.h b/app/node/filter/mosaic/mosaicfilternode.h index 85a8d3c083..72eccf9071 100644 --- a/app/node/filter/mosaic/mosaicfilternode.h +++ b/app/node/filter/mosaic/mosaicfilternode.h @@ -56,12 +56,14 @@ class MosaicFilterNode : public Node virtual void Retranslate() override; virtual NodeValue Value(const ValueParams &p) const override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; static const QString kTextureInput; static const QString kHorizInput; static const QString kVertInput; +private: + static ShaderCode GetShaderCode(const QString &id); + }; } diff --git a/app/node/filter/stroke/stroke.cpp b/app/node/filter/stroke/stroke.cpp index 80c7ea9e2b..2809b30893 100644 --- a/app/node/filter/stroke/stroke.cpp +++ b/app/node/filter/stroke/stroke.cpp @@ -83,6 +83,11 @@ void StrokeFilterNode::Retranslate() SetInputName(kInnerInput, tr("Inner")); } +ShaderCode StrokeFilterNode::GetShaderCode(const QString &id) +{ + return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/stroke.frag")); +} + NodeValue StrokeFilterNode::Value(const ValueParams &p) const { NodeValue tex_meta = GetInputValue(p, kTextureInput); @@ -90,20 +95,13 @@ NodeValue StrokeFilterNode::Value(const ValueParams &p) const if (TexturePtr tex = tex_meta.toTexture()) { if (GetInputValue(p, kRadiusInput).toDouble() > 0.0 && GetInputValue(p, kOpacityInput).toDouble() > 0.0) { - ShaderJob job = CreateJob(p); - job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, tex->virtual_resolution(), this)); - return NodeValue(NodeValue::kTexture, tex->toJob(job), this); + ShaderJob job = CreateShaderJob(p, GetShaderCode); + job.Insert(QStringLiteral("resolution_in"), tex->virtual_resolution()); + return tex->toJob(job); } } return tex_meta; } -ShaderCode StrokeFilterNode::GetShaderCode(const ShaderRequest &request) const -{ - Q_UNUSED(request) - - return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/stroke.frag")); -} - } diff --git a/app/node/filter/stroke/stroke.h b/app/node/filter/stroke/stroke.h index 11e4b1c8b6..7a90285749 100644 --- a/app/node/filter/stroke/stroke.h +++ b/app/node/filter/stroke/stroke.h @@ -41,7 +41,6 @@ class StrokeFilterNode : public Node virtual void Retranslate() override; virtual NodeValue Value(const ValueParams &p) const override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; static const QString kTextureInput; static const QString kColorInput; @@ -49,6 +48,9 @@ class StrokeFilterNode : public Node static const QString kOpacityInput; static const QString kInnerInput; +private: + static ShaderCode GetShaderCode(const QString &id); + }; } diff --git a/app/node/generator/matrix/matrix.cpp b/app/node/generator/matrix/matrix.cpp index f09f9ef1ad..616b7c9d6c 100644 --- a/app/node/generator/matrix/matrix.cpp +++ b/app/node/generator/matrix/matrix.cpp @@ -90,8 +90,7 @@ void MatrixGenerator::Retranslate() NodeValue MatrixGenerator::Value(const ValueParams &p) const { // Push matrix output - QMatrix4x4 mat = GenerateMatrix(p, false, false, false, QMatrix4x4()); - return NodeValue(NodeValue::kMatrix, mat, this); + return GenerateMatrix(p, false, false, false, QMatrix4x4()); } QMatrix4x4 MatrixGenerator::GenerateMatrix(const ValueParams &p, bool ignore_anchor, bool ignore_position, bool ignore_scale, const QMatrix4x4 &mat) const diff --git a/app/node/generator/noise/noise.cpp b/app/node/generator/noise/noise.cpp index a52aebeb39..2ab408fc79 100644 --- a/app/node/generator/noise/noise.cpp +++ b/app/node/generator/noise/noise.cpp @@ -73,19 +73,19 @@ void NoiseGeneratorNode::Retranslate() SetInputName(kColorInput, tr("Color")); } -ShaderCode NoiseGeneratorNode::GetShaderCode(const ShaderRequest &request) const +ShaderCode NoiseGeneratorNode::GetShaderCode(const QString &id) { return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/noise.frag")); } NodeValue NoiseGeneratorNode::Value(const ValueParams &p) const { - ShaderJob job = CreateJob(p); + ShaderJob job = CreateShaderJob(p, GetShaderCode); - job.Insert(QStringLiteral("time_in"), NodeValue(NodeValue::kFloat, p.time().in().toDouble(), this)); + job.Insert(QStringLiteral("time_in"), p.time().in().toDouble()); TexturePtr base = GetInputValue(p, kBaseIn).toTexture(); - return NodeValue(NodeValue::kTexture, Texture::Job(base ? base->params() : p.vparams(), job), this); + return Texture::Job(base ? base->params() : p.vparams(), job); } } diff --git a/app/node/generator/noise/noise.h b/app/node/generator/noise/noise.h index 87ef79def0..893e44de6d 100644 --- a/app/node/generator/noise/noise.h +++ b/app/node/generator/noise/noise.h @@ -39,13 +39,15 @@ class NoiseGeneratorNode : public Node { virtual void Retranslate() override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; virtual NodeValue Value(const ValueParams &p) const override; static const QString kBaseIn; static const QString kColorInput; static const QString kStrengthInput; +private: + static ShaderCode GetShaderCode(const QString &id); + }; } // namespace olive diff --git a/app/node/generator/polygon/polygon.cpp b/app/node/generator/polygon/polygon.cpp index 66d7d352fe..23acd0c097 100644 --- a/app/node/generator/polygon/polygon.cpp +++ b/app/node/generator/polygon/polygon.cpp @@ -87,27 +87,7 @@ void PolygonGenerator::Retranslate() SetInputName(kColorInput, tr("Color")); } -ShaderJob PolygonGenerator::GetGenerateJob(const ValueParams &p, const VideoParams ¶ms) const -{ - VideoParams vp = params; - vp.set_format(PixelFormat::U8); - auto job = Texture::Job(vp, CreateJob(p)); - - // Conversion to RGB - ShaderJob rgb; - rgb.SetShaderID(QStringLiteral("rgb")); - rgb.Insert(QStringLiteral("texture_in"), NodeValue(NodeValue::kTexture, job, this)); - rgb.Insert(QStringLiteral("color_in"), GetInputValue(p, kColorInput)); - - return rgb; -} - -NodeValue PolygonGenerator::Value(const ValueParams &p) const -{ - return GetMergableJob(p, Texture::Job(p.vparams(), GetGenerateJob(p, p.vparams()))); -} - -void PolygonGenerator::GenerateFrame(FramePtr frame, const GenerateJob &job) const +void PolygonGenerator::GenerateFrame(FramePtr frame, const GenerateJob &job) { // This could probably be more optimized, but for now we use Qt to draw to a QImage. // QImages only support integer pixels and we use float pixels, so what we do here is draw onto @@ -118,7 +98,7 @@ void PolygonGenerator::GenerateFrame(FramePtr frame, const GenerateJob &job) con auto points = job.Get(kPointsInput).toArray(); - QPainterPath path = GeneratePath(points, InputArraySize(kPointsInput)); + QPainterPath path = GeneratePath(points, points.size()); QPainter p(&img); double par = frame->video_params().pixel_aspect_ratio().toDouble(); @@ -130,6 +110,31 @@ void PolygonGenerator::GenerateFrame(FramePtr frame, const GenerateJob &job) con p.drawPath(path); } +ShaderCode PolygonGenerator::GetShaderCode(const QString &id) +{ + return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/rgb.frag")); +} + +ShaderJob PolygonGenerator::GetGenerateJob(const ValueParams &p, const VideoParams ¶ms) const +{ + VideoParams vp = params; + vp.set_format(PixelFormat::U8); + auto job = Texture::Job(vp, CreateGenerateJob(p, GenerateFrame)); + + // Conversion to RGB + ShaderJob rgb; + rgb.set_function(GetShaderCode); + rgb.Insert(QStringLiteral("texture_in"), job); + rgb.Insert(QStringLiteral("color_in"), GetInputValue(p, kColorInput)); + + return rgb; +} + +NodeValue PolygonGenerator::Value(const ValueParams &p) const +{ + return GetMergableJob(p, Texture::Job(p.vparams(), GetGenerateJob(p, p.vparams()))); +} + template NodeGizmo *PolygonGenerator::CreateAppropriateGizmo() { @@ -221,15 +226,6 @@ void PolygonGenerator::UpdateGizmoPositions(const ValueParams &p) poly_gizmo_->SetPath(GeneratePath(points, pts_sz).translated(QPointF(half_res.x, half_res.y))); } -ShaderCode PolygonGenerator::GetShaderCode(const ShaderRequest &request) const -{ - if (request.id == QStringLiteral("rgb")) { - return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/rgb.frag")); - } else { - return super::GetShaderCode(request); - } -} - void PolygonGenerator::GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) { DraggableGizmo *gizmo = static_cast(sender()); diff --git a/app/node/generator/polygon/polygon.h b/app/node/generator/polygon/polygon.h index f9106a2592..c02a7091e8 100644 --- a/app/node/generator/polygon/polygon.h +++ b/app/node/generator/polygon/polygon.h @@ -49,11 +49,9 @@ class PolygonGenerator : public GeneratorWithMerge virtual NodeValue Value(const ValueParams &p) const override; - virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const override; - virtual void UpdateGizmoPositions(const ValueParams &p) override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; + static QPainterPath GeneratePath(const NodeValueArray &points, int size); static const QString kPointsInput; static const QString kColorInput; @@ -67,7 +65,9 @@ protected slots: private: static void AddPointToPath(QPainterPath *path, const Bezier &before, const Bezier &after); - static QPainterPath GeneratePath(const NodeValueArray &points, int size); + static void GenerateFrame(FramePtr frame, const GenerateJob &job); + + static ShaderCode GetShaderCode(const QString &id); template void ValidateGizmoVectorSize(QVector &vec, int new_sz); diff --git a/app/node/generator/shape/generatorwithmerge.cpp b/app/node/generator/shape/generatorwithmerge.cpp index 2e087343f0..afcdcc4ded 100644 --- a/app/node/generator/shape/generatorwithmerge.cpp +++ b/app/node/generator/shape/generatorwithmerge.cpp @@ -42,13 +42,9 @@ void GeneratorWithMerge::Retranslate() SetInputName(kBaseInput, tr("Base")); } -ShaderCode GeneratorWithMerge::GetShaderCode(const ShaderRequest &request) const +ShaderCode GeneratorWithMerge::GetShaderCode(const QString &id) { - if (request.id == QStringLiteral("mrg")) { - return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/alphaover.frag")); - } - - return ShaderCode(); + return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/alphaover.frag")); } NodeValue GeneratorWithMerge::GetMergableJob(const ValueParams &p, TexturePtr job) const @@ -60,13 +56,14 @@ NodeValue GeneratorWithMerge::GetMergableJob(const ValueParams &p, TexturePtr jo ShaderJob merge; merge.SetShaderID(QStringLiteral("mrg")); + merge.set_function(GetShaderCode); merge.Insert(MergeNode::kBaseIn, tex_meta); - merge.Insert(MergeNode::kBlendIn, NodeValue(NodeValue::kTexture, job, this)); + merge.Insert(MergeNode::kBlendIn, job); - return NodeValue(NodeValue::kTexture, base->toJob(merge), this); + return base->toJob(merge); } else { // Just push generate job - return NodeValue(NodeValue::kTexture, job, this); + return job; } } diff --git a/app/node/generator/shape/generatorwithmerge.h b/app/node/generator/shape/generatorwithmerge.h index 9b4bfa358b..2bf2941995 100644 --- a/app/node/generator/shape/generatorwithmerge.h +++ b/app/node/generator/shape/generatorwithmerge.h @@ -33,13 +33,14 @@ class GeneratorWithMerge : public Node virtual void Retranslate() override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - static const QString kBaseInput; protected: NodeValue GetMergableJob(const ValueParams &p, TexturePtr job) const; +private: + static ShaderCode GetShaderCode(const QString &id); + }; } diff --git a/app/node/generator/shape/shapenode.cpp b/app/node/generator/shape/shapenode.cpp index fc4a0d5bbd..72bff4784a 100644 --- a/app/node/generator/shape/shapenode.cpp +++ b/app/node/generator/shape/shapenode.cpp @@ -66,23 +66,18 @@ void ShapeNode::Retranslate() SetComboBoxStrings(kTypeInput, {tr("Rectangle"), tr("Ellipse"), tr("Rounded Rectangle")}); } -ShaderCode ShapeNode::GetShaderCode(const ShaderRequest &request) const +ShaderCode ShapeNode::GetShaderCode(const QString &id) { - if (request.id == QStringLiteral("shape")) { - return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/shape.frag"))); - } else { - return super::GetShaderCode(request); - } + return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/shape.frag"))); } NodeValue ShapeNode::Value(const ValueParams &p) const { TexturePtr base = GetInputValue(p, kBaseInput).toTexture(); - ShaderJob job = CreateJob(p); + ShaderJob job = CreateShaderJob(p, GetShaderCode); - job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, base ? base->virtual_resolution() : p.square_resolution(), this)); - job.SetShaderID(QStringLiteral("shape")); + job.Insert(QStringLiteral("resolution_in"), base ? base->virtual_resolution() : p.square_resolution()); return GetMergableJob(p, Texture::Job(base ? base->params() : p.vparams(), job)); } diff --git a/app/node/generator/shape/shapenode.h b/app/node/generator/shape/shapenode.h index 2c67338330..8e81b27c18 100644 --- a/app/node/generator/shape/shapenode.h +++ b/app/node/generator/shape/shapenode.h @@ -46,7 +46,6 @@ class ShapeNode : public ShapeNodeBase virtual void Retranslate() override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; virtual NodeValue Value(const ValueParams &p) const override; static QString kTypeInput; @@ -55,6 +54,9 @@ class ShapeNode : public ShapeNodeBase protected: virtual void InputValueChangedEvent(const QString &input, int element) override; +private: + static ShaderCode GetShaderCode(const QString &id); + }; } diff --git a/app/node/generator/solid/solid.cpp b/app/node/generator/solid/solid.cpp index 2b0cfb0b0d..1e4d3d5cea 100644 --- a/app/node/generator/solid/solid.cpp +++ b/app/node/generator/solid/solid.cpp @@ -59,16 +59,14 @@ void SolidGenerator::Retranslate() SetInputName(kColorInput, tr("Color")); } -NodeValue SolidGenerator::Value(const ValueParams &p) const +ShaderCode SolidGenerator::GetShaderCode(const QString &id) { - return NodeValue(NodeValue::kTexture, Texture::Job(p.vparams(), CreateJob(p)), this); + return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/solid.frag")); } -ShaderCode SolidGenerator::GetShaderCode(const ShaderRequest &request) const +NodeValue SolidGenerator::Value(const ValueParams &p) const { - Q_UNUSED(request) - - return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/solid.frag")); + return Texture::Job(p.vparams(), CreateShaderJob(p, GetShaderCode)); } } diff --git a/app/node/generator/solid/solid.h b/app/node/generator/solid/solid.h index 25f8a59f43..f41f01a036 100644 --- a/app/node/generator/solid/solid.h +++ b/app/node/generator/solid/solid.h @@ -41,10 +41,12 @@ class SolidGenerator : public Node virtual void Retranslate() override; virtual NodeValue Value(const ValueParams &p) const override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; static const QString kColorInput; +private: + static ShaderCode GetShaderCode(const QString &id); + }; } diff --git a/app/node/generator/text/textv1.cpp b/app/node/generator/text/textv1.cpp index 598d03c856..f160504183 100644 --- a/app/node/generator/text/textv1.cpp +++ b/app/node/generator/text/textv1.cpp @@ -90,16 +90,7 @@ void TextGeneratorV1::Retranslate() SetComboBoxStrings(kVAlignInput, {tr("Top"), tr("Center"), tr("Bottom")}); } -NodeValue TextGeneratorV1::Value(const ValueParams &p) const -{ - if (!GetInputValue(p, kTextInput).toString().isEmpty()) { - return NodeValue(NodeValue::kTexture, Texture::Job(p.vparams(), CreateJob(p)), this); - } - - return NodeValue(); -} - -void TextGeneratorV1::GenerateFrame(FramePtr frame, const GenerateJob& job) const +void TextGeneratorV1::GenerateFrame(FramePtr frame, const GenerateJob& job) { // This could probably be more optimized, but for now we use Qt to draw to a QImage. // QImages only support integer pixels and we use float pixels, so what we do here is draw onto @@ -172,4 +163,13 @@ void TextGeneratorV1::GenerateFrame(FramePtr frame, const GenerateJob& job) cons } } +NodeValue TextGeneratorV1::Value(const ValueParams &p) const +{ + if (!GetInputValue(p, kTextInput).toString().isEmpty()) { + return Texture::Job(p.vparams(), CreateGenerateJob(p, GenerateFrame)); + } + + return NodeValue(); +} + } diff --git a/app/node/generator/text/textv1.h b/app/node/generator/text/textv1.h index b49f85ff5f..92607257fe 100644 --- a/app/node/generator/text/textv1.h +++ b/app/node/generator/text/textv1.h @@ -42,8 +42,6 @@ class TextGeneratorV1 : public Node virtual NodeValue Value(const ValueParams &p) const override; - virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const override; - static const QString kTextInput; static const QString kHtmlInput; static const QString kColorInput; @@ -51,6 +49,9 @@ class TextGeneratorV1 : public Node static const QString kFontInput; static const QString kFontSizeInput; +private: + static void GenerateFrame(FramePtr frame, const GenerateJob& job); + }; } diff --git a/app/node/generator/text/textv2.cpp b/app/node/generator/text/textv2.cpp index 0ad628f001..3a80b0a07c 100644 --- a/app/node/generator/text/textv2.cpp +++ b/app/node/generator/text/textv2.cpp @@ -91,19 +91,7 @@ void TextGeneratorV2::Retranslate() SetComboBoxStrings(kVAlignInput, {tr("Top"), tr("Center"), tr("Bottom")}); } -NodeValue TextGeneratorV2::Value(const ValueParams &p) const -{ - if (!GetInputValue(p, kTextInput).toString().isEmpty()) { - GenerateJob job = CreateJob(p); - auto text_params = p.vparams(); - text_params.set_format(PixelFormat::F32); - return NodeValue(NodeValue::kTexture, Texture::Job(text_params, job), this); - } - - return NodeValue(); -} - -void TextGeneratorV2::GenerateFrame(FramePtr frame, const GenerateJob& job) const +void TextGeneratorV2::GenerateFrame(FramePtr frame, const GenerateJob& job) { // This could probably be more optimized, but for now we use Qt to draw to a QImage. // QImages only support integer pixels and we use float pixels, so what we do here is draw onto @@ -197,4 +185,16 @@ void TextGeneratorV2::GenerateFrame(FramePtr frame, const GenerateJob& job) cons } } +NodeValue TextGeneratorV2::Value(const ValueParams &p) const +{ + if (!GetInputValue(p, kTextInput).toString().isEmpty()) { + GenerateJob job = CreateGenerateJob(p, GenerateFrame); + auto text_params = p.vparams(); + text_params.set_format(PixelFormat::F32); + return Texture::Job(text_params, job); + } + + return NodeValue(); +} + } diff --git a/app/node/generator/text/textv2.h b/app/node/generator/text/textv2.h index f2c51c3ee8..5f988eb124 100644 --- a/app/node/generator/text/textv2.h +++ b/app/node/generator/text/textv2.h @@ -42,14 +42,15 @@ class TextGeneratorV2 : public ShapeNodeBase virtual NodeValue Value(const ValueParams &p) const override; - virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const override; - static const QString kTextInput; static const QString kHtmlInput; static const QString kVAlignInput; static const QString kFontInput; static const QString kFontSizeInput; +private: + static void GenerateFrame(FramePtr frame, const GenerateJob& job); + }; } diff --git a/app/node/generator/text/textv3.cpp b/app/node/generator/text/textv3.cpp index 4774deb4f5..5204809345 100644 --- a/app/node/generator/text/textv3.cpp +++ b/app/node/generator/text/textv3.cpp @@ -96,42 +96,7 @@ void TextGeneratorV3::Retranslate() SetInputName(kArgsInput, tr("Arguments")); } -NodeValue TextGeneratorV3::Value(const ValueParams &p) const -{ - QString text = GetInputValue(p, kTextInput).toString(); - - if (GetInputValue(p, kUseArgsInput).toBool()) { - auto args = GetInputValue(p, kArgsInput).toArray(); - if (!args.empty()) { - QStringList list; - list.reserve(args.size()); - for (size_t i=0; iparams() : p.vparams(); - text_params.set_format(PixelFormat::U8); - text_params.set_colorspace(project()->color_manager()->GetDefaultInputColorSpace()); - - GenerateJob job = CreateJob(p); - job.Insert(kTextInput, NodeValue(NodeValue::kText, text)); - - return GetMergableJob(p, Texture::Job(text_params, job)); - } - - return base_val; -} - -void TextGeneratorV3::GenerateFrame(FramePtr frame, const GenerateJob& job) const +void TextGeneratorV3::GenerateFrame(FramePtr frame, const GenerateJob& job) { QImage img(reinterpret_cast(frame->data()), frame->width(), frame->height(), frame->linesize_bytes(), QImage::Format_RGBA8888_Premultiplied); img.fill(Qt::transparent); @@ -159,14 +124,14 @@ void TextGeneratorV3::GenerateFrame(FramePtr frame, const GenerateJob& job) cons p.translate(frame->video_params().width()/2, frame->video_params().height()/2); p.setClipRect(0, 0, size.x(), size.y()); - switch (static_cast(job.Get(kVerticalAlignmentInput).toInt())) { - case kVAlignTop: + switch (static_cast(job.Get(kVerticalAlignmentInput).toInt())) { + case TextGeneratorV3::kVAlignTop: // Do nothing break; - case kVAlignMiddle: + case TextGeneratorV3::kVAlignMiddle: p.translate(0, size.y()/2-text_doc.size().height()/2); break; - case kVAlignBottom: + case TextGeneratorV3::kVAlignBottom: p.translate(0, size.y()-text_doc.size().height()); break; } @@ -178,6 +143,41 @@ void TextGeneratorV3::GenerateFrame(FramePtr frame, const GenerateJob& job) cons text_doc.documentLayout()->draw(&p, ctx); } +NodeValue TextGeneratorV3::Value(const ValueParams &p) const +{ + QString text = GetInputValue(p, kTextInput).toString(); + + if (GetInputValue(p, kUseArgsInput).toBool()) { + auto args = GetInputValue(p, kArgsInput).toArray(); + if (!args.empty()) { + QStringList list; + list.reserve(args.size()); + for (size_t i=0; iparams() : p.vparams(); + text_params.set_format(PixelFormat::U8); + text_params.set_colorspace(project()->color_manager()->GetDefaultInputColorSpace()); + + GenerateJob job = CreateGenerateJob(p, GenerateFrame); + job.Insert(kTextInput, NodeValue(NodeValue::kText, text)); + + return GetMergableJob(p, Texture::Job(text_params, job)); + } + + return base_val; +} + void TextGeneratorV3::UpdateGizmoPositions(const ValueParams &p) { super::UpdateGizmoPositions(p); diff --git a/app/node/generator/text/textv3.h b/app/node/generator/text/textv3.h index f6a665d522..1009613b6f 100644 --- a/app/node/generator/text/textv3.h +++ b/app/node/generator/text/textv3.h @@ -43,8 +43,6 @@ class TextGeneratorV3 : public ShapeNodeBase virtual NodeValue Value(const ValueParams &p) const override; - virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const override; - virtual void UpdateGizmoPositions(const ValueParams &p) override; enum VerticalAlignment @@ -73,6 +71,8 @@ class TextGeneratorV3 : public ShapeNodeBase virtual void InputValueChangedEvent(const QString &input, int element) override; private: + static void GenerateFrame(FramePtr frame, const GenerateJob& job); + TextGizmo *text_gizmo_; bool dont_emit_valign_; diff --git a/app/node/input/multicam/multicamnode.cpp b/app/node/input/multicam/multicamnode.cpp index 6f3672488b..735b262105 100644 --- a/app/node/input/multicam/multicamnode.cpp +++ b/app/node/input/multicam/multicamnode.cpp @@ -44,21 +44,12 @@ QString MultiCamNode::Description() const return tr("Allows easy switching between multiple sources."); } -ShaderCode MultiCamNode::GetShaderCode(const ShaderRequest &request) const -{ - auto l = request.id.split(':'); - int rows = l.at(0).toInt(); - int cols = l.at(1).toInt(); - - return ShaderCode(GenerateShaderCode(rows, cols)); -} - QString dblToGlsl(double d) { return QString::number(d, 'f'); } -QString MultiCamNode::GenerateShaderCode(int rows, int cols) +QString GenerateShaderCode(int rows, int cols) { int multiplier = std::max(cols, rows); @@ -118,6 +109,15 @@ QString MultiCamNode::GenerateShaderCode(int rows, int cols) return shader.join('\n'); } +ShaderCode MultiCamNode::GetShaderCode(const QString &id) +{ + auto l = id.split(':'); + int rows = l.at(0).toInt(); + int cols = l.at(1).toInt(); + + return ShaderCode(GenerateShaderCode(rows, cols)); +} + NodeValue MultiCamNode::Value(const ValueParams &p) const { if (p.output() == QStringLiteral("all")) { @@ -128,6 +128,7 @@ NodeValue MultiCamNode::Value(const ValueParams &p) const ShaderJob job; job.SetShaderID(QStringLiteral("%1:%2").arg(QString::number(rows), QString::number(cols))); + job.set_function(GetShaderCode); for (int i=0; i Category() const override; virtual QString Description() const override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - virtual NodeValue Value(const ValueParams &p) const override; virtual void Retranslate() override; @@ -62,7 +60,7 @@ class MultiCamNode : public Node virtual void InputDisconnectedEvent(const QString &input, int element, Node *output) override; private: - static QString GenerateShaderCode(int rows, int cols); + static ShaderCode GetShaderCode(const QString &id); Node *GetSourceNode(int source) const; diff --git a/app/node/input/time/timeinput.cpp b/app/node/input/time/timeinput.cpp index e6f16c0398..2f481fc5e5 100644 --- a/app/node/input/time/timeinput.cpp +++ b/app/node/input/time/timeinput.cpp @@ -50,11 +50,7 @@ QString TimeInput::Description() const NodeValue TimeInput::Value(const ValueParams &p) const { - return NodeValue(NodeValue::kFloat, - p.time().in().toDouble(), - this, - false, - QStringLiteral("time")); + return p.time().in().toDouble(); } } diff --git a/app/node/keying/chromakey/chromakey.cpp b/app/node/keying/chromakey/chromakey.cpp index d203244d7c..8ed4520cc0 100644 --- a/app/node/keying/chromakey/chromakey.cpp +++ b/app/node/keying/chromakey/chromakey.cpp @@ -113,9 +113,9 @@ void ChromaKeyNode::InputValueChangedEvent(const QString &input, int element) GenerateProcessor(); } -ShaderCode ChromaKeyNode::GetShaderCode(const ShaderRequest &request) const +ShaderCode GetColorTransformCode(const QString &stub) { - return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/chromakey.frag")).arg(request.stub)); + return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/chromakey.frag")).arg(stub)); } void ChromaKeyNode::GenerateProcessor() @@ -136,14 +136,14 @@ NodeValue ChromaKeyNode::Value(const ValueParams &p) const if (TexturePtr tex = tex_meta.toTexture()) { if (processor()) { - ColorTransformJob job = CreateJob(p); + ColorTransformJob job = CreateColorTransformJob(p); job.SetColorProcessor(processor()); job.SetInputTexture(GetInputValue(p, kTextureInput)); - job.SetNeedsCustomShader(this); + job.SetCustomShaderFunction(GetColorTransformCode); job.SetFunctionName(QStringLiteral("SceneLinearToCIEXYZ_d65")); - return NodeValue(NodeValue::kTexture, tex->toJob(job), this); + return tex->toJob(job); } } diff --git a/app/node/keying/chromakey/chromakey.h b/app/node/keying/chromakey/chromakey.h index 1eac20856d..6b101e7910 100644 --- a/app/node/keying/chromakey/chromakey.h +++ b/app/node/keying/chromakey/chromakey.h @@ -36,7 +36,6 @@ class ChromaKeyNode : public OCIOBaseNode { virtual void InputValueChangedEvent(const QString& input, int element) override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; virtual NodeValue Value(const ValueParams &p) const override; virtual void ConfigChanged() override; diff --git a/app/node/keying/colordifferencekey/colordifferencekey.cpp b/app/node/keying/colordifferencekey/colordifferencekey.cpp index d0f405b11b..04da186705 100644 --- a/app/node/keying/colordifferencekey/colordifferencekey.cpp +++ b/app/node/keying/colordifferencekey/colordifferencekey.cpp @@ -85,9 +85,8 @@ void ColorDifferenceKeyNode::Retranslate() SetInputName(kMaskOnlyInput, tr("Show Mask Only")); } -ShaderCode ColorDifferenceKeyNode::GetShaderCode(const ShaderRequest &request) const +ShaderCode ColorDifferenceKeyNode::GetShaderCode(const QString &id) { - Q_UNUSED(request) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/colordifferencekey.frag")); } @@ -97,8 +96,8 @@ NodeValue ColorDifferenceKeyNode::Value(const ValueParams &p) const NodeValue tex_meta = GetInputValue(p, kTextureInput); if (TexturePtr tex = tex_meta.toTexture()) { - ShaderJob job = CreateJob(p); - return NodeValue(NodeValue::kTexture, tex->toJob(job), this); + ShaderJob job = CreateShaderJob(p, GetShaderCode); + return tex->toJob(job); } return tex_meta; diff --git a/app/node/keying/colordifferencekey/colordifferencekey.h b/app/node/keying/colordifferencekey/colordifferencekey.h index 231d6cc4eb..d4fdd4efcf 100644 --- a/app/node/keying/colordifferencekey/colordifferencekey.h +++ b/app/node/keying/colordifferencekey/colordifferencekey.h @@ -33,7 +33,6 @@ class ColorDifferenceKeyNode : public Node { virtual void Retranslate() override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; virtual NodeValue Value(const ValueParams &p) const override; static const QString kTextureInput; @@ -44,6 +43,9 @@ class ColorDifferenceKeyNode : public Node { static const QString kHighlightsInput; static const QString kMaskOnlyInput; +private: + static ShaderCode GetShaderCode(const QString &id); + }; } // namespace olive diff --git a/app/node/keying/despill/despill.cpp b/app/node/keying/despill/despill.cpp index 889db1a6a6..1514c8d65d 100644 --- a/app/node/keying/despill/despill.cpp +++ b/app/node/keying/despill/despill.cpp @@ -75,8 +75,8 @@ void DespillNode::Retranslate() SetInputName(kPreserveLuminanceInput, tr("Preserve Luminance")); } -ShaderCode DespillNode::GetShaderCode(const ShaderRequest &request) const { - Q_UNUSED(request) +ShaderCode DespillNode::GetShaderCode(const QString &id) +{ return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/despill.frag")); } @@ -85,7 +85,7 @@ NodeValue DespillNode::Value(const ValueParams &p) const NodeValue tex_meta = GetInputValue(p, kTextureInput); if (TexturePtr tex = tex_meta.toTexture()) { - ShaderJob job = CreateJob(p); + ShaderJob job = CreateShaderJob(p, GetShaderCode); // Set luma coefficients double luma_coeffs[3] = {0.0f, 0.0f, 0.0f}; @@ -93,7 +93,7 @@ NodeValue DespillNode::Value(const ValueParams &p) const job.Insert(QStringLiteral("luma_coeffs"), NodeValue(NodeValue::kVec3, QVector3D(luma_coeffs[0], luma_coeffs[1], luma_coeffs[2]))); - return NodeValue(NodeValue::kTexture, tex->toJob(job), this); + return tex->toJob(job); } return tex_meta; diff --git a/app/node/keying/despill/despill.h b/app/node/keying/despill/despill.h index 2d6844aaa0..8f677acd0c 100644 --- a/app/node/keying/despill/despill.h +++ b/app/node/keying/despill/despill.h @@ -34,7 +34,6 @@ class DespillNode : public Node { virtual void Retranslate() override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; virtual NodeValue Value(const ValueParams &globals) const override; static const QString kTextureInput; @@ -42,6 +41,8 @@ class DespillNode : public Node { static const QString kMethodInput; static const QString kPreserveLuminanceInput; +private: + static ShaderCode GetShaderCode(const QString &id); }; diff --git a/app/node/math/math/math.cpp b/app/node/math/math/math.cpp index 378ab90d1e..792d496372 100644 --- a/app/node/math/math/math.cpp +++ b/app/node/math/math/math.cpp @@ -88,11 +88,6 @@ void MathNode::Retranslate() SetComboBoxStrings(kMethodIn, operations); } -ShaderCode MathNode::GetShaderCode(const ShaderRequest &request) const -{ - return GetShaderCodeInternal(request.id, kParamAIn, kParamBIn); -} - NodeValue MathNode::Value(const ValueParams &p) const { // Auto-detect what values to operate with @@ -123,16 +118,4 @@ NodeValue MathNode::Value(const ValueParams &p) const return NodeValue(); } -void MathNode::ProcessSamples(const SampleJob &job, SampleBuffer &output) const -{ - const ValueParams &p = job.value_params(); - int pairing = job.Get(QStringLiteral("pairing")).toInt(); - - if (pairing == kPairSampleNumber) { - return ProcessSamplesNumberInternal(p, GetOperation(), job.Get(QStringLiteral("number")).toString(), job.Get(QStringLiteral("samples")).toSamples(), output); - } else { - return ProcessSamplesSamplesInternal(p, GetOperation(), job.Get(QStringLiteral("a")).toSamples(), job.Get(QStringLiteral("b")).toSamples(), output); - } -} - } diff --git a/app/node/math/math/math.h b/app/node/math/math/math.h index 7cd0dc0980..162f82b978 100644 --- a/app/node/math/math/math.h +++ b/app/node/math/math/math.h @@ -40,8 +40,6 @@ class MathNode : public MathNodeBase virtual void Retranslate() override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; - Operation GetOperation() const { return static_cast(GetStandardValue(kMethodIn).toInt()); @@ -54,8 +52,6 @@ class MathNode : public MathNodeBase virtual NodeValue Value(const ValueParams &p) const override; - virtual void ProcessSamples(const SampleJob &job, SampleBuffer &output) const override; - static const QString kMethodIn; static const QString kParamAIn; static const QString kParamBIn; diff --git a/app/node/math/math/mathbase.cpp b/app/node/math/math/mathbase.cpp index 9218a45657..b1a5bbdc03 100644 --- a/app/node/math/math/mathbase.cpp +++ b/app/node/math/math/mathbase.cpp @@ -28,7 +28,10 @@ namespace olive { -ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, const QString& param_a_in, const QString& param_b_in) const +static const QString param_a = QStringLiteral("param_a"); +static const QString param_b = QStringLiteral("param_b"); + +ShaderCode MathNodeBase::GetShaderCode(const QString &shader_id) { QStringList code_id = shader_id.split('.'); @@ -42,8 +45,8 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, const Q if (pairing == kPairTextureMatrix && op == kOpMultiply) { // Override the operation for this operation since we multiply texture COORDS by the matrix rather than - const QString& tex_in = (type_a == NodeValue::kTexture) ? param_a_in : param_b_in; - const QString& mat_in = (type_a == NodeValue::kTexture) ? param_b_in : param_a_in; + const QString& tex_in = (type_a == NodeValue::kTexture) ? param_a : param_b; + const QString& mat_in = (type_a == NodeValue::kTexture) ? param_b : param_a; // No-op frag shader (can we return QString() instead?) operation = QStringLiteral("texture(%1, ove_texcoord)").arg(tex_in); @@ -88,8 +91,8 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, const Q break; } - operation = operation.arg(GetShaderVariableCall(param_a_in, type_a), - GetShaderVariableCall(param_b_in, type_b)); + operation = operation.arg(GetShaderVariableCall(param_a, type_a), + GetShaderVariableCall(param_b, type_b)); } frag = QStringLiteral("uniform %1 %3;\n" @@ -104,8 +107,8 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, const Q " frag_color = c;\n" "}\n").arg(GetShaderUniformType(type_a), GetShaderUniformType(type_b), - param_a_in, - param_b_in, + param_a, + param_b, operation); return ShaderCode(frag, vert); @@ -152,11 +155,11 @@ NodeValue MathNodeBase::PushVector(olive::NodeValue::Type type, const QVector4D { switch (type) { case NodeValue::kVec2: - return NodeValue(type, QVector2D(vec), this); + return QVector2D(vec); case NodeValue::kVec3: - return NodeValue(type, QVector3D(vec), this); + return QVector3D(vec); case NodeValue::kVec4: - return NodeValue(type, vec, this); + return vec; default: break; } @@ -177,17 +180,17 @@ QString MathNodeBase::GetOperationName(Operation o) return QString(); } -void MathNodeBase::PerformAllOnFloatBuffer(Operation operation, float *a, float b, int start, int end) +void MathNodeBase::PerformAllOnFloatBuffer(Operation operation, const float *input, float *output, float b, size_t start, size_t end) { - for (int j=start;j(context); + + const ValueParams &p = job.value_params(); + const SampleBuffer input = job.Get(QStringLiteral("samples")).toSamples(); + const QString number_in = job.Get(QStringLiteral("number")).toString(); + const Operation operation = static_cast(job.Get(QStringLiteral("operation")).toInt()); + + if (n->IsInputStatic(number_in)) { + auto f = n->GetStandardValue(number_in).toDouble(); + + for (int i=0;iGetInputValue(p.time_transformed(this_sample_range), number_in).toDouble(); + + for (int i=0;i(operation, input.data(i)[j], v); + } + } + } +} + NodeValue MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QString& param_a_in, const NodeValue& val_a, const QString& param_b_in, const NodeValue& val_b, const ValueParams &p) const { switch (pairing) { @@ -234,13 +270,9 @@ NodeValue MathNodeBase::ValueInternal(Operation operation, Pairing pairing, cons { if (val_a.type() == NodeValue::kRational && val_b.type() == NodeValue::kRational && operation != kOpPower) { // Preserve rationals - return NodeValue(NodeValue::kRational, - QVariant::fromValue(PerformAddSubMultDiv(operation, val_a.toRational(), val_b.toRational())), - this); + return PerformAddSubMultDiv(operation, val_a.toRational(), val_b.toRational()); } else { - return NodeValue(NodeValue::kFloat, - PerformAll(operation, RetrieveNumber(val_a), RetrieveNumber(val_b)), - this); + return PerformAll(operation, RetrieveNumber(val_a), RetrieveNumber(val_b)); } } @@ -275,7 +307,7 @@ NodeValue MathNodeBase::ValueInternal(Operation operation, Pairing pairing, cons { QMatrix4x4 mat_a = val_a.toMatrix(); QMatrix4x4 mat_b = val_b.toMatrix(); - return NodeValue(NodeValue::kMatrix, PerformAddSubMult(operation, mat_a, mat_b), this); + return PerformAddSubMult(operation, mat_a, mat_b); } case kPairColorColor: @@ -284,7 +316,7 @@ NodeValue MathNodeBase::ValueInternal(Operation operation, Pairing pairing, cons Color col_b = val_b.toColor(); // Only add and subtract are valid operations - return NodeValue(NodeValue::kColor, QVariant::fromValue(PerformAddSub(operation, col_a, col_b)), this); + return PerformAddSub(operation, col_a, col_b); } @@ -294,7 +326,7 @@ NodeValue MathNodeBase::ValueInternal(Operation operation, Pairing pairing, cons float num = (val_a.type() == NodeValue::kColor) ? val_b.toDouble() : val_a.toDouble(); // Only multiply and divide are valid operations - return NodeValue(NodeValue::kColor, QVariant::fromValue(PerformMult(operation, col, num)), this); + return PerformMult(operation, col, num); } case kPairSampleSample: @@ -303,9 +335,11 @@ NodeValue MathNodeBase::ValueInternal(Operation operation, Pairing pairing, cons job.Insert(QStringLiteral("a"), val_a); job.Insert(QStringLiteral("b"), val_b); - job.Insert(QStringLiteral("pairing"), NodeValue(NodeValue::kInt, int(pairing))); + job.Insert(QStringLiteral("operation"), int(operation)); - return NodeValue(NodeValue::kSamples, QVariant::fromValue(job), this); + job.set_function(MathNodeBase::ProcessSamplesSamples, this); + + return job; } case kPairTextureColor: @@ -314,13 +348,14 @@ NodeValue MathNodeBase::ValueInternal(Operation operation, Pairing pairing, cons case kPairTextureMatrix: { ShaderJob job; + job.set_function(MathNodeBase::GetShaderCode); job.SetShaderID(QStringLiteral("%1.%2.%3.%4").arg(QString::number(operation), QString::number(pairing), QString::number(val_a.type()), QString::number(val_b.type()))); - job.Insert(param_a_in, val_a); - job.Insert(param_b_in, val_b); + job.Insert(param_a, val_a); + job.Insert(param_b, val_b); bool operation_is_noop = false; @@ -348,8 +383,7 @@ NodeValue MathNodeBase::ValueInternal(Operation operation, Pairing pairing, cons operation_is_noop = true; } else { // Replace with adjusted matrix - job.Insert(val_a.type() == NodeValue::kTexture ? param_b_in : param_a_in, - NodeValue(NodeValue::kMatrix, adjusted_matrix, this)); + job.Insert(val_a.type() == NodeValue::kTexture ? param_b_in : param_a_in, adjusted_matrix); } } @@ -358,7 +392,7 @@ NodeValue MathNodeBase::ValueInternal(Operation operation, Pairing pairing, cons return texture_val; } else { // Push shader job - return NodeValue(NodeValue::kTexture, Texture::Job(p.vparams(), job), this); + return Texture::Job(p.vparams(), job); } break; } @@ -379,10 +413,12 @@ NodeValue MathNodeBase::ValueInternal(Operation operation, Pairing pairing, cons SampleJob job(p); job.Insert(QStringLiteral("samples"), sample_val); - job.Insert(QStringLiteral("pairing"), NodeValue(NodeValue::kInt, int(pairing))); - job.Insert(QStringLiteral("number"), NodeValue(NodeValue::kText, val_a.type() == NodeValue::kSamples ? param_b_in : param_a_in)); + job.Insert(QStringLiteral("number"), val_a.type() == NodeValue::kSamples ? param_b_in : param_a_in); + job.Insert(QStringLiteral("operation"), int(operation)); + + job.set_function(MathNodeBase::ProcessSamplesNumber, this); - return NodeValue(NodeValue::kSamples, QVariant::fromValue(job), this); + return job; } break; } @@ -395,8 +431,12 @@ NodeValue MathNodeBase::ValueInternal(Operation operation, Pairing pairing, cons return NodeValue(); } -void MathNodeBase::ProcessSamplesSamplesInternal(const ValueParams &p, Operation operation, const SampleBuffer &samples_a, const SampleBuffer &samples_b, SampleBuffer &mixed_samples) const +void MathNodeBase::ProcessSamplesSamples(const void *context, const SampleJob &job, SampleBuffer &mixed_samples) { + const SampleBuffer samples_a = job.Get(QStringLiteral("a")).toSamples(); + const SampleBuffer samples_b = job.Get(QStringLiteral("b")).toSamples(); + const Operation operation = static_cast(job.Get(QStringLiteral("operation")).toInt()); + size_t max_samples = qMax(samples_a.sample_count(), samples_b.sample_count()); size_t min_samples = qMin(samples_a.sample_count(), samples_b.sample_count()); @@ -420,33 +460,6 @@ void MathNodeBase::ProcessSamplesSamplesInternal(const ValueParams &p, Operation } } -void MathNodeBase::ProcessSamplesNumberInternal(const ValueParams &p, MathNodeBase::Operation operation, const QString &number_in, const olive::SampleBuffer &input, olive::SampleBuffer &output) const -{ - if (IsInputStatic(number_in)) { - auto f = GetStandardValue(number_in).toDouble(); - output = input; - - for (int i=0;i(operation, input.data(i)[j], v); - } - } - } -} - float MathNodeBase::RetrieveNumber(const NodeValue &val) { if (val.type() == NodeValue::kRational) { diff --git a/app/node/math/math/mathbase.h b/app/node/math/math/mathbase.h index 7d48b384c5..6b389a5d31 100644 --- a/app/node/math/math/mathbase.h +++ b/app/node/math/math/mathbase.h @@ -40,6 +40,10 @@ class MathNodeBase : public Node static QString GetOperationName(Operation o); + static ShaderCode GetShaderCode(const QString &shader_id); + static void ProcessSamplesNumber(const void *context, const SampleJob &job, SampleBuffer &mixed_samples); + static void ProcessSamplesSamples(const void *context, const SampleJob &job, SampleBuffer &mixed_samples); + protected: enum Pairing { kPairNone = -1, @@ -102,10 +106,10 @@ class MathNodeBase : public Node template static T PerformAddSubMultDiv(Operation operation, T a, U b); - static void PerformAllOnFloatBuffer(Operation operation, float *a, float b, int start, int end); + static void PerformAllOnFloatBuffer(Operation operation, const float *input, float *output, float b, size_t start, size_t end); #if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM) - static void PerformAllOnFloatBufferSSE(Operation operation, float *a, float b, int start, int end); + static void PerformAllOnFloatBufferSSE(Operation operation, const float *input, float *output, float b, size_t start, size_t end); #endif static QString GetShaderUniformType(const NodeValue::Type& type); @@ -118,15 +122,10 @@ class MathNodeBase : public Node static bool NumberIsNoOp(const Operation& op, const float& number); - ShaderCode GetShaderCodeInternal(const QString &shader_id, const QString ¶m_a_in, const QString ¶m_b_in) const; - NodeValue PushVector(NodeValue::Type type, const QVector4D& vec) const; NodeValue ValueInternal(Operation operation, Pairing pairing, const QString& param_a_in, const NodeValue &val_a, const QString& param_b_in, const NodeValue& val_b, const ValueParams &p) const; - void ProcessSamplesSamplesInternal(const ValueParams &p, Operation operation, const SampleBuffer& a, const SampleBuffer &b, SampleBuffer &output) const; - void ProcessSamplesNumberInternal(const ValueParams &p, Operation operation, const QString& number_in, const SampleBuffer &input, SampleBuffer &output) const; - }; } diff --git a/app/node/math/merge/merge.cpp b/app/node/math/merge/merge.cpp index 03417c2538..08d756fb59 100644 --- a/app/node/math/merge/merge.cpp +++ b/app/node/math/merge/merge.cpp @@ -65,10 +65,8 @@ void MergeNode::Retranslate() SetInputName(kBlendIn, tr("Blend")); } -ShaderCode MergeNode::GetShaderCode(const ShaderRequest &request) const +ShaderCode MergeNode::GetShaderCode(const QString &id) { - Q_UNUSED(request) - return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/alphaover.frag")); } @@ -88,7 +86,7 @@ NodeValue MergeNode::Value(const ValueParams &p) const // We only have a base texture, no need to alpha over return base_val; } else { - return NodeValue(NodeValue::kTexture, base_tex->toJob(CreateJob(p)), this); + return base_tex->toJob(CreateShaderJob(p, GetShaderCode)); } } diff --git a/app/node/math/merge/merge.h b/app/node/math/merge/merge.h index 5cb3555903..5d54ff9248 100644 --- a/app/node/math/merge/merge.h +++ b/app/node/math/merge/merge.h @@ -40,13 +40,14 @@ class MergeNode : public Node virtual void Retranslate() override; - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const override; virtual NodeValue Value(const ValueParams &p) const override; static const QString kBaseIn; static const QString kBlendIn; private: + static ShaderCode GetShaderCode(const QString &id); + NodeInput* base_in_; NodeInput* blend_in_; diff --git a/app/node/math/trigonometry/trigonometry.cpp b/app/node/math/trigonometry/trigonometry.cpp index ec82f479d2..883480ae0b 100644 --- a/app/node/math/trigonometry/trigonometry.cpp +++ b/app/node/math/trigonometry/trigonometry.cpp @@ -111,7 +111,7 @@ NodeValue TrigonometryNode::Value(const ValueParams &p) const break; } - return NodeValue(NodeValue::kFloat, x, this); + return x; } } diff --git a/app/node/node.cpp b/app/node/node.cpp index 30c94991e8..f0c24e2db0 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -866,43 +866,41 @@ int Node::InputArraySize(const QString &id) const NodeValue Node::GetInputValue(const ValueParams &g, const QString &input, int element) const { - if (g.is_cancelled()) { - return NodeValue(); - } - - if (Node *output = GetConnectedOutput(input, element)) { - return GetFakeConnectedValue(g, output, input, element); - } else { + if (!g.is_cancelled()) { + if (Node *output = GetConnectedOutput(input, element)) { + return GetFakeConnectedValue(g, output, input, element); + } else { + NodeValue::Type type = GetInputDataType(input); + TimeRange adjusted_time = InputTimeAdjustment(input, element, g.time(), true); - TimeRange adjusted_time = InputTimeAdjustment(input, element, g.time(), true); + if (element == -1 && InputIsArray(input)) { + NodeValueArray array(InputArraySize(input)); - if (element == -1 && InputIsArray(input)) { - NodeValueArray array(InputArraySize(input)); + for (size_t i = 0; i < array.size(); i++) { + array[i] = NodeValue(GetInputDataType(input), GetValueAtTime(input, adjusted_time.in(), i)); + } - for (size_t i = 0; i < array.size(); i++) { - array[i] = NodeValue(GetInputDataType(input), GetValueAtTime(input, adjusted_time.in(), i), this, false); + return NodeValue(type, array); + } else { + return NodeValue(type, GetValueAtTime(input, adjusted_time.in(), element)); } - - return NodeValue(GetInputDataType(input), array, this, true); - } else { - return NodeValue(GetInputDataType(input), GetValueAtTime(input, adjusted_time.in(), element), this, false); } } + + return NodeValue(); } NodeValue Node::GetFakeConnectedValue(const ValueParams &g, Node *output, const QString &input, int element) const { - if (g.is_cancelled()) { - return NodeValue(); - } - - TimeRange adjusted_time = InputTimeAdjustment(input, element, g.time(), true); + if (!g.is_cancelled()) { + TimeRange adjusted_time = InputTimeAdjustment(input, element, g.time(), true); - while (output) { - if (output->is_enabled()) { - return output->Value(g.time_transformed(adjusted_time).output_edited(this->GetValueHintForInput(input, element).tag())); - } else { - output = output->GetConnectedOutput(output->GetEffectInput()); + while (output) { + if (output->is_enabled()) { + return output->Value(g.time_transformed(adjusted_time).output_edited(this->GetValueHintForInput(input, element).tag())); + } else { + output = output->GetConnectedOutput(output->GetEffectInput()); + } } } @@ -966,12 +964,6 @@ void Node::SetInputFlag(const QString &input, InputFlag f, bool on) } } -NodeValue Node::Value(const ValueParams &p) const -{ - Q_UNUSED(p) - return NodeValue(); -} - void Node::InvalidateCache(const TimeRange &range, const QString &from, int element, InvalidateCacheOptions options) { Q_UNUSED(from) @@ -2059,21 +2051,6 @@ QVector Node::GetImmediateDependencies() const return GetDependenciesInternal(false, false); } -ShaderCode Node::GetShaderCode(const ShaderRequest &request) const -{ - return ShaderCode(QString(), QString()); -} - -void Node::ProcessSamples(const SampleJob &, SampleBuffer &) const -{ -} - -void Node::GenerateFrame(FramePtr frame, const GenerateJob &job) const -{ - Q_UNUSED(frame) - Q_UNUSED(job) -} - bool Node::InputsFrom(Node *n, bool recursively) const { for (auto it=input_connections_.cbegin(); it!=input_connections_.cend(); it++) { diff --git a/app/node/node.h b/app/node/node.h index 6257e58931..4ad365ccce 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -38,10 +38,10 @@ #include "render/audioplaybackcache.h" #include "render/audiowaveformcache.h" #include "render/framehashcache.h" +#include "render/job/colortransformjob.h" #include "render/job/generatejob.h" #include "render/job/samplejob.h" #include "render/job/shaderjob.h" -#include "render/shadercode.h" #include "splitvalue.h" namespace olive { @@ -663,42 +663,6 @@ class Node : public QObject */ QVector GetImmediateDependencies() const; - struct ShaderRequest - { - ShaderRequest(const QString &shader_id) - { - id = shader_id; - } - - ShaderRequest(const QString &shader_id, const QString &shader_stub) - { - id = shader_id; - stub = shader_stub; - } - - QString id; - QString stub; - }; - - /** - * @brief Generate hardware accelerated code for this Node - */ - virtual ShaderCode GetShaderCode(const ShaderRequest &request) const; - - /** - * @brief If Value() pushes a ShaderJob, this is the function that will process them. - */ - virtual void ProcessSamples(const SampleJob &job, SampleBuffer &output) const; - - /** - * @brief If Value() pushes a GenerateJob, override this function for the image to create - * - * @param frame - * - * The destination buffer. It will already be allocated and ready for writing to. - */ - virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const; - /** * @brief Returns whether this node ever receives an input from a particular node instance */ @@ -818,7 +782,7 @@ class Node : public QObject * corresponding output if it's connected to one. If your node doesn't directly deal with time, the default behavior * of the NodeParam objects will handle everything related to it automatically. */ - virtual NodeValue Value(const ValueParams &p) const; + virtual NodeValue Value(const ValueParams &p) const {return NodeValue();} bool HasGizmos() const { @@ -992,14 +956,23 @@ class Node : public QObject return AddDraggableGizmo(refs, behavior); } - template - T CreateJob(const ValueParams &p) const + ShaderJob CreateShaderJob(const ValueParams &p, ShaderJob::GetShaderCodeFunction_t f) const { - T job; - for (const QString &input : inputs()) { - job.Insert(input, GetInputValue(p, input)); - } - return job; + ShaderJob j = CreateJob(p); + j.set_function(f); + return j; + } + + GenerateJob CreateGenerateJob(const ValueParams &p, GenerateJob::GenerateFrameFunction_t f) const + { + GenerateJob j = CreateJob(p); + j.set_function(f); + return j; + } + + ColorTransformJob CreateColorTransformJob(const ValueParams &p) const + { + return CreateJob(p); } protected slots: @@ -1122,6 +1095,16 @@ protected slots: template static void FindInputNodeInternal(const Node* n, QVector& list, int maximum); + template + T CreateJob(const ValueParams &p) const + { + T job; + for (const QString &input : inputs()) { + job.Insert(input, GetInputValue(p, input)); + } + return job; + } + QVector GetDependenciesInternal(bool traverse, bool exclusive_only) const; void ParameterValueChanged(const QString &input, int element, const olive::core::TimeRange &range); diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index 96edae3bb2..2a3b3f4ff9 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -98,6 +98,96 @@ QString Track::Description() const return tr("Node for representing and processing a single array of Blocks sorted by time. Also represents the end of a Sequence."); } +void ProcessAudio(const void *context, const SampleJob &job, SampleBuffer &block_range_buffer) +{ + const QVector *blocks = static_cast *>(context); + + const ValueParams &p = job.value_params(); + const TimeRange &range = job.value_params().time(); + + // All these blocks will need to output to a buffer so we create one here + block_range_buffer.silence(); + + for (auto it = job.GetValues().cbegin(); it != job.GetValues().cend(); it++) { + Block *b = blocks->at(it.key().toInt()); + + TimeRange range_for_block(qMax(b->in(), range.in()), + qMin(b->out(), range.out())); + + qint64 source_offset = 0; + qint64 destination_offset = p.aparams().time_to_samples(range_for_block.in() - range.in()); + qint64 max_dest_sz = p.aparams().time_to_samples(range_for_block.length()); + + // Destination buffer + SampleBuffer samples_from_this_block = it.value().toSamples(); + + if (samples_from_this_block.is_allocated()) { + // If this is a clip, we might have extra speed/reverse information + if (ClipBlock *clip_cast = dynamic_cast(b)) { + double speed_value = clip_cast->speed(); + bool reversed = clip_cast->reverse(); + + if (qIsNull(speed_value)) { + // Just silence, don't think there's any other practical application of 0 speed audio + samples_from_this_block.silence(); + } else if (!qFuzzyCompare(speed_value, 1.0)) { + if (clip_cast->maintain_audio_pitch()) { + AudioProcessor processor; + + if (processor.Open(samples_from_this_block.audio_params(), samples_from_this_block.audio_params(), speed_value)) { + AudioProcessor::Buffer out; + + // FIXME: This is not the best way to do this, the TempoProcessor works best + // when it's given a continuous stream of audio, which is challenging + // in our current "modular" audio system. This should still work reasonably + // well on export (assuming audio is all generated at once on export), but + // users may hear clicks and pops in the audio during preview due to this + // approach. + int r = processor.Convert(samples_from_this_block.to_raw_ptrs().data(), samples_from_this_block.sample_count(), nullptr); + + if (r < 0) { + qCritical() << "Failed to change tempo of audio:" << r; + } else { + processor.Flush(); + + processor.Convert(nullptr, 0, &out); + + if (!out.empty()) { + int nb_samples = out.front().size() * samples_from_this_block.audio_params().bytes_per_sample_per_channel(); + + if (nb_samples) { + SampleBuffer new_samples(samples_from_this_block.audio_params(), nb_samples); + + for (int i=0; i 0) { @@ -131,6 +221,8 @@ NodeValue Track::Value(const ValueParams &p) const SampleJob job(p); + job.set_function(ProcessAudio, &blocks_); + for (int i=start; i<=end; i++) { Block *b = blocks_.at(i); if (b->is_enabled() && (dynamic_cast(b) || dynamic_cast(b))) { @@ -138,7 +230,7 @@ NodeValue Track::Value(const ValueParams &p) const } } - return NodeValue(NodeValue::kSamples, job, this); + return job; } } @@ -583,94 +675,6 @@ int Track::GetArrayIndexFromBlock(Block *block) const return block_array_indexes_.at(blocks_.indexOf(block)); } -void Track::ProcessSamples(const SampleJob &job, SampleBuffer &block_range_buffer) const -{ - const ValueParams &p = job.value_params(); - const TimeRange &range = job.value_params().time(); - - // All these blocks will need to output to a buffer so we create one here - block_range_buffer.silence(); - - for (auto it = job.GetValues().cbegin(); it != job.GetValues().cend(); it++) { - Block *b = blocks_.at(it.key().toInt()); - - TimeRange range_for_block(qMax(b->in(), range.in()), - qMin(b->out(), range.out())); - - qint64 source_offset = 0; - qint64 destination_offset = p.aparams().time_to_samples(range_for_block.in() - range.in()); - qint64 max_dest_sz = p.aparams().time_to_samples(range_for_block.length()); - - // Destination buffer - SampleBuffer samples_from_this_block = it.value().toSamples(); - - if (samples_from_this_block.is_allocated()) { - // If this is a clip, we might have extra speed/reverse information - if (ClipBlock *clip_cast = dynamic_cast(b)) { - double speed_value = clip_cast->speed(); - bool reversed = clip_cast->reverse(); - - if (qIsNull(speed_value)) { - // Just silence, don't think there's any other practical application of 0 speed audio - samples_from_this_block.silence(); - } else if (!qFuzzyCompare(speed_value, 1.0)) { - if (clip_cast->maintain_audio_pitch()) { - AudioProcessor processor; - - if (processor.Open(samples_from_this_block.audio_params(), samples_from_this_block.audio_params(), speed_value)) { - AudioProcessor::Buffer out; - - // FIXME: This is not the best way to do this, the TempoProcessor works best - // when it's given a continuous stream of audio, which is challenging - // in our current "modular" audio system. This should still work reasonably - // well on export (assuming audio is all generated at once on export), but - // users may hear clicks and pops in the audio during preview due to this - // approach. - int r = processor.Convert(samples_from_this_block.to_raw_ptrs().data(), samples_from_this_block.sample_count(), nullptr); - - if (r < 0) { - qCritical() << "Failed to change tempo of audio:" << r; - } else { - processor.Flush(); - - processor.Convert(nullptr, 0, &out); - - if (!out.empty()) { - int nb_samples = out.front().size() * samples_from_this_block.audio_params().bytes_per_sample_per_channel(); - - if (nb_samples) { - SampleBuffer new_samples(samples_from_this_block.audio_params(), nb_samples); - - for (int i=0; icache_path()); - return NodeValue(NodeValue::kSamples, QVariant::fromValue(job), this, ref.ToString()); + return NodeValue(NodeValue::kSamples, job); } break; case Track::kSubtitle: diff --git a/app/node/time/timeformat/timeformat.cpp b/app/node/time/timeformat/timeformat.cpp index a0f6fa49bf..edbf9135c5 100644 --- a/app/node/time/timeformat/timeformat.cpp +++ b/app/node/time/timeformat/timeformat.cpp @@ -72,8 +72,7 @@ NodeValue TimeFormatNode::Value(const ValueParams &p) const bool time_is_local = GetInputValue(p, kLocalTimeInput).toBool(); QDateTime dt = QDateTime::fromMSecsSinceEpoch(ms_since_epoch, time_is_local ? Qt::LocalTime : Qt::UTC); QString format = GetInputValue(p, kFormatInput).toString(); - QString output = dt.toString(format); - return NodeValue(NodeValue::kText, output, this); + return dt.toString(format); } } diff --git a/app/node/value.cpp b/app/node/value.cpp index 3931fe8a15..c6247f5910 100644 --- a/app/node/value.cpp +++ b/app/node/value.cpp @@ -27,6 +27,7 @@ #include #include "common/tohex.h" +#include "render/job/samplejob.h" #include "render/subtitleparams.h" #include "render/videoparams.h" @@ -259,6 +260,11 @@ void NodeValue::ValidateVectorString(QStringList* list, int count) } } +NodeValue::NodeValue(const SampleJob &samples) : + NodeValue(kSamples, samples) +{ +} + QString NodeValue::GetPrettyDataTypeName(Type type) { switch (type) { diff --git a/app/node/value.h b/app/node/value.h index 5240a52395..5b5532703d 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -34,6 +34,7 @@ namespace olive { class Node; class NodeValue; +class SampleJob; using NodeValueArray = std::vector; @@ -196,26 +197,90 @@ class NodeValue kDataTypeCount }; + template + NodeValue(Type type, const T& data) + { + type_ = type; + set_value(data); + array_ = false; + } + + NodeValue(Type type, const NodeValueArray &array) + { + type_ = type; + set_value(array); + array_ = true; + } + NodeValue() : - type_(kNone), - from_(nullptr), - array_(false) + NodeValue(kNone, QVariant()) { } - template - NodeValue(Type type, const T& data, const Node* from = nullptr, bool array = false, const QString& tag = QString()) : - type_(type), - from_(from), - tag_(tag), - array_(array) + NodeValue(const QMatrix4x4& data) : + NodeValue(NodeValue::kMatrix, data) { - set_value(data); } - template - NodeValue(Type type, const T& data, const Node* from, const QString& tag) : - NodeValue(type, data, from, false, tag) + NodeValue(TexturePtr texture) : + NodeValue(NodeValue::kTexture, texture) + { + } + + NodeValue(const SampleJob &samples); + + NodeValue(const SampleBuffer &samples) : + NodeValue(NodeValue::kSamples, samples) + { + } + + NodeValue(const QVector2D &vec) : + NodeValue(NodeValue::kVec2, vec) + { + } + + NodeValue(const QVector3D &vec) : + NodeValue(NodeValue::kVec3, vec) + { + } + + NodeValue(const QVector4D &vec) : + NodeValue(NodeValue::kVec4, vec) + { + } + + NodeValue(const double &d) : + NodeValue(NodeValue::kFloat, d) + { + } + + NodeValue(const int64_t &i) : + NodeValue(NodeValue::kInt, i) + { + } + + NodeValue(const int &i) : + NodeValue(NodeValue::kInt, i) + { + } + + NodeValue(const bool &i) : + NodeValue(NodeValue::kBoolean, i) + { + } + + NodeValue(const Color &i) : + NodeValue(NodeValue::kColor, i) + { + } + + NodeValue(const QString &i) : + NodeValue(NodeValue::kText, i) + { + } + + NodeValue(const rational &i) : + NodeValue(NodeValue::kRational, i) { } @@ -244,21 +309,6 @@ class NodeValue return data_.canConvert(); } - const QString& tag() const - { - return tag_; - } - - void set_tag(const QString& tag) - { - tag_ = tag; - } - - const Node* source() const - { - return from_; - } - bool array() const { return array_; @@ -266,7 +316,7 @@ class NodeValue bool operator==(const NodeValue& rhs) const { - return type_ == rhs.type_ && tag_ == rhs.tag_ && data_ == rhs.data_; + return type_ == rhs.type_ && data_ == rhs.data_; } operator bool() const @@ -354,8 +404,6 @@ class NodeValue private: Type type_; QVariant data_; - const Node* from_; - QString tag_; bool array_; }; diff --git a/app/render/CMakeLists.txt b/app/render/CMakeLists.txt index 7b632d89f4..8b4b282578 100644 --- a/app/render/CMakeLists.txt +++ b/app/render/CMakeLists.txt @@ -57,7 +57,6 @@ set(OLIVE_SOURCES render/renderprocessor.h render/renderticket.cpp render/renderticket.h - render/shadercode.h render/subtitleparams.cpp render/subtitleparams.h render/texture.cpp diff --git a/app/render/job/acceleratedjob.h b/app/render/job/acceleratedjob.h index cc1a221ca7..d5667e5470 100644 --- a/app/render/job/acceleratedjob.h +++ b/app/render/job/acceleratedjob.h @@ -21,7 +21,7 @@ #ifndef ACCELERATEDJOB_H #define ACCELERATEDJOB_H -#include "node/param.h" +#include "node/value.h" namespace olive { diff --git a/app/render/job/colortransformjob.h b/app/render/job/colortransformjob.h index 15951b22ef..2b55cfb8f8 100644 --- a/app/render/job/colortransformjob.h +++ b/app/render/job/colortransformjob.h @@ -28,6 +28,7 @@ #include "render/alphaassoc.h" #include "render/colorprocessor.h" #include "render/texture.h" +#include "shaderjob.h" namespace olive { @@ -39,7 +40,7 @@ class ColorTransformJob : public AcceleratedJob ColorTransformJob() { processor_ = nullptr; - custom_shader_src_ = nullptr; + custom_shader_ = nullptr; input_alpha_association_ = kAlphaNone; clear_destination_ = true; } @@ -75,13 +76,8 @@ class ColorTransformJob : public AcceleratedJob const AlphaAssociated &GetInputAlphaAssociation() const { return input_alpha_association_; } void SetInputAlphaAssociation(const AlphaAssociated &e) { input_alpha_association_ = e; } - const Node *CustomShaderSource() const { return custom_shader_src_; } - const QString &CustomShaderID() const { return custom_shader_id_; } - void SetNeedsCustomShader(const Node *node, const QString &id = QString()) - { - custom_shader_src_ = node; - custom_shader_id_ = id; - } + ShaderJob::GetShaderCodeFunction_t GetCustomShaderFunction() const { return custom_shader_; } + void SetCustomShaderFunction(ShaderJob::GetShaderCodeFunction_t shader) { custom_shader_ = shader; } bool IsClearDestinationEnabled() const { return clear_destination_; } void SetClearDestinationEnabled(bool e) { clear_destination_ = e; } @@ -101,8 +97,7 @@ class ColorTransformJob : public AcceleratedJob NodeValue input_texture_; - const Node *custom_shader_src_; - QString custom_shader_id_; + ShaderJob::GetShaderCodeFunction_t custom_shader_; AlphaAssociated input_alpha_association_; diff --git a/app/render/job/generatejob.h b/app/render/job/generatejob.h index ed13998efa..acba2d9697 100644 --- a/app/render/job/generatejob.h +++ b/app/render/job/generatejob.h @@ -29,13 +29,34 @@ namespace olive { class GenerateJob : public AcceleratedJob { public: - GenerateJob() = default; + typedef void (*GenerateFrameFunction_t)(FramePtr frame, const GenerateJob &job); + + GenerateJob() + { + function_ = nullptr; + } + GenerateJob(const NodeValueRow &row) : GenerateJob() { Insert(row); } + void do_function(FramePtr frame) const + { + if (function_) { + function_(frame, *this); + } + } + + void set_function(GenerateFrameFunction_t function) + { + function_ = function; + } + +private: + GenerateFrameFunction_t function_; + }; } diff --git a/app/render/job/samplejob.h b/app/render/job/samplejob.h index 2f3f4ca270..b39a155bc4 100644 --- a/app/render/job/samplejob.h +++ b/app/render/job/samplejob.h @@ -29,15 +29,21 @@ namespace olive { class SampleJob : public AcceleratedJob { public: + typedef void (*ProcessSamplesCallback_t)(const void *context, const SampleJob &job, SampleBuffer &output); + SampleJob() { sample_count_ = 0; + function_ = nullptr; + function_context_ = nullptr; } SampleJob(const ValueParams &p, size_t sample_count) { value_params_ = p; sample_count_ = sample_count; + function_ = nullptr; + function_context_ = nullptr; } SampleJob(const ValueParams &p) : @@ -49,10 +55,26 @@ class SampleJob : public AcceleratedJob const AudioParams &audio_params() const { return value_params_.aparams(); } size_t sample_count() const { return sample_count_; } + void do_function(SampleBuffer &out) const + { + if (function_) { + function_(function_context_, *this, out); + } + } + + void set_function(ProcessSamplesCallback_t f, const void *context) + { + function_ = f; + function_context_ = context; + } + private: ValueParams value_params_; size_t sample_count_; + ProcessSamplesCallback_t function_; + const void *function_context_; + }; } diff --git a/app/render/job/shaderjob.h b/app/render/job/shaderjob.h index ace65cfe6a..9bf27e04d4 100644 --- a/app/render/job/shaderjob.h +++ b/app/render/job/shaderjob.h @@ -25,17 +25,42 @@ #include #include "acceleratedjob.h" +#include "common/filefunctions.h" #include "render/texture.h" namespace olive { +class ShaderCode { +public: + ShaderCode(const QString& frag_code = QString(), const QString& vert_code = QString()) : + frag_code_(frag_code), + vert_code_(vert_code) + { + } + + const QString& frag_code() const { return frag_code_; } + void set_frag_code(const QString &f) { frag_code_ = f; } + + const QString& vert_code() const { return vert_code_; } + void set_vert_code(const QString &v) { vert_code_ = v; } + +private: + QString frag_code_; + + QString vert_code_; + +}; + class ShaderJob : public AcceleratedJob { public: + typedef ShaderCode (*GetShaderCodeFunction_t)(const QString &id); + ShaderJob() { iterations_ = 1; iterative_input_ = nullptr; + function_ = nullptr; } ShaderJob(const NodeValueRow &row) : @@ -44,11 +69,6 @@ class ShaderJob : public AcceleratedJob Insert(row); } - void SetIterations(int iterations, const NodeInput& iterative_input) - { - SetIterations(iterations, iterative_input.input()); - } - void SetIterations(int iterations, const QString& iterative_input) { iterations_ = iterations; @@ -75,11 +95,6 @@ class ShaderJob : public AcceleratedJob return interpolation_; } - void SetInterpolation(const NodeInput& input, Texture::Interpolation interp) - { - interpolation_.insert(input.input(), interp); - } - void SetInterpolation(const QString& id, Texture::Interpolation interp) { interpolation_.insert(id, interp); @@ -95,6 +110,13 @@ class ShaderJob : public AcceleratedJob return vertex_overrides_; } + GetShaderCodeFunction_t function() const { return function_; } + void set_function(GetShaderCodeFunction_t f) { function_ = f; } + ShaderCode do_function() const + { + return function_ ? function_(GetShaderID()) : ShaderCode(); + } + private: int iterations_; @@ -104,6 +126,8 @@ class ShaderJob : public AcceleratedJob QVector vertex_overrides_; + GetShaderCodeFunction_t function_; + }; } diff --git a/app/render/renderer.cpp b/app/render/renderer.cpp index 9e3c1fd9ca..ebe7550e98 100644 --- a/app/render/renderer.cpp +++ b/app/render/renderer.cpp @@ -189,9 +189,9 @@ bool Renderer::GetColorContext(const ColorTransformJob &color_job, Renderer::Col color_job.GetColorProcessor()->GetProcessor()->getDefaultGPUProcessor()->extractGpuShaderInfo(shader_desc); ShaderCode code; - if (const Node *shader_src = color_job.CustomShaderSource()) { + if (ShaderJob::GetShaderCodeFunction_t cf = color_job.GetCustomShaderFunction()) { // Use shader code from associated node - code = shader_src->GetShaderCode({color_job.CustomShaderID(), shader_desc->getShaderText()}); + code = cf(shader_desc->getShaderText()); } else { // Generate shader code using OCIO stub and our auto-generated name code = FileFunctions::ReadFileAsString(QStringLiteral(":shaders/colormanage.frag")); diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index d55f050704..d974bce75e 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -114,8 +114,8 @@ FramePtr RenderProcessor::GenerateFrame(TexturePtr texture, const rational& time // No color transform, just blit ShaderJob job; - job.Insert(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture))); - job.Insert(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, matrix)); + job.Insert(QStringLiteral("ove_maintex"), texture); + job.Insert(QStringLiteral("ove_mvpmat"), matrix); render_ctx_->BlitToTexture(render_ctx_->GetDefaultShader(), job, blit_tex.get()); @@ -412,13 +412,13 @@ void RenderProcessor::ProcessAudioFootage(SampleBuffer &destination, const Foota } } -void RenderProcessor::ProcessShader(TexturePtr destination, const Node *node, const ShaderJob *job) +void RenderProcessor::ProcessShader(TexturePtr destination, const ShaderJob *job) { if (!render_ctx_) { return; } - QString full_shader_id = QStringLiteral("%1:%2").arg(node->id(), job->GetShaderID()); + QString full_shader_id = QStringLiteral("%1:%2").arg(QString::number(reinterpret_cast(job->function())), job->GetShaderID()); QMutexLocker locker(shader_cache_->mutex()); @@ -426,7 +426,7 @@ void RenderProcessor::ProcessShader(TexturePtr destination, const Node *node, co if (shader.isNull()) { // Since we have shader code, compile it now - shader = render_ctx_->CreateNativeShader(node->GetShaderCode(job->GetShaderID())); + shader = render_ctx_->CreateNativeShader(job->do_function()); if (shader.isNull()) { // Couldn't find or build the shader required @@ -442,12 +442,12 @@ void RenderProcessor::ProcessShader(TexturePtr destination, const Node *node, co render_ctx_->BlitToTexture(shader, *job, destination.get()); } -void RenderProcessor::ProcessSamples(SampleBuffer &destination, const Node *node, const SampleJob &job) +void RenderProcessor::ProcessSamples(SampleBuffer &destination, const SampleJob &job) { - node->ProcessSamples(job, destination); + job.do_function(destination); } -void RenderProcessor::ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob *job) +void RenderProcessor::ProcessColorTransform(TexturePtr destination, const ColorTransformJob *job) { if (!render_ctx_) { return; @@ -456,7 +456,7 @@ void RenderProcessor::ProcessColorTransform(TexturePtr destination, const Node * render_ctx_->BlitColorManaged(*job, destination.get()); } -void RenderProcessor::ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob *job) +void RenderProcessor::ProcessFrameGeneration(TexturePtr destination, const GenerateJob *job) { if (!render_ctx_) { return; @@ -467,7 +467,7 @@ void RenderProcessor::ProcessFrameGeneration(TexturePtr destination, const Node frame->set_video_params(destination->params()); frame->allocate(); - node->GenerateFrame(frame, *job); + job->do_function(frame); destination->Upload(frame->data(), frame->linesize_pixels()); } @@ -567,7 +567,7 @@ void RenderProcessor::ResolveJobs(NodeValue &val) ResolveJobs(v); ctj->SetInputTexture(v); - ProcessColorTransform(dest, val.source(), ctj); + ProcessColorTransform(dest, ctj); val.set_value(dest); @@ -577,7 +577,7 @@ void RenderProcessor::ResolveJobs(NodeValue &val) TexturePtr tex = CreateTexture(tex_params); - ProcessShader(tex, val.source(), sj); + ProcessShader(tex, sj); val.set_value(tex); @@ -587,7 +587,7 @@ void RenderProcessor::ResolveJobs(NodeValue &val) TexturePtr tex = CreateTexture(tex_params); - ProcessFrameGeneration(tex, val.source(), gj); + ProcessFrameGeneration(tex, gj); // Convert to reference space const QString &colorspace = tex_params.colorspace(); @@ -644,7 +644,7 @@ void RenderProcessor::ResolveJobs(NodeValue &val) } SampleBuffer output_buffer = CreateSampleBuffer(job.audio_params(), job.sample_count()); - ProcessSamples(output_buffer, val.source(), job); + ProcessSamples(output_buffer, job); val.set_value(QVariant::fromValue(output_buffer)); } else if (val.canConvert()) { diff --git a/app/render/renderprocessor.h b/app/render/renderprocessor.h index ed9d9d8322..8c1bf30d29 100644 --- a/app/render/renderprocessor.h +++ b/app/render/renderprocessor.h @@ -50,13 +50,13 @@ class RenderProcessor void ProcessAudioFootage(SampleBuffer &destination, const FootageJob *stream, const TimeRange &input_time); - void ProcessShader(TexturePtr destination, const Node *node, const ShaderJob *job); + void ProcessShader(TexturePtr destination, const ShaderJob *job); - void ProcessSamples(SampleBuffer &destination, const Node *node, const SampleJob &job); + void ProcessSamples(SampleBuffer &destination, const SampleJob &job); - void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob *job); + void ProcessColorTransform(TexturePtr destination, const ColorTransformJob *job); - void ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob *job); + void ProcessFrameGeneration(TexturePtr destination, const GenerateJob *job); TexturePtr ProcessVideoCacheJob(const CacheJob *val); diff --git a/app/render/shadercode.h b/app/render/shadercode.h deleted file mode 100644 index 238d512435..0000000000 --- a/app/render/shadercode.h +++ /dev/null @@ -1,51 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2022 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#ifndef SHADERCODE_H -#define SHADERCODE_H - -#include "common/filefunctions.h" - -namespace olive { - -class ShaderCode { -public: - ShaderCode(const QString& frag_code = QString(), const QString& vert_code = QString()) : - frag_code_(frag_code), - vert_code_(vert_code) - { - } - - const QString& frag_code() const { return frag_code_; } - void set_frag_code(const QString &f) { frag_code_ = f; } - - const QString& vert_code() const { return vert_code_; } - void set_vert_code(const QString &v) { vert_code_ = v; } - -private: - QString frag_code_; - - QString vert_code_; - -}; - -} - -#endif // SHADERCODE_H diff --git a/app/widget/scope/histogram/histogram.cpp b/app/widget/scope/histogram/histogram.cpp index da4136a5fe..0cda42142e 100644 --- a/app/widget/scope/histogram/histogram.cpp +++ b/app/widget/scope/histogram/histogram.cpp @@ -70,7 +70,7 @@ void HistogramScope::DrawScope(TexturePtr managed_tex, QVariant pipeline) ShaderJob shader_job; - shader_job.Insert(QStringLiteral("viewport"), NodeValue(NodeValue::kVec2, QVector2D(width(), height()))); + shader_job.Insert(QStringLiteral("viewport"), QVector2D(width(), height())); shader_job.Insert(QStringLiteral("histogram_scale"), NodeValue(NodeValue::kFloat, histogram_scale)); shader_job.Insert(QStringLiteral("histogram_power"), NodeValue(NodeValue::kFloat, histogram_power)); @@ -83,11 +83,11 @@ void HistogramScope::DrawScope(TexturePtr managed_tex, QVariant pipeline) } // Draw managed texture to a sums texture - shader_job.Insert(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(managed_tex))); + shader_job.Insert(QStringLiteral("ove_maintex"), managed_tex); renderer()->BlitToTexture(pipeline, shader_job, texture_row_sums_.get()); // Draw sums into a histogram - shader_job.Insert(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture_row_sums_))); + shader_job.Insert(QStringLiteral("ove_maintex"), texture_row_sums_); renderer()->Blit(pipeline_secondary_, shader_job, texture_row_sums_->params()); // Draw line overlays From 9178612aafd9cc1ab7698c4cad2135a53089c6a1 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 29 Mar 2023 15:59:22 -0700 Subject: [PATCH 18/71] reinstated NodeValueTree --- app/node/node.cpp | 44 +++++++++++++- app/node/node.h | 34 ++++++----- app/node/output/viewer/viewer.cpp | 35 ++++++++++- app/node/output/viewer/viewer.h | 3 + app/task/precache/precachetask.cpp | 2 +- .../nodeparamviewconnectedlabel.cpp | 2 +- app/widget/nodevaluetree/nodevaluetree.cpp | 59 +++++++++++-------- app/widget/nodevaluetree/nodevaluetree.h | 6 +- app/widget/timelinewidget/tool/import.cpp | 4 +- 9 files changed, 137 insertions(+), 52 deletions(-) diff --git a/app/node/node.cpp b/app/node/node.cpp index f0c24e2db0..9be2024566 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -49,6 +49,7 @@ Node::Node() : caches_enabled_(true) { AddInput(kEnabledInput, NodeValue::kBoolean, true); + AddOutput(QString()); video_cache_ = new FrameHashCache(this); thumbnail_cache_ = new ThumbnailCache(this); @@ -101,6 +102,7 @@ QString Node::Description() const void Node::Retranslate() { SetInputName(kEnabledInput, tr("Enabled")); + SetOutputName(QString(), tr("Default")); } QVariant Node::data(const DataType &d) const @@ -1713,8 +1715,8 @@ void Node::InsertInput(const QString &id, NodeValue::Type type, const QVariant & return; } - if (HasParamWithID(id)) { - qWarning() << "Failed to add input to node" << this->id() << "- param with ID" << id << "already exists"; + if (HasInputWithID(id)) { + qWarning() << "Failed to add input to node" << this->id() << "- ID" << id << "already exists"; return; } @@ -1750,6 +1752,34 @@ void Node::RemoveInput(const QString &id) emit InputRemoved(id); } +void Node::AddOutput(const QString &id) +{ + for (auto it = outputs_.constBegin(); it != outputs_.constEnd(); it++) { + if (it->id == id) { + qWarning() << "Failed to add output to node" << this->id() << "- ID" << id << "already exists"; + return; + } + } + + Output o; + o.id = id; + outputs_.append(o); + + emit OutputAdded(id); +} + +void Node::RemoveOutput(const QString &id) +{ + for (auto it = outputs_.begin(); it != outputs_.end(); it++) { + if (it->id == id) { + outputs_.erase(it); + break; + } + } + + emit OutputRemoved(id); +} + void Node::ReportInvalidInput(const char *attempted_action, const QString& id, int element) const { qWarning() << "Failed to" << attempted_action << "parameter" << id << "element" << element @@ -1846,6 +1876,16 @@ void Node::SetInputName(const QString &id, const QString &name) } } +void Node::SetOutputName(const QString &id, const QString &name) +{ + for (auto it = outputs_.begin(); it != outputs_.end(); it++) { + if (it->id == id) { + it->name = name; + break; + } + } +} + const QString &Node::GetLabel() const { return label_; diff --git a/app/node/node.h b/app/node/node.h index 4ad365ccce..c984343253 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -202,8 +202,14 @@ class Node : public QObject const QVector& inputs() const { return input_ids_; } + struct Output + { + QString id; + QString name; + }; + const QVector& outputs() const { return outputs_; } + bool HasInputWithID(const QString& id) const { return input_ids_.contains(id); } - bool HasParamWithID(const QString& id) const { return HasInputWithID(id); } // Node caches FrameHashCache* video_frame_cache() const { return video_cache_; } @@ -333,6 +339,7 @@ class Node : public QObject virtual QString GetInputName(const QString& id) const; void SetInputName(const QString& id, const QString& name); + void SetOutputName(const QString &id, const QString &name); bool IsInputHidden(const QString& input) const; bool IsInputConnectable(const QString& input) const; @@ -574,21 +581,7 @@ class Node : public QObject class ValueHint { public: - explicit ValueHint(const QVector &types = QVector(), int index = -1, const QString &tag = QString()) : - tag_(tag) - { - } - - explicit ValueHint(const QVector &types, const QString &tag) : - tag_(tag) - { - } - - explicit ValueHint(int index) - { - } - - explicit ValueHint(const QString &tag) : + ValueHint(const QString &tag = QString()) : tag_(tag) { } @@ -886,6 +879,10 @@ class Node : public QObject void RemoveInput(const QString& id); + void AddOutput(const QString &id); + + void RemoveOutput(const QString &id); + void SetComboBoxStrings(const QString& id, const QStringList& strings) { SetInputProperty(id, QStringLiteral("combo_str"), strings); @@ -1022,6 +1019,10 @@ protected slots: void InputRemoved(const QString& id); + void OutputAdded(const QString &id); + + void OutputRemoved(const QString &id); + void InputNameChanged(const QString& id, const QString& name); void InputDataTypeChanged(const QString& id, NodeValue::Type type); @@ -1142,6 +1143,7 @@ protected slots: QVector input_ids_; QVector input_data_; + QVector outputs_; QMap standard_immediates_; diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index caf5c91495..a0c9c1328a 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -64,6 +64,8 @@ ViewerOutput::ViewerOutput(bool create_buffer_inputs, bool create_default_stream workarea_ = new TimelineWorkArea(this); markers_ = new TimelineMarkerList(this); + + connect(this, &ViewerOutput::InputArraySizeChanged, this, &ViewerOutput::ArraySizeChanged); } QString ViewerOutput::Name() const @@ -304,6 +306,20 @@ void ViewerOutput::Retranslate() if (HasInputWithID(kSamplesInput)) { SetInputName(kSamplesInput, tr("Samples")); } + + { + int vsc = GetVideoStreamCount(); + for (int i = 0; i < vsc; i++) { + SetOutputName(Track::Reference(Track::kVideo, i).ToString(), tr("Video %1").arg(i + 1)); + } + } + + { + int asc = GetAudioStreamCount(); + for (int i = 0; i < asc; i++) { + SetOutputName(Track::Reference(Track::kAudio, i).ToString(), tr("Audio %1").arg(i + 1)); + } + } } void ViewerOutput::VerifyLength() @@ -410,9 +426,7 @@ NodeValue ViewerOutput::Value(const ValueParams &p) const Track::Reference ref = Track::Reference::FromString(p.output()); if (ref.type() == Track::kVideo && HasInputWithID(kTextureInput)) { - NodeValue v = GetInputValue(p, kTextureInput); - qDebug() << "what the fuck" << v.data(); - return v; + return GetInputValue(p, kTextureInput); } if (ref.type() == Track::kAudio && HasInputWithID(kSamplesInput)) { return GetInputValue(p, kSamplesInput); @@ -582,6 +596,21 @@ int ViewerOutput::SetStream(Track::Type type, const QVariant &value, int index_i return index; } +void ViewerOutput::ArraySizeChanged(const QString &id, int old_size, int new_size) +{ + if (id == kVideoParamsInput || id == kAudioParamsInput) { + Track::Type type = (id == kVideoParamsInput) ? Track::kVideo : Track::kAudio; + + for (int i = old_size; i < new_size; i++) { + AddOutput(Track::Reference(type, i).ToString()); + } + + for (int i = new_size; i < old_size; i++) { + RemoveOutput(Track::Reference(type, i).ToString()); + } + } +} + QVector ViewerOutput::GetEnabledVideoStreams() const { QVector streams; diff --git a/app/node/output/viewer/viewer.h b/app/node/output/viewer/viewer.h index e6f294f4ba..2469a9e285 100644 --- a/app/node/output/viewer/viewer.h +++ b/app/node/output/viewer/viewer.h @@ -263,6 +263,9 @@ public slots: rational playhead_; +private slots: + void ArraySizeChanged(const QString &id, int old_size, int new_size); + }; } diff --git a/app/task/precache/precachetask.cpp b/app/task/precache/precachetask.cpp index 8357b5a7d0..e8e4d134a2 100644 --- a/app/task/precache/precachetask.cpp +++ b/app/task/precache/precachetask.cpp @@ -48,7 +48,7 @@ PreCacheTask::PreCacheTask(Footage *footage, int index, Sequence* sequence) Node::CopyInputs(footage, footage_, false); Node::ConnectEdge(footage_, NodeInput(viewer(), ViewerOutput::kTextureInput)); - viewer()->SetValueHintForInput(ViewerOutput::kTextureInput, Node::ValueHint({NodeValue::kTexture}, Track::Reference(Track::kVideo, index).ToString())); + viewer()->SetValueHintForInput(ViewerOutput::kTextureInput, Track::Reference(Track::kVideo, index).ToString()); SetTitle(tr("Pre-caching %1:%2").arg(footage_->filename(), QString::number(index))); } diff --git a/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp b/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp index 370164f5f9..1ce061c87f 100644 --- a/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp +++ b/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp @@ -166,7 +166,7 @@ void NodeParamViewConnectedLabel::UpdateLabel() void NodeParamViewConnectedLabel::UpdateValueTree() { if (value_tree_ && viewer_ && value_tree_->isVisible()) { - value_tree_->SetNode(input_, viewer_->GetPlayhead()); + value_tree_->SetNode(input_); } } diff --git a/app/widget/nodevaluetree/nodevaluetree.cpp b/app/widget/nodevaluetree/nodevaluetree.cpp index 5e4c13405a..d0989944e7 100644 --- a/app/widget/nodevaluetree/nodevaluetree.cpp +++ b/app/widget/nodevaluetree/nodevaluetree.cpp @@ -2,6 +2,9 @@ #include +#include "core.h" +#include "node/nodeundo.h" + namespace olive { #define super QTreeWidget @@ -10,7 +13,7 @@ NodeValueTree::NodeValueTree(QWidget *parent) : super(parent) { setColumnWidth(0, 0); - setColumnCount(4); + setColumnCount(2); QSizePolicy p = sizePolicy(); p.setHorizontalStretch(1); @@ -22,38 +25,37 @@ NodeValueTree::NodeValueTree(QWidget *parent) : Retranslate(); } -void NodeValueTree::SetNode(const NodeInput &input, const rational &time) +void NodeValueTree::SetNode(const NodeInput &input) { - /* clear(); - Node *connected_node = input.GetConnectedOutput(); + input_ = input; - ValueParams p(VideoParams(), AudioParams(), time, LoopMode::kLoopModeOff); - NodeValue v = connected_node->Value(p); + if (Node *connected_node = input.GetConnectedOutput()) { + connected_node->Retranslate(); - int index = traverser.GenerateRowValueElementIndex(input.node(), input.input(), input.element(), &table); + const QVector &outputs = connected_node->outputs(); - for (int i=0; iGetValueHintForInput(input.input(), input.element()); - Node::ValueHint hint({value.type()}, table.Count()-1-i, value.tag()); + for (const Node::Output &o : outputs) { + // Add default output + QTreeWidgetItem *item = new QTreeWidgetItem(this); - QRadioButton *radio = new QRadioButton(this); - radio->setProperty("input", QVariant::fromValue(input)); - radio->setProperty("hint", QVariant::fromValue(hint)); - if (i == index) { - radio->setChecked(true); - } - connect(radio, &QRadioButton::clicked, this, &NodeValueTree::RadioButtonChecked); + item->setText(1, o.name); + + QRadioButton *radio = new QRadioButton(this); + radio->setProperty("input", QVariant::fromValue(input)); + radio->setProperty("output", o.id); + connect(radio, &QRadioButton::clicked, this, &NodeValueTree::RadioButtonChecked); + + if (vh.tag() == o.id) { + radio->setChecked(true); + } - setItemWidget(item, 0, radio); - item->setText(1, NodeValue::GetPrettyDataTypeName(value.type())); - item->setText(2, NodeValue::ValueToString(value, false)); - item->setText(3, value.source()->GetLabelAndName()); + setItemWidget(item, 0, radio); + } } - */ } void NodeValueTree::changeEvent(QEvent *event) @@ -67,18 +69,23 @@ void NodeValueTree::changeEvent(QEvent *event) void NodeValueTree::Retranslate() { - setHeaderLabels({QString(), tr("Type"), tr("Value"), tr("Source")}); + setHeaderLabels({QString(), tr("Output")}); } void NodeValueTree::RadioButtonChecked(bool e) { if (e) { QRadioButton *btn = static_cast(sender()); - Node::ValueHint hint = btn->property("hint").value(); + Node::ValueHint hint = btn->property("output").toString(); NodeInput input = btn->property("input").value(); - input.node()->SetValueHintForInput(input.input(), hint, input.element()); + Core::instance()->undo_stack()->push(new NodeSetValueHintCommand(input, hint), tr("Switched Connected Output Parameter")); } } +void NodeValueTree::Update() +{ + SetNode(input_); +} + } diff --git a/app/widget/nodevaluetree/nodevaluetree.h b/app/widget/nodevaluetree/nodevaluetree.h index 94f71ebf1f..58275d4351 100644 --- a/app/widget/nodevaluetree/nodevaluetree.h +++ b/app/widget/nodevaluetree/nodevaluetree.h @@ -14,7 +14,7 @@ class NodeValueTree : public QTreeWidget public: NodeValueTree(QWidget *parent = nullptr); - void SetNode(const NodeInput &input, const rational &time); + void SetNode(const NodeInput &input); protected: virtual void changeEvent(QEvent *event) override; @@ -22,9 +22,13 @@ class NodeValueTree : public QTreeWidget private: void Retranslate(); + NodeInput input_; + private slots: void RadioButtonChecked(bool e); + void Update(); + }; } diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index 3e1b454fd7..5f0921c21c 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -454,7 +454,7 @@ void ImportTool::DropGhosts(bool insert, MultiUndoCommand *parent_command) TransformDistortNode* transform = new TransformDistortNode(); command->add_child(new NodeAddCommand(dst_graph, transform)); - command->add_child(new NodeSetValueHintCommand(transform, TransformDistortNode::kTextureInput, -1, Node::ValueHint({NodeValue::kTexture}, footage_stream.output))); + command->add_child(new NodeSetValueHintCommand(transform, TransformDistortNode::kTextureInput, -1, footage_stream.output)); command->add_child(new NodeEdgeAddCommand(footage_stream.footage, NodeInput(transform, TransformDistortNode::kTextureInput))); command->add_child(new NodeEdgeAddCommand(transform, NodeInput(clip, ClipBlock::kBufferIn))); @@ -466,7 +466,7 @@ void ImportTool::DropGhosts(bool insert, MultiUndoCommand *parent_command) VolumeNode* volume_node = new VolumeNode(); command->add_child(new NodeAddCommand(dst_graph, volume_node)); - command->add_child(new NodeSetValueHintCommand(volume_node, VolumeNode::kSamplesInput, -1, Node::ValueHint({NodeValue::kSamples}, footage_stream.output))); + command->add_child(new NodeSetValueHintCommand(volume_node, VolumeNode::kSamplesInput, -1, footage_stream.output)); command->add_child(new NodeEdgeAddCommand(footage_stream.footage, NodeInput(volume_node, VolumeNode::kSamplesInput))); command->add_child(new NodeEdgeAddCommand(volume_node, NodeInput(clip, ClipBlock::kBufferIn))); From bb27a30bce063113103a6b7ab856665e7bf75a6b Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Mon, 3 Apr 2023 16:12:53 -0700 Subject: [PATCH 19/71] initial new value type implementation --- CMakeLists.txt | 1 + app/codec/ffmpeg/ffmpegdecoder.cpp | 26 +- app/config/config.cpp | 231 +++--- app/config/config.h | 18 +- app/core.cpp | 6 +- .../preferences/tabs/preferencesaudiotab.cpp | 10 +- .../preferences/tabs/preferencesdisktab.cpp | 4 +- .../tabs/preferencesgeneraltab.cpp | 12 +- .../projectproperties/projectproperties.cpp | 1 + app/dialog/sequence/sequence.cpp | 6 +- app/node/CMakeLists.txt | 1 - app/node/audio/pan/pan.cpp | 8 +- app/node/audio/pan/pan.h | 2 +- app/node/audio/volume/volume.cpp | 6 +- app/node/audio/volume/volume.h | 2 +- app/node/block/block.cpp | 7 +- app/node/block/clip/clip.cpp | 19 +- app/node/block/clip/clip.h | 2 +- app/node/block/subtitle/subtitle.cpp | 2 +- .../diptocolor/diptocolortransition.cpp | 2 +- app/node/block/transition/transition.cpp | 32 +- app/node/block/transition/transition.h | 2 +- .../displaytransform/displaytransform.cpp | 6 +- app/node/color/ociobase/ociobase.cpp | 4 +- app/node/color/ociobase/ociobase.h | 2 +- .../ociogradingtransformlinear.cpp | 22 +- .../ociogradingtransformlinear.h | 2 +- .../cornerpin/cornerpindistortnode.cpp | 20 +- .../distort/cornerpin/cornerpindistortnode.h | 2 +- app/node/distort/crop/cropdistortnode.cpp | 12 +- app/node/distort/crop/cropdistortnode.h | 2 +- app/node/distort/flip/flipdistortnode.cpp | 10 +- app/node/distort/flip/flipdistortnode.h | 2 +- app/node/distort/mask/mask.cpp | 10 +- app/node/distort/mask/mask.h | 2 +- app/node/distort/ripple/rippledistortnode.cpp | 24 +- app/node/distort/ripple/rippledistortnode.h | 2 +- app/node/distort/swirl/swirldistortnode.cpp | 16 +- app/node/distort/swirl/swirldistortnode.h | 2 +- app/node/distort/tile/tiledistortnode.cpp | 20 +- app/node/distort/tile/tiledistortnode.h | 2 +- .../transform/transformdistortnode.cpp | 26 +- .../distort/transform/transformdistortnode.h | 2 +- app/node/distort/wave/wavedistortnode.cpp | 14 +- app/node/distort/wave/wavedistortnode.h | 2 +- app/node/effect/opacity/opacityeffect.cpp | 10 +- app/node/effect/opacity/opacityeffect.h | 2 +- app/node/filter/blur/blur.cpp | 26 +- app/node/filter/blur/blur.h | 2 +- .../filter/dropshadow/dropshadowfilter.cpp | 20 +- app/node/filter/dropshadow/dropshadowfilter.h | 2 +- app/node/filter/mosaic/mosaicfilternode.cpp | 10 +- app/node/filter/mosaic/mosaicfilternode.h | 2 +- app/node/filter/stroke/stroke.cpp | 14 +- app/node/filter/stroke/stroke.h | 2 +- app/node/generator/matrix/matrix.cpp | 12 +- app/node/generator/matrix/matrix.h | 2 +- app/node/generator/noise/noise.cpp | 8 +- app/node/generator/noise/noise.h | 2 +- app/node/generator/polygon/polygon.cpp | 23 +- app/node/generator/polygon/polygon.h | 4 +- .../generator/shape/generatorwithmerge.cpp | 6 +- app/node/generator/shape/generatorwithmerge.h | 2 +- app/node/generator/shape/shapenode.cpp | 6 +- app/node/generator/shape/shapenode.h | 2 +- app/node/generator/shape/shapenodebase.cpp | 16 +- app/node/generator/solid/solid.cpp | 4 +- app/node/generator/solid/solid.h | 2 +- app/node/generator/text/textv1.cpp | 16 +- app/node/generator/text/textv1.h | 2 +- app/node/generator/text/textv2.cpp | 16 +- app/node/generator/text/textv2.h | 2 +- app/node/generator/text/textv3.cpp | 14 +- app/node/generator/text/textv3.h | 2 +- app/node/group/group.cpp | 14 +- app/node/input/multicam/multicamnode.cpp | 16 +- app/node/input/multicam/multicamnode.h | 2 +- app/node/input/time/timeinput.cpp | 2 +- app/node/input/time/timeinput.h | 2 +- app/node/input/value/valuenode.cpp | 96 ++- app/node/input/value/valuenode.h | 13 +- app/node/inputdragger.cpp | 17 +- app/node/inputdragger.h | 8 +- app/node/inputimmediate.cpp | 25 +- app/node/inputimmediate.h | 22 +- app/node/keyframe.cpp | 14 +- app/node/keyframe.h | 14 +- app/node/keying/chromakey/chromakey.cpp | 22 +- app/node/keying/chromakey/chromakey.h | 2 +- .../colordifferencekey/colordifferencekey.cpp | 18 +- .../colordifferencekey/colordifferencekey.h | 2 +- app/node/keying/despill/despill.cpp | 15 +- app/node/keying/despill/despill.h | 2 +- app/node/math/math/math.cpp | 19 +- app/node/math/math/math.h | 2 +- app/node/math/math/mathbase.cpp | 202 ++--- app/node/math/math/mathbase.h | 34 +- app/node/math/merge/merge.cpp | 12 +- app/node/math/merge/merge.h | 2 +- app/node/math/trigonometry/trigonometry.cpp | 6 +- app/node/math/trigonometry/trigonometry.h | 2 +- app/node/node.cpp | 273 ++++--- app/node/node.h | 159 ++-- app/node/nodeundo.cpp | 10 +- app/node/nodeundo.h | 36 +- app/node/output/track/track.cpp | 12 +- app/node/output/track/track.h | 4 +- app/node/output/viewer/viewer.cpp | 26 +- app/node/output/viewer/viewer.h | 16 +- app/node/param.cpp | 37 +- app/node/param.h | 149 +--- app/node/project/folder/folder.cpp | 2 +- app/node/project/footage/footage.cpp | 14 +- app/node/project/footage/footage.h | 2 +- app/node/project/sequence/sequence.cpp | 2 +- .../project/serializer/serializer210528.cpp | 18 +- .../project/serializer/serializer210907.cpp | 18 +- .../project/serializer/serializer211228.cpp | 18 +- .../project/serializer/serializer220403.cpp | 26 +- .../project/serializer/serializer220403.h | 10 +- app/node/serializeddata.h | 8 +- app/node/time/timeformat/timeformat.cpp | 8 +- app/node/time/timeformat/timeformat.h | 2 +- app/node/time/timeoffset/timeoffsetnode.cpp | 6 +- app/node/time/timeoffset/timeoffsetnode.h | 2 +- app/node/time/timeremap/timeremap.cpp | 6 +- app/node/time/timeremap/timeremap.h | 2 +- app/node/value.cpp | 439 ++++------- app/node/value.h | 462 +++++------ app/render/job/acceleratedjob.h | 6 +- app/render/job/cachejob.h | 8 +- app/render/job/colortransformjob.h | 8 +- app/render/opengl/openglrenderer.cpp | 81 +- app/render/previewautocacher.cpp | 1 + app/render/renderer.cpp | 16 +- app/render/renderprocessor.cpp | 36 +- app/render/renderprocessor.h | 2 +- app/task/export/export.cpp | 1 + app/task/render/render.cpp | 1 + app/widget/colorbutton/colorbutton.cpp | 1 + app/widget/colorwheel/colorgradientwidget.cpp | 1 + app/widget/colorwheel/colorwheelwidget.cpp | 1 + app/widget/curvewidget/curveview.cpp | 17 +- app/widget/curvewidget/curveview.h | 2 +- app/widget/curvewidget/curvewidget.cpp | 4 +- app/widget/nodeparamview/CMakeLists.txt | 2 + .../nodeparamview/nodeparamviewitem.cpp | 38 +- .../nodeparamviewkeyframecontrol.cpp | 8 +- .../nodeparamviewwidgetbridge.cpp | 728 ++---------------- .../nodeparamview/nodeparamviewwidgetbridge.h | 42 +- .../nodeparamview/paramwidget/CMakeLists.txt | 46 ++ .../paramwidget/abstractparamwidget.cpp | 83 ++ .../paramwidget/abstractparamwidget.h | 69 ++ .../paramwidget/arrayparamwidget.cpp | 42 + .../paramwidget/arrayparamwidget.h | 49 ++ .../paramwidget/bezierparamwidget.cpp | 81 ++ .../paramwidget/bezierparamwidget.h | 45 ++ .../paramwidget/boolparamwidget.cpp | 61 ++ .../paramwidget/boolparamwidget.h} | 26 +- .../paramwidget/colorparamwidget.cpp | 89 +++ .../paramwidget/colorparamwidget.h | 45 ++ .../paramwidget/comboparamwidget.cpp | 127 +++ .../paramwidget/comboparamwidget.h | 52 ++ .../paramwidget/fileparamwidget.cpp | 79 ++ .../paramwidget/fileparamwidget.h | 47 ++ .../paramwidget/floatsliderparamwidget.cpp | 83 ++ .../paramwidget/floatsliderparamwidget.h | 44 ++ .../paramwidget/fontparamwidget.cpp | 64 ++ .../paramwidget/fontparamwidget.h | 45 ++ .../paramwidget/integersliderparamwidget.cpp | 65 ++ .../paramwidget/integersliderparamwidget.h | 44 ++ .../paramwidget/numericsliderparamwidget.cpp | 62 ++ .../paramwidget/numericsliderparamwidget.h | 62 ++ .../paramwidget/rationalsliderparamwidget.cpp | 73 ++ .../paramwidget/rationalsliderparamwidget.h | 45 ++ .../paramwidget/textparamwidget.cpp | 76 ++ .../paramwidget/textparamwidget.h | 50 ++ app/widget/nodetreeview/nodetreeview.cpp | 6 +- app/widget/nodeview/nodeview.cpp | 2 +- app/widget/nodeview/nodeviewcontext.cpp | 1 + .../resizabletimelinescrollbar.cpp | 1 + app/widget/scope/histogram/histogram.cpp | 4 +- app/widget/scope/scopebase/scopebase.cpp | 2 +- app/widget/scope/waveform/waveform.cpp | 12 +- app/widget/slider/base/numericsliderbase.cpp | 24 +- app/widget/slider/base/numericsliderbase.h | 24 +- app/widget/slider/base/sliderbase.cpp | 18 +- app/widget/slider/base/sliderbase.h | 33 +- app/widget/slider/floatslider.cpp | 26 +- app/widget/slider/floatslider.h | 10 +- app/widget/slider/integerslider.cpp | 33 +- app/widget/slider/integerslider.h | 10 +- app/widget/slider/rationalslider.cpp | 34 +- app/widget/slider/rationalslider.h | 14 +- app/widget/slider/stringslider.cpp | 17 +- app/widget/slider/stringslider.h | 8 +- app/widget/standardcombos/frameratecombobox.h | 1 + app/widget/timelinewidget/timelinewidget.cpp | 8 +- .../timelinewidget/view/timelineview.cpp | 2 +- .../view/timelineviewghostitem.h | 1 + app/widget/viewer/footageviewer.cpp | 6 +- app/widget/viewer/viewer.cpp | 16 +- app/widget/viewer/viewerdisplay.cpp | 8 +- 203 files changed, 3456 insertions(+), 2712 deletions(-) create mode 100644 app/widget/nodeparamview/paramwidget/CMakeLists.txt create mode 100644 app/widget/nodeparamview/paramwidget/abstractparamwidget.cpp create mode 100644 app/widget/nodeparamview/paramwidget/abstractparamwidget.h create mode 100644 app/widget/nodeparamview/paramwidget/arrayparamwidget.cpp create mode 100644 app/widget/nodeparamview/paramwidget/arrayparamwidget.h create mode 100644 app/widget/nodeparamview/paramwidget/bezierparamwidget.cpp create mode 100644 app/widget/nodeparamview/paramwidget/bezierparamwidget.h create mode 100644 app/widget/nodeparamview/paramwidget/boolparamwidget.cpp rename app/{node/splitvalue.h => widget/nodeparamview/paramwidget/boolparamwidget.h} (61%) create mode 100644 app/widget/nodeparamview/paramwidget/colorparamwidget.cpp create mode 100644 app/widget/nodeparamview/paramwidget/colorparamwidget.h create mode 100644 app/widget/nodeparamview/paramwidget/comboparamwidget.cpp create mode 100644 app/widget/nodeparamview/paramwidget/comboparamwidget.h create mode 100644 app/widget/nodeparamview/paramwidget/fileparamwidget.cpp create mode 100644 app/widget/nodeparamview/paramwidget/fileparamwidget.h create mode 100644 app/widget/nodeparamview/paramwidget/floatsliderparamwidget.cpp create mode 100644 app/widget/nodeparamview/paramwidget/floatsliderparamwidget.h create mode 100644 app/widget/nodeparamview/paramwidget/fontparamwidget.cpp create mode 100644 app/widget/nodeparamview/paramwidget/fontparamwidget.h create mode 100644 app/widget/nodeparamview/paramwidget/integersliderparamwidget.cpp create mode 100644 app/widget/nodeparamview/paramwidget/integersliderparamwidget.h create mode 100644 app/widget/nodeparamview/paramwidget/numericsliderparamwidget.cpp create mode 100644 app/widget/nodeparamview/paramwidget/numericsliderparamwidget.h create mode 100644 app/widget/nodeparamview/paramwidget/rationalsliderparamwidget.cpp create mode 100644 app/widget/nodeparamview/paramwidget/rationalsliderparamwidget.h create mode 100644 app/widget/nodeparamview/paramwidget/textparamwidget.cpp create mode 100644 app/widget/nodeparamview/paramwidget/textparamwidget.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ecd36e73f4..19b452e46e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,6 +60,7 @@ else() -Wextra -Wno-unused-parameter -Wshadow + -Wno-deprecated-declarations ) if (USE_WERROR) list(APPEND OLIVE_COMPILE_OPTIONS "-Werror") diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index 938ff3f221..4289ab389b 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -167,17 +167,19 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVi TexturePtr v_plane = p.renderer->CreateTexture(plane_params, hw_in->data[2], hw_in->linesize[2] / px_size); ShaderJob job; - job.Insert(QStringLiteral("y_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(y_plane))); - job.Insert(QStringLiteral("u_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(u_plane))); - job.Insert(QStringLiteral("v_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(v_plane))); - job.Insert(QStringLiteral("bits_per_pixel"), NodeValue(NodeValue::kInt, bits_per_pixel)); - job.Insert(QStringLiteral("full_range"), NodeValue(NodeValue::kBoolean, hw_in->color_range == AVCOL_RANGE_JPEG)); + job.Insert(QStringLiteral("y_channel"), y_plane); + job.Insert(QStringLiteral("u_channel"), u_plane); + job.Insert(QStringLiteral("v_channel"), v_plane); + job.Insert(QStringLiteral("bits_per_pixel"), bits_per_pixel); + job.Insert(QStringLiteral("full_range"), hw_in->color_range == AVCOL_RANGE_JPEG); const int *yuv_coeffs = sws_getCoefficients(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(hw_in->colorspace)); - job.Insert(QStringLiteral("yuv_crv"), NodeValue(NodeValue::kFloat, yuv_coeffs[0]/65536.0)); - job.Insert(QStringLiteral("yuv_cgu"), NodeValue(NodeValue::kFloat, yuv_coeffs[2]/65536.0)); - job.Insert(QStringLiteral("yuv_cgv"), NodeValue(NodeValue::kFloat, yuv_coeffs[3]/65536.0)); - job.Insert(QStringLiteral("yuv_cbu"), NodeValue(NodeValue::kFloat, yuv_coeffs[1]/65536.0)); + job.Insert(QStringLiteral("yuv_crv"), yuv_coeffs[0]/65536.0); + job.Insert(QStringLiteral("yuv_cgu"), yuv_coeffs[2]/65536.0); + job.Insert(QStringLiteral("yuv_cgv"), yuv_coeffs[3]/65536.0); + job.Insert(QStringLiteral("yuv_cbu"), yuv_coeffs[1]/65536.0); + + qDebug() << "sending job with keys:" << job.GetValues().keys(); tex = p.renderer->CreateTexture(vp); p.renderer->BlitToTexture(Yuv2RgbShader, job, tex.get(), false); @@ -219,9 +221,9 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVi TexturePtr deinterlaced = p.renderer->CreateTexture(tex->params()); ShaderJob job; - job.Insert(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, tex)); - job.Insert(QStringLiteral("interlacing"), NodeValue(NodeValue::kInt, interlacing)); - job.Insert(QStringLiteral("pixel_height"), NodeValue(NodeValue::kInt, original->height)); + job.Insert(QStringLiteral("ove_maintex"), tex); + job.Insert(QStringLiteral("interlacing"), interlacing); + job.Insert(QStringLiteral("pixel_height"), original->height); p.renderer->BlitToTexture(DeinterlaceShader, job, deinterlaced.get(), false); diff --git a/app/config/config.cpp b/app/config/config.cpp index 336b1eeb49..c73b88f464 100644 --- a/app/config/config.cpp +++ b/app/config/config.cpp @@ -46,11 +46,6 @@ Config::Config() SetDefaults(); } -void Config::SetEntryInternal(const QString &key, NodeValue::Type type, const QVariant &data) -{ - config_map_[key] = {type, data}; -} - QString Config::GetConfigFilePath() { return QDir(FileFunctions::GetConfigurationLocation()).filePath(QStringLiteral("config.xml")); @@ -64,106 +59,107 @@ Config &Config::Current() void Config::SetDefaults() { config_map_.clear(); - SetEntryInternal(QStringLiteral("Style"), NodeValue::kText, StyleManager::kDefaultStyle); - SetEntryInternal(QStringLiteral("TimecodeDisplay"), NodeValue::kInt, Timecode::kTimecodeDropFrame); - SetEntryInternal(QStringLiteral("DefaultStillLength"), NodeValue::kRational, QVariant::fromValue(rational(2))); - SetEntryInternal(QStringLiteral("HoverFocus"), NodeValue::kBoolean, false); - SetEntryInternal(QStringLiteral("AudioScrubbing"), NodeValue::kBoolean, true); - SetEntryInternal(QStringLiteral("AutorecoveryEnabled"), NodeValue::kBoolean, true); - SetEntryInternal(QStringLiteral("AutorecoveryInterval"), NodeValue::kInt, 1); - SetEntryInternal(QStringLiteral("AutorecoveryMaximum"), NodeValue::kInt, 20); - SetEntryInternal(QStringLiteral("DiskCacheSaveInterval"), NodeValue::kInt, 10000); - SetEntryInternal(QStringLiteral("Language"), NodeValue::kText, QString()); - SetEntryInternal(QStringLiteral("ScrollZooms"), NodeValue::kBoolean, false); - SetEntryInternal(QStringLiteral("EnableSeekToImport"), NodeValue::kBoolean, false); - SetEntryInternal(QStringLiteral("EditToolAlsoSeeks"), NodeValue::kBoolean, false); - SetEntryInternal(QStringLiteral("EditToolSelectsLinks"), NodeValue::kBoolean, false); - SetEntryInternal(QStringLiteral("EnableDragFilesToTimeline"), NodeValue::kBoolean, true); - SetEntryInternal(QStringLiteral("InvertTimelineScrollAxes"), NodeValue::kBoolean, true); - SetEntryInternal(QStringLiteral("SelectAlsoSeeks"), NodeValue::kBoolean, false); - SetEntryInternal(QStringLiteral("PasteSeeks"), NodeValue::kBoolean, true); - SetEntryInternal(QStringLiteral("SeekAlsoSelects"), NodeValue::kBoolean, false); - SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean, false); - SetEntryInternal(QStringLiteral("AutoSeekToBeginning"), NodeValue::kBoolean, true); - SetEntryInternal(QStringLiteral("DropFileOnMediaToReplace"), NodeValue::kBoolean, false); - SetEntryInternal(QStringLiteral("AddDefaultEffectsToClips"), NodeValue::kBoolean, true); - SetEntryInternal(QStringLiteral("AutoscaleByDefault"), NodeValue::kBoolean, false); - SetEntryInternal(QStringLiteral("Autoscroll"), NodeValue::kInt, AutoScroll::kPage); - SetEntryInternal(QStringLiteral("AutoSelectDivider"), NodeValue::kBoolean, true); - SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean, false); - SetEntryInternal(QStringLiteral("RectifiedWaveforms"), NodeValue::kBoolean, true); - SetEntryInternal(QStringLiteral("DropWithoutSequenceBehavior"), NodeValue::kInt, ImportTool::kDWSAsk); - SetEntryInternal(QStringLiteral("Loop"), NodeValue::kBoolean, false); - SetEntryInternal(QStringLiteral("SplitClipsCopyNodes"), NodeValue::kBoolean, true); - SetEntryInternal(QStringLiteral("UseGradients"), NodeValue::kBoolean, true); - SetEntryInternal(QStringLiteral("AutoMergeTracks"), NodeValue::kBoolean, true); - SetEntryInternal(QStringLiteral("UseSliderLadders"), NodeValue::kBoolean, true); - SetEntryInternal(QStringLiteral("ShowWelcomeDialog"), NodeValue::kBoolean, true); - SetEntryInternal(QStringLiteral("ShowClipWhileDragging"), NodeValue::kBoolean, true); - SetEntryInternal(QStringLiteral("StopPlaybackOnLastFrame"), NodeValue::kBoolean, false); - SetEntryInternal(QStringLiteral("UseLegacyColorInInputTab"), NodeValue::kBoolean, false); - SetEntryInternal(QStringLiteral("ReassocLinToNonLin"), NodeValue::kBoolean, false); - SetEntryInternal(QStringLiteral("PreviewNonFloatDontAskAgain"), NodeValue::kBoolean, false); - SetEntryInternal(QStringLiteral("UseGLFinish"), NodeValue::kBoolean, false); - - SetEntryInternal(QStringLiteral("TimelineThumbnailMode"), NodeValue::kInt, Timeline::kThumbnailInOut); - SetEntryInternal(QStringLiteral("TimelineWaveformMode"), NodeValue::kInt, Timeline::kWaveformsEnabled); - - SetEntryInternal(QStringLiteral("DefaultVideoTransition"), NodeValue::kText, QStringLiteral("org.olivevideoeditor.Olive.crossdissolve")); - SetEntryInternal(QStringLiteral("DefaultAudioTransition"), NodeValue::kText, QStringLiteral("org.olivevideoeditor.Olive.crossdissolve")); - SetEntryInternal(QStringLiteral("DefaultTransitionLength"), NodeValue::kRational, QVariant::fromValue(rational(1))); - - SetEntryInternal(QStringLiteral("DefaultSubtitleSize"), NodeValue::kInt, 48); - SetEntryInternal(QStringLiteral("DefaultSubtitleFamily"), NodeValue::kText, QString()); - SetEntryInternal(QStringLiteral("DefaultSubtitleWeight"), NodeValue::kInt, QFont::Bold); - SetEntryInternal(QStringLiteral("AntialiasSubtitles"), NodeValue::kBoolean, true); - - SetEntryInternal(QStringLiteral("AutoCacheDelay"), NodeValue::kInt, 1000); - - SetEntryInternal(QStringLiteral("CatColor0"), NodeValue::kInt, ColorCoding::kRed); - SetEntryInternal(QStringLiteral("CatColor1"), NodeValue::kInt, ColorCoding::kMaroon); - SetEntryInternal(QStringLiteral("CatColor2"), NodeValue::kInt, ColorCoding::kOrange); - SetEntryInternal(QStringLiteral("CatColor3"), NodeValue::kInt, ColorCoding::kBrown); - SetEntryInternal(QStringLiteral("CatColor4"), NodeValue::kInt, ColorCoding::kYellow); - SetEntryInternal(QStringLiteral("CatColor5"), NodeValue::kInt, ColorCoding::kOlive); - SetEntryInternal(QStringLiteral("CatColor6"), NodeValue::kInt, ColorCoding::kLime); - SetEntryInternal(QStringLiteral("CatColor7"), NodeValue::kInt, ColorCoding::kGreen); - SetEntryInternal(QStringLiteral("CatColor8"), NodeValue::kInt, ColorCoding::kCyan); - SetEntryInternal(QStringLiteral("CatColor9"), NodeValue::kInt, ColorCoding::kTeal); - SetEntryInternal(QStringLiteral("CatColor10"), NodeValue::kInt, ColorCoding::kBlue); - SetEntryInternal(QStringLiteral("CatColor11"), NodeValue::kInt, ColorCoding::kNavy); - - 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::kText, QString::fromStdString(SampleFormat(SampleFormat::S16).to_string())); - - SetEntryInternal(QStringLiteral("AudioRecordingFormat"), NodeValue::kInt, ExportFormat::kFormatWAV); - SetEntryInternal(QStringLiteral("AudioRecordingCodec"), NodeValue::kInt, ExportCodec::kCodecPCM); - SetEntryInternal(QStringLiteral("AudioRecordingSampleRate"), NodeValue::kInt, 48000); - SetEntryInternal(QStringLiteral("AudioRecordingChannelLayout"), NodeValue::kInt, AV_CH_LAYOUT_STEREO); - SetEntryInternal(QStringLiteral("AudioRecordingSampleFormat"), NodeValue::kText, QString::fromStdString(SampleFormat(SampleFormat::S16).to_string())); - SetEntryInternal(QStringLiteral("AudioRecordingBitRate"), NodeValue::kInt, 320); - - SetEntryInternal(QStringLiteral("DiskCacheBehind"), NodeValue::kRational, QVariant::fromValue(rational(0))); - SetEntryInternal(QStringLiteral("DiskCacheAhead"), NodeValue::kRational, QVariant::fromValue(rational(60))); - - SetEntryInternal(QStringLiteral("DefaultSequenceWidth"), NodeValue::kInt, 1920); - SetEntryInternal(QStringLiteral("DefaultSequenceHeight"), NodeValue::kInt, 1080); - SetEntryInternal(QStringLiteral("DefaultSequencePixelAspect"), NodeValue::kRational, QVariant::fromValue(rational(1))); - SetEntryInternal(QStringLiteral("DefaultSequenceFrameRate"), NodeValue::kRational, QVariant::fromValue(rational(1001, 30000))); - SetEntryInternal(QStringLiteral("DefaultSequenceInterlacing"), NodeValue::kInt, VideoParams::kInterlaceNone); - SetEntryInternal(QStringLiteral("DefaultSequenceAutoCache2"), NodeValue::kBoolean, false); - SetEntryInternal(QStringLiteral("DefaultSequenceAudioFrequency"), NodeValue::kInt, 48000); - SetEntryInternal(QStringLiteral("DefaultSequenceAudioLayout"), NodeValue::kInt, QVariant::fromValue(static_cast(AV_CH_LAYOUT_STEREO))); + + OLIVE_CONFIG("Style") = StyleManager::kDefaultStyle; + OLIVE_CONFIG("TimecodeDisplay") = Timecode::kTimecodeDropFrame; + OLIVE_CONFIG("DefaultStillLength") = rational(2); + OLIVE_CONFIG("HoverFocus") = false; + OLIVE_CONFIG("AudioScrubbing") = true; + OLIVE_CONFIG("AutorecoveryEnabled") = true; + OLIVE_CONFIG("AutorecoveryInterval") = 1; + OLIVE_CONFIG("AutorecoveryMaximum") = 20; + OLIVE_CONFIG("DiskCacheSaveInterval") = 10000; + OLIVE_CONFIG("Language") = QString(); + OLIVE_CONFIG("ScrollZooms") = false; + OLIVE_CONFIG("EnableSeekToImport") = false; + OLIVE_CONFIG("EditToolAlsoSeeks") = false; + OLIVE_CONFIG("EditToolSelectsLinks") = false; + OLIVE_CONFIG("EnableDragFilesToTimeline") = true; + OLIVE_CONFIG("InvertTimelineScrollAxes") = true; + OLIVE_CONFIG("SelectAlsoSeeks") = false; + OLIVE_CONFIG("PasteSeeks") = true; + OLIVE_CONFIG("SeekAlsoSelects") = false; + OLIVE_CONFIG("SetNameWithMarker") = false; + OLIVE_CONFIG("AutoSeekToBeginning") = true; + OLIVE_CONFIG("DropFileOnMediaToReplace") = false; + OLIVE_CONFIG("AddDefaultEffectsToClips") = true; + OLIVE_CONFIG("AutoscaleByDefault") = false; + OLIVE_CONFIG("Autoscroll") = AutoScroll::kPage; + OLIVE_CONFIG("AutoSelectDivider") = true; + OLIVE_CONFIG("SetNameWithMarker") = false; + OLIVE_CONFIG("RectifiedWaveforms") = true; + OLIVE_CONFIG("DropWithoutSequenceBehavior") = ImportTool::kDWSAsk; + OLIVE_CONFIG("Loop") = false; + OLIVE_CONFIG("SplitClipsCopyNodes") = true; + OLIVE_CONFIG("UseGradients") = true; + OLIVE_CONFIG("AutoMergeTracks") = true; + OLIVE_CONFIG("UseSliderLadders") = true; + OLIVE_CONFIG("ShowWelcomeDialog") = true; + OLIVE_CONFIG("ShowClipWhileDragging") = true; + OLIVE_CONFIG("StopPlaybackOnLastFrame") = false; + OLIVE_CONFIG("UseLegacyColorInInputTab") = false; + OLIVE_CONFIG("ReassocLinToNonLin") = false; + OLIVE_CONFIG("PreviewNonFloatDontAskAgain") = false; + OLIVE_CONFIG("UseGLFinish") = false; + + OLIVE_CONFIG("TimelineThumbnailMode") = Timeline::kThumbnailInOut; + OLIVE_CONFIG("TimelineWaveformMode") = Timeline::kWaveformsEnabled; + + OLIVE_CONFIG("DefaultVideoTransition") = QStringLiteral("org.olivevideoeditor.Olive.crossdissolve"); + OLIVE_CONFIG("DefaultAudioTransition") = QStringLiteral("org.olivevideoeditor.Olive.crossdissolve"); + OLIVE_CONFIG("DefaultTransitionLength") = rational(1); + + OLIVE_CONFIG("DefaultSubtitleSize") = 48; + OLIVE_CONFIG("DefaultSubtitleFamily") = QString(); + OLIVE_CONFIG("DefaultSubtitleWeight") = QFont::Bold; + OLIVE_CONFIG("AntialiasSubtitles") = true; + + OLIVE_CONFIG("AutoCacheDelay") = 1000; + + OLIVE_CONFIG("CatColor0") = ColorCoding::kRed; + OLIVE_CONFIG("CatColor1") = ColorCoding::kMaroon; + OLIVE_CONFIG("CatColor2") = ColorCoding::kOrange; + OLIVE_CONFIG("CatColor3") = ColorCoding::kBrown; + OLIVE_CONFIG("CatColor4") = ColorCoding::kYellow; + OLIVE_CONFIG("CatColor5") = ColorCoding::kOlive; + OLIVE_CONFIG("CatColor6") = ColorCoding::kLime; + OLIVE_CONFIG("CatColor7") = ColorCoding::kGreen; + OLIVE_CONFIG("CatColor8") = ColorCoding::kCyan; + OLIVE_CONFIG("CatColor9") = ColorCoding::kTeal; + OLIVE_CONFIG("CatColor10") = ColorCoding::kBlue; + OLIVE_CONFIG("CatColor11") = ColorCoding::kNavy; + + OLIVE_CONFIG("AudioOutput") = QString(); + OLIVE_CONFIG("AudioInput") = QString(); + + OLIVE_CONFIG("AudioOutputSampleRate") = 48000; + OLIVE_CONFIG("AudioOutputChannelLayout") = int64_t(AV_CH_LAYOUT_STEREO); + OLIVE_CONFIG("AudioOutputSampleFormat") = QString::fromStdString(SampleFormat(SampleFormat::S16).to_string()); + + OLIVE_CONFIG("AudioRecordingFormat") = ExportFormat::kFormatWAV; + OLIVE_CONFIG("AudioRecordingCodec") = ExportCodec::kCodecPCM; + OLIVE_CONFIG("AudioRecordingSampleRate") = 48000; + OLIVE_CONFIG("AudioRecordingChannelLayout") = int64_t(AV_CH_LAYOUT_STEREO); + OLIVE_CONFIG("AudioRecordingSampleFormat") = QString::fromStdString(SampleFormat(SampleFormat::S16).to_string()); + OLIVE_CONFIG("AudioRecordingBitRate") = 320; + + OLIVE_CONFIG("DiskCacheBehind") = rational(0); + OLIVE_CONFIG("DiskCacheAhead") = rational(60); + + OLIVE_CONFIG("DefaultSequenceWidth") = 1920; + OLIVE_CONFIG("DefaultSequenceHeight") = 1080; + OLIVE_CONFIG("DefaultSequencePixelAspect") = rational(1); + OLIVE_CONFIG("DefaultSequenceFrameRate") = rational(1001, 30000); + OLIVE_CONFIG("DefaultSequenceInterlacing") = VideoParams::kInterlaceNone; + OLIVE_CONFIG("DefaultSequenceAutoCache2") = false; + OLIVE_CONFIG("DefaultSequenceAudioFrequency") = 48000; + OLIVE_CONFIG("DefaultSequenceAudioLayout") = int64_t(AV_CH_LAYOUT_STEREO); // Online/offline settings - SetEntryInternal(QStringLiteral("OnlinePixelFormat"), NodeValue::kInt, PixelFormat::F32); - SetEntryInternal(QStringLiteral("OfflinePixelFormat"), NodeValue::kInt, PixelFormat::F16); + OLIVE_CONFIG("OnlinePixelFormat") = PixelFormat::F32; + OLIVE_CONFIG("OfflinePixelFormat") = PixelFormat::F16; - SetEntryInternal(QStringLiteral("MarkerColor"), NodeValue::kInt, ColorCoding::kLime); + OLIVE_CONFIG("MarkerColor") = ColorCoding::kLime; } void Config::Load() @@ -221,9 +217,10 @@ void Config::Load() qDebug() << " CONFIG: Closest match was" << match.toDouble(); - current_config_[key] = QVariant::fromValue(match.flipped()); + current_config_[key] = match.flipped(); } else { - current_config_[key] = NodeValue::StringToValue(current_config_.GetConfigEntryType(key), value, false); + value_t &v = current_config_[key]; + v = value_t(value).converted(v.type()); } } @@ -271,17 +268,8 @@ void Config::Save() // Anything after the hyphen is considered "unimportant" information writer.writeTextElement("Version", QCoreApplication::applicationVersion().split('-').first()); - QMapIterator iterator(current_config_.config_map_); - while (iterator.hasNext()) { - iterator.next(); - - QString value = NodeValue::ValueToString(iterator.value().type, iterator.value().data, false); - - if (iterator.value().type == NodeValue::kNone) { - qWarning() << "Config key" << iterator.key() << "had null type and was discarded"; - } else { - writer.writeTextElement(iterator.key(), value); - } + for (auto it = current_config_.config_map_.cbegin(); it != current_config_.config_map_.cend(); it++) { + writer.writeTextElement(it.key(), it.value().converted(TYPE_STRING).toString()); } writer.writeEndElement(); // Configuration @@ -296,19 +284,14 @@ void Config::Save() } } -QVariant Config::operator[](const QString &key) const -{ - return config_map_[key].data; -} - -QVariant &Config::operator[](const QString &key) +value_t Config::operator[](const QString &key) const { - return config_map_[key].data; + return config_map_[key]; } -NodeValue::Type Config::GetConfigEntryType(const QString &key) const +value_t &Config::operator[](const QString &key) { - return config_map_[key].type; + return config_map_[key]; } } diff --git a/app/config/config.h b/app/config/config.h index 4179858dc1..97419f301d 100644 --- a/app/config/config.h +++ b/app/config/config.h @@ -32,7 +32,8 @@ namespace olive { #define OLIVE_CONFIG(x) Config::Current()[QStringLiteral(x)] #define OLIVE_CONFIG_STR(x) Config::Current()[x] -class Config { +class Config +{ public: static Config& Current(); @@ -42,23 +43,14 @@ class Config { static void Save(); - QVariant operator[](const QString&) const; + value_t operator[](const QString&) const; - QVariant& operator[](const QString&); - - NodeValue::Type GetConfigEntryType(const QString& key) const; + value_t &operator[](const QString&); private: Config(); - struct ConfigEntry { - NodeValue::Type type; - QVariant data; - }; - - void SetEntryInternal(const QString& key, NodeValue::Type type, const QVariant& data); - - QMap config_map_; + QMap config_map_; static Config current_config_; diff --git a/app/core.cpp b/app/core.cpp index 5030a54b1c..417cef9d56 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -108,7 +108,6 @@ Core *Core::instance() void Core::DeclareTypesForQt() { qRegisterMetaType(); - qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); @@ -125,6 +124,9 @@ void Core::DeclareTypesForQt() void Core::Start() { + // Register value conversions + value_t::registerDefaultConverters(); + // Load application config Config::Load(); @@ -977,7 +979,7 @@ void Core::SaveAutorecovery() realname_file.close(); } - int64_t max_recoveries_per_file = OLIVE_CONFIG("AutorecoveryMaximum").toLongLong(); + int64_t max_recoveries_per_file = OLIVE_CONFIG("AutorecoveryMaximum").toInt(); // Since we write an extra file, increment total allowed files by 1 max_recoveries_per_file++; diff --git a/app/dialog/preferences/tabs/preferencesaudiotab.cpp b/app/dialog/preferences/tabs/preferencesaudiotab.cpp index 211f070cb7..5bacf60dc5 100644 --- a/app/dialog/preferences/tabs/preferencesaudiotab.cpp +++ b/app/dialog/preferences/tabs/preferencesaudiotab.cpp @@ -91,7 +91,7 @@ PreferencesAudioTab::PreferencesAudioTab() 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_ch_layout_combo_->SetChannelLayout(OLIVE_CONFIG("AudioOutputChannelLayout").toInt()); output_param_layout->addWidget(output_ch_layout_combo_, output_row, 1); output_row++; @@ -140,7 +140,7 @@ PreferencesAudioTab::PreferencesAudioTab() record_options_->SetFormat(record_format_combo_->GetFormat()); record_options_->SetCodec(static_cast(OLIVE_CONFIG("AudioRecordingCodec").toInt())); record_options_->sample_rate_combobox()->SetSampleRate(OLIVE_CONFIG("AudioRecordingSampleRate").toInt()); - record_options_->channel_layout_combobox()->SetChannelLayout(OLIVE_CONFIG("AudioRecordingChannelLayout").toULongLong()); + record_options_->channel_layout_combobox()->SetChannelLayout(OLIVE_CONFIG("AudioRecordingChannelLayout").toInt()); record_options_->bit_rate_slider()->SetValue(OLIVE_CONFIG("AudioRecordingBitRate").toInt()); record_options_->sample_format_combobox()->SetSampleFormat(SampleFormat::from_string(OLIVE_CONFIG("AudioRecordingSampleFormat").toString().toStdString())); recording_layout->addWidget(record_options_); @@ -181,14 +181,14 @@ void PreferencesAudioTab::Accept(MultiUndoCommand *command) AudioManager::instance()->SetInputDevice(input_device); OLIVE_CONFIG("AudioOutputSampleRate") = output_rate_combo_->GetSampleRate(); - OLIVE_CONFIG("AudioOutputChannelLayout") = QVariant::fromValue(output_ch_layout_combo_->GetChannelLayout()); + OLIVE_CONFIG("AudioOutputChannelLayout") = output_ch_layout_combo_->GetChannelLayout(); OLIVE_CONFIG("AudioOutputSampleFormat") = QString::fromStdString(output_fmt_combo_->GetSampleFormat().to_string()); 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("AudioRecordingChannelLayout") = record_options_->channel_layout_combobox()->GetChannelLayout(); + OLIVE_CONFIG("AudioRecordingBitRate") = record_options_->bit_rate_slider()->GetValue(); OLIVE_CONFIG("AudioRecordingSampleFormat") = QString::fromStdString(record_options_->sample_format_combobox()->GetSampleFormat().to_string()); emit AudioManager::instance()->OutputParamsChanged(); diff --git a/app/dialog/preferences/tabs/preferencesdisktab.cpp b/app/dialog/preferences/tabs/preferencesdisktab.cpp index 8aea10aab1..301e4ce7d3 100644 --- a/app/dialog/preferences/tabs/preferencesdisktab.cpp +++ b/app/dialog/preferences/tabs/preferencesdisktab.cpp @@ -115,8 +115,8 @@ void PreferencesDiskTab::Accept(MultiUndoCommand *command) default_disk_cache_folder_->SetPath(disk_cache_location_->text()); } - OLIVE_CONFIG("DiskCacheBehind") = QVariant::fromValue(rational::fromDouble(cache_behind_slider_->GetValue())); - OLIVE_CONFIG("DiskCacheAhead") = QVariant::fromValue(rational::fromDouble(cache_ahead_slider_->GetValue())); + OLIVE_CONFIG("DiskCacheBehind") = rational::fromDouble(cache_behind_slider_->GetValue()); + OLIVE_CONFIG("DiskCacheAhead") = rational::fromDouble(cache_ahead_slider_->GetValue()); } } diff --git a/app/dialog/preferences/tabs/preferencesgeneraltab.cpp b/app/dialog/preferences/tabs/preferencesgeneraltab.cpp index 765e91dfeb..a3474227dd 100644 --- a/app/dialog/preferences/tabs/preferencesgeneraltab.cpp +++ b/app/dialog/preferences/tabs/preferencesgeneraltab.cpp @@ -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(OLIVE_CONFIG("AutorecoveryInterval").toLongLong()); + autorecovery_interval_->SetValue(OLIVE_CONFIG("AutorecoveryInterval").toInt()); 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(OLIVE_CONFIG("AutorecoveryMaximum").toLongLong()); + autorecovery_maximum_->SetValue(OLIVE_CONFIG("AutorecoveryMaximum").toInt()); autorecovery_layout->addWidget(autorecovery_maximum_, row, 1); row++; @@ -159,9 +159,9 @@ void PreferencesGeneralTab::Accept(MultiUndoCommand *command) OLIVE_CONFIG("RectifiedWaveforms") = rectified_waveforms_->isChecked(); - OLIVE_CONFIG("Autoscroll") = autoscroll_method_->currentData(); + OLIVE_CONFIG("Autoscroll") = autoscroll_method_->currentData().toInt(); - OLIVE_CONFIG("DefaultStillLength") = QVariant::fromValue(default_still_length_->GetValue()); + OLIVE_CONFIG("DefaultStillLength") = default_still_length_->GetValue(); QString set_language = language_combobox_->currentData().toString(); if (QLocale::system().name() == set_language) { @@ -176,8 +176,8 @@ void PreferencesGeneralTab::Accept(MultiUndoCommand *command) } OLIVE_CONFIG("AutorecoveryEnabled") = autorecovery_enabled_->isChecked(); - OLIVE_CONFIG("AutorecoveryInterval") = QVariant::fromValue(autorecovery_interval_->GetValue()); - OLIVE_CONFIG("AutorecoveryMaximum") = QVariant::fromValue(autorecovery_maximum_->GetValue()); + OLIVE_CONFIG("AutorecoveryInterval") = autorecovery_interval_->GetValue(); + OLIVE_CONFIG("AutorecoveryMaximum") = autorecovery_maximum_->GetValue(); Core::instance()->SetAutorecoveryInterval(autorecovery_interval_->GetValue()); } diff --git a/app/dialog/projectproperties/projectproperties.cpp b/app/dialog/projectproperties/projectproperties.cpp index e059e9ac99..873a06cba8 100644 --- a/app/dialog/projectproperties/projectproperties.cpp +++ b/app/dialog/projectproperties/projectproperties.cpp @@ -29,6 +29,7 @@ #include #include "common/filefunctions.h" +#include "common/qtutils.h" #include "node/color/colormanager/colormanager.h" #include "render/diskmanager.h" diff --git a/app/dialog/sequence/sequence.cpp b/app/dialog/sequence/sequence.cpp index d6a109f1c4..04a8b351a2 100644 --- a/app/dialog/sequence/sequence.cpp +++ b/app/dialog/sequence/sequence.cpp @@ -172,11 +172,11 @@ void SequenceDialog::SetAsDefaultClicked() // Maybe replace with Preset system 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("DefaultSequencePixelAspect") = parameter_tab_->GetSelectedVideoPixelAspect(); + OLIVE_CONFIG("DefaultSequenceFrameRate") = 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("DefaultSequenceAudioLayout") = parameter_tab_->GetSelectedAudioChannelLayout(); } } diff --git a/app/node/CMakeLists.txt b/app/node/CMakeLists.txt index 38c1a53167..e2d1a682ee 100644 --- a/app/node/CMakeLists.txt +++ b/app/node/CMakeLists.txt @@ -50,7 +50,6 @@ set(OLIVE_SOURCES node/param.h node/project.cpp node/project.h - node/splitvalue.h node/value.cpp node/value.h PARENT_SCOPE diff --git a/app/node/audio/pan/pan.cpp b/app/node/audio/pan/pan.cpp index 6b71c33cea..d38fdd9ac9 100644 --- a/app/node/audio/pan/pan.cpp +++ b/app/node/audio/pan/pan.cpp @@ -31,9 +31,9 @@ const QString PanNode::kPanningInput = QStringLiteral("panning_in"); PanNode::PanNode() { - AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable)); + AddInput(kSamplesInput, TYPE_SAMPLES, kInputFlagNotKeyframable); - AddInput(kPanningInput, NodeValue::kFloat, 0.0); + AddInput(kPanningInput, TYPE_DOUBLE, 0.0); SetInputProperty(kPanningInput, QStringLiteral("min"), -1.0); SetInputProperty(kPanningInput, QStringLiteral("max"), 1.0); SetInputProperty(kPanningInput, QStringLiteral("view"), FloatSlider::kPercentage); @@ -100,10 +100,10 @@ void PanNode::ProcessSamples(const void *context, const SampleJob &job, SampleBu } } -NodeValue PanNode::Value(const ValueParams &p) const +value_t PanNode::Value(const ValueParams &p) const { // Create a sample job - NodeValue samples_original = GetInputValue(p, kSamplesInput); + value_t samples_original = GetInputValue(p, kSamplesInput); if (!IsInputStatic(kPanningInput) || !qIsNull(GetInputValue(p, kPanningInput).toDouble())) { // Requires sample job diff --git a/app/node/audio/pan/pan.h b/app/node/audio/pan/pan.h index 0d5686bb06..de84990173 100644 --- a/app/node/audio/pan/pan.h +++ b/app/node/audio/pan/pan.h @@ -38,7 +38,7 @@ class PanNode : public Node virtual QVector Category() const override; virtual QString Description() const override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; virtual void Retranslate() override; diff --git a/app/node/audio/volume/volume.cpp b/app/node/audio/volume/volume.cpp index 162e388740..d5e7547731 100644 --- a/app/node/audio/volume/volume.cpp +++ b/app/node/audio/volume/volume.cpp @@ -31,9 +31,9 @@ const QString VolumeNode::kVolumeInput = QStringLiteral("volume_in"); VolumeNode::VolumeNode() { - AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable)); + AddInput(kSamplesInput, TYPE_SAMPLES, kInputFlagNotKeyframable); - AddInput(kVolumeInput, NodeValue::kFloat, 1.0); + AddInput(kVolumeInput, TYPE_DOUBLE, 1.0); SetInputProperty(kVolumeInput, QStringLiteral("min"), 0.0); SetInputProperty(kVolumeInput, QStringLiteral("view"), FloatSlider::kDecibel); @@ -61,7 +61,7 @@ QString VolumeNode::Description() const return tr("Adjusts the volume of an audio source."); } -NodeValue VolumeNode::Value(const ValueParams &p) const +value_t VolumeNode::Value(const ValueParams &p) const { return ValueInternal(kOpMultiply, kPairSampleNumber, kSamplesInput, GetInputValue(p, kSamplesInput), kVolumeInput, GetInputValue(p, kVolumeInput), p); } diff --git a/app/node/audio/volume/volume.h b/app/node/audio/volume/volume.h index 4677b3b546..8d7262d814 100644 --- a/app/node/audio/volume/volume.h +++ b/app/node/audio/volume/volume.h @@ -38,7 +38,7 @@ class VolumeNode : public MathNodeBase virtual QVector Category() const override; virtual QString Description() const override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; virtual void Retranslate() override; diff --git a/app/node/block/block.cpp b/app/node/block/block.cpp index 483887df58..88bae696ff 100644 --- a/app/node/block/block.cpp +++ b/app/node/block/block.cpp @@ -22,6 +22,7 @@ #include +#include "common/qtutils.h" #include "node/inputdragger.h" #include "node/output/track/track.h" #include "transition/transition.h" @@ -38,8 +39,8 @@ Block::Block() : next_(nullptr), track_(nullptr) { - AddInput(kLengthInput, NodeValue::kRational, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagHidden)); - SetInputProperty(kLengthInput, QStringLiteral("min"), QVariant::fromValue(rational(0, 1))); + AddInput(kLengthInput, TYPE_RATIONAL, kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagHidden); + SetInputProperty(kLengthInput, QStringLiteral("min"), rational(0, 1)); SetInputProperty(kLengthInput, QStringLiteral("view"), RationalSlider::kTime); SetInputProperty(kLengthInput, QStringLiteral("viewlock"), true); @@ -91,7 +92,7 @@ void Block::InputValueChangedEvent(const QString &input, int element) void Block::set_length_internal(const rational &length) { - SetStandardValue(kLengthInput, QVariant::fromValue(length)); + SetStandardValue(kLengthInput, length); } void Block::Retranslate() diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index 01da6131b4..0f4ffb00f1 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -45,26 +45,25 @@ ClipBlock::ClipBlock() : out_transition_(nullptr), connected_viewer_(nullptr) { - AddInput(kMediaInInput, NodeValue::kRational, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + AddInput(kMediaInInput, TYPE_RATIONAL, kInputFlagNotConnectable | kInputFlagNotKeyframable); SetInputProperty(kMediaInInput, QStringLiteral("view"), RationalSlider::kTime); SetInputProperty(kMediaInInput, QStringLiteral("viewlock"), true); - AddInput(kSpeedInput, NodeValue::kFloat, 1.0, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + AddInput(kSpeedInput, TYPE_DOUBLE, 1.0, kInputFlagNotConnectable | kInputFlagNotKeyframable); SetInputProperty(kSpeedInput, QStringLiteral("view"), FloatSlider::kPercentage); SetInputProperty(kSpeedInput, QStringLiteral("min"), 0.0); - AddInput(kReverseInput, NodeValue::kBoolean, false, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + AddInput(kReverseInput, TYPE_BOOL, false, kInputFlagNotConnectable | kInputFlagNotKeyframable); - AddInput(kMaintainAudioPitchInput, NodeValue::kBoolean, false, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + AddInput(kMaintainAudioPitchInput, TYPE_BOOL, false, kInputFlagNotConnectable | kInputFlagNotKeyframable); - AddInput(kAutoCacheInput, NodeValue::kBoolean, false, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + AddInput(kAutoCacheInput, TYPE_BOOL, false, kInputFlagNotConnectable | kInputFlagNotKeyframable); - PrependInput(kBufferIn, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable)); - //SetValueHintForInput(kBufferIn, ValueHint(NodeValue::kBuffer)); + PrependInput(kBufferIn, kInputFlagNotKeyframable); SetEffectInput(kBufferIn); - AddInput(kLoopModeInput, NodeValue::kCombo, 0, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + AddInput(kLoopModeInput, TYPE_COMBO, 0, kInputFlagNotConnectable | kInputFlagNotKeyframable); } QString ClipBlock::Name() const @@ -130,7 +129,7 @@ rational ClipBlock::media_in() const void ClipBlock::set_media_in(const rational &media_in) { - SetStandardValue(kMediaInInput, QVariant::fromValue(media_in)); + SetStandardValue(kMediaInInput, media_in); RequestInvalidatedFromConnected(); } @@ -484,7 +483,7 @@ TimeRange ClipBlock::OutputTimeAdjustment(const QString& input, int element, con return super::OutputTimeAdjustment(input, element, input_time); } -NodeValue ClipBlock::Value(const ValueParams &p) const +value_t ClipBlock::Value(const ValueParams &p) const { return GetInputValue(p.loop_mode_edited(this->loop_mode()), kBufferIn); } diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index d766aca4cf..05a79c69be 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -72,7 +72,7 @@ class ClipBlock : public Block virtual TimeRange OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; virtual void Retranslate() override; diff --git a/app/node/block/subtitle/subtitle.cpp b/app/node/block/subtitle/subtitle.cpp index b645a15951..66333479b9 100644 --- a/app/node/block/subtitle/subtitle.cpp +++ b/app/node/block/subtitle/subtitle.cpp @@ -28,7 +28,7 @@ const QString SubtitleBlock::kTextIn = QStringLiteral("text_in"); SubtitleBlock::SubtitleBlock() { - AddInput(kTextIn, NodeValue::kText, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + AddInput(kTextIn, TYPE_STRING, kInputFlagNotConnectable | kInputFlagNotKeyframable); SetInputFlag(kBufferIn, kInputFlagHidden); SetInputFlag(kLengthInput, kInputFlagHidden); diff --git a/app/node/block/transition/diptocolor/diptocolortransition.cpp b/app/node/block/transition/diptocolor/diptocolortransition.cpp index cfeee782f1..6f37bdd803 100644 --- a/app/node/block/transition/diptocolor/diptocolortransition.cpp +++ b/app/node/block/transition/diptocolor/diptocolortransition.cpp @@ -28,7 +28,7 @@ const QString DipToColorTransition::kColorInput = QStringLiteral("color_in"); DipToColorTransition::DipToColorTransition() { - AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(0, 0, 0))); + AddInput(kColorInput, TYPE_COLOR, Color(0, 0, 0)); } QString DipToColorTransition::Name() const diff --git a/app/node/block/transition/transition.cpp b/app/node/block/transition/transition.cpp index 5d631ddc59..7ed02ebf19 100644 --- a/app/node/block/transition/transition.cpp +++ b/app/node/block/transition/transition.cpp @@ -37,13 +37,13 @@ TransitionBlock::TransitionBlock() : connected_out_block_(nullptr), connected_in_block_(nullptr) { - AddInput(kOutBlockInput, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable)); + AddInput(kOutBlockInput, kInputFlagNotKeyframable); - AddInput(kInBlockInput, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable)); + AddInput(kInBlockInput, kInputFlagNotKeyframable); - AddInput(kCurveInput, NodeValue::kCombo, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable)); + AddInput(kCurveInput, TYPE_COMBO, kInputFlagNotKeyframable | kInputFlagNotConnectable); - AddInput(kCenterInput, NodeValue::kRational, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable)); + AddInput(kCenterInput, TYPE_RATIONAL, kInputFlagNotKeyframable | kInputFlagNotConnectable); SetInputProperty(kCenterInput, QStringLiteral("view"), RationalSlider::kTime); SetInputProperty(kCenterInput, QStringLiteral("viewlock"), true); @@ -92,7 +92,7 @@ rational TransitionBlock::offset_center() const void TransitionBlock::set_offset_center(const rational &r) { - SetStandardValue(kCenterInput, QVariant::fromValue(r)); + SetStandardValue(kCenterInput, r); } void TransitionBlock::set_offsets_and_length(const rational &in_offset, const rational &out_offset) @@ -154,27 +154,27 @@ void TransitionBlock::InsertTransitionTimes(AcceleratedJob *job, const double &t job->Insert(QStringLiteral("ove_tprog_in"), GetInProgress(time)); } -NodeValue TransitionBlock::Value(const ValueParams &p) const +value_t TransitionBlock::Value(const ValueParams &p) const { - NodeValue out_buffer = GetInputValue(p, kOutBlockInput); - NodeValue in_buffer = GetInputValue(p, kInBlockInput); + value_t out_buffer = GetInputValue(p, kOutBlockInput); + value_t in_buffer = GetInputValue(p, kInBlockInput); - NodeValue::Type data_type = (out_buffer.type() != NodeValue::kNone) ? out_buffer.type() : in_buffer.type(); + type_t data_type = (out_buffer.type() != TYPE_NONE) ? out_buffer.type() : in_buffer.type(); - if (data_type == NodeValue::kTexture) { + if (data_type == TYPE_TEXTURE) { // This must be a visual transition ShaderJob job; - if (out_buffer.type() != NodeValue::kNone) { + if (out_buffer.type() != TYPE_NONE) { job.Insert(kOutBlockInput, out_buffer); } else { - job.Insert(kOutBlockInput, NodeValue(NodeValue::kTexture, nullptr)); + job.Insert(kOutBlockInput, TexturePtr(nullptr)); } - if (in_buffer.type() != NodeValue::kNone) { + if (in_buffer.type() != TYPE_NONE) { job.Insert(kInBlockInput, in_buffer); } else { - job.Insert(kInBlockInput, NodeValue(NodeValue::kTexture, nullptr)); + job.Insert(kInBlockInput, TexturePtr(nullptr)); } job.Insert(kCurveInput, GetInputValue(p, kCurveInput)); @@ -185,7 +185,7 @@ NodeValue TransitionBlock::Value(const ValueParams &p) const ShaderJobEvent(p, &job); return Texture::Job(p.vparams(), job); - } else if (data_type == NodeValue::kSamples) { + } else if (data_type == TYPE_SAMPLES) { // This must be an audio transition SampleJob job(p); @@ -197,7 +197,7 @@ NodeValue TransitionBlock::Value(const ValueParams &p) const return job; } - return NodeValue(); + return value_t(); } void TransitionBlock::InvalidateCache(const TimeRange &range, const QString &from, int element, InvalidateCacheOptions options) diff --git a/app/node/block/transition/transition.h b/app/node/block/transition/transition.h index e56b349d63..bc1f1e3aec 100644 --- a/app/node/block/transition/transition.h +++ b/app/node/block/transition/transition.h @@ -65,7 +65,7 @@ class TransitionBlock : public Block double TransformCurve(double linear) const; - virtual NodeValue Value(const ValueParams &globals) const override; + virtual value_t Value(const ValueParams &globals) const override; virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1, InvalidateCacheOptions options = InvalidateCacheOptions()) override; diff --git a/app/node/color/displaytransform/displaytransform.cpp b/app/node/color/displaytransform/displaytransform.cpp index 6bf2116c3f..a23ee75812 100644 --- a/app/node/color/displaytransform/displaytransform.cpp +++ b/app/node/color/displaytransform/displaytransform.cpp @@ -32,11 +32,11 @@ const QString DisplayTransformNode::kDirectionInput = QStringLiteral("dir_in"); DisplayTransformNode::DisplayTransformNode() { - AddInput(kDisplayInput, NodeValue::kCombo, 0, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable)); + AddInput(kDisplayInput, TYPE_COMBO, 0, kInputFlagNotKeyframable | kInputFlagNotConnectable); - AddInput(kViewInput, NodeValue::kCombo, 0, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable)); + AddInput(kViewInput, TYPE_COMBO, 0, kInputFlagNotKeyframable | kInputFlagNotConnectable); - AddInput(kDirectionInput, NodeValue::kCombo, 0, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable)); + AddInput(kDirectionInput, TYPE_COMBO, 0, kInputFlagNotKeyframable | kInputFlagNotConnectable); } QString DisplayTransformNode::Name() const diff --git a/app/node/color/ociobase/ociobase.cpp b/app/node/color/ociobase/ociobase.cpp index 6abbcaf9a2..101af326d2 100644 --- a/app/node/color/ociobase/ociobase.cpp +++ b/app/node/color/ociobase/ociobase.cpp @@ -31,7 +31,7 @@ OCIOBaseNode::OCIOBaseNode() : manager_(nullptr), processor_(nullptr) { - AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + AddInput(kTextureInput, TYPE_TEXTURE, kInputFlagNotKeyframable); SetEffectInput(kTextureInput); @@ -53,7 +53,7 @@ void OCIOBaseNode::RemovedFromGraphEvent(Project *p) } } -NodeValue OCIOBaseNode::Value(const ValueParams &p) const +value_t OCIOBaseNode::Value(const ValueParams &p) const { auto tex_met = GetInputValue(p, kTextureInput); TexturePtr t = tex_met.toTexture(); diff --git a/app/node/color/ociobase/ociobase.h b/app/node/color/ociobase/ociobase.h index ad79f46c4d..60e451491a 100644 --- a/app/node/color/ociobase/ociobase.h +++ b/app/node/color/ociobase/ociobase.h @@ -35,7 +35,7 @@ class OCIOBaseNode : public Node virtual void AddedToGraphEvent(Project *p) override; virtual void RemovedFromGraphEvent(Project *p) override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; static const QString kTextureInput; diff --git a/app/node/color/ociogradingtransformlinear/ociogradingtransformlinear.cpp b/app/node/color/ociogradingtransformlinear/ociogradingtransformlinear.cpp index 5c73b6e5c1..20ec92588f 100644 --- a/app/node/color/ociogradingtransformlinear/ociogradingtransformlinear.cpp +++ b/app/node/color/ociogradingtransformlinear/ociogradingtransformlinear.cpp @@ -43,36 +43,36 @@ const QString OCIOGradingTransformLinearNode::kClampWhiteInput = QStringLiteral( OCIOGradingTransformLinearNode::OCIOGradingTransformLinearNode() { - AddInput(kContrastInput, NodeValue::kVec4, QVector4D{1.0, 1.0, 1.0, 1.0}); + AddInput(kContrastInput, TYPE_VEC4, 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}); + AddInput(kOffsetInput, TYPE_VEC4, 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}); + AddInput(kExposureInput, TYPE_VEC4, QVector4D{0.0, 0.0, 0.0, 0.0}); SetInputProperty(kExposureInput, QStringLiteral("base"), 0.01); SetVec4InputColors(kExposureInput); - AddInput(kSaturationInput, NodeValue::kFloat, 1.0); + AddInput(kSaturationInput, TYPE_DOUBLE, 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 + AddInput(kPivotInput, TYPE_DOUBLE, 0.18); // Default listed in OCIO::GradingPrimary SetInputProperty(kPivotInput, QStringLiteral("base"), 0.01); - AddInput(kClampBlackEnableInput, NodeValue::kBoolean, false); + AddInput(kClampBlackEnableInput, TYPE_BOOL, false); - AddInput(kClampBlackInput, NodeValue::kFloat, 0.0); + AddInput(kClampBlackInput, TYPE_DOUBLE, 0.0); SetInputProperty(kClampBlackInput, QStringLiteral("enabled"), GetStandardValue(kClampBlackEnableInput).toBool()); SetInputProperty(kClampBlackInput, QStringLiteral("base"), 0.01); - AddInput(kClampWhiteEnableInput, NodeValue::kBoolean, false); + AddInput(kClampWhiteEnableInput, TYPE_BOOL, false); - AddInput(kClampWhiteInput, NodeValue::kFloat, 1.0); + AddInput(kClampWhiteInput, TYPE_DOUBLE, 1.0); SetInputProperty(kClampWhiteInput, QStringLiteral("enabled"), GetStandardValue(kClampWhiteEnableInput).toBool()); SetInputProperty(kClampWhiteInput, QStringLiteral("base"), 0.01); @@ -153,9 +153,9 @@ void OCIOGradingTransformLinearNode::GenerateProcessor() } } -NodeValue OCIOGradingTransformLinearNode::Value(const ValueParams &p) const +value_t OCIOGradingTransformLinearNode::Value(const ValueParams &p) const { - NodeValue tex_meta = GetInputValue(p, kTextureInput); + value_t tex_meta = GetInputValue(p, kTextureInput); if (TexturePtr tex = tex_meta.toTexture()) { if (processor()) { diff --git a/app/node/color/ociogradingtransformlinear/ociogradingtransformlinear.h b/app/node/color/ociogradingtransformlinear/ociogradingtransformlinear.h index 4e0eaa113a..d3fd302492 100644 --- a/app/node/color/ociogradingtransformlinear/ociogradingtransformlinear.h +++ b/app/node/color/ociogradingtransformlinear/ociogradingtransformlinear.h @@ -43,7 +43,7 @@ class OCIOGradingTransformLinearNode : public OCIOBaseNode virtual void InputValueChangedEvent(const QString &input, int element) override; void GenerateProcessor(); - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; static const QString kContrastInput; static const QString kOffsetInput; diff --git a/app/node/distort/cornerpin/cornerpindistortnode.cpp b/app/node/distort/cornerpin/cornerpindistortnode.cpp index 7b2ed0ca03..b10d603c8b 100644 --- a/app/node/distort/cornerpin/cornerpindistortnode.cpp +++ b/app/node/distort/cornerpin/cornerpindistortnode.cpp @@ -37,12 +37,12 @@ const QString CornerPinDistortNode::kPerspectiveInput = QStringLiteral("perspect CornerPinDistortNode::CornerPinDistortNode() { - AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); - AddInput(kPerspectiveInput, NodeValue::kBoolean, true); - AddInput(kTopLeftInput, NodeValue::kVec2, QVector2D(0.0, 0.0)); - AddInput(kTopRightInput, NodeValue::kVec2, QVector2D(0.0, 0.0)); - AddInput(kBottomRightInput, NodeValue::kVec2, QVector2D(0.0, 0.0)); - AddInput(kBottomLeftInput, NodeValue::kVec2, QVector2D(0.0, 0.0)); + AddInput(kTextureInput, TYPE_TEXTURE, kInputFlagNotKeyframable); + AddInput(kPerspectiveInput, TYPE_BOOL, true); + AddInput(kTopLeftInput, TYPE_VEC2, QVector2D(0.0, 0.0)); + AddInput(kTopRightInput, TYPE_VEC2, QVector2D(0.0, 0.0)); + AddInput(kBottomRightInput, TYPE_VEC2, QVector2D(0.0, 0.0)); + AddInput(kBottomLeftInput, TYPE_VEC2, QVector2D(0.0, 0.0)); // Initiate gizmos gizmo_whole_rect_ = AddDraggableGizmo(); @@ -73,10 +73,10 @@ ShaderCode CornerPinDistortNode::GetShaderCode(const QString &id) FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/cornerpin.vert"))); } -NodeValue CornerPinDistortNode::Value(const ValueParams &p) const +value_t CornerPinDistortNode::Value(const ValueParams &p) const { // If no texture do nothing - NodeValue tex_meta = GetInputValue(p, kTextureInput); + value_t tex_meta = GetInputValue(p, kTextureInput); if (TexturePtr tex = tex_meta.toTexture()) { // In the special case that all sliders are in their default position just @@ -143,8 +143,8 @@ void CornerPinDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardM DraggableGizmo *gizmo = static_cast(sender()); if (gizmo != gizmo_whole_rect_) { - gizmo->GetDraggers()[0].Drag(gizmo->GetDraggers()[0].GetStartValue().toDouble() + x); - gizmo->GetDraggers()[1].Drag(gizmo->GetDraggers()[1].GetStartValue().toDouble() + y); + gizmo->GetDraggers()[0].Drag(gizmo->GetDraggers()[0].GetStartValue().get() + x); + gizmo->GetDraggers()[1].Drag(gizmo->GetDraggers()[1].GetStartValue().get() + y); } } diff --git a/app/node/distort/cornerpin/cornerpindistortnode.h b/app/node/distort/cornerpin/cornerpindistortnode.h index 35124c9d70..5273fbbd7e 100644 --- a/app/node/distort/cornerpin/cornerpindistortnode.h +++ b/app/node/distort/cornerpin/cornerpindistortnode.h @@ -59,7 +59,7 @@ class CornerPinDistortNode : public Node virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; virtual void UpdateGizmoPositions(const ValueParams &p) override; diff --git a/app/node/distort/crop/cropdistortnode.cpp b/app/node/distort/crop/cropdistortnode.cpp index e52d3ea56b..fdb700a2f1 100644 --- a/app/node/distort/crop/cropdistortnode.cpp +++ b/app/node/distort/crop/cropdistortnode.cpp @@ -37,14 +37,14 @@ const QString CropDistortNode::kFeatherInput = QStringLiteral("feather_in"); CropDistortNode::CropDistortNode() { - AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + AddInput(kTextureInput, TYPE_TEXTURE, kInputFlagNotKeyframable); CreateCropSideInput(kLeftInput); CreateCropSideInput(kTopInput); CreateCropSideInput(kRightInput); CreateCropSideInput(kBottomInput); - AddInput(kFeatherInput, NodeValue::kFloat, 0.0); + AddInput(kFeatherInput, TYPE_DOUBLE, 0.0); SetInputProperty(kFeatherInput, QStringLiteral("min"), 0.0); // Initiate gizmos @@ -80,9 +80,9 @@ ShaderCode CropDistortNode::GetShaderCode(const QString &id) return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/crop.frag"))); } -NodeValue CropDistortNode::Value(const ValueParams &p) const +value_t CropDistortNode::Value(const ValueParams &p) const { - NodeValue tex_meta = GetInputValue(p, kTextureInput); + value_t tex_meta = GetInputValue(p, kTextureInput); if (TexturePtr texture = tex_meta.toTexture()) { ShaderJob job = CreateShaderJob(p, GetShaderCode); @@ -135,7 +135,7 @@ void CropDistortNode::GizmoDragMove(double x_diff, double y_diff, const Qt::Keyb for (int j=0; jGetDraggers().size(); j++) { NodeInputDragger& i = gizmo->GetDraggers()[j]; - double s = i.GetStartValue().toDouble(); + double s = i.GetStartValue().get(); if (i.GetInput().input().input() == kLeftInput) { i.Drag(s + x_diff); } else if (i.GetInput().input().input() == kTopInput) { @@ -150,7 +150,7 @@ void CropDistortNode::GizmoDragMove(double x_diff, double y_diff, const Qt::Keyb void CropDistortNode::CreateCropSideInput(const QString &id) { - AddInput(id, NodeValue::kFloat, 0.0); + AddInput(id, TYPE_DOUBLE, 0.0); SetInputProperty(id, QStringLiteral("min"), 0.0); SetInputProperty(id, QStringLiteral("max"), 1.0); SetInputProperty(id, QStringLiteral("view"), FloatSlider::kPercentage); diff --git a/app/node/distort/crop/cropdistortnode.h b/app/node/distort/crop/cropdistortnode.h index 4d758ed888..156e6462ef 100644 --- a/app/node/distort/crop/cropdistortnode.h +++ b/app/node/distort/crop/cropdistortnode.h @@ -60,7 +60,7 @@ class CropDistortNode : public Node virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; virtual void UpdateGizmoPositions(const ValueParams &p) override; diff --git a/app/node/distort/flip/flipdistortnode.cpp b/app/node/distort/flip/flipdistortnode.cpp index 8cbb6d7d57..d3f0b4efe1 100644 --- a/app/node/distort/flip/flipdistortnode.cpp +++ b/app/node/distort/flip/flipdistortnode.cpp @@ -30,11 +30,11 @@ const QString FlipDistortNode::kVerticalInput = QStringLiteral("vert_in"); FlipDistortNode::FlipDistortNode() { - AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + AddInput(kTextureInput, TYPE_TEXTURE, kInputFlagNotKeyframable); - AddInput(kHorizontalInput, NodeValue::kBoolean, false); + AddInput(kHorizontalInput, TYPE_BOOL, false); - AddInput(kVerticalInput, NodeValue::kBoolean, false); + AddInput(kVerticalInput, TYPE_BOOL, false); SetFlag(kVideoEffect); SetEffectInput(kTextureInput); @@ -74,10 +74,10 @@ ShaderCode FlipDistortNode::GetShaderCode(const QString &id) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/flip.frag")); } -NodeValue FlipDistortNode::Value(const ValueParams &p) const +value_t FlipDistortNode::Value(const ValueParams &p) const { // If there's no texture, no need to run an operation - NodeValue v = GetInputValue(p, kTextureInput); + value_t v = GetInputValue(p, kTextureInput); if (TexturePtr tex = v.toTexture()) { // Only run shader if at least one of flip or flop are selected diff --git a/app/node/distort/flip/flipdistortnode.h b/app/node/distort/flip/flipdistortnode.h index e0980cb1b6..e6fc238145 100644 --- a/app/node/distort/flip/flipdistortnode.h +++ b/app/node/distort/flip/flipdistortnode.h @@ -40,7 +40,7 @@ class FlipDistortNode : public Node virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &globals) const override; + virtual value_t Value(const ValueParams &globals) const override; static const QString kTextureInput; static const QString kHorizontalInput; diff --git a/app/node/distort/mask/mask.cpp b/app/node/distort/mask/mask.cpp index 1341641738..9852e74462 100644 --- a/app/node/distort/mask/mask.cpp +++ b/app/node/distort/mask/mask.cpp @@ -34,9 +34,9 @@ MaskDistortNode::MaskDistortNode() // Mask should always be (1.0, 1.0, 1.0) for multiply to work correctly SetInputFlag(kColorInput, kInputFlagHidden); - AddInput(kInvertInput, NodeValue::kBoolean, false); + AddInput(kInvertInput, TYPE_BOOL, false); - AddInput(kFeatherInput, NodeValue::kFloat, 0.0); + AddInput(kFeatherInput, TYPE_DOUBLE, 0.0); SetInputProperty(kFeatherInput, QStringLiteral("min"), 0.0); } @@ -64,12 +64,12 @@ void MaskDistortNode::Retranslate() SetInputName(kFeatherInput, tr("Feather")); } -NodeValue MaskDistortNode::Value(const ValueParams &p) const +value_t MaskDistortNode::Value(const ValueParams &p) const { TexturePtr texture = GetInputValue(p, kBaseInput).toTexture(); VideoParams job_params = texture ? texture->params() : p.vparams(); - NodeValue job = Texture::Job(job_params, GetGenerateJob(p, job_params)); + value_t job = Texture::Job(job_params, GetGenerateJob(p, job_params)); if (GetInputValue(p, kInvertInput).toBool()) { ShaderJob invert; @@ -104,7 +104,7 @@ NodeValue MaskDistortNode::Value(const ValueParams &p) const merge.Insert(QStringLiteral("tex_b"), job); } - return NodeValue(NodeValue::kTexture, Texture::Job(job_params, merge)); + return Texture::Job(job_params, merge); } else { return job; } diff --git a/app/node/distort/mask/mask.h b/app/node/distort/mask/mask.h index 38f31ab63a..4fd7f4d822 100644 --- a/app/node/distort/mask/mask.h +++ b/app/node/distort/mask/mask.h @@ -55,7 +55,7 @@ class MaskDistortNode : public PolygonGenerator virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; static const QString kInvertInput; static const QString kFeatherInput; diff --git a/app/node/distort/ripple/rippledistortnode.cpp b/app/node/distort/ripple/rippledistortnode.cpp index 112e0cd6db..9b91e95acb 100644 --- a/app/node/distort/ripple/rippledistortnode.cpp +++ b/app/node/distort/ripple/rippledistortnode.cpp @@ -33,16 +33,16 @@ const QString RippleDistortNode::kStretchInput = QStringLiteral("stretch_in"); RippleDistortNode::RippleDistortNode() { - AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + AddInput(kTextureInput, TYPE_TEXTURE, kInputFlagNotKeyframable); - AddInput(kEvolutionInput, NodeValue::kFloat, 0); - AddInput(kIntensityInput, NodeValue::kFloat, 100); + AddInput(kEvolutionInput, TYPE_DOUBLE, 0); + AddInput(kIntensityInput, TYPE_DOUBLE, 100); - AddInput(kFrequencyInput, NodeValue::kFloat, 1); + AddInput(kFrequencyInput, TYPE_DOUBLE, 1); SetInputProperty(kFrequencyInput, QStringLiteral("base"), 0.01); - AddInput(kPositionInput, NodeValue::kVec2, QVector2D(0, 0)); - AddInput(kStretchInput, NodeValue::kBoolean, false); + AddInput(kPositionInput, TYPE_VEC2, QVector2D(0, 0)); + AddInput(kStretchInput, TYPE_BOOL, false); SetFlag(kVideoEffect); SetEffectInput(kTextureInput); @@ -91,14 +91,14 @@ ShaderCode RippleDistortNode::GetShaderCode(const QString &id) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/ripple.frag")); } -NodeValue RippleDistortNode::Value(const ValueParams &p) const +value_t RippleDistortNode::Value(const ValueParams &p) const { // If there's no texture, no need to run an operation - NodeValue texture = GetInputValue(p, kTextureInput); + value_t texture = GetInputValue(p, kTextureInput); if (TexturePtr tex = texture.toTexture()) { // Only run shader if at least one of flip or flop are selected - NodeValue intensity = GetInputValue(p, kIntensityInput); + value_t intensity = GetInputValue(p, kIntensityInput); if (!qIsNull(intensity.toDouble())) { ShaderJob job; @@ -119,7 +119,7 @@ NodeValue RippleDistortNode::Value(const ValueParams &p) const } } - return NodeValue(); + return value_t(); } void RippleDistortNode::UpdateGizmoPositions(const ValueParams &p) @@ -135,8 +135,8 @@ void RippleDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardModi 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); + x_drag.Drag(x_drag.GetStartValue().get() + x); + y_drag.Drag(y_drag.GetStartValue().get() + y); } } diff --git a/app/node/distort/ripple/rippledistortnode.h b/app/node/distort/ripple/rippledistortnode.h index 81ae9c40ef..43841fd984 100644 --- a/app/node/distort/ripple/rippledistortnode.h +++ b/app/node/distort/ripple/rippledistortnode.h @@ -41,7 +41,7 @@ class RippleDistortNode : public Node virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; virtual void UpdateGizmoPositions(const ValueParams &p) override; diff --git a/app/node/distort/swirl/swirldistortnode.cpp b/app/node/distort/swirl/swirldistortnode.cpp index 0a3d290f78..09eaad3747 100644 --- a/app/node/distort/swirl/swirldistortnode.cpp +++ b/app/node/distort/swirl/swirldistortnode.cpp @@ -31,15 +31,15 @@ const QString SwirlDistortNode::kPositionInput = QStringLiteral("pos_in"); SwirlDistortNode::SwirlDistortNode() { - AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + AddInput(kTextureInput, TYPE_TEXTURE, kInputFlagNotKeyframable); - AddInput(kRadiusInput, NodeValue::kFloat, 200); + AddInput(kRadiusInput, TYPE_DOUBLE, 200); SetInputProperty(kRadiusInput, QStringLiteral("min"), 0); - AddInput(kAngleInput, NodeValue::kFloat, 10); + AddInput(kAngleInput, TYPE_DOUBLE, 10); SetInputProperty(kAngleInput, QStringLiteral("base"), 0.1); - AddInput(kPositionInput, NodeValue::kVec2, QVector2D(0, 0)); + AddInput(kPositionInput, TYPE_VEC2, QVector2D(0, 0)); SetFlag(kVideoEffect); SetEffectInput(kTextureInput); @@ -86,10 +86,10 @@ ShaderCode SwirlDistortNode::GetShaderCode(const QString &id) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/swirl.frag")); } -NodeValue SwirlDistortNode::Value(const ValueParams &p) const +value_t SwirlDistortNode::Value(const ValueParams &p) const { // If there's no texture, no need to run an operation - NodeValue tex_meta = GetInputValue(p, kTextureInput); + value_t tex_meta = GetInputValue(p, kTextureInput); if (TexturePtr tex = tex_meta.toTexture()) { // Only run shader if at least one of flip or flop are selected @@ -115,8 +115,8 @@ void SwirlDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardModif 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); + x_drag.Drag(x_drag.GetStartValue().get() + x); + y_drag.Drag(y_drag.GetStartValue().get() + y); } } diff --git a/app/node/distort/swirl/swirldistortnode.h b/app/node/distort/swirl/swirldistortnode.h index 27577cd384..9e3bed93bd 100644 --- a/app/node/distort/swirl/swirldistortnode.h +++ b/app/node/distort/swirl/swirldistortnode.h @@ -41,7 +41,7 @@ class SwirlDistortNode : public Node virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; virtual void UpdateGizmoPositions(const ValueParams &p) override; diff --git a/app/node/distort/tile/tiledistortnode.cpp b/app/node/distort/tile/tiledistortnode.cpp index 63b1e67c3c..79f796a41a 100644 --- a/app/node/distort/tile/tiledistortnode.cpp +++ b/app/node/distort/tile/tiledistortnode.cpp @@ -35,18 +35,18 @@ const QString TileDistortNode::kMirrorYInput = QStringLiteral("mirrory_in"); TileDistortNode::TileDistortNode() { - AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + AddInput(kTextureInput, TYPE_TEXTURE, kInputFlagNotKeyframable); - AddInput(kScaleInput, NodeValue::kFloat, 0.5); + AddInput(kScaleInput, TYPE_DOUBLE, 0.5); SetInputProperty(kScaleInput, QStringLiteral("min"), 0); SetInputProperty(kScaleInput, QStringLiteral("view"), FloatSlider::kPercentage); - AddInput(kPositionInput, NodeValue::kVec2, QVector2D(0, 0)); + AddInput(kPositionInput, TYPE_VEC2, QVector2D(0, 0)); - AddInput(kAnchorInput, NodeValue::kCombo, kMiddleCenter); + AddInput(kAnchorInput, TYPE_COMBO, kMiddleCenter); - AddInput(kMirrorXInput, NodeValue::kBoolean, false); - AddInput(kMirrorYInput, NodeValue::kBoolean, false); + AddInput(kMirrorXInput, TYPE_BOOL, false); + AddInput(kMirrorYInput, TYPE_BOOL, false); SetFlag(kVideoEffect); SetEffectInput(kTextureInput); @@ -107,10 +107,10 @@ ShaderCode TileDistortNode::GetShaderCode(const QString &id) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/tile.frag")); } -NodeValue TileDistortNode::Value(const ValueParams &p) const +value_t TileDistortNode::Value(const ValueParams &p) const { // If there's no texture, no need to run an operation - NodeValue texture = GetInputValue(p, kTextureInput); + value_t texture = GetInputValue(p, kTextureInput); if (TexturePtr tex = texture.toTexture()) { // Only run shader if at least one of flip or flop are selected @@ -157,8 +157,8 @@ void TileDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardModifi 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); + x_drag.Drag(x_drag.GetStartValue().get() + x); + y_drag.Drag(y_drag.GetStartValue().get() + y); } } diff --git a/app/node/distort/tile/tiledistortnode.h b/app/node/distort/tile/tiledistortnode.h index 62f0db962d..4d9a84a58d 100644 --- a/app/node/distort/tile/tiledistortnode.h +++ b/app/node/distort/tile/tiledistortnode.h @@ -41,7 +41,7 @@ class TileDistortNode : public Node virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; virtual void UpdateGizmoPositions(const ValueParams &p) override; diff --git a/app/node/distort/transform/transformdistortnode.cpp b/app/node/distort/transform/transformdistortnode.cpp index 377edda061..689e979165 100644 --- a/app/node/distort/transform/transformdistortnode.cpp +++ b/app/node/distort/transform/transformdistortnode.cpp @@ -33,13 +33,13 @@ const QString TransformDistortNode::kInterpolationInput = QStringLiteral("interp TransformDistortNode::TransformDistortNode() { - AddInput(kParentInput, NodeValue::kMatrix); + AddInput(kParentInput, TYPE_MATRIX); - AddInput(kAutoscaleInput, NodeValue::kCombo, 0); + AddInput(kAutoscaleInput, TYPE_COMBO, 0); - AddInput(kInterpolationInput, NodeValue::kCombo, 2); + AddInput(kInterpolationInput, TYPE_COMBO, 2); - PrependInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + PrependInput(kTextureInput, TYPE_TEXTURE, kInputFlagNotKeyframable); // Initiate gizmos rotation_gizmo_ = AddDraggableGizmo(); @@ -87,13 +87,13 @@ ShaderCode TransformDistortNode::GetShaderCode(const QString &id) return ShaderCode(); } -NodeValue TransformDistortNode::Value(const ValueParams &p) const +value_t TransformDistortNode::Value(const ValueParams &p) const { // Generate matrix QMatrix4x4 generated_matrix = GenerateMatrix(p, false, false, false, GetInputValue(p, kParentInput).toMatrix()); // Pop texture - NodeValue texture_meta = GetInputValue(p, kTextureInput); + value_t texture_meta = GetInputValue(p, kTextureInput); TexturePtr job_to_push = nullptr; @@ -196,8 +196,8 @@ void TransformDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardM 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); + x_drag.Drag(x_drag.GetStartValue().get() + x); + y_drag.Drag(y_drag.GetStartValue().get() + y); } else if (gizmo == anchor_gizmo_) { @@ -208,10 +208,10 @@ void TransformDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardM QPointF inverted_movement(gizmo_inverted_transform_.map(QPointF(x, y))); - x_anchor_drag.Drag(x_anchor_drag.GetStartValue().toDouble() + inverted_movement.x()); - y_anchor_drag.Drag(y_anchor_drag.GetStartValue().toDouble() + inverted_movement.y()); - x_pos_drag.Drag(x_pos_drag.GetStartValue().toDouble() + x); - y_pos_drag.Drag(y_pos_drag.GetStartValue().toDouble() + y); + x_anchor_drag.Drag(x_anchor_drag.GetStartValue().get() + inverted_movement.x()); + y_anchor_drag.Drag(y_anchor_drag.GetStartValue().get() + inverted_movement.y()); + x_pos_drag.Drag(x_pos_drag.GetStartValue().get() + x); + y_pos_drag.Drag(y_pos_drag.GetStartValue().get() + y); } else if (gizmo == rotation_gizmo_) { @@ -248,7 +248,7 @@ void TransformDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardM double rotation_difference = (current_angle - gizmo_start_angle_) * 57.2958; NodeInputDragger &d = gizmo->GetDraggers()[0]; - d.Drag(d.GetStartValue().toDouble() + rotation_difference); + d.Drag(d.GetStartValue().get() + rotation_difference); } else if (IsAScaleGizmo(gizmo)) { diff --git a/app/node/distort/transform/transformdistortnode.h b/app/node/distort/transform/transformdistortnode.h index 7f55f6e066..ebbb6c5a44 100644 --- a/app/node/distort/transform/transformdistortnode.h +++ b/app/node/distort/transform/transformdistortnode.h @@ -64,7 +64,7 @@ class TransformDistortNode : public MatrixGenerator virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; enum AutoScaleType { kAutoScaleNone, diff --git a/app/node/distort/wave/wavedistortnode.cpp b/app/node/distort/wave/wavedistortnode.cpp index b9e3e519a4..5679b4b552 100644 --- a/app/node/distort/wave/wavedistortnode.cpp +++ b/app/node/distort/wave/wavedistortnode.cpp @@ -32,13 +32,13 @@ const QString WaveDistortNode::kVerticalInput = QStringLiteral("vertical_in"); WaveDistortNode::WaveDistortNode() { - AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + AddInput(kTextureInput, TYPE_TEXTURE, kInputFlagNotKeyframable); - AddInput(kFrequencyInput, NodeValue::kFloat, 10); - AddInput(kIntensityInput, NodeValue::kFloat, 10); - AddInput(kEvolutionInput, NodeValue::kFloat, 0); + AddInput(kFrequencyInput, TYPE_DOUBLE, 10); + AddInput(kIntensityInput, TYPE_DOUBLE, 10); + AddInput(kEvolutionInput, TYPE_DOUBLE, 0); - AddInput(kVerticalInput, NodeValue::kCombo, false); + AddInput(kVerticalInput, TYPE_COMBO, false); SetFlag(kVideoEffect); SetEffectInput(kTextureInput); @@ -81,10 +81,10 @@ ShaderCode WaveDistortNode::GetShaderCode(const QString &id) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/wave.frag")); } -NodeValue WaveDistortNode::Value(const ValueParams &p) const +value_t WaveDistortNode::Value(const ValueParams &p) const { // If there's no texture, no need to run an operation - NodeValue tex_meta = GetInputValue(p, kTextureInput); + value_t tex_meta = GetInputValue(p, kTextureInput); if (TexturePtr texture = tex_meta.toTexture()) { // Only run shader if at least one of flip or flop are selected diff --git a/app/node/distort/wave/wavedistortnode.h b/app/node/distort/wave/wavedistortnode.h index cf2d89abaa..b7b1b8aa91 100644 --- a/app/node/distort/wave/wavedistortnode.h +++ b/app/node/distort/wave/wavedistortnode.h @@ -40,7 +40,7 @@ class WaveDistortNode : public Node virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &globals) const override; + virtual value_t Value(const ValueParams &globals) const override; static const QString kTextureInput; static const QString kFrequencyInput; diff --git a/app/node/effect/opacity/opacityeffect.cpp b/app/node/effect/opacity/opacityeffect.cpp index 81fbb8c8b4..f503bbeb2b 100644 --- a/app/node/effect/opacity/opacityeffect.cpp +++ b/app/node/effect/opacity/opacityeffect.cpp @@ -18,9 +18,9 @@ OpacityEffect::OpacityEffect() SetNodePositionInContext(math, QPointF(0, 0)); - AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + AddInput(kTextureInput, TYPE_TEXTURE, kInputFlagNotKeyframable); - AddInput(kValueInput, NodeValue::kFloat, 1.0); + AddInput(kValueInput, TYPE_DOUBLE, 1.0); SetInputProperty(kValueInput, QStringLiteral("view"), FloatSlider::kPercentage); SetInputProperty(kValueInput, QStringLiteral("min"), 0.0); SetInputProperty(kValueInput, QStringLiteral("max"), 1.0); @@ -47,11 +47,11 @@ ShaderCode GetNumberShaderCode(const QString &id) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/opacity.frag")); } -NodeValue OpacityEffect::Value(const ValueParams &p) const +value_t OpacityEffect::Value(const ValueParams &p) const { // If there's no texture, no need to run an operation - NodeValue texture = GetInputValue(p, kTextureInput); - NodeValue value = GetInputValue(p, kValueInput); + value_t texture = GetInputValue(p, kTextureInput); + value_t value = GetInputValue(p, kValueInput); if (TexturePtr tex = texture.toTexture()) { if (TexturePtr opacity_tex = value.toTexture()) { diff --git a/app/node/effect/opacity/opacityeffect.h b/app/node/effect/opacity/opacityeffect.h index 94ec474c01..f057d61fcd 100644 --- a/app/node/effect/opacity/opacityeffect.h +++ b/app/node/effect/opacity/opacityeffect.h @@ -34,7 +34,7 @@ class OpacityEffect : public Node virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; static const QString kTextureInput; static const QString kValueInput; diff --git a/app/node/filter/blur/blur.cpp b/app/node/filter/blur/blur.cpp index 4e61946147..50da0e3f53 100644 --- a/app/node/filter/blur/blur.cpp +++ b/app/node/filter/blur/blur.cpp @@ -37,34 +37,34 @@ const QString BlurFilterNode::kRadialCenterInput = QStringLiteral("radial_center BlurFilterNode::BlurFilterNode() { - AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + AddInput(kTextureInput, TYPE_TEXTURE, kInputFlagNotKeyframable); - Method default_method = kGaussian; + const Method default_method = kGaussian; - AddInput(kMethodInput, NodeValue::kCombo, default_method, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable)); + AddInput(kMethodInput, TYPE_COMBO, default_method, kInputFlagNotKeyframable | kInputFlagNotConnectable); - AddInput(kRadiusInput, NodeValue::kFloat, 10.0); + AddInput(kRadiusInput, TYPE_DOUBLE, 10.0); SetInputProperty(kRadiusInput, QStringLiteral("min"), 0.0); { // Box and gaussian only - AddInput(kHorizInput, NodeValue::kBoolean, true); - AddInput(kVertInput, NodeValue::kBoolean, true); + AddInput(kHorizInput, TYPE_BOOL, true); + AddInput(kVertInput, TYPE_BOOL, true); } { // Directional only - AddInput(kDirectionalDegreesInput, NodeValue::kFloat, 0.0); + AddInput(kDirectionalDegreesInput, TYPE_DOUBLE, 0.0); } { // Radial only - AddInput(kRadialCenterInput, NodeValue::kVec2, QVector2D(0, 0)); + AddInput(kRadialCenterInput, TYPE_VEC2, QVector2D(0, 0)); } UpdateInputs(default_method); - AddInput(kRepeatEdgePixelsInput, NodeValue::kBoolean, true); + AddInput(kRepeatEdgePixelsInput, TYPE_BOOL, true); SetFlag(kVideoEffect); SetEffectInput(kTextureInput); @@ -116,10 +116,10 @@ ShaderCode BlurFilterNode::GetShaderCode(const QString &id) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/blur.frag")); } -NodeValue BlurFilterNode::Value(const ValueParams &p) const +value_t BlurFilterNode::Value(const ValueParams &p) const { // If there's no texture, no need to run an operation - NodeValue tex_meta = GetInputValue(p, kTextureInput); + value_t tex_meta = GetInputValue(p, kTextureInput); if (TexturePtr tex = tex_meta.toTexture()) { Method method = static_cast(GetInputValue(p, kMethodInput).toInt()); @@ -192,8 +192,8 @@ void BlurFilterNode::GizmoDragMove(double x, double y, const Qt::KeyboardModifie 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); + x_drag.Drag(x_drag.GetStartValue().get() + x); + y_drag.Drag(y_drag.GetStartValue().get() + y); } } diff --git a/app/node/filter/blur/blur.h b/app/node/filter/blur/blur.h index 5b45a82081..cc7ebedd9d 100644 --- a/app/node/filter/blur/blur.h +++ b/app/node/filter/blur/blur.h @@ -48,7 +48,7 @@ class BlurFilterNode : public Node virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &globals) const override; + virtual value_t Value(const ValueParams &globals) const override; Method GetMethod() const { diff --git a/app/node/filter/dropshadow/dropshadowfilter.cpp b/app/node/filter/dropshadow/dropshadowfilter.cpp index e7cade7cba..3a8449e56f 100644 --- a/app/node/filter/dropshadow/dropshadowfilter.cpp +++ b/app/node/filter/dropshadow/dropshadowfilter.cpp @@ -36,22 +36,22 @@ const QString DropShadowFilter::kFastInput = QStringLiteral("fast_in"); DropShadowFilter::DropShadowFilter() { - AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + AddInput(kTextureInput, TYPE_TEXTURE, kInputFlagNotKeyframable); - AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(0.0, 0.0, 0.0))); + AddInput(kColorInput, TYPE_COLOR, Color(0.0, 0.0, 0.0)); - AddInput(kDistanceInput, NodeValue::kFloat, 10.0); + AddInput(kDistanceInput, TYPE_DOUBLE, 10.0); - AddInput(kAngleInput, NodeValue::kFloat, 135.0); + AddInput(kAngleInput, TYPE_DOUBLE, 135.0); - AddInput(kSoftnessInput, NodeValue::kFloat, 10.0); + AddInput(kSoftnessInput, TYPE_DOUBLE, 10.0); SetInputProperty(kSoftnessInput, QStringLiteral("min"), 0.0); - AddInput(kOpacityInput, NodeValue::kFloat, 1.0); + AddInput(kOpacityInput, TYPE_DOUBLE, 1.0); SetInputProperty(kOpacityInput, QStringLiteral("min"), 0.0); SetInputProperty(kOpacityInput, QStringLiteral("view"), FloatSlider::kPercentage); - AddInput(kFastInput, NodeValue::kBoolean, false); + AddInput(kFastInput, TYPE_BOOL, false); SetEffectInput(kTextureInput); SetFlag(kVideoEffect); @@ -75,9 +75,9 @@ ShaderCode DropShadowFilter::GetShaderCode(const QString &id) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/dropshadow.frag")); } -NodeValue DropShadowFilter::Value(const ValueParams &p) const +value_t DropShadowFilter::Value(const ValueParams &p) const { - NodeValue tex_meta = GetInputValue(p, kTextureInput); + value_t tex_meta = GetInputValue(p, kTextureInput); if (TexturePtr tex = tex_meta.toTexture()) { ShaderJob job = CreateShaderJob(p, GetShaderCode); @@ -94,7 +94,7 @@ NodeValue DropShadowFilter::Value(const ValueParams &p) const return tex->toJob(job); } - return NodeValue(); + return value_t(); } } diff --git a/app/node/filter/dropshadow/dropshadowfilter.h b/app/node/filter/dropshadow/dropshadowfilter.h index 1d8e639cac..75189301f6 100644 --- a/app/node/filter/dropshadow/dropshadowfilter.h +++ b/app/node/filter/dropshadow/dropshadowfilter.h @@ -40,7 +40,7 @@ class DropShadowFilter : public Node virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; static const QString kTextureInput; static const QString kColorInput; diff --git a/app/node/filter/mosaic/mosaicfilternode.cpp b/app/node/filter/mosaic/mosaicfilternode.cpp index 3b6800ae4a..b90d14bb54 100644 --- a/app/node/filter/mosaic/mosaicfilternode.cpp +++ b/app/node/filter/mosaic/mosaicfilternode.cpp @@ -30,12 +30,12 @@ const QString MosaicFilterNode::kVertInput = QStringLiteral("vert_in"); MosaicFilterNode::MosaicFilterNode() { - AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + AddInput(kTextureInput, TYPE_TEXTURE, kInputFlagNotKeyframable); - AddInput(kHorizInput, NodeValue::kFloat, 32.0); + AddInput(kHorizInput, TYPE_DOUBLE, 32.0); SetInputProperty(kHorizInput, QStringLiteral("min"), 1.0); - AddInput(kVertInput, NodeValue::kFloat, 18.0); + AddInput(kVertInput, TYPE_DOUBLE, 18.0); SetInputProperty(kVertInput, QStringLiteral("min"), 1.0); SetFlag(kVideoEffect); @@ -56,9 +56,9 @@ ShaderCode MosaicFilterNode::GetShaderCode(const QString &id) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/mosaic.frag")); } -NodeValue MosaicFilterNode::Value(const ValueParams &p) const +value_t MosaicFilterNode::Value(const ValueParams &p) const { - NodeValue tex_meta = GetInputValue(p, kTextureInput); + value_t tex_meta = GetInputValue(p, kTextureInput); if (TexturePtr texture = tex_meta.toTexture()) { if (texture diff --git a/app/node/filter/mosaic/mosaicfilternode.h b/app/node/filter/mosaic/mosaicfilternode.h index 72eccf9071..8617c3f523 100644 --- a/app/node/filter/mosaic/mosaicfilternode.h +++ b/app/node/filter/mosaic/mosaicfilternode.h @@ -55,7 +55,7 @@ class MosaicFilterNode : public Node virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; static const QString kTextureInput; static const QString kHorizInput; diff --git a/app/node/filter/stroke/stroke.cpp b/app/node/filter/stroke/stroke.cpp index 2809b30893..0f15207b65 100644 --- a/app/node/filter/stroke/stroke.cpp +++ b/app/node/filter/stroke/stroke.cpp @@ -34,19 +34,19 @@ const QString StrokeFilterNode::kInnerInput = QStringLiteral("inner_in"); StrokeFilterNode::StrokeFilterNode() { - AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + AddInput(kTextureInput, TYPE_TEXTURE, kInputFlagNotKeyframable); - AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0f, 1.0f, 1.0f, 1.0f))); + AddInput(kColorInput, TYPE_COLOR, Color(1.0f, 1.0f, 1.0f, 1.0f)); - AddInput(kRadiusInput, NodeValue::kFloat, 10.0); + AddInput(kRadiusInput, TYPE_DOUBLE, 10.0); SetInputProperty(kRadiusInput, QStringLiteral("min"), 0.0); - AddInput(kOpacityInput, NodeValue::kFloat, 1.0f); + AddInput(kOpacityInput, TYPE_DOUBLE, 1.0f); SetInputProperty(kOpacityInput, QStringLiteral("view"), FloatSlider::kPercentage); SetInputProperty(kOpacityInput, QStringLiteral("min"), 0.0f); SetInputProperty(kOpacityInput, QStringLiteral("max"), 1.0f); - AddInput(kInnerInput, NodeValue::kBoolean, false); + AddInput(kInnerInput, TYPE_BOOL, false); SetFlag(kVideoEffect); SetEffectInput(kTextureInput); @@ -88,9 +88,9 @@ ShaderCode StrokeFilterNode::GetShaderCode(const QString &id) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/stroke.frag")); } -NodeValue StrokeFilterNode::Value(const ValueParams &p) const +value_t StrokeFilterNode::Value(const ValueParams &p) const { - NodeValue tex_meta = GetInputValue(p, kTextureInput); + value_t tex_meta = GetInputValue(p, kTextureInput); if (TexturePtr tex = tex_meta.toTexture()) { if (GetInputValue(p, kRadiusInput).toDouble() > 0.0 diff --git a/app/node/filter/stroke/stroke.h b/app/node/filter/stroke/stroke.h index 7a90285749..872b34e19d 100644 --- a/app/node/filter/stroke/stroke.h +++ b/app/node/filter/stroke/stroke.h @@ -40,7 +40,7 @@ class StrokeFilterNode : public Node virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; static const QString kTextureInput; static const QString kColorInput; diff --git a/app/node/generator/matrix/matrix.cpp b/app/node/generator/matrix/matrix.cpp index 616b7c9d6c..e31f1630b9 100644 --- a/app/node/generator/matrix/matrix.cpp +++ b/app/node/generator/matrix/matrix.cpp @@ -37,18 +37,18 @@ const QString MatrixGenerator::kAnchorInput = QStringLiteral("anchor_in"); MatrixGenerator::MatrixGenerator() { - AddInput(kPositionInput, NodeValue::kVec2, QVector2D(0.0, 0.0)); + AddInput(kPositionInput, TYPE_VEC2, QVector2D(0.0, 0.0)); - AddInput(kRotationInput, NodeValue::kFloat, 0.0); + AddInput(kRotationInput, TYPE_DOUBLE, 0.0); - AddInput(kScaleInput, NodeValue::kVec2, QVector2D(1.0f, 1.0f)); + AddInput(kScaleInput, TYPE_VEC2, QVector2D(1.0f, 1.0f)); SetInputProperty(kScaleInput, QStringLiteral("min"), QVector2D(0, 0)); SetInputProperty(kScaleInput, QStringLiteral("view"), FloatSlider::kPercentage); SetInputProperty(kScaleInput, QStringLiteral("disable1"), true); - AddInput(kUniformScaleInput, NodeValue::kBoolean, true, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + AddInput(kUniformScaleInput, TYPE_BOOL, true, kInputFlagNotConnectable | kInputFlagNotKeyframable); - AddInput(kAnchorInput, NodeValue::kVec2, QVector2D(0.0, 0.0)); + AddInput(kAnchorInput, TYPE_VEC2, QVector2D(0.0, 0.0)); } QString MatrixGenerator::Name() const @@ -87,7 +87,7 @@ void MatrixGenerator::Retranslate() SetInputName(kAnchorInput, tr("Anchor Point")); } -NodeValue MatrixGenerator::Value(const ValueParams &p) const +value_t MatrixGenerator::Value(const ValueParams &p) const { // Push matrix output return GenerateMatrix(p, false, false, false, QMatrix4x4()); diff --git a/app/node/generator/matrix/matrix.h b/app/node/generator/matrix/matrix.h index 064a4d6426..d4dff2eb66 100644 --- a/app/node/generator/matrix/matrix.h +++ b/app/node/generator/matrix/matrix.h @@ -44,7 +44,7 @@ class MatrixGenerator : public Node virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; static const QString kPositionInput; static const QString kRotationInput; diff --git a/app/node/generator/noise/noise.cpp b/app/node/generator/noise/noise.cpp index 2ab408fc79..a4fca19157 100644 --- a/app/node/generator/noise/noise.cpp +++ b/app/node/generator/noise/noise.cpp @@ -32,13 +32,13 @@ const QString NoiseGeneratorNode::kStrengthInput = QStringLiteral("strength_in") NoiseGeneratorNode::NoiseGeneratorNode() { - AddInput(kBaseIn, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + AddInput(kBaseIn, TYPE_TEXTURE, kInputFlagNotKeyframable); - AddInput(kStrengthInput, NodeValue::kFloat, 0.2); + AddInput(kStrengthInput, TYPE_DOUBLE, 0.2); SetInputProperty(kStrengthInput, QStringLiteral("view"), FloatSlider::kPercentage); SetInputProperty(kStrengthInput, QStringLiteral("min"), 0); - AddInput(kColorInput, NodeValue::kBoolean, false); + AddInput(kColorInput, TYPE_BOOL, false); SetEffectInput(kBaseIn); SetFlag(kVideoEffect); @@ -78,7 +78,7 @@ ShaderCode NoiseGeneratorNode::GetShaderCode(const QString &id) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/noise.frag")); } -NodeValue NoiseGeneratorNode::Value(const ValueParams &p) const +value_t NoiseGeneratorNode::Value(const ValueParams &p) const { ShaderJob job = CreateShaderJob(p, GetShaderCode); diff --git a/app/node/generator/noise/noise.h b/app/node/generator/noise/noise.h index 893e44de6d..84f6ad2293 100644 --- a/app/node/generator/noise/noise.h +++ b/app/node/generator/noise/noise.h @@ -39,7 +39,7 @@ class NoiseGeneratorNode : public Node { virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; static const QString kBaseIn; static const QString kColorInput; diff --git a/app/node/generator/polygon/polygon.cpp b/app/node/generator/polygon/polygon.cpp index 23acd0c097..247eb95760 100644 --- a/app/node/generator/polygon/polygon.cpp +++ b/app/node/generator/polygon/polygon.cpp @@ -32,9 +32,9 @@ const QString PolygonGenerator::kColorInput = QStringLiteral("color_in"); PolygonGenerator::PolygonGenerator() { - AddInput(kPointsInput, NodeValue::kBezier, QVector2D(0, 0), InputFlags(kInputFlagArray)); + AddInput(kPointsInput, TYPE_BEZIER, kInputFlagArray); - AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0, 1.0, 1.0))); + AddInput(kColorInput, TYPE_COLOR, Color(1.0, 1.0, 1.0)); const int kMiddleX = 135; const int kMiddleY = 45; @@ -115,6 +115,11 @@ ShaderCode PolygonGenerator::GetShaderCode(const QString &id) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/rgb.frag")); } +Bezier PolygonGenerator::GetBezier(const value_t &v) +{ + return Bezier(v.value(0), v.value(1), v.value(2), v.value(3), v.value(4), v.value(5)); +} + ShaderJob PolygonGenerator::GetGenerateJob(const ValueParams &p, const VideoParams ¶ms) const { VideoParams vp = params; @@ -130,7 +135,7 @@ ShaderJob PolygonGenerator::GetGenerateJob(const ValueParams &p, const VideoPara return rgb; } -NodeValue PolygonGenerator::Value(const ValueParams &p) const +value_t PolygonGenerator::Value(const ValueParams &p) const { return GetMergableJob(p, Texture::Job(p.vparams(), GetGenerateJob(p, p.vparams()))); } @@ -208,7 +213,7 @@ void PolygonGenerator::UpdateGizmoPositions(const ValueParams &p) int pts_sz = InputArraySize(kPointsInput); if (!points.empty()) { for (int i=0; iGetDraggers()[0]; NodeInputDragger &y_drag = gizmo->GetDraggers()[1]; - x_drag.Drag(x_drag.GetStartValue().toDouble() + x); - y_drag.Drag(y_drag.GetStartValue().toDouble() + y); + x_drag.Drag(x_drag.GetStartValue().get() + x); + y_drag.Drag(y_drag.GetStartValue().get() + y); } } @@ -254,15 +259,15 @@ QPainterPath PolygonGenerator::GeneratePath(const NodeValueArray &points, int si QPainterPath path; if (!points.empty()) { - const Bezier &first_pt = points.at(0).toBezier(); + const Bezier &first_pt = GetBezier(points.at(0)); Imath::V2d v = first_pt.to_vec(); path.moveTo(QPointF(v.x, v.y)); for (int i=1; i void ValidateGizmoVectorSize(QVector &vec, int new_sz); diff --git a/app/node/generator/shape/generatorwithmerge.cpp b/app/node/generator/shape/generatorwithmerge.cpp index afcdcc4ded..d45ae6b931 100644 --- a/app/node/generator/shape/generatorwithmerge.cpp +++ b/app/node/generator/shape/generatorwithmerge.cpp @@ -30,7 +30,7 @@ const QString GeneratorWithMerge::kBaseInput = QStringLiteral("base_in"); GeneratorWithMerge::GeneratorWithMerge() { - AddInput(kBaseInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + AddInput(kBaseInput, TYPE_TEXTURE, kInputFlagNotKeyframable); SetEffectInput(kBaseInput); SetFlag(kVideoEffect); } @@ -47,9 +47,9 @@ ShaderCode GeneratorWithMerge::GetShaderCode(const QString &id) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/alphaover.frag")); } -NodeValue GeneratorWithMerge::GetMergableJob(const ValueParams &p, TexturePtr job) const +value_t GeneratorWithMerge::GetMergableJob(const ValueParams &p, TexturePtr job) const { - NodeValue tex_meta = GetInputValue(p, kBaseInput); + value_t tex_meta = GetInputValue(p, kBaseInput); if (TexturePtr base = tex_meta.toTexture()) { // Push as merge node diff --git a/app/node/generator/shape/generatorwithmerge.h b/app/node/generator/shape/generatorwithmerge.h index 2bf2941995..1ecff49fe4 100644 --- a/app/node/generator/shape/generatorwithmerge.h +++ b/app/node/generator/shape/generatorwithmerge.h @@ -36,7 +36,7 @@ class GeneratorWithMerge : public Node static const QString kBaseInput; protected: - NodeValue GetMergableJob(const ValueParams &p, TexturePtr job) const; + value_t GetMergableJob(const ValueParams &p, TexturePtr job) const; private: static ShaderCode GetShaderCode(const QString &id); diff --git a/app/node/generator/shape/shapenode.cpp b/app/node/generator/shape/shapenode.cpp index 72bff4784a..504be7194b 100644 --- a/app/node/generator/shape/shapenode.cpp +++ b/app/node/generator/shape/shapenode.cpp @@ -29,9 +29,9 @@ QString ShapeNode::kRadiusInput = QStringLiteral("radius_in"); ShapeNode::ShapeNode() { - PrependInput(kTypeInput, NodeValue::kCombo); + PrependInput(kTypeInput, TYPE_COMBO); - AddInput(kRadiusInput, NodeValue::kFloat, 20.0); + AddInput(kRadiusInput, TYPE_DOUBLE, 20.0); SetInputProperty(kRadiusInput, QStringLiteral("min"), 0.0); } @@ -71,7 +71,7 @@ ShaderCode ShapeNode::GetShaderCode(const QString &id) return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/shape.frag"))); } -NodeValue ShapeNode::Value(const ValueParams &p) const +value_t ShapeNode::Value(const ValueParams &p) const { TexturePtr base = GetInputValue(p, kBaseInput).toTexture(); diff --git a/app/node/generator/shape/shapenode.h b/app/node/generator/shape/shapenode.h index 8e81b27c18..51516731f6 100644 --- a/app/node/generator/shape/shapenode.h +++ b/app/node/generator/shape/shapenode.h @@ -46,7 +46,7 @@ class ShapeNode : public ShapeNodeBase virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; static QString kTypeInput; static QString kRadiusInput; diff --git a/app/node/generator/shape/shapenodebase.cpp b/app/node/generator/shape/shapenodebase.cpp index bdbf949d68..067009afce 100644 --- a/app/node/generator/shape/shapenodebase.cpp +++ b/app/node/generator/shape/shapenodebase.cpp @@ -37,12 +37,12 @@ const QString ShapeNodeBase::kColorInput = QStringLiteral("color_in"); ShapeNodeBase::ShapeNodeBase(bool create_color_input) { - AddInput(kPositionInput, NodeValue::kVec2, QVector2D(0, 0)); - AddInput(kSizeInput, NodeValue::kVec2, QVector2D(100, 100)); + AddInput(kPositionInput, TYPE_VEC2, QVector2D(0, 0)); + AddInput(kSizeInput, TYPE_VEC2, QVector2D(100, 100)); SetInputProperty(kSizeInput, QStringLiteral("min"), QVector2D(0, 0)); if (create_color_input) { - AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0, 0.0, 0.0, 1.0))); + AddInput(kColorInput, TYPE_COLOR, Color(1.0, 0.0, 0.0, 1.0)); } // Initiate gizmos @@ -126,8 +126,8 @@ void ShapeNodeBase::GizmoDragMove(double x, double y, const Qt::KeyboardModifier NodeInputDragger &y_drag = gizmo->GetDraggers()[1]; if (gizmo == poly_gizmo_) { - x_drag.Drag(x_drag.GetStartValue().toDouble() + x); - y_drag.Drag(y_drag.GetStartValue().toDouble() + y); + x_drag.Drag(x_drag.GetStartValue().get() + x); + y_drag.Drag(y_drag.GetStartValue().get() + y); } else { bool from_center = modifiers & Qt::AltModifier; bool keep_ratio = modifiers & Qt::ShiftModifier; @@ -135,8 +135,8 @@ void ShapeNodeBase::GizmoDragMove(double x, double y, const Qt::KeyboardModifier NodeInputDragger &w_drag = gizmo->GetDraggers()[2]; NodeInputDragger &h_drag = gizmo->GetDraggers()[3]; - QVector2D gizmo_sz_start(w_drag.GetStartValue().toDouble(), h_drag.GetStartValue().toDouble()); - QVector2D gizmo_pos_start(x_drag.GetStartValue().toDouble(), y_drag.GetStartValue().toDouble()); + QVector2D gizmo_sz_start(w_drag.GetStartValue().get(), h_drag.GetStartValue().get()); + QVector2D gizmo_pos_start(x_drag.GetStartValue().get(), y_drag.GetStartValue().get()); QVector2D gizmo_half_res = gizmo->GetGlobals().square_resolution()/2; QVector2D adjusted_pt(x, y); QVector2D new_size; @@ -147,7 +147,7 @@ void ShapeNodeBase::GizmoDragMove(double x, double y, const Qt::KeyboardModifier double original_ratio; if (keep_ratio) { - original_ratio = w_drag.GetStartValue().toDouble() / h_drag.GetStartValue().toDouble(); + original_ratio = w_drag.GetStartValue().get() / h_drag.GetStartValue().get(); } // Calculate new size diff --git a/app/node/generator/solid/solid.cpp b/app/node/generator/solid/solid.cpp index 1e4d3d5cea..c6ad8da3ae 100644 --- a/app/node/generator/solid/solid.cpp +++ b/app/node/generator/solid/solid.cpp @@ -29,7 +29,7 @@ const QString SolidGenerator::kColorInput = QStringLiteral("color_in"); SolidGenerator::SolidGenerator() { // Default to a color that isn't black - AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0f, 0.0f, 0.0f, 1.0f))); + AddInput(kColorInput, TYPE_COLOR, Color(1.0f, 0.0f, 0.0f, 1.0f)); } QString SolidGenerator::Name() const @@ -64,7 +64,7 @@ ShaderCode SolidGenerator::GetShaderCode(const QString &id) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/solid.frag")); } -NodeValue SolidGenerator::Value(const ValueParams &p) const +value_t SolidGenerator::Value(const ValueParams &p) const { return Texture::Job(p.vparams(), CreateShaderJob(p, GetShaderCode)); } diff --git a/app/node/generator/solid/solid.h b/app/node/generator/solid/solid.h index f41f01a036..714dfd3c4b 100644 --- a/app/node/generator/solid/solid.h +++ b/app/node/generator/solid/solid.h @@ -40,7 +40,7 @@ class SolidGenerator : public Node virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; static const QString kColorInput; diff --git a/app/node/generator/text/textv1.cpp b/app/node/generator/text/textv1.cpp index f160504183..f7f74d461a 100644 --- a/app/node/generator/text/textv1.cpp +++ b/app/node/generator/text/textv1.cpp @@ -42,17 +42,17 @@ const QString TextGeneratorV1::kFontSizeInput = QStringLiteral("font_size_in"); TextGeneratorV1::TextGeneratorV1() { - AddInput(kTextInput, NodeValue::kText, tr("Sample Text")); + AddInput(kTextInput, TYPE_STRING, tr("Sample Text")); - AddInput(kHtmlInput, NodeValue::kBoolean, false); + AddInput(kHtmlInput, TYPE_BOOL, false); - AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0f, 1.0f, 1.0))); + AddInput(kColorInput, TYPE_COLOR, Color(1.0f, 1.0f, 1.0)); - AddInput(kVAlignInput, NodeValue::kCombo, 1); + AddInput(kVAlignInput, TYPE_COMBO, 1); - AddInput(kFontInput, NodeValue::kFont); + AddInput(kFontInput, TYPE_FONT); - AddInput(kFontSizeInput, NodeValue::kFloat, 72.0f); + AddInput(kFontSizeInput, TYPE_DOUBLE, 72.0f); SetFlag(kDontShowInCreateMenu); } @@ -163,13 +163,13 @@ void TextGeneratorV1::GenerateFrame(FramePtr frame, const GenerateJob& job) } } -NodeValue TextGeneratorV1::Value(const ValueParams &p) const +value_t TextGeneratorV1::Value(const ValueParams &p) const { if (!GetInputValue(p, kTextInput).toString().isEmpty()) { return Texture::Job(p.vparams(), CreateGenerateJob(p, GenerateFrame)); } - return NodeValue(); + return value_t(); } } diff --git a/app/node/generator/text/textv1.h b/app/node/generator/text/textv1.h index 92607257fe..8936408349 100644 --- a/app/node/generator/text/textv1.h +++ b/app/node/generator/text/textv1.h @@ -40,7 +40,7 @@ class TextGeneratorV1 : public Node virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; static const QString kTextInput; static const QString kHtmlInput; diff --git a/app/node/generator/text/textv2.cpp b/app/node/generator/text/textv2.cpp index 3a80b0a07c..c0fe55b915 100644 --- a/app/node/generator/text/textv2.cpp +++ b/app/node/generator/text/textv2.cpp @@ -43,17 +43,17 @@ const QString TextGeneratorV2::kFontSizeInput = QStringLiteral("font_size_in"); TextGeneratorV2::TextGeneratorV2() { - AddInput(kTextInput, NodeValue::kText, tr("Sample Text")); + AddInput(kTextInput, TYPE_STRING, tr("Sample Text")); - AddInput(kHtmlInput, NodeValue::kBoolean, false); + AddInput(kHtmlInput, TYPE_BOOL, false); - AddInput(kVAlignInput, NodeValue::kCombo, kVerticalAlignTop); + AddInput(kVAlignInput, TYPE_COMBO, kVerticalAlignTop); - AddInput(kFontInput, NodeValue::kFont); + AddInput(kFontInput, TYPE_FONT); - AddInput(kFontSizeInput, NodeValue::kFloat, 72.0f); + AddInput(kFontSizeInput, TYPE_DOUBLE, 72.0f); - SetStandardValue(kColorInput, QVariant::fromValue(Color(1.0f, 1.0f, 1.0))); + SetStandardValue(kColorInput, Color(1.0f, 1.0f, 1.0)); SetStandardValue(kSizeInput, QVector2D(400, 300)); SetFlag(kDontShowInCreateMenu); @@ -185,7 +185,7 @@ void TextGeneratorV2::GenerateFrame(FramePtr frame, const GenerateJob& job) } } -NodeValue TextGeneratorV2::Value(const ValueParams &p) const +value_t TextGeneratorV2::Value(const ValueParams &p) const { if (!GetInputValue(p, kTextInput).toString().isEmpty()) { GenerateJob job = CreateGenerateJob(p, GenerateFrame); @@ -194,7 +194,7 @@ NodeValue TextGeneratorV2::Value(const ValueParams &p) const return Texture::Job(text_params, job); } - return NodeValue(); + return value_t(); } } diff --git a/app/node/generator/text/textv2.h b/app/node/generator/text/textv2.h index 5f988eb124..3358d1278a 100644 --- a/app/node/generator/text/textv2.h +++ b/app/node/generator/text/textv2.h @@ -40,7 +40,7 @@ class TextGeneratorV2 : public ShapeNodeBase virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; static const QString kTextInput; static const QString kHtmlInput; diff --git a/app/node/generator/text/textv3.cpp b/app/node/generator/text/textv3.cpp index 5204809345..728bbda3c2 100644 --- a/app/node/generator/text/textv3.cpp +++ b/app/node/generator/text/textv3.cpp @@ -48,16 +48,16 @@ TextGeneratorV3::TextGeneratorV3() : ShapeNodeBase(false), dont_emit_valign_(false) { - AddInput(kTextInput, NodeValue::kText, QStringLiteral("

%1

").arg(tr("Sample Text"))); + AddInput(kTextInput, TYPE_STRING, QStringLiteral("

%1

").arg(tr("Sample Text"))); SetInputProperty(kTextInput, QStringLiteral("vieweronly"), true); SetStandardValue(kSizeInput, QVector2D(400, 300)); - AddInput(kVerticalAlignmentInput, NodeValue::kCombo, InputFlags(kInputFlagHidden | kInputFlagStatic)); + AddInput(kVerticalAlignmentInput, TYPE_COMBO, kInputFlagHidden | kInputFlagStatic); - AddInput(kUseArgsInput, NodeValue::kBoolean, true, InputFlags(kInputFlagHidden | kInputFlagStatic)); + AddInput(kUseArgsInput, TYPE_BOOL, true, kInputFlagHidden | kInputFlagStatic); - AddInput(kArgsInput, NodeValue::kText, InputFlags(kInputFlagArray)); + AddInput(kArgsInput, TYPE_STRING, kInputFlagArray); SetInputProperty(kArgsInput, QStringLiteral("arraystart"), 1); text_gizmo_ = new TextGizmo(this); @@ -143,7 +143,7 @@ void TextGeneratorV3::GenerateFrame(FramePtr frame, const GenerateJob& job) text_doc.documentLayout()->draw(&p, ctx); } -NodeValue TextGeneratorV3::Value(const ValueParams &p) const +value_t TextGeneratorV3::Value(const ValueParams &p) const { QString text = GetInputValue(p, kTextInput).toString(); @@ -160,7 +160,7 @@ NodeValue TextGeneratorV3::Value(const ValueParams &p) const } } - NodeValue base_val = GetInputValue(p, kTextInput); + value_t base_val = GetInputValue(p, kTextInput); if (!text.isEmpty()) { TexturePtr base = base_val.toTexture(); @@ -170,7 +170,7 @@ NodeValue TextGeneratorV3::Value(const ValueParams &p) const text_params.set_colorspace(project()->color_manager()->GetDefaultInputColorSpace()); GenerateJob job = CreateGenerateJob(p, GenerateFrame); - job.Insert(kTextInput, NodeValue(NodeValue::kText, text)); + job.Insert(kTextInput, text); return GetMergableJob(p, Texture::Job(text_params, job)); } diff --git a/app/node/generator/text/textv3.h b/app/node/generator/text/textv3.h index 1009613b6f..d74d08d5eb 100644 --- a/app/node/generator/text/textv3.h +++ b/app/node/generator/text/textv3.h @@ -41,7 +41,7 @@ class TextGeneratorV3 : public ShapeNodeBase virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; virtual void UpdateGizmoPositions(const ValueParams &p) override; diff --git a/app/node/group/group.cpp b/app/node/group/group.cpp index ac8de38a92..7e4fba9593 100644 --- a/app/node/group/group.cpp +++ b/app/node/group/group.cpp @@ -83,11 +83,11 @@ bool NodeGroup::LoadCustom(QXmlStreamReader *reader, SerializedData *data) } else if (reader->name() == QStringLiteral("name")) { link.custom_name = reader->readElementText(); } else if (reader->name() == QStringLiteral("flags")) { - link.custom_flags = InputFlags(reader->readElementText().toULongLong()); + link.custom_flags = InputFlag(reader->readElementText().toULongLong()); } else if (reader->name() == QStringLiteral("type")) { - link.data_type = NodeValue::GetDataTypeFromName(reader->readElementText()); + link.data_type = type_t::fromString(reader->readElementText()); } else if (reader->name() == QStringLiteral("default")) { - link.default_val = NodeValue::StringToValue(link.data_type, reader->readElementText(), false); + link.default_val = value_t(reader->readElementText()).converted(link.data_type); } else if (reader->name() == QStringLiteral("properties")) { while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("property")) { @@ -152,10 +152,10 @@ void NodeGroup::SaveCustom(QXmlStreamWriter *writer) const writer->writeTextElement(QStringLiteral("flags"), QString::number((GetInputFlags(input) & ~ip.second.GetFlags()).value())); - NodeValue::Type data_type = GetInputDataType(input); - writer->writeTextElement(QStringLiteral("type"), NodeValue::GetDataTypeName(data_type)); + type_t data_type = GetInputDataType(input); + writer->writeTextElement(QStringLiteral("type"), data_type.toString()); - writer->writeTextElement(QStringLiteral("default"), NodeValue::ValueToString(data_type, GetDefaultValue(input), false)); + writer->writeTextElement(QStringLiteral("default"), GetDefaultValue(input).toSerializedString()); writer->writeStartElement(QStringLiteral("properties")); auto p = GetInputProperties(input); @@ -185,7 +185,7 @@ void NodeGroup::PostLoadEvent(SerializedData *data) l.group->AddInputPassthrough(resolved, l.passthrough_id); - l.group->SetInputFlag(l.passthrough_id, InputFlag(l.custom_flags.value())); + l.group->SetInputFlag(l.passthrough_id, l.custom_flags); if (!l.custom_name.isEmpty()) { l.group->SetInputName(l.passthrough_id, l.custom_name); diff --git a/app/node/input/multicam/multicamnode.cpp b/app/node/input/multicam/multicamnode.cpp index 735b262105..abde41f020 100644 --- a/app/node/input/multicam/multicamnode.cpp +++ b/app/node/input/multicam/multicamnode.cpp @@ -13,13 +13,13 @@ const QString MultiCamNode::kSequenceTypeInput = QStringLiteral("sequence_type_i MultiCamNode::MultiCamNode() { - AddInput(kCurrentInput, NodeValue::kCombo, InputFlags(kInputFlagStatic)); + AddInput(kCurrentInput, TYPE_COMBO, kInputFlagStatic); - AddInput(kSourcesInput, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable | kInputFlagArray)); + AddInput(kSourcesInput, kInputFlagNotKeyframable | kInputFlagArray); SetInputProperty(kSourcesInput, QStringLiteral("arraystart"), 1); - AddInput(kSequenceInput, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable)); - AddInput(kSequenceTypeInput, NodeValue::kCombo, InputFlags(kInputFlagStatic | kInputFlagHidden)); + AddInput(kSequenceInput, kInputFlagNotKeyframable); + AddInput(kSequenceTypeInput, TYPE_COMBO, kInputFlagStatic | kInputFlagHidden); sequence_ = nullptr; } @@ -118,7 +118,7 @@ ShaderCode MultiCamNode::GetShaderCode(const QString &id) return ShaderCode(GenerateShaderCode(rows, cols)); } -NodeValue MultiCamNode::Value(const ValueParams &p) const +value_t MultiCamNode::Value(const ValueParams &p) const { if (p.output() == QStringLiteral("all")) { // Switcher mode: output a collage of all sources @@ -135,12 +135,12 @@ NodeValue MultiCamNode::Value(const ValueParams &p) const MultiCamNode::IndexToRowCols(i, rows, cols, &r, &c); if (Node *n = GetSourceNode(i)) { - NodeValue v = GetFakeConnectedValue(p, n, kSourcesInput, i); + value_t v = GetFakeConnectedValue(p, n, kSourcesInput, i); job.Insert(QStringLiteral("tex_%1_%2").arg(QString::number(r), QString::number(c)), v); } } - return NodeValue(Texture::Job(p.vparams(), job)); + return value_t(Texture::Job(p.vparams(), job)); } else { // Default behavior: output currently selected source int current = GetInputValue(p, kCurrentInput).toInt(); @@ -150,7 +150,7 @@ NodeValue MultiCamNode::Value(const ValueParams &p) const } } - return NodeValue(); + return value_t(); } void MultiCamNode::IndexToRowCols(int index, int total_rows, int total_cols, int *row, int *col) diff --git a/app/node/input/multicam/multicamnode.h b/app/node/input/multicam/multicamnode.h index c55870134e..592dbe8bf7 100644 --- a/app/node/input/multicam/multicamnode.h +++ b/app/node/input/multicam/multicamnode.h @@ -21,7 +21,7 @@ class MultiCamNode : public Node virtual QVector Category() const override; virtual QString Description() const override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; virtual void Retranslate() override; diff --git a/app/node/input/time/timeinput.cpp b/app/node/input/time/timeinput.cpp index 2f481fc5e5..059d3f7084 100644 --- a/app/node/input/time/timeinput.cpp +++ b/app/node/input/time/timeinput.cpp @@ -48,7 +48,7 @@ QString TimeInput::Description() const return tr("Generates the time (in seconds) at this frame."); } -NodeValue TimeInput::Value(const ValueParams &p) const +value_t TimeInput::Value(const ValueParams &p) const { return p.time().in().toDouble(); } diff --git a/app/node/input/time/timeinput.h b/app/node/input/time/timeinput.h index 53d64b7147..7a1e6de3c9 100644 --- a/app/node/input/time/timeinput.h +++ b/app/node/input/time/timeinput.h @@ -38,7 +38,7 @@ class TimeInput : public Node virtual QVector Category() const override; virtual QString Description() const override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; }; diff --git a/app/node/input/value/valuenode.cpp b/app/node/input/value/valuenode.cpp index ac62976771..cd4c4fcdd0 100644 --- a/app/node/input/value/valuenode.cpp +++ b/app/node/input/value/valuenode.cpp @@ -24,27 +24,41 @@ namespace olive { const QString ValueNode::kTypeInput = QStringLiteral("type_in"); const QString ValueNode::kValueInput = QStringLiteral("value_in"); -const QVector ValueNode::kSupportedTypes = { - NodeValue::kFloat, - NodeValue::kInt, - NodeValue::kRational, - NodeValue::kVec2, - NodeValue::kVec3, - NodeValue::kVec4, - NodeValue::kColor, - NodeValue::kText, - NodeValue::kMatrix, - NodeValue::kFont, - NodeValue::kBoolean, -}; #define super Node +constexpr type_t OUR_DOUBLE = "dbl"; +constexpr type_t OUR_INTEGER = "int"; +constexpr type_t OUR_RATIONAL = "rational"; +constexpr type_t OUR_VEC2 = "vec2"; +constexpr type_t OUR_VEC3 = "vec3"; +constexpr type_t OUR_VEC4 = "vec4"; +constexpr type_t OUR_COLOR = "color"; +constexpr type_t OUR_STRING = "str"; +constexpr type_t OUR_MATRIX = "mat"; +constexpr type_t OUR_FONT = "font"; +constexpr type_t OUR_BOOL = "bool"; + +const QVector ValueNode::kSupportedTypes = +{ + {OUR_DOUBLE, TYPE_DOUBLE, 1}, + {OUR_INTEGER, TYPE_INTEGER, 1}, + {OUR_RATIONAL, TYPE_RATIONAL, 1}, + {OUR_VEC2, TYPE_DOUBLE, 2}, + {OUR_VEC3, TYPE_DOUBLE, 3}, + {OUR_VEC4, TYPE_DOUBLE, 4}, + {OUR_COLOR, TYPE_DOUBLE, 4}, + {OUR_STRING, TYPE_STRING, 1}, + {OUR_MATRIX, TYPE_MATRIX, 1}, + {OUR_FONT, TYPE_STRING, 1}, + {OUR_BOOL, TYPE_DOUBLE, 1}, +}; + ValueNode::ValueNode() { - AddInput(kTypeInput, NodeValue::kCombo, 0, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + AddInput(kTypeInput, TYPE_COMBO, 0, kInputFlagNotConnectable | kInputFlagNotKeyframable); - AddInput(kValueInput, kSupportedTypes.first(), QVariant(), InputFlags(kInputFlagNotConnectable)); + AddInput(kValueInput, TYPE_DOUBLE, kInputFlagNotConnectable); } void ValueNode::Retranslate() @@ -56,13 +70,26 @@ void ValueNode::Retranslate() QStringList type_names; type_names.reserve(kSupportedTypes.size()); - foreach (NodeValue::Type type, kSupportedTypes) { - type_names.append(NodeValue::GetPrettyDataTypeName(type)); + for (const Type &type : kSupportedTypes) { + type_names.append(GetPrettyTypeName(type.our_type)); } + + type_names.append(tr("Double")); + type_names.append(tr("Integer")); + type_names.append(tr("Rational")); + type_names.append(tr("Vector 2D")); + type_names.append(tr("Vector 3D")); + type_names.append(tr("Vector 4D")); + type_names.append(tr("Color")); + type_names.append(tr("Text")); + type_names.append(tr("Matrix")); + type_names.append(tr("Font")); + type_names.append(tr("Boolean")); + SetComboBoxStrings(kTypeInput, type_names); } -NodeValue ValueNode::Value(const ValueParams &p) const +value_t ValueNode::Value(const ValueParams &p) const { Q_UNUSED(p) @@ -73,10 +100,41 @@ NodeValue ValueNode::Value(const ValueParams &p) const void ValueNode::InputValueChangedEvent(const QString &input, int element) { if (input == kTypeInput) { - SetInputDataType(kValueInput, kSupportedTypes.at(GetStandardValue(kTypeInput).toInt())); + int64_t k = GetStandardValue(kTypeInput).toInt(); + + SetInputDataType(kValueInput, kSupportedTypes.at(k).base_type, kSupportedTypes.at(k).channel_count); } super::InputValueChangedEvent(input, element); } +QString ValueNode::GetPrettyTypeName(const type_t &id) +{ + if (id == OUR_DOUBLE) { + return tr("Double"); + } else if (id == OUR_INTEGER) { + return tr("Integer"); + } else if (id == OUR_RATIONAL) { + return tr("Rational"); + } else if (id == OUR_VEC2) { + return tr("Vector 2D"); + } else if (id == OUR_VEC3) { + return tr("Vector 3D"); + } else if (id == OUR_VEC4) { + return tr("Vector 4D"); + } else if (id == OUR_COLOR) { + return tr("Color"); + } else if (id == OUR_STRING) { + return tr("Text"); + } else if (id == OUR_MATRIX) { + return tr("Matrix"); + } else if (id == OUR_FONT) { + return tr("Font"); + } else if (id == OUR_BOOL) { + return tr("Boolean"); + } + + return QString(); +} + } diff --git a/app/node/input/value/valuenode.h b/app/node/input/value/valuenode.h index f436e4fdcd..9bb2a6f680 100644 --- a/app/node/input/value/valuenode.h +++ b/app/node/input/value/valuenode.h @@ -58,13 +58,22 @@ class ValueNode : public Node virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; protected: virtual void InputValueChangedEvent(const QString &input, int element) override; private: - static const QVector kSupportedTypes; + static QString GetPrettyTypeName(const type_t &id); + + struct Type + { + type_t our_type; + type_t base_type; + size_t channel_count; + }; + + static const QVector kSupportedTypes; }; diff --git a/app/node/inputdragger.cpp b/app/node/inputdragger.cpp index 5832d0097b..83758f2ca5 100644 --- a/app/node/inputdragger.cpp +++ b/app/node/inputdragger.cpp @@ -67,7 +67,7 @@ void NodeInputDragger::Start(const NodeKeyframeTrackReference &input, const rati created_keys_.append(dragging_key_); if (create_key_on_all_tracks) { - int nb_tracks = NodeValue::get_number_of_keyframe_tracks(input.input().node()->GetInputDataType(input.input().input())); + int nb_tracks = input.input().node()->GetNumberOfKeyframeTracks(input.input().input()); for (int i=0; iHasInputProperty(input, QStringLiteral("min"))) { // Assumes the value is a double of some kind - double min = node->GetInputProperty(input, QStringLiteral("min")).toDouble(); - double v = value.toDouble(); + double min = node->GetInputProperty(input, QStringLiteral("min")).converted(TYPE_DOUBLE).toDouble(); + double v = value.converted(type, TYPE_DOUBLE).get(); if (v < min) { - value = min; + value = value_t::component_t(min).converted(TYPE_DOUBLE, type); } } if (node->HasInputProperty(input, QStringLiteral("max"))) { - double max = node->GetInputProperty(input, QStringLiteral("max")).toDouble(); - double v = value.toDouble(); + double max = node->GetInputProperty(input, QStringLiteral("max")).converted(TYPE_DOUBLE).toDouble(); + double v = value.converted(type, TYPE_DOUBLE).get(); if (v > max) { - value = max; + value = value_t::component_t(max).converted(TYPE_DOUBLE, type); } } diff --git a/app/node/inputdragger.h b/app/node/inputdragger.h index 7bdab14720..7e1ba54c6d 100644 --- a/app/node/inputdragger.h +++ b/app/node/inputdragger.h @@ -36,7 +36,7 @@ class NodeInputDragger void Start(const NodeKeyframeTrackReference& input, const rational& time, bool create_key_on_all_tracks = true); - void Drag(QVariant value); + void Drag(value_t::component_t value); void End(MultiUndoCommand *command); @@ -45,7 +45,7 @@ class NodeInputDragger return input_being_dragged; } - const QVariant &GetStartValue() const + const value_t::component_t &GetStartValue() const { return start_value_; } @@ -65,9 +65,9 @@ class NodeInputDragger rational time_; - QVariant start_value_; + value_t::component_t start_value_; - QVariant end_value_; + value_t::component_t end_value_; NodeKeyframe* dragging_key_; QVector created_keys_; diff --git a/app/node/inputimmediate.cpp b/app/node/inputimmediate.cpp index 7821b7bc80..e3afa1ba61 100644 --- a/app/node/inputimmediate.cpp +++ b/app/node/inputimmediate.cpp @@ -25,21 +25,20 @@ namespace olive { -NodeInputImmediate::NodeInputImmediate(NodeValue::Type type, const SplitValue &default_val) : - default_value_(default_val), +NodeInputImmediate::NodeInputImmediate(type_t type, size_t channels) : keyframing_(false) { - set_data_type(type); + standard_value_.resize(channels); } -void NodeInputImmediate::set_standard_value_on_track(const QVariant &value, int track) +void NodeInputImmediate::set_standard_value_on_track(const value_t::component_t &value, size_t track) { - standard_value_.replace(track, value); + standard_value_[track] = value; } -void NodeInputImmediate::set_split_standard_value(const SplitValue &value) +void NodeInputImmediate::set_split_standard_value(const value_t &value) { - for (int i=0; i NodeInputImmediate::get_keyframe_at_time(const rational & return keys; } -NodeKeyframe* NodeInputImmediate::get_keyframe_at_time_on_track(const rational &time, int track) const +NodeKeyframe* NodeInputImmediate::get_keyframe_at_time_on_track(const rational &time, size_t track) const { if (!is_using_standard_value(track)) { foreach (NodeKeyframe* key, keyframe_tracks_.at(track)) { @@ -174,16 +173,6 @@ bool NodeInputImmediate::has_keyframe_at_time(const rational &time) const return false; } -void NodeInputImmediate::set_data_type(NodeValue::Type type) -{ - int track_size = NodeValue::get_number_of_keyframe_tracks(type); - - keyframe_tracks_.resize(track_size); - standard_value_.resize(track_size); - - set_split_standard_value(default_value_); -} - NodeKeyframe *NodeInputImmediate::get_earliest_keyframe() const { NodeKeyframe* earliest = nullptr; diff --git a/app/node/inputimmediate.h b/app/node/inputimmediate.h index 06c3fad068..c41a53bdae 100644 --- a/app/node/inputimmediate.h +++ b/app/node/inputimmediate.h @@ -24,7 +24,6 @@ #include "common/xmlutils.h" #include "node/keyframe.h" #include "node/value.h" -#include "splitvalue.h" namespace olive { @@ -33,7 +32,7 @@ class NodeInput; class NodeInputImmediate { public: - NodeInputImmediate(NodeValue::Type type, const SplitValue& default_val); + NodeInputImmediate(type_t type, size_t channels); /** * @brief Internal insert function, automatically does an insertion sort based on the keyframe's time @@ -47,19 +46,19 @@ class NodeInputImmediate /** * @brief Get non-keyframed value split into components (the way it's stored) */ - const SplitValue& get_split_standard_value() const + const std::vector &get_standard_value() const { return standard_value_; } - const QVariant& get_split_standard_value_on_track(int track) const + value_t::component_t get_split_standard_value_on_track(size_t track) const { return standard_value_.at(track); } - void set_standard_value_on_track(const QVariant &value, int track); + void set_standard_value_on_track(const value_t::component_t &value, size_t track); - void set_split_standard_value(const SplitValue& value); + void set_split_standard_value(const value_t& value); /** * @brief Retrieve a list of keyframe objects for all tracks at a given time @@ -75,7 +74,7 @@ class NodeInputImmediate * * The keyframe object at this time or nullptr if there isn't one or if is_keyframing() is false. */ - NodeKeyframe* get_keyframe_at_time_on_track(const rational& time, int track) const; + NodeKeyframe* get_keyframe_at_time_on_track(const rational& time, size_t track) const; /** * @brief Gets the closest keyframe to a time @@ -150,18 +149,11 @@ class NodeInputImmediate return (!is_keyframing() || keyframe_tracks_.at(track).isEmpty()); } - void set_data_type(NodeValue::Type type); - private: /** * @brief Non-keyframed value */ - SplitValue standard_value_; - - /** - * @brief Default value - */ - SplitValue default_value_; + std::vector standard_value_; /** * @brief Internal keyframe array diff --git a/app/node/keyframe.cpp b/app/node/keyframe.cpp index d329add422..23e14c3dd1 100644 --- a/app/node/keyframe.cpp +++ b/app/node/keyframe.cpp @@ -26,7 +26,7 @@ namespace olive { const NodeKeyframe::Type NodeKeyframe::kDefaultType = kLinear; -NodeKeyframe::NodeKeyframe(const rational &time, const QVariant &value, Type type, int track, int element, const QString &input, QObject *parent) : +NodeKeyframe::NodeKeyframe(const rational &time, const value_t::component_t &value, Type type, int track, int element, const QString &input, QObject *parent) : time_(time), value_(value), type_(type), @@ -80,12 +80,12 @@ void NodeKeyframe::set_time(const rational &time) emit TimeChanged(time_); } -const QVariant &NodeKeyframe::value() const +const value_t::component_t &NodeKeyframe::value() const { return value_; } -void NodeKeyframe::set_value(const QVariant &value) +void NodeKeyframe::set_value(const value_t::component_t &value) { value_ = value; emit ValueChanged(value_); @@ -210,7 +210,7 @@ bool NodeKeyframe::has_sibling_at_time(const rational &t) const return k && k != this; } -bool NodeKeyframe::load(QXmlStreamReader *reader, NodeValue::Type data_type) +bool NodeKeyframe::load(QXmlStreamReader *reader, type_t data_type) { QString key_input; QPointF key_in_handle; @@ -234,7 +234,7 @@ bool NodeKeyframe::load(QXmlStreamReader *reader, NodeValue::Type data_type) } } - this->set_value(NodeValue::StringToValue(data_type, reader->readElementText(), true)); + this->set_value(value_t::component_t::fromSerializedString(data_type, reader->readElementText())); this->set_bezier_control_in(key_in_handle); this->set_bezier_control_out(key_out_handle); @@ -242,7 +242,7 @@ bool NodeKeyframe::load(QXmlStreamReader *reader, NodeValue::Type data_type) return true; } -void NodeKeyframe::save(QXmlStreamWriter *writer, NodeValue::Type data_type) const +void NodeKeyframe::save(QXmlStreamWriter *writer, type_t data_type) const { writer->writeAttribute(QStringLiteral("input"), this->input()); writer->writeAttribute(QStringLiteral("time"), QString::fromStdString(this->time().toString())); @@ -252,7 +252,7 @@ void NodeKeyframe::save(QXmlStreamWriter *writer, NodeValue::Type data_type) con writer->writeAttribute(QStringLiteral("outhandlex"), QString::number(this->bezier_control_out().x())); writer->writeAttribute(QStringLiteral("outhandley"), QString::number(this->bezier_control_out().y())); - writer->writeCharacters(NodeValue::ValueToString(data_type, this->value(), true)); + writer->writeCharacters(this->value().toSerializedString(data_type)); } } diff --git a/app/node/keyframe.h b/app/node/keyframe.h index c59f3ffa42..7c5f0b3c3f 100644 --- a/app/node/keyframe.h +++ b/app/node/keyframe.h @@ -61,7 +61,7 @@ class NodeKeyframe : public QObject /** * @brief NodeKeyframe Constructor */ - NodeKeyframe(const rational& time, const QVariant& value, Type type, int track, int element, const QString& input, QObject* parent = nullptr); + NodeKeyframe(const rational& time, const value_t::component_t& value, Type type, int track, int element, const QString& input, QObject* parent = nullptr); NodeKeyframe(); virtual ~NodeKeyframe() override; @@ -88,8 +88,8 @@ class NodeKeyframe : public QObject /** * @brief The value of this keyframe (i.e. the value to use at this keyframe's time) */ - const QVariant& value() const; - void set_value(const QVariant &value); + const value_t::component_t& value() const; + void set_value(const value_t::component_t &value); /** * @brief The method of interpolation to use with this keyframe @@ -165,8 +165,8 @@ class NodeKeyframe : public QObject bool has_sibling_at_time(const rational &t) const; - bool load(QXmlStreamReader *reader, NodeValue::Type data_type); - void save(QXmlStreamWriter *writer, NodeValue::Type data_type) const; + bool load(QXmlStreamReader *reader, type_t data_type); + void save(QXmlStreamWriter *writer, type_t data_type) const; signals: /** @@ -177,7 +177,7 @@ class NodeKeyframe : public QObject /** * @brief Signal emitted when this keyframe's value is changed */ - void ValueChanged(const QVariant& value); + void ValueChanged(const value_t::component_t &value); /** * @brief Signal emitted when this keyframe's value is changed @@ -197,7 +197,7 @@ class NodeKeyframe : public QObject private: rational time_; - QVariant value_; + value_t::component_t value_; Type type_; diff --git a/app/node/keying/chromakey/chromakey.cpp b/app/node/keying/chromakey/chromakey.cpp index 8ed4520cc0..f086045091 100644 --- a/app/node/keying/chromakey/chromakey.cpp +++ b/app/node/keying/chromakey/chromakey.cpp @@ -34,13 +34,13 @@ const QString ChromaKeyNode::kHighlightsInput = QStringLiteral("highlights_in"); ChromaKeyNode::ChromaKeyNode() { - AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(0.0f, 1.0f, 0.0f, 1.0f))); + AddInput(kColorInput, TYPE_COLOR, Color(0.0f, 1.0f, 0.0f, 1.0f)); - AddInput(kLowerToleranceInput, NodeValue::kFloat, 5.0); + AddInput(kLowerToleranceInput, TYPE_DOUBLE, 5.0); SetInputProperty(kLowerToleranceInput, QStringLiteral("min"), 0.0); SetInputProperty(kLowerToleranceInput, QStringLiteral("base"), 0.1); - AddInput(kUpperToleranceInput, NodeValue::kFloat, 25.0); + AddInput(kUpperToleranceInput, TYPE_DOUBLE, 25.0); SetInputProperty(kUpperToleranceInput, QStringLiteral("base"), 0.1); // FIXME: Temporarily disabled. This will break if "lower tolerance" is keyframed or connected to @@ -48,21 +48,21 @@ ChromaKeyNode::ChromaKeyNode() // we can look into re-enabling this. //SetInputProperty(kUpperToleranceInput, QStringLiteral("min"), GetStandardValue(kLowerToleranceInput).toDouble()); - AddInput(kGarbageMatteInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + AddInput(kGarbageMatteInput, TYPE_TEXTURE, kInputFlagNotKeyframable); - AddInput(kCoreMatteInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + AddInput(kCoreMatteInput, TYPE_TEXTURE, kInputFlagNotKeyframable); - AddInput(kHighlightsInput, NodeValue::kFloat, 100.0f); + AddInput(kHighlightsInput, TYPE_DOUBLE, 100.0f); SetInputProperty(kHighlightsInput, QStringLiteral("min"), 0.0); SetInputProperty(kHighlightsInput, QStringLiteral("base"), 0.1); - AddInput(kShadowsInput, NodeValue::kFloat, 100.0f); + AddInput(kShadowsInput, TYPE_DOUBLE, 100.0f); SetInputProperty(kShadowsInput, QStringLiteral("min"), 0.0); SetInputProperty(kShadowsInput, QStringLiteral("base"), 0.1); - AddInput(kInvertInput, NodeValue::kBoolean, false); + AddInput(kInvertInput, TYPE_BOOL, false); - AddInput(kMaskOnlyInput, NodeValue::kBoolean, false); + AddInput(kMaskOnlyInput, TYPE_BOOL, false); } QString ChromaKeyNode::Name() const @@ -130,9 +130,9 @@ void ChromaKeyNode::GenerateProcessor() } } -NodeValue ChromaKeyNode::Value(const ValueParams &p) const +value_t ChromaKeyNode::Value(const ValueParams &p) const { - NodeValue tex_meta = GetInputValue(p, kTextureInput); + value_t tex_meta = GetInputValue(p, kTextureInput); if (TexturePtr tex = tex_meta.toTexture()) { if (processor()) { diff --git a/app/node/keying/chromakey/chromakey.h b/app/node/keying/chromakey/chromakey.h index 6b101e7910..14249b02a9 100644 --- a/app/node/keying/chromakey/chromakey.h +++ b/app/node/keying/chromakey/chromakey.h @@ -36,7 +36,7 @@ class ChromaKeyNode : public OCIOBaseNode { virtual void InputValueChangedEvent(const QString& input, int element) override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; virtual void ConfigChanged() override; diff --git a/app/node/keying/colordifferencekey/colordifferencekey.cpp b/app/node/keying/colordifferencekey/colordifferencekey.cpp index 04da186705..8933262086 100644 --- a/app/node/keying/colordifferencekey/colordifferencekey.cpp +++ b/app/node/keying/colordifferencekey/colordifferencekey.cpp @@ -29,23 +29,23 @@ const QString ColorDifferenceKeyNode::kMaskOnlyInput = QStringLiteral("mask_only ColorDifferenceKeyNode::ColorDifferenceKeyNode() { - AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + AddInput(kTextureInput, TYPE_TEXTURE, kInputFlagNotKeyframable); - AddInput(kGarbageMatteInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + AddInput(kGarbageMatteInput, TYPE_TEXTURE, kInputFlagNotKeyframable); - AddInput(kCoreMatteInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + AddInput(kCoreMatteInput, TYPE_TEXTURE, kInputFlagNotKeyframable); - AddInput(kColorInput, NodeValue::kCombo, 0); + AddInput(kColorInput, TYPE_COMBO, 0); - AddInput(kHighlightsInput, NodeValue::kFloat, 1.0f); + AddInput(kHighlightsInput, TYPE_DOUBLE, 1.0f); SetInputProperty(kHighlightsInput, QStringLiteral("min"), 0.0); SetInputProperty(kHighlightsInput, QStringLiteral("base"), 0.01); - AddInput(kShadowsInput, NodeValue::kFloat, 1.0f); + AddInput(kShadowsInput, TYPE_DOUBLE, 1.0f); SetInputProperty(kShadowsInput, QStringLiteral("min"), 0.0); SetInputProperty(kShadowsInput, QStringLiteral("base"), 0.01); - AddInput(kMaskOnlyInput, NodeValue::kBoolean, false); + AddInput(kMaskOnlyInput, TYPE_BOOL, false); SetFlag(kVideoEffect); SetEffectInput(kTextureInput); @@ -90,10 +90,10 @@ ShaderCode ColorDifferenceKeyNode::GetShaderCode(const QString &id) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/colordifferencekey.frag")); } -NodeValue ColorDifferenceKeyNode::Value(const ValueParams &p) const +value_t ColorDifferenceKeyNode::Value(const ValueParams &p) const { // If there's no texture, no need to run an operation - NodeValue tex_meta = GetInputValue(p, kTextureInput); + value_t tex_meta = GetInputValue(p, kTextureInput); if (TexturePtr tex = tex_meta.toTexture()) { ShaderJob job = CreateShaderJob(p, GetShaderCode); diff --git a/app/node/keying/colordifferencekey/colordifferencekey.h b/app/node/keying/colordifferencekey/colordifferencekey.h index d4fdd4efcf..100da677d8 100644 --- a/app/node/keying/colordifferencekey/colordifferencekey.h +++ b/app/node/keying/colordifferencekey/colordifferencekey.h @@ -33,7 +33,7 @@ class ColorDifferenceKeyNode : public Node { virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; static const QString kTextureInput; static const QString kGarbageMatteInput; diff --git a/app/node/keying/despill/despill.cpp b/app/node/keying/despill/despill.cpp index 1514c8d65d..328f1345cb 100644 --- a/app/node/keying/despill/despill.cpp +++ b/app/node/keying/despill/despill.cpp @@ -28,13 +28,13 @@ const QString DespillNode::kPreserveLuminanceInput = QStringLiteral("preserve_lu DespillNode::DespillNode() { - AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + AddInput(kTextureInput, TYPE_TEXTURE, kInputFlagNotKeyframable); - AddInput(kColorInput, NodeValue::kCombo, 0); + AddInput(kColorInput, TYPE_COMBO, 0); - AddInput(kMethodInput, NodeValue::kCombo, 0); + AddInput(kMethodInput, TYPE_COMBO, 0); - AddInput(kPreserveLuminanceInput, NodeValue::kBoolean, false); + AddInput(kPreserveLuminanceInput, TYPE_BOOL, false); SetFlag(kVideoEffect); SetEffectInput(kTextureInput); @@ -80,9 +80,9 @@ ShaderCode DespillNode::GetShaderCode(const QString &id) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/despill.frag")); } -NodeValue DespillNode::Value(const ValueParams &p) const +value_t DespillNode::Value(const ValueParams &p) const { - NodeValue tex_meta = GetInputValue(p, kTextureInput); + value_t tex_meta = GetInputValue(p, kTextureInput); if (TexturePtr tex = tex_meta.toTexture()) { ShaderJob job = CreateShaderJob(p, GetShaderCode); @@ -90,8 +90,7 @@ NodeValue DespillNode::Value(const ValueParams &p) const // Set luma coefficients double luma_coeffs[3] = {0.0f, 0.0f, 0.0f}; project()->color_manager()->GetDefaultLumaCoefs(luma_coeffs); - job.Insert(QStringLiteral("luma_coeffs"), - NodeValue(NodeValue::kVec3, QVector3D(luma_coeffs[0], luma_coeffs[1], luma_coeffs[2]))); + job.Insert(QStringLiteral("luma_coeffs"), QVector3D(luma_coeffs[0], luma_coeffs[1], luma_coeffs[2])); return tex->toJob(job); } diff --git a/app/node/keying/despill/despill.h b/app/node/keying/despill/despill.h index 8f677acd0c..249f02c921 100644 --- a/app/node/keying/despill/despill.h +++ b/app/node/keying/despill/despill.h @@ -34,7 +34,7 @@ class DespillNode : public Node { virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &globals) const override; + virtual value_t Value(const ValueParams &globals) const override; static const QString kTextureInput; static const QString kColorInput; diff --git a/app/node/math/math/math.cpp b/app/node/math/math/math.cpp index 792d496372..4455f6b2f2 100644 --- a/app/node/math/math/math.cpp +++ b/app/node/math/math/math.cpp @@ -31,13 +31,13 @@ const QString MathNode::kParamCIn = QStringLiteral("param_c_in"); MathNode::MathNode() { - AddInput(kMethodIn, NodeValue::kCombo, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + AddInput(kMethodIn, TYPE_COMBO, kInputFlagNotConnectable | kInputFlagNotKeyframable); - AddInput(kParamAIn, NodeValue::kFloat, 0.0); + AddInput(kParamAIn, TYPE_DOUBLE, 0.0); SetInputProperty(kParamAIn, QStringLiteral("decimalplaces"), 8); SetInputProperty(kParamAIn, QStringLiteral("autotrim"), true); - AddInput(kParamBIn, NodeValue::kFloat, 0.0); + AddInput(kParamBIn, TYPE_DOUBLE, 0.0); SetInputProperty(kParamBIn, QStringLiteral("decimalplaces"), 8); SetInputProperty(kParamBIn, QStringLiteral("autotrim"), true); } @@ -88,21 +88,20 @@ void MathNode::Retranslate() SetComboBoxStrings(kMethodIn, operations); } -NodeValue MathNode::Value(const ValueParams &p) const +value_t MathNode::Value(const ValueParams &p) const { // Auto-detect what values to operate with - // FIXME: Very inefficient auto aval = GetInputValue(p, kParamAIn); auto bval = GetInputValue(p, kParamBIn); - if (!bval.data().isValid()) { + if (!bval.isValid()) { return aval; } - if (!aval.data().isValid()) { + if (!aval.isValid()) { return bval; } - PairingCalculator calc(aval, bval); + /*PairingCalculator calc(aval, bval); // Do nothing if no pairing was found if (calc.FoundMostLikelyPairing()) { @@ -113,9 +112,9 @@ NodeValue MathNode::Value(const ValueParams &p) const kParamBIn, calc.GetMostLikelyValueB(), p); - } + }*/ - return NodeValue(); + return value_t(); } } diff --git a/app/node/math/math/math.h b/app/node/math/math/math.h index 162f82b978..bf8fec1615 100644 --- a/app/node/math/math/math.h +++ b/app/node/math/math/math.h @@ -50,7 +50,7 @@ class MathNode : public MathNodeBase SetStandardValue(kMethodIn, o); } - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; static const QString kMethodIn; static const QString kParamAIn; diff --git a/app/node/math/math/mathbase.cpp b/app/node/math/math/mathbase.cpp index b1a5bbdc03..005bbd7aff 100644 --- a/app/node/math/math/mathbase.cpp +++ b/app/node/math/math/mathbase.cpp @@ -37,16 +37,18 @@ ShaderCode MathNodeBase::GetShaderCode(const QString &shader_id) Operation op = static_cast(code_id.at(0).toInt()); Pairing pairing = static_cast(code_id.at(1).toInt()); - NodeValue::Type type_a = static_cast(code_id.at(2).toInt()); - NodeValue::Type type_b = static_cast(code_id.at(3).toInt()); + type_t type_a = code_id.at(2).toUtf8().constData(); + type_t type_b = code_id.at(3).toUtf8().constData(); + size_t size_a = code_id.at(4).toULongLong(); + size_t size_b = code_id.at(5).toULongLong(); QString operation, frag, vert; if (pairing == kPairTextureMatrix && op == kOpMultiply) { // Override the operation for this operation since we multiply texture COORDS by the matrix rather than - const QString& tex_in = (type_a == NodeValue::kTexture) ? param_a : param_b; - const QString& mat_in = (type_a == NodeValue::kTexture) ? param_b : param_a; + const QString& tex_in = (type_a == TYPE_TEXTURE) ? param_a : param_b; + const QString& mat_in = (type_a == TYPE_TEXTURE) ? param_b : param_a; // No-op frag shader (can we return QString() instead?) operation = QStringLiteral("texture(%1, ove_texcoord)").arg(tex_in); @@ -80,7 +82,7 @@ ShaderCode MathNodeBase::GetShaderCode(const QString &shader_id) case kOpPower: if (pairing == kPairTextureNumber) { // The "number" in this operation has to be declared a vec4 - if (NodeValue::type_is_numeric(type_a)) { + if (type_a == TYPE_INTEGER || type_a == TYPE_DOUBLE || type_a == TYPE_RATIONAL) { operation = QStringLiteral("pow(%2, vec4(%1))"); } else { operation = QStringLiteral("pow(%1, vec4(%2))"); @@ -105,8 +107,8 @@ ShaderCode MathNodeBase::GetShaderCode(const QString &shader_id) " vec4 c = %5;\n" " c.a = clamp(c.a, 0.0, 1.0);\n" // Ensure alpha is between 0.0 and 1.0 " frag_color = c;\n" - "}\n").arg(GetShaderUniformType(type_a), - GetShaderUniformType(type_b), + "}\n").arg(GetShaderUniformType(type_a, size_a), + GetShaderUniformType(type_b, size_b), param_a, param_b, operation); @@ -114,57 +116,48 @@ ShaderCode MathNodeBase::GetShaderCode(const QString &shader_id) return ShaderCode(frag, vert); } -QString MathNodeBase::GetShaderUniformType(const olive::NodeValue::Type &type) +QString MathNodeBase::GetShaderUniformType(const olive::type_t &type, size_t channels) { - switch (type) { - case NodeValue::kTexture: + if (type == TYPE_TEXTURE) { return QStringLiteral("sampler2D"); - case NodeValue::kColor: - return QStringLiteral("vec4"); - case NodeValue::kMatrix: + } else if (type == TYPE_MATRIX) { return QStringLiteral("mat4"); - default: - return QStringLiteral("float"); + } else if (type == TYPE_DOUBLE) { + switch (channels) { + case 1: return QStringLiteral("float"); + case 2: return QStringLiteral("vec2"); + case 3: return QStringLiteral("vec3"); + case 4: return QStringLiteral("vec4"); + } } + + // Fallback (shouldn't ever get here) + return QString(); } -QString MathNodeBase::GetShaderVariableCall(const QString &input_id, const NodeValue::Type &type, const QString& coord_op) +QString MathNodeBase::GetShaderVariableCall(const QString &input_id, const type_t &type, const QString& coord_op) { - if (type == NodeValue::kTexture) { + if (type == TYPE_TEXTURE) { return QStringLiteral("texture(%1, ove_texcoord%2)").arg(input_id, coord_op); } return input_id; } -QVector4D MathNodeBase::RetrieveVector(const NodeValue &val) +QVector4D MathNodeBase::RetrieveVector(const value_t &val) { - // QVariant doesn't know that QVector*D can convert themselves so we do it here - switch (val.type()) { - case NodeValue::kVec2: - return QVector4D(val.toVec2()); - case NodeValue::kVec3: - return QVector4D(val.toVec3()); - case NodeValue::kVec4: - default: - return val.toVec4(); - } + return val.toVec4(); } -NodeValue MathNodeBase::PushVector(olive::NodeValue::Type type, const QVector4D &vec) const +value_t MathNodeBase::PushVector(size_t channels, const QVector4D &vec) const { - switch (type) { - case NodeValue::kVec2: - return QVector2D(vec); - case NodeValue::kVec3: - return QVector3D(vec); - case NodeValue::kVec4: - return vec; - default: - break; + switch (channels) { + case 2: return vec.toVector2D(); + case 3: return vec.toVector3D(); + case 4: return vec; } - return NodeValue(); + return value_t(); } QString MathNodeBase::GetOperationName(Operation o) @@ -262,13 +255,14 @@ void MathNodeBase::ProcessSamplesNumber(const void *context, const SampleJob &jo } } -NodeValue MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QString& param_a_in, const NodeValue& val_a, const QString& param_b_in, const NodeValue& val_b, const ValueParams &p) const +value_t MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QString& param_a_in, const value_t& val_a, const QString& param_b_in, const value_t& val_b, const ValueParams &p) const { + /* switch (pairing) { case kPairNumberNumber: { - if (val_a.type() == NodeValue::kRational && val_b.type() == NodeValue::kRational && operation != kOpPower) { + if (val_a.type() == TYPE_RATIONAL && val_b.type() == TYPE_RATIONAL && operation != kOpPower) { // Preserve rationals return PerformAddSubMultDiv(operation, val_a.toRational(), val_b.toRational()); } else { @@ -286,8 +280,8 @@ NodeValue MathNodeBase::ValueInternal(Operation operation, Pairing pairing, cons case kPairMatrixVec: { - QMatrix4x4 matrix = (val_a.type() == NodeValue::kMatrix) ? val_a.toMatrix() : val_b.toMatrix(); - QVector4D vec = (val_a.type() == NodeValue::kMatrix) ? RetrieveVector(val_b) : RetrieveVector(val_a); + QMatrix4x4 matrix = (val_a.type() == TYPE_MATRIX) ? val_a.toMatrix() : val_b.toMatrix(); + QVector4D vec = (val_a.type() == TYPE_MATRIX) ? RetrieveVector(val_b) : RetrieveVector(val_a); // Only valid operation is multiply return PushVector(qMax(val_a.type(), val_b.type()), @@ -297,7 +291,7 @@ NodeValue MathNodeBase::ValueInternal(Operation operation, Pairing pairing, cons case kPairVecNumber: { QVector4D vec = (NodeValue::type_is_vector(val_a.type()) ? RetrieveVector(val_a) : RetrieveVector(val_b)); - float number = RetrieveNumber((val_a.type() & NodeValue::kMatrix) ? val_b : val_a); + float number = RetrieveNumber((val_a.type() & TYPE_MATRIX) ? val_b : val_a); // Only multiply and divide are valid operations return PushVector(val_a.type(), PerformMultDiv(operation, vec, number)); @@ -349,18 +343,20 @@ NodeValue MathNodeBase::ValueInternal(Operation operation, Pairing pairing, cons { ShaderJob job; job.set_function(MathNodeBase::GetShaderCode); - job.SetShaderID(QStringLiteral("%1.%2.%3.%4").arg(QString::number(operation), - QString::number(pairing), - QString::number(val_a.type()), - QString::number(val_b.type()))); + job.SetShaderID(QStringLiteral("%1.%2.%3.%4.%5.%6").arg(QString::number(operation), + QString::number(pairing), + val_a.type(), + val_b.type(), + QString::number(val_a.size()), + QString::number(val_b.size()))); job.Insert(param_a, val_a); job.Insert(param_b, val_b); bool operation_is_noop = false; - const NodeValue& number_val = val_a.type() == NodeValue::kTexture ? val_b : val_a; - const NodeValue& texture_val = val_a.type() == NodeValue::kTexture ? val_a : val_b; + const Value& number_val = val_a.type() == NodeValue::TYPE_TEXTURE ? val_b : val_a; + const Value& texture_val = val_a.type() == NodeValue::TYPE_TEXTURE ? val_a : val_b; TexturePtr texture = texture_val.toTexture(); if (!texture) { @@ -400,12 +396,12 @@ NodeValue MathNodeBase::ValueInternal(Operation operation, Pairing pairing, cons case kPairSampleNumber: { // Queue a sample job - const NodeValue& number_val = val_a.type() == NodeValue::kSamples ? val_b : val_a; + const Value& number_val = val_a.type() == NodeValue::kSamples ? val_b : val_a; const QString& number_param = val_a.type() == NodeValue::kSamples ? param_b_in : param_a_in; float number = RetrieveNumber(number_val); - const NodeValue &sample_val = val_a.type() == NodeValue::kSamples ? val_a : val_b; + const Value &sample_val = val_a.type() == NodeValue::kSamples ? val_a : val_b; if (IsInputStatic(number_param) && NumberIsNoOp(operation, number)) { return sample_val; @@ -428,7 +424,9 @@ NodeValue MathNodeBase::ValueInternal(Operation operation, Pairing pairing, cons break; } - return NodeValue(); + return Value(); + */ + return value_t(); } void MathNodeBase::ProcessSamplesSamples(const void *context, const SampleJob &job, SampleBuffer &mixed_samples) @@ -460,9 +458,9 @@ void MathNodeBase::ProcessSamplesSamples(const void *context, const SampleJob &j } } -float MathNodeBase::RetrieveNumber(const NodeValue &val) +float MathNodeBase::RetrieveNumber(const value_t &val) { - if (val.type() == NodeValue::kRational) { + if (val.type() == TYPE_RATIONAL) { return val.toRational().toDouble(); } else { return val.toDouble(); @@ -490,100 +488,6 @@ bool MathNodeBase::NumberIsNoOp(const MathNodeBase::Operation &op, const float & return false; } -MathNodeBase::PairingCalculator::PairingCalculator(const NodeValue &table_a, const NodeValue &table_b) -{ - QVector pair_likelihood_a = GetPairLikelihood(table_a); - QVector pair_likelihood_b = GetPairLikelihood(table_b); - - int weight_a = 0; - int weight_b = 0; - - QVector likelihoods(kPairCount); - - for (int i=0;i -1) { - if (most_likely_pairing_ == kPairNone - || likelihoods.at(i) > likelihoods.at(most_likely_pairing_)) { - most_likely_pairing_ = static_cast(i); - } - } - } - - if (most_likely_pairing_ != kPairNone) { - most_likely_value_a_ = table_a; - most_likely_value_b_ = table_b; - } -} - -QVector MathNodeBase::PairingCalculator::GetPairLikelihood(const NodeValue &table) -{ - QVector likelihood(kPairCount, -1); - - NodeValue::Type type = table.type(); - - int weight = 0; - - if (NodeValue::type_is_vector(type)) { - likelihood.replace(kPairVecVec, weight); - likelihood.replace(kPairVecNumber, weight); - likelihood.replace(kPairMatrixVec, weight); - } else if (type == NodeValue::kMatrix) { - likelihood.replace(kPairMatrixMatrix, weight); - likelihood.replace(kPairMatrixVec, weight); - likelihood.replace(kPairTextureMatrix, weight); - } else if (type == NodeValue::kColor) { - likelihood.replace(kPairColorColor, weight); - likelihood.replace(kPairNumberColor, weight); - likelihood.replace(kPairTextureColor, weight); - } else if (NodeValue::type_is_numeric(type)) { - likelihood.replace(kPairNumberNumber, weight); - likelihood.replace(kPairVecNumber, weight); - likelihood.replace(kPairNumberColor, weight); - likelihood.replace(kPairTextureNumber, weight); - likelihood.replace(kPairSampleNumber, weight); - } else if (type == NodeValue::kSamples) { - likelihood.replace(kPairSampleSample, weight); - likelihood.replace(kPairSampleNumber, weight); - } else if (type == NodeValue::kTexture) { - likelihood.replace(kPairTextureTexture, weight); - likelihood.replace(kPairTextureNumber, weight); - likelihood.replace(kPairTextureColor, weight); - likelihood.replace(kPairTextureMatrix, weight); - } - - return likelihood; -} - -bool MathNodeBase::PairingCalculator::FoundMostLikelyPairing() const -{ - return (most_likely_pairing_ > kPairNone && most_likely_pairing_ < kPairCount); -} - -MathNodeBase::Pairing MathNodeBase::PairingCalculator::GetMostLikelyPairing() const -{ - return most_likely_pairing_; -} - -const NodeValue &MathNodeBase::PairingCalculator::GetMostLikelyValueA() const -{ - return most_likely_value_a_; -} - -const NodeValue &MathNodeBase::PairingCalculator::GetMostLikelyValueB() const -{ - return most_likely_value_b_; -} - template T MathNodeBase::PerformAll(Operation operation, T a, U b) { diff --git a/app/node/math/math/mathbase.h b/app/node/math/math/mathbase.h index 6b389a5d31..0e39c03003 100644 --- a/app/node/math/math/mathbase.h +++ b/app/node/math/math/mathbase.h @@ -66,28 +66,6 @@ class MathNodeBase : public Node kPairCount }; - class PairingCalculator - { - public: - PairingCalculator(const NodeValue &table_a, const NodeValue &table_b); - - bool FoundMostLikelyPairing() const; - Pairing GetMostLikelyPairing() const; - - const NodeValue& GetMostLikelyValueA() const; - const NodeValue& GetMostLikelyValueB() const; - - private: - static QVector GetPairLikelihood(const NodeValue &table); - - Pairing most_likely_pairing_; - - NodeValue most_likely_value_a_; - - NodeValue most_likely_value_b_; - - }; - template static T PerformAll(Operation operation, T a, U b); @@ -112,19 +90,19 @@ class MathNodeBase : public Node static void PerformAllOnFloatBufferSSE(Operation operation, const float *input, float *output, float b, size_t start, size_t end); #endif - static QString GetShaderUniformType(const NodeValue::Type& type); + static QString GetShaderUniformType(const type_t& type, size_t channels); - static QString GetShaderVariableCall(const QString& input_id, const NodeValue::Type& type, const QString &coord_op = QString()); + static QString GetShaderVariableCall(const QString& input_id, const type_t& type, const QString &coord_op = QString()); - static QVector4D RetrieveVector(const NodeValue& val); + static QVector4D RetrieveVector(const value_t& val); - static float RetrieveNumber(const NodeValue& val); + static float RetrieveNumber(const value_t& val); static bool NumberIsNoOp(const Operation& op, const float& number); - NodeValue PushVector(NodeValue::Type type, const QVector4D& vec) const; + value_t PushVector(size_t channels, const QVector4D& vec) const; - NodeValue ValueInternal(Operation operation, Pairing pairing, const QString& param_a_in, const NodeValue &val_a, const QString& param_b_in, const NodeValue& val_b, const ValueParams &p) const; + value_t ValueInternal(Operation operation, Pairing pairing, const QString& param_a_in, const value_t &val_a, const QString& param_b_in, const value_t& val_b, const ValueParams &p) const; }; diff --git a/app/node/math/merge/merge.cpp b/app/node/math/merge/merge.cpp index 08d756fb59..c14f2e257a 100644 --- a/app/node/math/merge/merge.cpp +++ b/app/node/math/merge/merge.cpp @@ -29,9 +29,9 @@ const QString MergeNode::kBlendIn = QStringLiteral("blend_in"); MergeNode::MergeNode() { - AddInput(kBaseIn, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + AddInput(kBaseIn, TYPE_TEXTURE, kInputFlagNotKeyframable); - AddInput(kBlendIn, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); + AddInput(kBlendIn, TYPE_TEXTURE, kInputFlagNotKeyframable); SetFlag(kDontShowInParamView); } @@ -70,10 +70,10 @@ ShaderCode MergeNode::GetShaderCode(const QString &id) return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/alphaover.frag")); } -NodeValue MergeNode::Value(const ValueParams &p) const +value_t MergeNode::Value(const ValueParams &p) const { - NodeValue base_val = GetInputValue(p, kBaseIn); - NodeValue blend_val = GetInputValue(p, kBlendIn); + value_t base_val = GetInputValue(p, kBaseIn); + value_t blend_val = GetInputValue(p, kBlendIn); TexturePtr base_tex = base_val.toTexture(); TexturePtr blend_tex = blend_val.toTexture(); @@ -90,7 +90,7 @@ NodeValue MergeNode::Value(const ValueParams &p) const } } - return NodeValue(); + return value_t(); } } diff --git a/app/node/math/merge/merge.h b/app/node/math/merge/merge.h index 5d54ff9248..b0f64bd22e 100644 --- a/app/node/math/merge/merge.h +++ b/app/node/math/merge/merge.h @@ -40,7 +40,7 @@ class MergeNode : public Node virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; static const QString kBaseIn; static const QString kBlendIn; diff --git a/app/node/math/trigonometry/trigonometry.cpp b/app/node/math/trigonometry/trigonometry.cpp index 883480ae0b..0579c1ce66 100644 --- a/app/node/math/trigonometry/trigonometry.cpp +++ b/app/node/math/trigonometry/trigonometry.cpp @@ -29,9 +29,9 @@ const QString TrigonometryNode::kXIn = QStringLiteral("x_in"); TrigonometryNode::TrigonometryNode() { - AddInput(kMethodIn, NodeValue::kCombo, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + AddInput(kMethodIn, TYPE_COMBO, kInputFlagNotConnectable | kInputFlagNotKeyframable); - AddInput(kXIn, NodeValue::kFloat, 0.0); + AddInput(kXIn, TYPE_DOUBLE, 0.0); } QString TrigonometryNode::Name() const @@ -77,7 +77,7 @@ void TrigonometryNode::Retranslate() SetInputName(kXIn, tr("Value")); } -NodeValue TrigonometryNode::Value(const ValueParams &p) const +value_t TrigonometryNode::Value(const ValueParams &p) const { double x = GetInputValue(p, kXIn).toDouble(); diff --git a/app/node/math/trigonometry/trigonometry.h b/app/node/math/trigonometry/trigonometry.h index d28c645d61..74118c9c11 100644 --- a/app/node/math/trigonometry/trigonometry.h +++ b/app/node/math/trigonometry/trigonometry.h @@ -40,7 +40,7 @@ class TrigonometryNode : public Node virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; static const QString kMethodIn; static const QString kXIn; diff --git a/app/node/node.cpp b/app/node/node.cpp index 9be2024566..60a121b15c 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -26,8 +26,9 @@ #include #include "common/lerp.h" -#include "core.h" +#include "common/qtutils.h" #include "config/config.h" +#include "core.h" #include "node/group/group.h" #include "node/project/serializer/typeserializer.h" #include "nodeundo.h" @@ -48,7 +49,7 @@ Node::Node() : flags_(kNone), caches_enabled_(true) { - AddInput(kEnabledInput, NodeValue::kBoolean, true); + AddInput(kEnabledInput, TYPE_BOOL, true); AddOutput(QString()); video_cache_ = new FrameHashCache(this); @@ -332,7 +333,7 @@ bool Node::IsUsingStandardValue(const QString &input, int track, int element) co } } -NodeValue::Type Node::GetInputDataType(const QString &id) const +type_t Node::GetInputDataType(const QString &id) const { const Input* i = GetInternalInputData(id); @@ -340,21 +341,22 @@ NodeValue::Type Node::GetInputDataType(const QString &id) const return i->type; } else { ReportInvalidInput("get data type of", id, -1); - return NodeValue::kNone; + return TYPE_NONE; } } -void Node::SetInputDataType(const QString &id, const NodeValue::Type &type) +void Node::SetInputDataType(const QString &id, const type_t &type, size_t channels) { Input* input_meta = GetInternalInputData(id); if (input_meta) { input_meta->type = type; + input_meta->channel_count = channels; - int array_sz = InputArraySize(id); + /*int array_sz = InputArraySize(id); for (int i=-1; iset_data_type(type); - } + GetImmediate(id, i)->set_data_type(type, input_meta->channel_count); + }*/ emit InputDataTypeChanged(id, type); } else { @@ -374,7 +376,7 @@ bool Node::HasInputProperty(const QString &id, const QString &name) const } } -QHash Node::GetInputProperties(const QString &id) const +QHash Node::GetInputProperties(const QString &id) const { const Input* i = GetInternalInputData(id); @@ -382,11 +384,11 @@ QHash Node::GetInputProperties(const QString &id) const return i->properties; } else { ReportInvalidInput("get property table of", id, -1); - return QHash(); + return QHash(); } } -QVariant Node::GetInputProperty(const QString &id, const QString &name) const +value_t Node::GetInputProperty(const QString &id, const QString &name) const { const Input* i = GetInternalInputData(id); @@ -394,11 +396,11 @@ QVariant Node::GetInputProperty(const QString &id, const QString &name) const return i->properties.value(name); } else { ReportInvalidInput("get property of", id, -1); - return QVariant(); + return value_t(); } } -void Node::SetInputProperty(const QString &id, const QString &name, const QVariant &value) +void Node::SetInputProperty(const QString &id, const QString &name, const value_t &value) { Input* i = GetInternalInputData(id); @@ -411,20 +413,18 @@ void Node::SetInputProperty(const QString &id, const QString &name, const QVaria } } -SplitValue Node::GetSplitValueAtTime(const QString &input, const rational &time, int element) const +value_t Node::GetValueAtTime(const QString &input, const rational &time, int element) const { - SplitValue vals; + value_t v(GetInputDataType(input), GetNumberOfKeyframeTracks(input)); - int nb_tracks = GetNumberOfKeyframeTracks(input); - - for (int i=0;ivalue(); } - NodeValue::Type type = GetInputDataType(input); + type_t type = GetInputDataType(input); // If we're here, the time must be somewhere in between the keyframes NodeKeyframe *before = nullptr, *after = nullptr; @@ -462,9 +462,11 @@ QVariant Node::GetSplitValueAtTimeOnTrack(const QString &input, const rational & } } + bool can_be_interpolated = (type == TYPE_INTEGER || type == TYPE_DOUBLE || type == TYPE_RATIONAL); + if (before) { if (before->time() == time - || ((!NodeValue::type_can_be_interpolated(type) || before->type() == NodeKeyframe::kHold) && after->time() > time)) { + || ((!can_be_interpolated || before->type() == NodeKeyframe::kHold) && after->time() > time)) { // Time == keyframe time, so value is precise return before->value(); @@ -478,12 +480,15 @@ QVariant Node::GetSplitValueAtTimeOnTrack(const QString &input, const rational & // We must interpolate between these keyframes double before_val, after_val, interpolated; - if (type == NodeValue::kRational) { - before_val = before->value().value().toDouble(); - after_val = after->value().value().toDouble(); + if (type == TYPE_RATIONAL) { + before_val = before->value().get().toDouble(); + after_val = after->value().get().toDouble(); + } else if (type == TYPE_INTEGER) { + before_val = before->value().get(); + after_val = after->value().get(); } else { - before_val = before->value().toDouble(); - after_val = after->value().toDouble(); + before_val = before->value().get(); + after_val = after->value().get(); } if (before->type() == NodeKeyframe::kBezier && after->type() == NodeKeyframe::kBezier) { @@ -521,8 +526,10 @@ QVariant Node::GetSplitValueAtTimeOnTrack(const QString &input, const rational & interpolated = lerp(before_val, after_val, period_progress); } - if (type == NodeValue::kRational) { - return QVariant::fromValue(rational::fromDouble(interpolated)); + if (type == TYPE_RATIONAL) { + return rational::fromDouble(interpolated); + } else if (type == TYPE_INTEGER) { + return int64_t(std::round(interpolated)); } else { return interpolated; } @@ -535,14 +542,7 @@ QVariant Node::GetSplitValueAtTimeOnTrack(const QString &input, const rational & return GetSplitStandardValueOnTrack(input, track, element); } -QVariant Node::GetDefaultValue(const QString &input) const -{ - NodeValue::Type type = GetInputDataType(input); - - return NodeValue::combine_track_values_into_normal_value(type, GetSplitDefaultValue(input)); -} - -SplitValue Node::GetSplitDefaultValue(const QString &input) const +value_t Node::GetDefaultValue(const QString &input) const { const Input* i = GetInternalInputData(input); @@ -550,28 +550,21 @@ SplitValue Node::GetSplitDefaultValue(const QString &input) const return i->default_value; } else { ReportInvalidInput("retrieve default value of", input, -1); - return SplitValue(); + return value_t(); } } -QVariant Node::GetSplitDefaultValueOnTrack(const QString &input, int track) const +value_t::component_t Node::GetSplitDefaultValueOnTrack(const QString &input, size_t track) const { - SplitValue val = GetSplitDefaultValue(input); + value_t val = GetDefaultValue(input); if (track < val.size()) { return val.at(track); } else { - return QVariant(); + return value_t::component_t(); } } -void Node::SetDefaultValue(const QString &input, const QVariant &val) -{ - NodeValue::Type type = GetInputDataType(input); - - SetSplitDefaultValue(input, NodeValue::split_normal_value_into_track_values(type, val)); -} - -void Node::SetSplitDefaultValue(const QString &input, const SplitValue &val) +void Node::SetDefaultValue(const QString &input, const value_t &val) { Input* i = GetInternalInputData(input); @@ -582,7 +575,7 @@ void Node::SetSplitDefaultValue(const QString &input, const SplitValue &val) } } -void Node::SetSplitDefaultValueOnTrack(const QString &input, const QVariant &val, int track) +void Node::SetSplitDefaultValueOnTrack(const QString &input, const value_t::component_t &val, size_t track) { Input* i = GetInternalInputData(input); @@ -636,9 +629,15 @@ NodeKeyframe::Type Node::GetBestKeyframeTypeForTimeOnTrack(const QString &input, } } -int Node::GetNumberOfKeyframeTracks(const QString &id) const +size_t Node::GetNumberOfKeyframeTracks(const QString &id) const { - return NodeValue::get_number_of_keyframe_tracks(GetInputDataType(id)); + const Input* i = GetInternalInputData(id); + if (i) { + return i->channel_count; + } else { + ReportInvalidInput("get number of keyframe tracks of", id, -1); + return 1; + } } NodeKeyframe *Node::GetEarliestKeyframe(const QString &id, int element) const @@ -703,29 +702,23 @@ bool Node::HasKeyframeAtTime(const QString &id, const rational &time, int elemen QStringList Node::GetComboBoxStrings(const QString &id) const { - return GetInputProperty(id, QStringLiteral("combo_str")).toStringList(); -} - -QVariant Node::GetStandardValue(const QString &id, int element) const -{ - NodeValue::Type type = GetInputDataType(id); - - return NodeValue::combine_track_values_into_normal_value(type, GetSplitStandardValue(id, element)); + return GetInputProperty(id, QStringLiteral("combo_str")).value(); } -SplitValue Node::GetSplitStandardValue(const QString &id, int element) const +value_t Node::GetStandardValue(const QString &id, int element) const { + const Input *input = GetInternalInputData(id); NodeInputImmediate* imm = GetImmediate(id, element); if (imm) { - return imm->get_split_standard_value(); + return value_t(input->type, imm->get_standard_value()); } else { ReportInvalidInput("get standard value of", id, element); - return SplitValue(); + return value_t(); } } -QVariant Node::GetSplitStandardValueOnTrack(const QString &input, int track, int element) const +value_t::component_t Node::GetSplitStandardValueOnTrack(const QString &input, int track, int element) const { NodeInputImmediate* imm = GetImmediate(input, element); @@ -733,25 +726,18 @@ QVariant Node::GetSplitStandardValueOnTrack(const QString &input, int track, int return imm->get_split_standard_value_on_track(track); } else { ReportInvalidInput("get standard value of", input, element); - return QVariant(); + return value_t::component_t(); } } -void Node::SetStandardValue(const QString &id, const QVariant &value, int element) -{ - NodeValue::Type type = GetInputDataType(id); - - SetSplitStandardValue(id, NodeValue::split_normal_value_into_track_values(type, value), element); -} - -void Node::SetSplitStandardValue(const QString &id, const SplitValue &value, int element) +void Node::SetStandardValue(const QString &id, const value_t &value, int element) { NodeInputImmediate* imm = GetImmediate(id, element); if (imm) { imm->set_split_standard_value(value); - for (int i=0; iflags; } else { ReportInvalidInput("retrieve flags of", input, -1); - return InputFlags(kInputFlagNormal); + return kInputFlagNormal; } } @@ -1534,7 +1519,7 @@ void Node::SaveInput(QXmlStreamWriter *writer, const QString &id) const { writer->writeAttribute(QStringLiteral("id"), id); - if (!GetStandardValue(id, -1).isNull()) { + if (GetStandardValue(id, -1).isValid()) { writer->writeStartElement(QStringLiteral("primary")); SaveImmediate(writer, id, -1); @@ -1563,12 +1548,12 @@ void Node::SaveInput(QXmlStreamWriter *writer, const QString &id) const bool Node::LoadImmediate(QXmlStreamReader *reader, const QString &input, int element, SerializedData *data) { - NodeValue::Type data_type = this->GetInputDataType(input); + type_t data_type = this->GetInputDataType(input); // HACK: SubtitleParams contain the actual subtitle data, so loading/replacing it will overwrite // the valid subtitles. We hack around it by simply skipping loading subtitles, we'll see // if this ends up being an issue in the future. - if (data_type == NodeValue::kSubtitleParams) { + if (data_type == ViewerOutput::TYPE_SPARAM) { reader->skipCurrentElement(); return true; } @@ -1580,20 +1565,20 @@ bool Node::LoadImmediate(QXmlStreamReader *reader, const QString &input, int ele while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("track")) { - QVariant value_on_track; + value_t::component_t value_on_track; - if (data_type == NodeValue::kVideoParams) { + if (data_type == ViewerOutput::TYPE_VPARAM) { VideoParams vp; vp.Load(reader); - value_on_track = QVariant::fromValue(vp); - } else if (data_type == NodeValue::kAudioParams) { + value_on_track = vp; + } else if (data_type == ViewerOutput::TYPE_APARAM) { AudioParams ap = TypeSerializer::LoadAudioParams(reader); - value_on_track = QVariant::fromValue(ap); + value_on_track = ap; } else { QString value_text = reader->readElementText(); if (!value_text.isEmpty()) { - value_on_track = NodeValue::StringToValue(data_type, value_text, true); + value_on_track = value_t::component_t(value_text).converted(TYPE_STRING, data_type); } } @@ -1657,20 +1642,25 @@ void Node::SaveImmediate(QXmlStreamWriter *writer, const QString &input, int ele writer->writeTextElement(QStringLiteral("keyframing"), QString::number(is_keyframing)); } - NodeValue::Type data_type = this->GetInputDataType(input); + type_t data_type = this->GetInputDataType(input); // Write standard value writer->writeStartElement(QStringLiteral("standard")); - foreach (const QVariant& v, this->GetSplitStandardValue(input, element)) { + value_t value = this->GetStandardValue(input, element); + for (size_t i = 0; i < value.size(); i++) { + value_t::component_t v; + writer->writeStartElement(QStringLiteral("track")); - if (data_type == NodeValue::kVideoParams) { - v.value().Save(writer); - } else if (data_type == NodeValue::kAudioParams) { - TypeSerializer::SaveAudioParams(writer, v.value()); + // FIXME: We now have converters for custom types, so this should be handled with those instead. + // It'll probably require bumping the project version though... + if (data_type == ViewerOutput::TYPE_VPARAM) { + v.get().Save(writer); + } else if (data_type == ViewerOutput::TYPE_APARAM) { + TypeSerializer::SaveAudioParams(writer, v.get()); } else { - writer->writeCharacters(NodeValue::ValueToString(data_type, v, true)); + writer->writeCharacters(v.toSerializedString(data_type)); } writer->writeEndElement(); // track @@ -1699,7 +1689,7 @@ void Node::SaveImmediate(QXmlStreamWriter *writer, const QString &input, int ele writer->writeEndElement(); // keyframes } - if (data_type == NodeValue::kColor) { + if (this->HasInputProperty(input, QStringLiteral("col_input"))) { // Save color management information writer->writeTextElement(QStringLiteral("csinput"), this->GetInputProperty(input, QStringLiteral("col_input")).toString()); writer->writeTextElement(QStringLiteral("csdisplay"), this->GetInputProperty(input, QStringLiteral("col_display")).toString()); @@ -1708,7 +1698,7 @@ void Node::SaveImmediate(QXmlStreamWriter *writer, const QString &input, int ele } } -void Node::InsertInput(const QString &id, NodeValue::Type type, const QVariant &default_value, InputFlags flags, int index) +void Node::InsertInput(const QString &id, type_t type, size_t channel_count, const value_t &default_value, InputFlag flags, int index) { if (id.isEmpty()) { qWarning() << "Rejected adding input with an empty ID on node" << this->id(); @@ -1720,18 +1710,33 @@ void Node::InsertInput(const QString &id, NodeValue::Type type, const QVariant & return; } + QString subtype; + + type = ResolveSpecialType(type, channel_count, subtype); + Node::Input i; i.type = type; - i.default_value = NodeValue::split_normal_value_into_track_values(type, default_value); + i.default_value = default_value; i.flags = flags; i.array_size = 0; + i.channel_count = channel_count; + + //qDebug() << "creating" << id << "with channels" << i.channel_count; + + if (!subtype.isEmpty()) { + i.properties.insert(QStringLiteral("subtype"), subtype); + } input_ids_.insert(index, id); input_data_.insert(index, i); if (!standard_immediates_.value(id, nullptr)) { - standard_immediates_.insert(id, CreateImmediate(id)); + NodeInputImmediate *imm = CreateImmediate(id); + standard_immediates_.insert(id, imm); + if (default_value.isValid()) { + imm->set_split_standard_value(default_value); + } } emit InputAdded(id); @@ -1791,14 +1796,14 @@ NodeInputImmediate *Node::CreateImmediate(const QString &input) const Input* i = GetInternalInputData(input); if (i) { - return new NodeInputImmediate(i->type, i->default_value); + return new NodeInputImmediate(i->type, i->channel_count); } else { ReportInvalidInput("create immediate", input, -1); return nullptr; } } -void Node::ArrayResizeInternal(const QString &id, int size) +void Node::ArrayResizeInternal(const QString &id, size_t size) { Input* imm = GetInternalInputData(id); @@ -1812,7 +1817,7 @@ void Node::ArrayResizeInternal(const QString &id, int size) if (imm->array_size < size) { // Size is larger, create any immediates that don't exist QVector& subinputs = array_immediates_[id]; - for (int i=subinputs.size(); iGetSplitStandardValue(input, src_element); + value_t standard = src->GetStandardValue(input, src_element); if (command) { command->add_child(new NodeParamSetSplitStandardValueCommand(dst_input, standard)); } else { - dst->SetSplitStandardValue(input, standard, dst_element); + dst->SetStandardValue(input, standard, dst_element); } // Copy keyframes @@ -2256,7 +2261,43 @@ void Node::ClearElement(const QString& input, int index) SetInputIsKeyframing(input, false, index); } - SetSplitStandardValue(input, GetSplitDefaultValue(input), index); + SetStandardValue(input, GetDefaultValue(input), index); +} + +type_t Node::ResolveSpecialType(type_t type, size_t &channel_count, QString &subtype) +{ + if (type == TYPE_VEC2) { + channel_count = 2; + return TYPE_DOUBLE; + } else if (type == TYPE_VEC3) { + channel_count = 3; + return TYPE_DOUBLE; + } else if (type == TYPE_VEC4) { + channel_count = 4; + return TYPE_DOUBLE; + } else if (type == TYPE_COLOR) { + channel_count = 4; + subtype = QStringLiteral("color"); + return TYPE_DOUBLE; + } else if (type == TYPE_BOOL) { + subtype = QStringLiteral("bool"); + return TYPE_INTEGER; + } else if (type == TYPE_COMBO) { + subtype = QStringLiteral("combo"); + return TYPE_INTEGER; + } else if (type == TYPE_FONT) { + subtype = QStringLiteral("font"); + return TYPE_STRING; + } else if (type == TYPE_FILE) { + subtype = QStringLiteral("file"); + return TYPE_STRING; + } else if (type == TYPE_BEZIER) { + channel_count = 6; + subtype = QStringLiteral("bezier"); + return TYPE_DOUBLE; + } + + return type; } void Node::InputValueChangedEvent(const QString &input, int element) @@ -2415,7 +2456,7 @@ void Node::InvalidateFromKeyframeTypeChanged() emit KeyframeTypeChanged(key); } -void Node::SetValueAtTime(const NodeInput &input, const rational &time, const QVariant &value, int track, MultiUndoCommand *command, bool insert_on_all_tracks_if_no_key) +void Node::SetValueAtTime(const NodeInput &input, const rational &time, const value_t::component_t &value, size_t track, MultiUndoCommand *command, bool insert_on_all_tracks_if_no_key) { if (input.IsKeyframing()) { rational node_time = time; @@ -2426,9 +2467,9 @@ void Node::SetValueAtTime(const NodeInput &input, const rational &time, const QV command->add_child(new NodeParamSetKeyframeValueCommand(existing_key, value)); } else { // No existing key, create a new one - int nb_tracks = NodeValue::get_number_of_keyframe_tracks(input.node()->GetInputDataType(input.input())); - for (int i=0; iGetNumberOfKeyframeTracks(input.input()); + for (size_t i=0; i GetInputProperties(const QString& id) const; - QVariant GetInputProperty(const QString& id, const QString& name) const; - void SetInputProperty(const QString& id, const QString& name, const QVariant& value); - - QVariant GetValueAtTime(const QString& input, const rational& time, int element = -1) const - { - NodeValue::Type type = GetInputDataType(input); - - return NodeValue::combine_track_values_into_normal_value(type, GetSplitValueAtTime(input, time, element)); - } + QHash GetInputProperties(const QString& id) const; + value_t GetInputProperty(const QString& id, const QString& name) const; + void SetInputProperty(const QString& id, const QString& name, const value_t &value); - QVariant GetValueAtTime(const NodeInput& input, const rational& time) + value_t GetValueAtTime(const QString& input, const rational& time, int element = -1) const; + value_t GetValueAtTime(const NodeInput& input, const rational& time) { return GetValueAtTime(input.input(), time, input.element()); } - SplitValue GetSplitValueAtTime(const QString& input, const rational& time, int element = -1) const; - - SplitValue GetSplitValueAtTime(const NodeInput& input, const rational& time) - { - return GetSplitValueAtTime(input.input(), time, input.element()); - } - - QVariant GetSplitValueAtTimeOnTrack(const QString& input, const rational& time, int track, int element = -1) const; - QVariant GetSplitValueAtTimeOnTrack(const NodeInput& input, const rational& time, int track) const + value_t::component_t GetSplitValueAtTimeOnTrack(const QString& input, const rational& time, int track, int element = -1) const; + value_t::component_t GetSplitValueAtTimeOnTrack(const NodeInput& input, const rational& time, int track) const { return GetSplitValueAtTimeOnTrack(input.input(), time, track, input.element()); } - QVariant GetSplitValueAtTimeOnTrack(const NodeKeyframeTrackReference& input, const rational& time) const + value_t::component_t GetSplitValueAtTimeOnTrack(const NodeKeyframeTrackReference& input, const rational& time) const { return GetSplitValueAtTimeOnTrack(input.input(), time, input.track()); } - QVariant GetDefaultValue(const QString& input) const; - SplitValue GetSplitDefaultValue(const QString& input) const; - QVariant GetSplitDefaultValueOnTrack(const QString& input, int track) const; + value_t GetDefaultValue(const QString& input) const; + value_t::component_t GetSplitDefaultValueOnTrack(const QString& input, size_t track) const; - void SetDefaultValue(const QString& input, const QVariant &val); - void SetSplitDefaultValue(const QString& input, const SplitValue &val); - void SetSplitDefaultValueOnTrack(const QString& input, const QVariant &val, int track); + void SetDefaultValue(const QString& input, const value_t &val); + void SetSplitDefaultValueOnTrack(const QString& input, const value_t::component_t &val, size_t track); const QVector& GetKeyframeTracks(const QString& input, int element) const; const QVector& GetKeyframeTracks(const NodeInput& input) const @@ -466,7 +460,7 @@ class Node : public QObject return GetBestKeyframeTypeForTimeOnTrack(input.input(), time, input.track()); } - int GetNumberOfKeyframeTracks(const QString& id) const; + size_t GetNumberOfKeyframeTracks(const QString& id) const; int GetNumberOfKeyframeTracks(const NodeInput& id) const { return GetNumberOfKeyframeTracks(id.input()); @@ -504,38 +498,26 @@ class Node : public QObject QStringList GetComboBoxStrings(const QString& id) const; - QVariant GetStandardValue(const QString& id, int element = -1) const; - QVariant GetStandardValue(const NodeInput& id) const + value_t GetStandardValue(const QString& id, int element = -1) const; + value_t GetStandardValue(const NodeInput& id) const { return GetStandardValue(id.input(), id.element()); } - SplitValue GetSplitStandardValue(const QString& id, int element = -1) const; - SplitValue GetSplitStandardValue(const NodeInput& id) const - { - return GetSplitStandardValue(id.input(), id.element()); - } - - QVariant GetSplitStandardValueOnTrack(const QString& input, int track, int element = -1) const; - QVariant GetSplitStandardValueOnTrack(const NodeKeyframeTrackReference& id) const + value_t::component_t GetSplitStandardValueOnTrack(const QString& input, int track, int element = -1) const; + value_t::component_t GetSplitStandardValueOnTrack(const NodeKeyframeTrackReference& id) const { return GetSplitStandardValueOnTrack(id.input().input(), id.track(), id.input().element()); } - void SetStandardValue(const QString& id, const QVariant& value, int element = -1); - void SetStandardValue(const NodeInput& id, const QVariant& value) + void SetStandardValue(const QString& id, const value_t& value, int element = -1); + void SetStandardValue(const NodeInput& id, const value_t& value) { SetStandardValue(id.input(), value, id.element()); } - void SetSplitStandardValue(const QString& id, const SplitValue& value, int element = -1); - void SetSplitStandardValue(const NodeInput& id, const SplitValue& value) - { - SetSplitStandardValue(id.input(), value, id.element()); - } - - void SetSplitStandardValueOnTrack(const QString& id, int track, const QVariant& value, int element = -1); - void SetSplitStandardValueOnTrack(const NodeKeyframeTrackReference& id, const QVariant& value) + void SetSplitStandardValueOnTrack(const QString& id, int track, const value_t::component_t& value, int element = -1); + void SetSplitStandardValueOnTrack(const NodeKeyframeTrackReference& id, const value_t::component_t& value) { SetSplitStandardValueOnTrack(id.input().input(), id.track(), value, id.input().element()); } @@ -563,8 +545,8 @@ class Node : public QObject int InputArraySize(const QString& id) const; - NodeValue GetInputValue(const ValueParams &g, const QString &input, int element = -1) const; - NodeValue GetFakeConnectedValue(const ValueParams &g, Node *node, const QString &input, int element = -1) const; + value_t GetInputValue(const ValueParams &g, const QString &input, int element = -1) const; + value_t GetFakeConnectedValue(const ValueParams &g, Node *node, const QString &input, int element = -1) const; NodeInputImmediate* GetImmediate(const QString& input, int element) const; @@ -775,7 +757,7 @@ class Node : public QObject * corresponding output if it's connected to one. If your node doesn't directly deal with time, the default behavior * of the NodeParam objects will handle everything related to it automatically. */ - virtual NodeValue Value(const ValueParams &p) const {return NodeValue();} + virtual value_t Value(const ValueParams &p) const {return value_t();} bool HasGizmos() const { @@ -831,20 +813,20 @@ class Node : public QObject folder_ = folder; } - InputFlags GetInputFlags(const QString& input) const; + InputFlag GetInputFlags(const QString& input) const; void SetInputFlag(const QString &input, InputFlag f, bool on = true); virtual void LoadFinishedEvent(){} virtual void ConnectedToPreviewEvent(){} - static void SetValueAtTime(const NodeInput &input, const rational &time, const QVariant &value, int track, MultiUndoCommand *command, bool insert_on_all_tracks_if_no_key); + static void SetValueAtTime(const NodeInput &input, const rational &time, const value_t::component_t &value, size_t track, MultiUndoCommand *command, bool insert_on_all_tracks_if_no_key); /** * @brief Find path starting at `from` that outputs to arrive at `to` */ static std::list FindPath(Node *from, Node *to, int path_index); - void ArrayResizeInternal(const QString& id, int size); + void ArrayResizeInternal(const QString& id, size_t size); virtual void AddedToGraphEvent(Project *p){} virtual void RemovedFromGraphEvent(Project *p){} @@ -855,26 +837,50 @@ class Node : public QObject static const QString kEnabledInput; protected: - void InsertInput(const QString& id, NodeValue::Type type, const QVariant& default_value, InputFlags flags, int index); + void InsertInput(const QString& id, type_t type, size_t channel_count, const value_t& default_value, InputFlag flags, int index); + void InsertInput(const QString& id, type_t type, const value_t& default_value, InputFlag flags, int index) + { + return InsertInput(id, type, 1, default_value, flags, index); + } + + void PrependInput(const QString& id, type_t type, size_t channel_count, const value_t& default_value, InputFlag flags = kInputFlagNormal) + { + InsertInput(id, type, channel_count, default_value, flags, 0); + } + + void PrependInput(const QString& id, type_t type, const value_t& default_value, InputFlag flags = kInputFlagNormal) + { + InsertInput(id, type, 1, default_value, flags, 0); + } - void PrependInput(const QString& id, NodeValue::Type type, const QVariant& default_value, InputFlags flags = InputFlags(kInputFlagNormal)) + void PrependInput(const QString& id, type_t type, InputFlag flags = kInputFlagNormal) { - InsertInput(id, type, default_value, flags, 0); + PrependInput(id, type, value_t(), flags); } - void PrependInput(const QString& id, NodeValue::Type type, InputFlags flags = InputFlags(kInputFlagNormal)) + void PrependInput(const QString& id, InputFlag flags = kInputFlagNormal) { - PrependInput(id, type, QVariant(), flags); + PrependInput(id, TYPE_NONE, value_t(), flags); } - void AddInput(const QString& id, NodeValue::Type type, const QVariant& default_value, InputFlags flags = InputFlags(kInputFlagNormal)) + void AddInput(const QString& id, type_t type, size_t channel_count, const value_t& default_value, InputFlag flags = kInputFlagNormal) { - InsertInput(id, type, default_value, flags, input_ids_.size()); + InsertInput(id, type, channel_count, default_value, flags, input_ids_.size()); } - void AddInput(const QString& id, NodeValue::Type type, InputFlags flags = InputFlags(kInputFlagNormal)) + void AddInput(const QString& id, type_t type, const value_t& default_value, InputFlag flags = kInputFlagNormal) { - AddInput(id, type, QVariant(), flags); + return AddInput(id, type, 1, default_value, flags); + } + + void AddInput(const QString& id, type_t type, InputFlag flags = kInputFlagNormal) + { + AddInput(id, type, value_t(), flags); + } + + void AddInput(const QString& id, InputFlag flags = kInputFlagNormal) + { + AddInput(id, TYPE_NONE, value_t(), flags); } void RemoveInput(const QString& id); @@ -885,7 +891,7 @@ class Node : public QObject void SetComboBoxStrings(const QString& id, const QStringList& strings) { - SetInputProperty(id, QStringLiteral("combo_str"), strings); + SetInputProperty(id, QStringLiteral("combo_str"), value_t("strl", strings)); } void SendInvalidateCache(const TimeRange &range, const InvalidateCacheOptions &options); @@ -997,7 +1003,7 @@ protected slots: void InputValueHintChanged(const NodeInput& input); - void InputPropertyChanged(const QString& input, const QString& key, const QVariant& value); + void InputPropertyChanged(const QString& input, const QString& key, const value_t& value); void LinksChanged(); @@ -1025,7 +1031,7 @@ protected slots: void InputNameChanged(const QString& id, const QString& name); - void InputDataTypeChanged(const QString& id, NodeValue::Type type); + void InputDataTypeChanged(const QString& id, type_t type); void AddedToGraph(Project* graph); @@ -1037,16 +1043,17 @@ protected slots: void NodeRemovedFromContext(Node *node); - void InputFlagsChanged(const QString &input, const InputFlags &flags); + void InputFlagsChanged(const QString &input, const InputFlag &flags); private: struct Input { - NodeValue::Type type; - InputFlags flags; - SplitValue default_value; - QHash properties; + type_t type; + InputFlag flags; + value_t default_value; + QHash properties; QString human_name; - int array_size; + size_t array_size; + size_t channel_count; }; NodeInputImmediate* CreateImmediate(const QString& input); @@ -1126,6 +1133,8 @@ protected slots: void ClearElement(const QString &input, int index); + type_t ResolveSpecialType(type_t type, size_t &channel_count, QString &subtype); + /** * @brief Custom user label for node */ diff --git a/app/node/nodeundo.cpp b/app/node/nodeundo.cpp index f83f2666dc..7900d00e3a 100644 --- a/app/node/nodeundo.cpp +++ b/app/node/nodeundo.cpp @@ -398,14 +398,14 @@ void NodeParamSetKeyframingCommand::undo() input_.node()->SetInputIsKeyframing(input_, old_setting_); } -NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant& value) : +NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const value_t::component_t& value) : key_(key), old_value_(key_->value()), new_value_(value) { } -NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant &new_value, const QVariant &old_value) : +NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const value_t::component_t &new_value, const value_t::component_t &old_value) : key_(key), old_value_(old_value), new_value_(new_value) @@ -502,14 +502,14 @@ void NodeParamSetKeyframeTimeCommand::undo() key_->set_time(old_time_); } -NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(const NodeKeyframeTrackReference& input, const QVariant &value) : +NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(const NodeKeyframeTrackReference& input, const value_t::component_t &value) : ref_(input), - old_value_(ref_.input().node()->GetStandardValue(ref_.input())), + old_value_(ref_.input().node()->GetSplitStandardValueOnTrack(ref_)), new_value_(value) { } -NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(const NodeKeyframeTrackReference& input, const QVariant &new_value, const QVariant &old_value) : +NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(const NodeKeyframeTrackReference& input, const value_t::component_t &new_value, const value_t::component_t &old_value) : ref_(input), old_value_(old_value), new_value_(new_value) diff --git a/app/node/nodeundo.h b/app/node/nodeundo.h index 6df91caac4..d7ce564e85 100644 --- a/app/node/nodeundo.h +++ b/app/node/nodeundo.h @@ -244,7 +244,7 @@ class NodeArrayRemoveCommand : public UndoCommand if (node_->IsInputKeyframable(input_)) { is_keyframing_ = node_->IsInputKeyframing(input_, index_); } - standard_value_ = node_->GetSplitStandardValue(input_, index_); + standard_value_ = node_->GetStandardValue(input_, index_); keyframes_ = node_->GetKeyframeTracks(input_, index_); node_->GetImmediate(input_, index_)->delete_all_keyframes(&memory_manager_); @@ -261,7 +261,7 @@ class NodeArrayRemoveCommand : public UndoCommand key->setParent(node_); } } - node_->SetSplitStandardValue(input_, standard_value_, index_); + node_->SetStandardValue(input_, standard_value_, index_); if (node_->IsInputKeyframable(input_)) { node_->SetInputIsKeyframing(input_, is_keyframing_, index_); @@ -273,7 +273,7 @@ class NodeArrayRemoveCommand : public UndoCommand QString input_; int index_; - SplitValue standard_value_; + value_t standard_value_; bool is_keyframing_; QVector keyframes_; QObject memory_manager_; @@ -714,8 +714,8 @@ class NodeParamSetKeyframeTimeCommand : public UndoCommand class NodeParamSetKeyframeValueCommand : public UndoCommand { public: - NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant& value); - NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant& new_value, const QVariant& old_value); + NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const value_t::component_t &value); + NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const value_t::component_t &new_value, const value_t::component_t &old_value); virtual Project* GetRelevantProject() const override; @@ -726,16 +726,16 @@ class NodeParamSetKeyframeValueCommand : public UndoCommand private: NodeKeyframe* key_; - QVariant old_value_; - QVariant new_value_; + value_t::component_t old_value_; + value_t::component_t new_value_; }; class NodeParamSetStandardValueCommand : public UndoCommand { public: - NodeParamSetStandardValueCommand(const NodeKeyframeTrackReference& input, const QVariant& value); - NodeParamSetStandardValueCommand(const NodeKeyframeTrackReference& input, const QVariant& new_value, const QVariant& old_value); + NodeParamSetStandardValueCommand(const NodeKeyframeTrackReference& input, const value_t::component_t& value); + NodeParamSetStandardValueCommand(const NodeKeyframeTrackReference& input, const value_t::component_t& new_value, const value_t::component_t& old_value); virtual Project* GetRelevantProject() const override; @@ -746,22 +746,22 @@ class NodeParamSetStandardValueCommand : public UndoCommand private: NodeKeyframeTrackReference ref_; - QVariant old_value_; - QVariant new_value_; + value_t::component_t old_value_; + value_t::component_t new_value_; }; class NodeParamSetSplitStandardValueCommand : public UndoCommand { public: - NodeParamSetSplitStandardValueCommand(const NodeInput& input, const SplitValue& new_value, const SplitValue& old_value) : + NodeParamSetSplitStandardValueCommand(const NodeInput& input, const value_t& new_value, const value_t& old_value) : ref_(input), old_value_(old_value), new_value_(new_value) {} - NodeParamSetSplitStandardValueCommand(const NodeInput& input, const SplitValue& value) : - NodeParamSetSplitStandardValueCommand(input, value, input.node()->GetSplitStandardValue(input.input())) + NodeParamSetSplitStandardValueCommand(const NodeInput& input, const value_t& value) : + NodeParamSetSplitStandardValueCommand(input, value, input.node()->GetStandardValue(input.input())) {} virtual Project* GetRelevantProject() const override @@ -772,19 +772,19 @@ class NodeParamSetSplitStandardValueCommand : public UndoCommand protected: virtual void redo() override { - ref_.node()->SetSplitStandardValue(ref_.input(), new_value_, ref_.element()); + ref_.node()->SetStandardValue(ref_.input(), new_value_, ref_.element()); } virtual void undo() override { - ref_.node()->SetSplitStandardValue(ref_.input(), old_value_, ref_.element()); + ref_.node()->SetStandardValue(ref_.input(), old_value_, ref_.element()); } private: NodeInput ref_; - SplitValue old_value_; - SplitValue new_value_; + value_t old_value_; + value_t new_value_; }; diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index 2a3b3f4ff9..da2b4ed2e7 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -50,11 +50,11 @@ Track::Track() : arraymap_invalid_(false), ignore_arraymap_set_(false) { - AddInput(kBlockInput, NodeValue::kNone, InputFlags(kInputFlagArray | kInputFlagNotKeyframable | kInputFlagHidden | kInputFlagIgnoreInvalidations)); + AddInput(kBlockInput, kInputFlagArray | kInputFlagNotKeyframable | kInputFlagHidden | kInputFlagIgnoreInvalidations); - AddInput(kMutedInput, NodeValue::kBoolean, false, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + AddInput(kMutedInput, TYPE_BOOL, false, kInputFlagNotConnectable | kInputFlagNotKeyframable); - AddInput(kArrayMapInput, NodeValue::kBinary, InputFlags(kInputFlagStatic | kInputFlagHidden | kInputFlagIgnoreInvalidations)); + AddInput(kArrayMapInput, TYPE_BINARY, kInputFlagStatic | kInputFlagHidden | kInputFlagIgnoreInvalidations); // Set default height track_height_ = kTrackHeightDefault; @@ -188,7 +188,7 @@ void ProcessAudio(const void *context, const SampleJob &job, SampleBuffer &block } } -NodeValue Track::Value(const ValueParams &p) const +value_t Track::Value(const ValueParams &p) const { if (!IsMuted() && !blocks_.empty() && p.time().in() < track_length() && p.time().out() > 0) { if (this->type() == Track::kVideo) { @@ -234,7 +234,7 @@ NodeValue Track::Value(const ValueParams &p) const } } - return NodeValue(); + return value_t(); } TimeRange Track::InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time, bool clamp) const @@ -750,7 +750,7 @@ void Track::RefreshBlockCacheFromArrayMap() disconnect(b, &Block::LengthChanged, this, &Track::BlockLengthChanged); } - QByteArray bytes = GetStandardValue(kArrayMapInput).toByteArray(); + QByteArray bytes = GetStandardValue(kArrayMapInput).toBinary(); block_array_indexes_.resize(bytes.size() / sizeof(uint32_t)); memcpy(block_array_indexes_.data(), bytes.data(), bytes.size()); blocks_.clear(); diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index d6eb2379e0..2e325c5269 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -54,7 +54,7 @@ class Track : public Node virtual QVector Category() const override; virtual QString Description() const override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time, bool clamp) const override; @@ -459,7 +459,7 @@ public slots: int GetBlockIndexAtTime(const rational &time) const; - NodeValue ProcessAudioTrack(const ValueParams &p) const; + value_t ProcessAudioTrack(const ValueParams &p) const; int ConnectBlock(Block *b); diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index a0c9c1328a..695f4a262e 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -43,20 +43,20 @@ ViewerOutput::ViewerOutput(bool create_buffer_inputs, bool create_default_stream autocache_input_audio_(false), waveform_requests_enabled_(false) { - AddInput(kVideoParamsInput, NodeValue::kVideoParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray | kInputFlagHidden)); + AddInput(kVideoParamsInput, TYPE_VPARAM, kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray | kInputFlagHidden); - AddInput(kAudioParamsInput, NodeValue::kAudioParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray | kInputFlagHidden)); + AddInput(kAudioParamsInput, TYPE_APARAM, kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray | kInputFlagHidden); - AddInput(kSubtitleParamsInput, NodeValue::kSubtitleParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray | kInputFlagHidden)); + AddInput(kSubtitleParamsInput, TYPE_SPARAM, kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray | kInputFlagHidden); if (create_buffer_inputs) { - AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); - AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable)); + AddInput(kTextureInput, TYPE_TEXTURE, kInputFlagNotKeyframable); + AddInput(kSamplesInput, TYPE_SAMPLES, kInputFlagNotKeyframable); } if (create_default_streams) { - AddStream(Track::kVideo, QVariant()); - AddStream(Track::kAudio, QVariant()); + AddStream(Track::kVideo, value_t(TYPE_VPARAM, VideoParams())); + AddStream(Track::kAudio, value_t(TYPE_APARAM, AudioParams())); set_default_parameters(); } @@ -217,12 +217,12 @@ void ViewerOutput::set_default_parameters() static_cast(OLIVE_CONFIG("OfflinePixelFormat").toInt()), VideoParams::kInternalChannelCount, OLIVE_CONFIG("DefaultSequencePixelAspect").value(), - OLIVE_CONFIG("DefaultSequenceInterlacing").value(), + static_cast(OLIVE_CONFIG("DefaultSequenceInterlacing").toInt()), VideoParams::generate_auto_divider(width, height) )); SetAudioParams(AudioParams( OLIVE_CONFIG("DefaultSequenceAudioFrequency").toInt(), - OLIVE_CONFIG("DefaultSequenceAudioLayout").toULongLong(), + OLIVE_CONFIG("DefaultSequenceAudioLayout").toInt(), kDefaultSampleFormat )); } @@ -421,7 +421,7 @@ void ViewerOutput::SetWaveformEnabled(bool e) } } -NodeValue ViewerOutput::Value(const ValueParams &p) const +value_t ViewerOutput::Value(const ValueParams &p) const { Track::Reference ref = Track::Reference::FromString(p.output()); @@ -432,7 +432,7 @@ NodeValue ViewerOutput::Value(const ValueParams &p) const return GetInputValue(p, kSamplesInput); } - return NodeValue(); + return value_t(); } bool ViewerOutput::LoadCustom(QXmlStreamReader *reader, SerializedData *data) @@ -565,12 +565,12 @@ void ViewerOutput::set_parameters_from_footage(const QVector foo } } -int ViewerOutput::AddStream(Track::Type type, const QVariant& value) +int ViewerOutput::AddStream(Track::Type type, const value_t &value) { return SetStream(type, value, -1); } -int ViewerOutput::SetStream(Track::Type type, const QVariant &value, int index_in) +int ViewerOutput::SetStream(Track::Type type, const value_t &value, int index_in) { QString id; diff --git a/app/node/output/viewer/viewer.h b/app/node/output/viewer/viewer.h index 2469a9e285..160c93d091 100644 --- a/app/node/output/viewer/viewer.h +++ b/app/node/output/viewer/viewer.h @@ -44,6 +44,10 @@ class ViewerOutput : public Node { Q_OBJECT public: + static constexpr type_t TYPE_VPARAM = "vparam"; + static constexpr type_t TYPE_APARAM = "aparam"; + static constexpr type_t TYPE_SPARAM = "sparam"; + ViewerOutput(bool create_buffer_inputs = true, bool create_default_streams = true); NODE_DEFAULT_FUNCTIONS(ViewerOutput) @@ -98,17 +102,17 @@ class ViewerOutput : public Node void SetVideoParams(const VideoParams &video, int index = 0) { - SetStandardValue(kVideoParamsInput, QVariant::fromValue(video), index); + SetStandardValue(kVideoParamsInput, value_t(TYPE_VPARAM, video), index); } void SetAudioParams(const AudioParams &audio, int index = 0) { - SetStandardValue(kAudioParamsInput, QVariant::fromValue(audio), index); + SetStandardValue(kAudioParamsInput, value_t(TYPE_APARAM, audio), index); } void SetSubtitleParams(const SubtitleParams &subs, int index = 0) { - SetStandardValue(kSubtitleParamsInput, QVariant::fromValue(subs), index); + SetStandardValue(kSubtitleParamsInput, value_t(TYPE_SPARAM, subs), index); } int GetVideoStreamCount() const @@ -186,7 +190,7 @@ class ViewerOutput : public Node bool IsVideoAutoCacheEnabled() const { qDebug() << "sequence ac is a stub"; return false; } void SetVideoAutoCacheEnabled(bool e) { qDebug() << "sequence ac is a stub"; } - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; const EncodingParams &GetLastUsedEncodingParams() const { return last_used_encoding_params_; } void SetLastUsedEncodingParams(const EncodingParams &p) { last_used_encoding_params_ = p; } @@ -239,8 +243,8 @@ public slots: virtual void InputValueChangedEvent(const QString& input, int element) override; - int AddStream(Track::Type type, const QVariant &value); - int SetStream(Track::Type type, const QVariant &value, int index); + int AddStream(Track::Type type, const value_t &value); + int SetStream(Track::Type type, const value_t &value, int index); private: rational last_length_; diff --git a/app/node/param.cpp b/app/node/param.cpp index 456648f501..1d5b5a8b89 100644 --- a/app/node/param.cpp +++ b/app/node/param.cpp @@ -69,12 +69,12 @@ bool NodeInput::IsArray() const } } -InputFlags NodeInput::GetFlags() const +InputFlag NodeInput::GetFlags() const { if (IsValid()) { return node_->GetInputFlags(input_); } else { - return InputFlags(kInputFlagNormal); + return kInputFlagNormal; } } @@ -96,21 +96,30 @@ Node *NodeInput::GetConnectedOutput() const } } -NodeValue::Type NodeInput::GetDataType() const +type_t NodeInput::GetDataType() const { if (IsValid()) { return node_->GetInputDataType(input_); } else { - return NodeValue::kNone; + return TYPE_NONE; } } -QVariant NodeInput::GetDefaultValue() const +value_t NodeInput::GetDefaultValue() const { if (IsValid()) { return node_->GetDefaultValue(input_); } else { - return QVariant(); + return value_t(); + } +} + +size_t NodeInput::GetChannelCount() const +{ + if (IsValid()) { + return node_->GetNumberOfKeyframeTracks(input_); + } else { + return 0; } } @@ -123,30 +132,30 @@ QStringList NodeInput::GetComboBoxStrings() const } } -QVariant NodeInput::GetProperty(const QString &key) const +value_t NodeInput::GetProperty(const QString &key) const { if (IsValid()) { return node_->GetInputProperty(input_, key); } else { - return QVariant(); + return value_t(); } } -QHash NodeInput::GetProperties() const +QHash NodeInput::GetProperties() const { if (IsValid()) { return node_->GetInputProperties(input_); } else { - return QHash(); + return QHash(); } } -QVariant NodeInput::GetValueAtTime(const rational &time) const +value_t NodeInput::GetValueAtTime(const rational &time) const { if (IsValid()) { return node_->GetValueAtTime(*this, time); } else { - return QVariant(); + return value_t(); } } @@ -159,12 +168,12 @@ NodeKeyframe* NodeInput::GetKeyframeAtTimeOnTrack(const rational &time, int trac } } -QVariant NodeInput::GetSplitDefaultValueForTrack(int track) const +value_t::component_t NodeInput::GetSplitDefaultValueForTrack(int track) const { if (IsValid()) { return node_->GetSplitDefaultValueOnTrack(input_, track); } else { - return QVariant(); + return value_t::component_t(); } } diff --git a/app/node/param.h b/app/node/param.h index 60a90e33da..707032fa26 100644 --- a/app/node/param.h +++ b/app/node/param.h @@ -30,131 +30,62 @@ namespace olive { class Node; class NodeKeyframe; -enum InputFlag : uint64_t { - /// By default, inputs are keyframable, connectable, and NOT arrays - kInputFlagNormal = 0x0, - kInputFlagArray = 0x1, - kInputFlagNotKeyframable = 0x2, - kInputFlagNotConnectable = 0x4, - kInputFlagHidden = 0x8, - kInputFlagIgnoreInvalidations = 0x10, - - kInputFlagStatic = kInputFlagNotKeyframable | kInputFlagNotConnectable -}; - -class InputFlags { +class InputFlag +{ public: - explicit InputFlags() - { - f_ = kInputFlagNormal; - } - - explicit InputFlags(uint64_t flags) - { - f_ = flags; - } - - InputFlags operator|(const InputFlags &f) const - { - InputFlags i = *this; - i |= f; - return i; - } + explicit InputFlag(uint64_t f = 0) noexcept { f_ = f; } - InputFlags operator|(const InputFlag &f) const + InputFlag &operator|=(const InputFlag &i) { - InputFlags i = *this; - i |= f; - return i; - } - - InputFlags operator|(const uint64_t &f) const - { - InputFlags i = *this; - i |= f; - return i; - } - - InputFlags &operator|=(const InputFlags &f) - { - f_ |= f.f_; + f_ |= i.f_; return *this; - } + }; - InputFlags &operator|=(const InputFlag &f) + InputFlag operator|(const InputFlag &rhs) const { - f_ |= f; - return *this; - } + InputFlag f = *this; + f.f_ |= rhs.f_; + return f; + }; - InputFlags &operator|=(const uint64_t &f) + InputFlag &operator&=(const InputFlag &i) { - f_ |= f; + f_ &= i.f_; return *this; } - InputFlags operator&(const InputFlags &f) const + InputFlag operator&(const InputFlag &rhs) const { - InputFlags i = *this; - i &= f; - return i; + InputFlag f = *this; + f.f_ &= rhs.f_; + return f; } - InputFlags operator&(const InputFlag &f) const - { - InputFlags i = *this; - i &= f; - return i; - } + operator bool() const { return f_; } + bool operator!() const { return !f_; } - InputFlags operator&(const uint64_t &f) const + InputFlag operator~() const { - InputFlags i = *this; - i &= f; - return i; + return InputFlag(~f_); } - InputFlags &operator&=(const InputFlags &f) - { - f_ &= f.f_; - return *this; - } - - InputFlags &operator&=(const InputFlag &f) - { - f_ &= f; - return *this; - } - - InputFlags &operator&=(const uint64_t &f) - { - f_ &= f; - return *this; - } - - InputFlags operator~() const - { - InputFlags i = *this; - i.f_ = ~i.f_; - return i; - } - - inline operator bool() const - { - return f_; - } - - inline const uint64_t &value() const - { - return f_; - } + const uint64_t &value() const { return f_; } private: uint64_t f_; }; -struct NodeInputPair { +static const InputFlag kInputFlagNormal = InputFlag(0x0); +static const InputFlag kInputFlagArray = InputFlag(0x1); +static const InputFlag kInputFlagNotKeyframable = InputFlag(0x2); +static const InputFlag kInputFlagNotConnectable = InputFlag(0x4); +static const InputFlag kInputFlagHidden = InputFlag(0x8); +static const InputFlag kInputFlagIgnoreInvalidations = InputFlag(0x10); +static const InputFlag kInputFlagStatic = kInputFlagNotKeyframable | kInputFlagNotConnectable; + +struct NodeInputPair +{ bool operator==(const NodeInputPair& rhs) const { return node == rhs.node && input == rhs.input; @@ -256,26 +187,28 @@ class NodeInput bool IsArray() const; - InputFlags GetFlags() const; + InputFlag GetFlags() const; QString GetInputName() const; Node *GetConnectedOutput() const; - NodeValue::Type GetDataType() const; + type_t GetDataType() const; + + value_t GetDefaultValue() const; - QVariant GetDefaultValue() const; + size_t GetChannelCount() const; QStringList GetComboBoxStrings() const; - QVariant GetProperty(const QString& key) const; - QHash GetProperties() const; + value_t GetProperty(const QString& key) const; + QHash GetProperties() const; - QVariant GetValueAtTime(const rational& time) const; + value_t GetValueAtTime(const rational& time) const; NodeKeyframe *GetKeyframeAtTimeOnTrack(const rational& time, int track) const; - QVariant GetSplitDefaultValueForTrack(int track) const; + value_t::component_t GetSplitDefaultValueForTrack(int track) const; int GetArraySize() const; diff --git a/app/node/project/folder/folder.cpp b/app/node/project/folder/folder.cpp index 801f6cf7b7..b056f8a91b 100644 --- a/app/node/project/folder/folder.cpp +++ b/app/node/project/folder/folder.cpp @@ -36,7 +36,7 @@ Folder::Folder() { SetFlag(kIsItem); - AddInput(kChildInput, NodeValue::kNone, InputFlags(kInputFlagArray | kInputFlagNotKeyframable)); + AddInput(kChildInput, kInputFlagArray | kInputFlagNotKeyframable); } QVariant Folder::data(const DataType &d) const diff --git a/app/node/project/footage/footage.cpp b/app/node/project/footage/footage.cpp index 5f2ed48345..b0b00566d0 100644 --- a/app/node/project/footage/footage.cpp +++ b/app/node/project/footage/footage.cpp @@ -48,7 +48,7 @@ Footage::Footage(const QString &filename) : { SetFlag(kIsItem); - PrependInput(kFilenameInput, NodeValue::kFile, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + PrependInput(kFilenameInput, TYPE_FILE, kInputFlagNotConnectable | kInputFlagNotKeyframable); Clear(); @@ -231,7 +231,7 @@ QString Footage::DescribeSubtitleStream(const SubtitleParams ¶ms) .arg(QString::number(params.stream_index())); } -NodeValue Footage::Value(const ValueParams &p) const +value_t Footage::Value(const ValueParams &p) const { Track::Reference ref = Track::Reference::FromString(p.output()); @@ -271,7 +271,7 @@ NodeValue Footage::Value(const ValueParams &p) const job.set_audio_params(ap); job.set_cache_path(project()->cache_path()); - return NodeValue(NodeValue::kSamples, job); + return value_t(TYPE_SAMPLES, job); } break; case Track::kSubtitle: @@ -281,7 +281,7 @@ NodeValue Footage::Value(const ValueParams &p) const } } - return NodeValue(); + return value_t(); } QString Footage::GetStreamTypeName(Track::Type type) @@ -538,17 +538,17 @@ void Footage::Reprobe() } } - SetStream(Track::kVideo, QVariant::fromValue(video_stream), i); + SetStream(Track::kVideo, value_t(TYPE_VPARAM, video_stream), i); } InputArrayResize(kAudioParamsInput, footage_info.GetAudioStreams().size()); for (int i=0; i(i), track_input_id); track_lists_.replace(i, list); diff --git a/app/node/project/serializer/serializer210528.cpp b/app/node/project/serializer/serializer210528.cpp index 5a62daa694..4f12d51b9f 100644 --- a/app/node/project/serializer/serializer210528.cpp +++ b/app/node/project/serializer/serializer210528.cpp @@ -449,7 +449,7 @@ void ProjectSerializer210528::LoadImmediate(QXmlStreamReader *reader, Node *node { Q_UNUSED(xml_node_data) - NodeValue::Type data_type = node->GetInputDataType(input); + type_t data_type = node->GetInputDataType(input); while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("standard")) { @@ -462,20 +462,20 @@ void ProjectSerializer210528::LoadImmediate(QXmlStreamReader *reader, Node *node } if (reader->name() == QStringLiteral("track")) { - QVariant value_on_track; + value_t::component_t value_on_track; - if (data_type == NodeValue::kVideoParams) { + if (data_type == ViewerOutput::TYPE_VPARAM) { VideoParams vp; vp.Load(reader); - value_on_track = QVariant::fromValue(vp); - } else if (data_type == NodeValue::kAudioParams) { + value_on_track = vp; + } else if (data_type == ViewerOutput::TYPE_APARAM) { AudioParams ap = TypeSerializer::LoadAudioParams(reader); - value_on_track = QVariant::fromValue(ap); + value_on_track = ap; } else { QString value_text = reader->readElementText(); if (!value_text.isEmpty()) { - value_on_track = NodeValue::StringToValue(data_type, value_text, true); + value_on_track = value_t::component_t(value_text).toSerializedString(data_type); } } @@ -506,7 +506,7 @@ void ProjectSerializer210528::LoadImmediate(QXmlStreamReader *reader, Node *node QString key_input; rational key_time; NodeKeyframe::Type key_type = NodeKeyframe::kLinear; - QVariant key_value; + value_t::component_t key_value; QPointF key_in_handle; QPointF key_out_handle; @@ -532,7 +532,7 @@ void ProjectSerializer210528::LoadImmediate(QXmlStreamReader *reader, Node *node } } - key_value = NodeValue::StringToValue(data_type, reader->readElementText(), true); + key_value = value_t::component_t::fromSerializedString(data_type, reader->readElementText()); NodeKeyframe* key = new NodeKeyframe(key_time, key_value, key_type, track, element, key_input, node); key->set_bezier_control_in(key_in_handle); diff --git a/app/node/project/serializer/serializer210907.cpp b/app/node/project/serializer/serializer210907.cpp index a699d6d9e0..178cd81c34 100644 --- a/app/node/project/serializer/serializer210907.cpp +++ b/app/node/project/serializer/serializer210907.cpp @@ -446,7 +446,7 @@ void ProjectSerializer210907::LoadImmediate(QXmlStreamReader *reader, Node *node { Q_UNUSED(xml_node_data) - NodeValue::Type data_type = node->GetInputDataType(input); + type_t data_type = node->GetInputDataType(input); while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("standard")) { @@ -459,20 +459,20 @@ void ProjectSerializer210907::LoadImmediate(QXmlStreamReader *reader, Node *node } if (reader->name() == QStringLiteral("track")) { - QVariant value_on_track; + value_t::component_t value_on_track; - if (data_type == NodeValue::kVideoParams) { + if (data_type == ViewerOutput::TYPE_VPARAM) { VideoParams vp; vp.Load(reader); - value_on_track = QVariant::fromValue(vp); - } else if (data_type == NodeValue::kAudioParams) { + value_on_track = vp; + } else if (data_type == ViewerOutput::TYPE_APARAM) { AudioParams ap = TypeSerializer::LoadAudioParams(reader); - value_on_track = QVariant::fromValue(ap); + value_on_track = ap; } else { QString value_text = reader->readElementText(); if (!value_text.isEmpty()) { - value_on_track = NodeValue::StringToValue(data_type, value_text, true); + value_on_track = value_t::component_t(value_text).toSerializedString(data_type); } } @@ -503,7 +503,7 @@ void ProjectSerializer210907::LoadImmediate(QXmlStreamReader *reader, Node *node QString key_input; rational key_time; NodeKeyframe::Type key_type = NodeKeyframe::kLinear; - QVariant key_value; + value_t::component_t key_value; QPointF key_in_handle; QPointF key_out_handle; @@ -529,7 +529,7 @@ void ProjectSerializer210907::LoadImmediate(QXmlStreamReader *reader, Node *node } } - key_value = NodeValue::StringToValue(data_type, reader->readElementText(), true); + key_value = value_t::component_t::fromSerializedString(data_type, reader->readElementText()); NodeKeyframe* key = new NodeKeyframe(key_time, key_value, key_type, track, element, key_input, node); key->set_bezier_control_in(key_in_handle); diff --git a/app/node/project/serializer/serializer211228.cpp b/app/node/project/serializer/serializer211228.cpp index 4e8a638c34..032470f083 100644 --- a/app/node/project/serializer/serializer211228.cpp +++ b/app/node/project/serializer/serializer211228.cpp @@ -496,7 +496,7 @@ void ProjectSerializer211228::LoadImmediate(QXmlStreamReader *reader, Node *node { Q_UNUSED(xml_node_data) - NodeValue::Type data_type = node->GetInputDataType(input); + type_t data_type = node->GetInputDataType(input); while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("standard")) { @@ -509,20 +509,20 @@ void ProjectSerializer211228::LoadImmediate(QXmlStreamReader *reader, Node *node } if (reader->name() == QStringLiteral("track")) { - QVariant value_on_track; + value_t::component_t value_on_track; - if (data_type == NodeValue::kVideoParams) { + if (data_type == ViewerOutput::TYPE_VPARAM) { VideoParams vp; vp.Load(reader); - value_on_track = QVariant::fromValue(vp); - } else if (data_type == NodeValue::kAudioParams) { + value_on_track = vp; + } else if (data_type == ViewerOutput::TYPE_APARAM) { AudioParams ap = TypeSerializer::LoadAudioParams(reader); - value_on_track = QVariant::fromValue(ap); + value_on_track = ap; } else { QString value_text = reader->readElementText(); if (!value_text.isEmpty()) { - value_on_track = NodeValue::StringToValue(data_type, value_text, true); + value_on_track = value_t::component_t(value_text).toSerializedString(data_type); } } @@ -553,7 +553,7 @@ void ProjectSerializer211228::LoadImmediate(QXmlStreamReader *reader, Node *node QString key_input; rational key_time; NodeKeyframe::Type key_type = NodeKeyframe::kLinear; - QVariant key_value; + value_t::component_t key_value; QPointF key_in_handle; QPointF key_out_handle; @@ -579,7 +579,7 @@ void ProjectSerializer211228::LoadImmediate(QXmlStreamReader *reader, Node *node } } - key_value = NodeValue::StringToValue(data_type, reader->readElementText(), true); + key_value = value_t::component_t::fromSerializedString(data_type, reader->readElementText()); NodeKeyframe* key = new NodeKeyframe(key_time, key_value, key_type, track, element, key_input, node); key->set_bezier_control_in(key_in_handle); diff --git a/app/node/project/serializer/serializer220403.cpp b/app/node/project/serializer/serializer220403.cpp index 1630aba359..1841744fbf 100644 --- a/app/node/project/serializer/serializer220403.cpp +++ b/app/node/project/serializer/serializer220403.cpp @@ -653,12 +653,12 @@ void ProjectSerializer220403::LoadImmediate(QXmlStreamReader *reader, Node *node { Q_UNUSED(xml_node_data) - NodeValue::Type data_type = node->GetInputDataType(input); + type_t data_type = node->GetInputDataType(input); // HACK: SubtitleParams contain the actual subtitle data, so loading/replacing it will overwrite // the valid subtitles. We hack around it by simply skipping loading subtitles, we'll see // if this ends up being an issue in the future. - if (data_type == NodeValue::kSubtitleParams) { + if (data_type == ViewerOutput::TYPE_SPARAM) { reader->skipCurrentElement(); return; } @@ -674,20 +674,20 @@ void ProjectSerializer220403::LoadImmediate(QXmlStreamReader *reader, Node *node } if (reader->name() == QStringLiteral("track")) { - QVariant value_on_track; + value_t::component_t value_on_track; - if (data_type == NodeValue::kVideoParams) { + if (data_type == ViewerOutput::TYPE_VPARAM) { VideoParams vp; vp.Load(reader); - value_on_track = QVariant::fromValue(vp); - } else if (data_type == NodeValue::kAudioParams) { + value_on_track = vp; + } else if (data_type == ViewerOutput::TYPE_APARAM) { AudioParams ap = TypeSerializer::LoadAudioParams(reader); - value_on_track = QVariant::fromValue(ap); + value_on_track = ap; } else { QString value_text = reader->readElementText(); if (!value_text.isEmpty()) { - value_on_track = NodeValue::StringToValue(data_type, value_text, true); + value_on_track = value_t::component_t::fromSerializedString(data_type, value_text); } } @@ -746,7 +746,7 @@ void ProjectSerializer220403::LoadImmediate(QXmlStreamReader *reader, Node *node } } -void ProjectSerializer220403::LoadKeyframe(QXmlStreamReader *reader, NodeKeyframe *key, NodeValue::Type data_type) const +void ProjectSerializer220403::LoadKeyframe(QXmlStreamReader *reader, NodeKeyframe *key, type_t data_type) const { QString key_input; QPointF key_in_handle; @@ -774,7 +774,7 @@ void ProjectSerializer220403::LoadKeyframe(QXmlStreamReader *reader, NodeKeyfram } } - key->set_value(NodeValue::StringToValue(data_type, reader->readElementText(), true)); + key->set_value(value_t::component_t::fromSerializedString(data_type, reader->readElementText())); key->set_bezier_control_in(key_in_handle); key->set_bezier_control_out(key_out_handle); @@ -904,11 +904,11 @@ void ProjectSerializer220403::LoadNodeCustom(QXmlStreamReader *reader, Node *nod } else if (reader->name() == QStringLiteral("name")) { link.custom_name = reader->readElementText(); } else if (reader->name() == QStringLiteral("flags")) { - link.custom_flags = InputFlags(reader->readElementText().toULongLong()); + link.custom_flags = InputFlag(reader->readElementText().toULongLong()); } else if (reader->name() == QStringLiteral("type")) { - link.data_type = NodeValue::GetDataTypeFromName(reader->readElementText()); + link.data_type = type_t::fromString(reader->readElementText()); } else if (reader->name() == QStringLiteral("default")) { - link.default_val = NodeValue::StringToValue(link.data_type, reader->readElementText(), false); + link.default_val = value_t::fromSerializedString(link.data_type, reader->readElementText()); } else if (reader->name() == QStringLiteral("properties")) { while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("property")) { diff --git a/app/node/project/serializer/serializer220403.h b/app/node/project/serializer/serializer220403.h index 74c6ccad7b..21d0f48964 100644 --- a/app/node/project/serializer/serializer220403.h +++ b/app/node/project/serializer/serializer220403.h @@ -57,10 +57,10 @@ class ProjectSerializer220403 : public ProjectSerializer QString input_id; int input_element; QString custom_name; - InputFlags custom_flags; - NodeValue::Type data_type; - QVariant default_val; - QHash custom_properties; + InputFlag custom_flags; + type_t data_type; + value_t default_val; + QHash custom_properties; }; QHash node_ptrs; @@ -82,7 +82,7 @@ class ProjectSerializer220403 : public ProjectSerializer void LoadImmediate(QXmlStreamReader *reader, Node *node, const QString& input, int element, XMLNodeData& xml_node_data) const; - void LoadKeyframe(QXmlStreamReader *reader, NodeKeyframe *key, NodeValue::Type data_type) const; + void LoadKeyframe(QXmlStreamReader *reader, NodeKeyframe *key, type_t data_type) const; bool LoadPosition(QXmlStreamReader *reader, quintptr *node_ptr, Node::Position *pos) const; diff --git a/app/node/serializeddata.h b/app/node/serializeddata.h index 10ef7f132b..fcdc02b7a1 100644 --- a/app/node/serializeddata.h +++ b/app/node/serializeddata.h @@ -48,10 +48,10 @@ struct SerializedData { QString input_id; int input_element; QString custom_name; - InputFlags custom_flags; - NodeValue::Type data_type; - QVariant default_val; - QHash custom_properties; + InputFlag custom_flags; + type_t data_type; + value_t default_val; + QHash custom_properties; }; QMap > positions; diff --git a/app/node/time/timeformat/timeformat.cpp b/app/node/time/timeformat/timeformat.cpp index edbf9135c5..3709c75cc4 100644 --- a/app/node/time/timeformat/timeformat.cpp +++ b/app/node/time/timeformat/timeformat.cpp @@ -32,9 +32,9 @@ const QString TimeFormatNode::kLocalTimeInput = QStringLiteral("localtime_in"); TimeFormatNode::TimeFormatNode() { - AddInput(kTimeInput, NodeValue::kFloat); - AddInput(kFormatInput, NodeValue::kText, QStringLiteral("hh:mm:ss")); - AddInput(kLocalTimeInput, NodeValue::kBoolean); + AddInput(kTimeInput, TYPE_DOUBLE); + AddInput(kFormatInput, TYPE_STRING, QStringLiteral("hh:mm:ss")); + AddInput(kLocalTimeInput, TYPE_BOOL); } QString TimeFormatNode::Name() const @@ -66,7 +66,7 @@ void TimeFormatNode::Retranslate() SetInputName(kLocalTimeInput, tr("Interpret time as local time")); } -NodeValue TimeFormatNode::Value(const ValueParams &p) const +value_t TimeFormatNode::Value(const ValueParams &p) const { qint64 ms_since_epoch = GetInputValue(p, kTimeInput).toDouble()*1000; bool time_is_local = GetInputValue(p, kLocalTimeInput).toBool(); diff --git a/app/node/time/timeformat/timeformat.h b/app/node/time/timeformat/timeformat.h index ba112f376a..6f255e20b4 100644 --- a/app/node/time/timeformat/timeformat.h +++ b/app/node/time/timeformat/timeformat.h @@ -40,7 +40,7 @@ class TimeFormatNode : public Node virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &globals) const override; + virtual value_t Value(const ValueParams &globals) const override; static const QString kTimeInput; static const QString kFormatInput; diff --git a/app/node/time/timeoffset/timeoffsetnode.cpp b/app/node/time/timeoffset/timeoffsetnode.cpp index c4d6f9c275..0b4d424649 100644 --- a/app/node/time/timeoffset/timeoffsetnode.cpp +++ b/app/node/time/timeoffset/timeoffsetnode.cpp @@ -31,11 +31,11 @@ const QString TimeOffsetNode::kInputInput = QStringLiteral("input_in"); TimeOffsetNode::TimeOffsetNode() { - AddInput(kTimeInput, NodeValue::kRational, QVariant::fromValue(rational(0)), InputFlags(kInputFlagNotConnectable)); + AddInput(kTimeInput, TYPE_RATIONAL, rational(0), kInputFlagNotConnectable); SetInputProperty(kTimeInput, QStringLiteral("view"), RationalSlider::kTime); SetInputProperty(kTimeInput, QStringLiteral("viewlock"), true); - AddInput(kInputInput, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable)); + AddInput(kInputInput, kInputFlagNotKeyframable); } void TimeOffsetNode::Retranslate() @@ -67,7 +67,7 @@ TimeRange TimeOffsetNode::OutputTimeAdjustment(const QString &input, int element return super::OutputTimeAdjustment(input, element, input_time); } -NodeValue TimeOffsetNode::Value(const ValueParams &p) const +value_t TimeOffsetNode::Value(const ValueParams &p) const { return GetInputValue(p, kInputInput); } diff --git a/app/node/time/timeoffset/timeoffsetnode.h b/app/node/time/timeoffset/timeoffsetnode.h index 1aead05170..920e29cb4f 100644 --- a/app/node/time/timeoffset/timeoffsetnode.h +++ b/app/node/time/timeoffset/timeoffsetnode.h @@ -57,7 +57,7 @@ class TimeOffsetNode : public Node virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; static const QString kTimeInput; static const QString kInputInput; diff --git a/app/node/time/timeremap/timeremap.cpp b/app/node/time/timeremap/timeremap.cpp index 6737e10d05..a1de0e8724 100644 --- a/app/node/time/timeremap/timeremap.cpp +++ b/app/node/time/timeremap/timeremap.cpp @@ -31,11 +31,11 @@ const QString TimeRemapNode::kInputInput = QStringLiteral("input_in"); TimeRemapNode::TimeRemapNode() { - AddInput(kTimeInput, NodeValue::kRational, QVariant::fromValue(rational(0)), InputFlags(kInputFlagNotConnectable)); + AddInput(kTimeInput, TYPE_RATIONAL, rational(0), kInputFlagNotConnectable); SetInputProperty(kTimeInput, QStringLiteral("view"), RationalSlider::kTime); SetInputProperty(kTimeInput, QStringLiteral("viewlock"), true); - AddInput(kInputInput, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable)); + AddInput(kInputInput, kInputFlagNotKeyframable); } QString TimeRemapNode::Name() const @@ -87,7 +87,7 @@ void TimeRemapNode::Retranslate() SetInputName(kInputInput, QStringLiteral("Input")); } -NodeValue TimeRemapNode::Value(const ValueParams &p) const +value_t TimeRemapNode::Value(const ValueParams &p) const { return GetInputValue(p, kInputInput); } diff --git a/app/node/time/timeremap/timeremap.h b/app/node/time/timeremap/timeremap.h index 6f6f77bf3a..2790e6b6f3 100644 --- a/app/node/time/timeremap/timeremap.h +++ b/app/node/time/timeremap/timeremap.h @@ -43,7 +43,7 @@ class TimeRemapNode : public Node virtual void Retranslate() override; - virtual NodeValue Value(const ValueParams &p) const override; + virtual value_t Value(const ValueParams &p) const override; static const QString kTimeInput; static const QString kInputInput; diff --git a/app/node/value.cpp b/app/node/value.cpp index c6247f5910..2852833977 100644 --- a/app/node/value.cpp +++ b/app/node/value.cpp @@ -33,353 +33,204 @@ namespace olive { -QString NodeValue::ValueToString(Type data_type, const QVariant &value, bool value_is_a_key_track) -{ - if (!value_is_a_key_track && data_type == kVec2) { - QVector2D vec = value.value(); - - return QStringLiteral("%1:%2").arg(QString::number(vec.x()), - QString::number(vec.y())); - } else if (!value_is_a_key_track && data_type == kVec3) { - QVector3D vec = value.value(); - - return QStringLiteral("%1:%2:%3").arg(QString::number(vec.x()), - QString::number(vec.y()), - QString::number(vec.z())); - } else if (!value_is_a_key_track && data_type == kVec4) { - QVector4D vec = value.value(); - - return QStringLiteral("%1:%2:%3:%4").arg(QString::number(vec.x()), - QString::number(vec.y()), - QString::number(vec.z()), - QString::number(vec.w())); - } else if (!value_is_a_key_track && data_type == kColor) { - Color c = value.value(); - - return QStringLiteral("%1:%2:%3:%4").arg(QString::number(c.red()), - QString::number(c.green()), - QString::number(c.blue()), - QString::number(c.alpha())); - } else if (!value_is_a_key_track && data_type == kBezier) { - Bezier b = value.value(); - - return QStringLiteral("%1:%2:%3:%4:%5:%6").arg(QString::number(b.x()), - QString::number(b.y()), - QString::number(b.cp1_x()), - QString::number(b.cp1_y()), - QString::number(b.cp2_x()), - QString::number(b.cp2_y())); - } else if (data_type == kRational) { - return QString::fromStdString(value.value().toString()); - } else if (data_type == kTexture - || data_type == kSamples - || data_type == kNone) { - // These data types need no XML representation - return QString(); - } else if (data_type == kInt) { - return QString::number(value.value()); - } else if (data_type == kBinary) { - return value.toByteArray().toBase64(); - } else { - if (value.canConvert()) { - return value.toString(); - } +std::map > value_t::converters_; - if (!value.isNull()) { - qWarning() << "Failed to convert type" << ToHex(data_type) << "to string"; - } +QChar CHANNEL_SPLITTER = ':'; - return QString(); - } +value_t::value_t(const SampleJob &samples) : + value_t(TYPE_SAMPLES, samples) +{ } -QVector NodeValue::split_normal_value_into_track_values(Type type, const QVariant &value) +value_t value_t::fromSerializedString(type_t target_type, const QString &str) { - QVector vals(get_number_of_keyframe_tracks(type)); - - switch (type) { - case kVec2: - { - QVector2D vec = value.value(); - vals.replace(0, vec.x()); - vals.replace(1, vec.y()); - break; - } - case kVec3: - { - QVector3D vec = value.value(); - vals.replace(0, vec.x()); - vals.replace(1, vec.y()); - vals.replace(2, vec.z()); - break; - } - case kVec4: - { - QVector4D vec = value.value(); - vals.replace(0, vec.x()); - vals.replace(1, vec.y()); - vals.replace(2, vec.z()); - vals.replace(3, vec.w()); - break; - } - case kColor: - { - Color c = value.value(); - vals.replace(0, c.red()); - vals.replace(1, c.green()); - vals.replace(2, c.blue()); - vals.replace(3, c.alpha()); - break; - } - case kBezier: - { - Bezier b = value.value(); - vals.replace(0, b.x()); - vals.replace(1, b.y()); - vals.replace(2, b.cp1_x()); - vals.replace(3, b.cp1_y()); - vals.replace(4, b.cp2_x()); - vals.replace(5, b.cp2_y()); - break; + QStringList l = str.split(CHANNEL_SPLITTER); + value_t v(TYPE_STRING, l.size()); + + for (size_t i = 0; i < v.size(); i++) { + v[i] = l[i]; } - default: - vals.replace(0, value); + + if (target_type != TYPE_STRING) { + v = v.converted(target_type); } - return vals; + return v; } -QVariant NodeValue::combine_track_values_into_normal_value(Type type, const QVector &split) +QString value_t::toSerializedString() const { - if (split.isEmpty()) { - return QVariant(); + if (this->type() == TYPE_STRING) { + QStringList l; + for (auto it = data_.cbegin(); it != data_.cend(); it++) { + l.append(it->get()); + } + return l.join(CHANNEL_SPLITTER); + } else { + value_t stringified = this->converted(TYPE_STRING); + return stringified.toSerializedString(); } +} - switch (type) { - case kVec2: - { - return QVector2D(split.at(0).toFloat(), - split.at(1).toFloat()); - } - case kVec3: - { - return QVector3D(split.at(0).toFloat(), - split.at(1).toFloat(), - split.at(2).toFloat()); - } - case kVec4: - { - return QVector4D(split.at(0).toFloat(), - split.at(1).toFloat(), - split.at(2).toFloat(), - split.at(3).toFloat()); +value_t value_t::converted(type_t to) const +{ + // No-op if type is already requested + if (this->type() == to) { + return *this; } - case kColor: - { - return QVariant::fromValue(Color(split.at(0).toFloat(), - split.at(1).toFloat(), - split.at(2).toFloat(), - split.at(3).toFloat())); + + // Find converter, no-op if none found + Converter_t c = converters_[this->type()][to]; + if (!c) { + return *this; } - case kBezier: - return QVariant::fromValue(Bezier(split.at(0).toDouble(), - split.at(1).toDouble(), - split.at(2).toDouble(), - split.at(3).toDouble(), - split.at(4).toDouble(), - split.at(5).toDouble())); - default: - return split.first(); + + // Create new value with new type and same amount of channels + value_t v(to, data_.size()); + + // Run each channel through converter + for (size_t i = 0; i < data_.size(); i++) { + v.data_[0] = c(data_[0]); } + + qDebug() << "converted" << this->type().toString() << "to" << to.toString(); + + return v; } -int NodeValue::get_number_of_keyframe_tracks(Type type) +value_t::component_t converter_IntegerToString(const value_t::component_t &v) { - switch (type) { - case NodeValue::kVec2: - return 2; - case NodeValue::kVec3: - return 3; - case NodeValue::kVec4: - case NodeValue::kColor: - return 4; - case NodeValue::kBezier: - return 6; - default: - return 1; - } + return QString::number(v.get()); } -QVariant NodeValue::StringToValue(Type data_type, const QString &string, bool value_is_a_key_track) +value_t::component_t converter_StringToInteger(const value_t::component_t &v) { - if (!value_is_a_key_track && data_type == kVec2) { - QStringList vals = string.split(':'); + return int64_t(v.get().toLongLong()); +} + +value_t::component_t converter_DoubleToString(const value_t::component_t &v) +{ + return QString::number(v.get()); +} + +value_t::component_t converter_StringToDouble(const value_t::component_t &v) +{ + return v.get().toDouble(); +} - ValidateVectorString(&vals, 2); +value_t::component_t converter_RationalToString(const value_t::component_t &v) +{ + return QString::fromStdString(v.get().toString()); +} - return QVector2D(vals.at(0).toFloat(), vals.at(1).toFloat()); - } else if (!value_is_a_key_track && data_type == kVec3) { - QStringList vals = string.split(':'); +value_t::component_t converter_StringToRational(const value_t::component_t &v) +{ + return rational::fromString(v.get().toStdString()); +} - ValidateVectorString(&vals, 3); +value_t::component_t converter_BinaryToString(const value_t::component_t &v) +{ + return QString::fromLatin1(v.get().toBase64()); +} - return QVector3D(vals.at(0).toFloat(), vals.at(1).toFloat(), vals.at(2).toFloat()); - } else if (!value_is_a_key_track && data_type == kVec4) { - QStringList vals = string.split(':'); +value_t::component_t converter_StringToBinary(const value_t::component_t &v) +{ + return QByteArray::fromBase64(v.get().toLatin1()); +} - ValidateVectorString(&vals, 4); +value_t::component_t converter_IntegerToDouble(const value_t::component_t &v) +{ + return double(v.get()); +} - return QVector4D(vals.at(0).toFloat(), vals.at(1).toFloat(), vals.at(2).toFloat(), vals.at(3).toFloat()); - } else if (!value_is_a_key_track && data_type == kColor) { - QStringList vals = string.split(':'); +value_t::component_t converter_IntegerToRational(const value_t::component_t &v) +{ + return rational(v.get()); +} - ValidateVectorString(&vals, 4); +value_t::component_t converter_DoubleToInteger(const value_t::component_t &v) +{ + return int64_t(v.get()); +} - return QVariant::fromValue(Color(vals.at(0).toDouble(), vals.at(1).toDouble(), vals.at(2).toDouble(), vals.at(3).toDouble())); - } else if (!value_is_a_key_track && data_type == kBezier) { - QStringList vals = string.split(':'); +value_t::component_t converter_DoubleToRational(const value_t::component_t &v) +{ + return rational::fromDouble(v.get()); +} - ValidateVectorString(&vals, 6); +value_t::component_t converter_RationalToInteger(const value_t::component_t &v) +{ + return int64_t(v.get().toDouble()); +} - return QVariant::fromValue(Bezier(vals.at(0).toDouble(), vals.at(1).toDouble(), vals.at(2).toDouble(), vals.at(3).toDouble(), vals.at(4).toDouble(), vals.at(5).toDouble())); - } else if (data_type == kInt) { - return QVariant::fromValue(string.toLongLong()); - } else if (data_type == kRational) { - return QVariant::fromValue(rational::fromString(string.toStdString())); - } else if (data_type == kBinary) { - return QByteArray::fromBase64(string.toLatin1()); - } else { - return string; - } +value_t::component_t converter_RationalToDouble(const value_t::component_t &v) +{ + return v.get().toDouble(); } -void NodeValue::ValidateVectorString(QStringList* list, int count) +void value_t::registerConverter(const type_t &from, const type_t &to, Converter_t converter) { - while (list->size() < count) { - list->append(QStringLiteral("0")); + if (converters_[from][to]) { + qWarning() << "Failed to register converter from" << from.toString() << "to" << to.toString() << "- converter already exists"; + return; } + + converters_[from][to] = converter; } -NodeValue::NodeValue(const SampleJob &samples) : - NodeValue(kSamples, samples) +void value_t::registerDefaultConverters() { + registerConverter(TYPE_INTEGER, TYPE_STRING, converter_IntegerToString); + registerConverter(TYPE_DOUBLE, TYPE_STRING, converter_DoubleToString); + registerConverter(TYPE_RATIONAL, TYPE_STRING, converter_RationalToString); + registerConverter(TYPE_BINARY, TYPE_STRING, converter_BinaryToString); + + registerConverter(TYPE_STRING, TYPE_INTEGER, converter_StringToInteger); + registerConverter(TYPE_STRING, TYPE_DOUBLE, converter_StringToDouble); + registerConverter(TYPE_STRING, TYPE_RATIONAL, converter_StringToRational); + registerConverter(TYPE_STRING, TYPE_BINARY, converter_StringToBinary); + + registerConverter(TYPE_INTEGER, TYPE_DOUBLE, converter_IntegerToDouble); + registerConverter(TYPE_INTEGER, TYPE_RATIONAL, converter_IntegerToRational); + + registerConverter(TYPE_DOUBLE, TYPE_INTEGER, converter_DoubleToInteger); + registerConverter(TYPE_DOUBLE, TYPE_RATIONAL, converter_DoubleToRational); + + registerConverter(TYPE_RATIONAL, TYPE_INTEGER, converter_RationalToInteger); + registerConverter(TYPE_RATIONAL, TYPE_DOUBLE, converter_RationalToDouble); } -QString NodeValue::GetPrettyDataTypeName(Type type) +value_t::component_t value_t::component_t::converted(type_t from, type_t to) const { - switch (type) { - case kNone: - return QCoreApplication::translate("NodeValue", "None"); - case kInt: - case kCombo: - return QCoreApplication::translate("NodeValue", "Integer"); - case kFloat: - return QCoreApplication::translate("NodeValue", "Float"); - case kRational: - return QCoreApplication::translate("NodeValue", "Rational"); - case kBoolean: - return QCoreApplication::translate("NodeValue", "Boolean"); - case kColor: - return QCoreApplication::translate("NodeValue", "Color"); - case kMatrix: - return QCoreApplication::translate("NodeValue", "Matrix"); - case kText: - return QCoreApplication::translate("NodeValue", "Text"); - case kFont: - return QCoreApplication::translate("NodeValue", "Font"); - case kFile: - return QCoreApplication::translate("NodeValue", "File"); - case kTexture: - return QCoreApplication::translate("NodeValue", "Texture"); - case kSamples: - return QCoreApplication::translate("NodeValue", "Samples"); - case kVec2: - return QCoreApplication::translate("NodeValue", "Vector 2D"); - case kVec3: - return QCoreApplication::translate("NodeValue", "Vector 3D"); - case kVec4: - return QCoreApplication::translate("NodeValue", "Vector 4D"); - case kBezier: - return QCoreApplication::translate("NodeValue", "Bezier"); - case kVideoParams: - return QCoreApplication::translate("NodeValue", "Video Parameters"); - case kAudioParams: - return QCoreApplication::translate("NodeValue", "Audio Parameters"); - case kSubtitleParams: - return QCoreApplication::translate("NodeValue", "Subtitle Parameters"); - case kBinary: - return QCoreApplication::translate("NodeValue", "Binary"); - - case kDataTypeCount: - break; + if (Converter_t c = converters_[from][to]) { + return c(*this); } - return QCoreApplication::translate("NodeValue", "Unknown"); + return *this; } -QString NodeValue::GetDataTypeName(Type type) +value_t::component_t value_t::component_t::fromSerializedString(type_t to, const QString &str) { - switch (type) { - case kNone: - return QStringLiteral("none"); - case kInt: - return QStringLiteral("int"); - case kCombo: - return QStringLiteral("combo"); - case kFloat: - return QStringLiteral("float"); - case kRational: - return QStringLiteral("rational"); - case kBoolean: - return QStringLiteral("bool"); - case kColor: - return QStringLiteral("color"); - case kMatrix: - return QStringLiteral("matrix"); - case kText: - return QStringLiteral("text"); - case kFont: - return QStringLiteral("font"); - case kFile: - return QStringLiteral("file"); - case kTexture: - return QStringLiteral("texture"); - case kSamples: - return QStringLiteral("samples"); - case kVec2: - return QStringLiteral("vec2"); - case kVec3: - return QStringLiteral("vec3"); - case kVec4: - return QStringLiteral("vec4"); - case kBezier: - return QStringLiteral("bezier"); - case kVideoParams: - return QStringLiteral("vparam"); - case kAudioParams: - return QStringLiteral("aparam"); - case kSubtitleParams: - return QStringLiteral("sparam"); - case kBinary: - return QStringLiteral("binary"); - case kDataTypeCount: - break; + component_t c = str; + if (to != TYPE_STRING) { + c = c.converted(TYPE_STRING, to); } - - return QString(); + return c; } -NodeValue::Type NodeValue::GetDataTypeFromName(const QString &n) +QString value_t::component_t::toSerializedString(type_t from) const { - // Slow but easy to maintain - for (int i=0; i(i); - if (GetDataTypeName(t) == n) { - return t; - } + if (from == TYPE_STRING) { + return get(); + } else { + component_t c = this->converted(from, TYPE_STRING); + return c.get(); } +} - return NodeValue::kNone; +QDebug operator<<(QDebug dbg, const type_t &t) +{ + return dbg << t.toString(); } } diff --git a/app/node/value.h b/app/node/value.h index 5b5532703d..a6ee97e2fa 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -21,395 +21,291 @@ #ifndef NODEVALUE_H #define NODEVALUE_H +#include #include -#include -#include -#include -#include "common/qtutils.h" -#include "node/splitvalue.h" #include "render/texture.h" namespace olive { class Node; -class NodeValue; +class value_t; class SampleJob; -using NodeValueArray = std::vector; +using NodeValueArray = std::vector; -class NodeValue +class type_t { public: - /** - * @brief The types of data that can be passed between Nodes - */ - enum Type { - kNone, - - /** - ****************************** SPECIFIC IDENTIFIERS ****************************** - */ - - /** - * Integer type - * - * Resolves to int64_t. - */ - kInt, - - /** - * Decimal (floating-point) type - * - * Resolves to `double`. - */ - kFloat, - - /** - * Decimal (rational) type - * - * Resolves to `double`. - */ - kRational, - - /** - * Boolean type - * - * Resolves to `bool`. - */ - kBoolean, - - /** - * Floating-point type - * - * Resolves to `Color`. - * - * Colors passed around the nodes should always be in reference space and preferably use - */ - kColor, - - /** - * Matrix type - * - * Resolves to `QMatrix4x4`. - */ - kMatrix, - - /** - * Text type - * - * Resolves to `QString`. - */ - kText, - - /** - * Font type - * - * Resolves to `QFont`. - */ - kFont, - - /** - * File type - * - * Resolves to a `QString` containing an absolute file path. - */ - kFile, - - /** - * Image buffer type - * - * True value type depends on the render engine used. - */ - kTexture, - - /** - * Audio samples type - * - * Resolves to `SampleBufferPtr`. - */ - kSamples, - - /** - * Two-dimensional vector (XY) type - * - * Resolves to `QVector2D`. - */ - kVec2, - - /** - * Three-dimensional vector (XYZ) type - * - * Resolves to `QVector3D`. - */ - kVec3, - - /** - * Four-dimensional vector (XYZW) type - * - * Resolves to `QVector4D`. - */ - kVec4, - - /** - * Cubic bezier type that contains three X/Y coordinates, the main point, and two control points - * - * Resolves to `Bezier` - */ - kBezier, - - /** - * ComboBox type - * - * Resolves to `int` - the index currently selected - */ - kCombo, - - /** - * Video Parameters type - * - * Resolves to `VideoParams` - */ - kVideoParams, - - /** - * Audio Parameters type - * - * Resolves to `AudioParams` - */ - kAudioParams, - - /** - * Subtitle Parameters type - * - * Resolves to `SubtitleParams` - */ - kSubtitleParams, - - /** - * Binary Data - */ - kBinary, - - /** - * End of list - */ - kDataTypeCount - }; + constexpr type_t() : type_(0) {} + constexpr type_t(const char *x) : type_(insn_to_num(x)) {} - template - NodeValue(Type type, const T& data) + static type_t fromString(const QStringView &s) { return s.toUtf8().constData(); } + QString toString() const { - type_ = type; - set_value(data); - array_ = false; + const char *c = reinterpret_cast(&type_); + return QString::fromUtf8(c, strnlen(c, sizeof(type_))); } - NodeValue(Type type, const NodeValueArray &array) - { - type_ = type; - set_value(array); - array_ = true; - } + bool operator==(const type_t &t) const { return type_ == t.type_; } + bool operator!=(const type_t &t) const { return !(*this == t); } + bool operator<(const type_t &t) const { return type_ < t.type_; } + bool operator<=(const type_t &t) const { return type_ <= t.type_; } + bool operator>(const type_t &t) const { return type_ > t.type_; } + bool operator>=(const type_t &t) const { return type_ >= t.type_; } - NodeValue() : - NodeValue(kNone, QVariant()) +private: + constexpr uint64_t insn_to_num(const char* x) { + return *x ? *x + (insn_to_num(x+1) << 8) : 0; } - NodeValue(const QMatrix4x4& data) : - NodeValue(NodeValue::kMatrix, data) - { - } + uint64_t type_; - NodeValue(TexturePtr texture) : - NodeValue(NodeValue::kTexture, texture) - { - } +}; - NodeValue(const SampleJob &samples); +QDebug operator<<(QDebug dbg, const type_t &t); - NodeValue(const SampleBuffer &samples) : - NodeValue(NodeValue::kSamples, samples) - { - } +constexpr type_t TYPE_NONE; +constexpr type_t TYPE_INTEGER = "int"; +constexpr type_t TYPE_DOUBLE = "dbl"; +constexpr type_t TYPE_RATIONAL = "rational"; +constexpr type_t TYPE_STRING = "str"; +constexpr type_t TYPE_TEXTURE = "tex"; +constexpr type_t TYPE_SAMPLES = "smp"; +constexpr type_t TYPE_BINARY = "bin"; +constexpr type_t TYPE_ARRAY = "arr"; +constexpr type_t TYPE_MATRIX = "mat"; - NodeValue(const QVector2D &vec) : - NodeValue(NodeValue::kVec2, vec) +class value_t +{ +public: + class component_t { - } + public: + component_t() = default; + + template + component_t(const T &t) + { + set(t); + } + + // Bad initializers, must catch these at runtime + component_t(const value_t &t) { abort(); } + component_t(const QVariant &t) { abort(); } + component_t(const std::vector &t) { abort(); } + + template + T get(bool *ok = nullptr) const + { + if (data_.has_value()) { + qDebug() << "getting any type" << data_.type().name() << "attempting cast to" << typeid(T).name(); + return std::any_cast(data_); + } else { + return T(); + } + } + + template + void set(const T &t) + { + data_ = t; + } + + const std::any &data() const { return data_; } + + component_t converted(type_t from, type_t to) const; + + static component_t fromSerializedString(type_t to, const QString &s); + QString toSerializedString(type_t from) const; + + private: + std::any data_; + + }; - NodeValue(const QVector3D &vec) : - NodeValue(NodeValue::kVec3, vec) + value_t(const type_t &type, size_t channels = 1) : + type_(type) { + data_.resize(channels); } - NodeValue(const QVector4D &vec) : - NodeValue(NodeValue::kVec4, vec) + value_t(const type_t &type, const std::vector &components) : + type_(type) { + data_ = components; } - NodeValue(const double &d) : - NodeValue(NodeValue::kFloat, d) + template + value_t(const type_t &type, const T &v) : + value_t(type) { + data_[0] = v; } - NodeValue(const int64_t &i) : - NodeValue(NodeValue::kInt, i) + value_t() : + type_(TYPE_NONE) + {} + + value_t(TexturePtr texture) : + value_t(TYPE_TEXTURE, texture) { } - NodeValue(const int &i) : - NodeValue(NodeValue::kInt, i) + value_t(const SampleJob &samples); + + value_t(const QVector2D &vec) : + value_t(TYPE_DOUBLE, size_t(2)) { + data_[0] = double(vec.x()); + data_[1] = double(vec.y()); } - NodeValue(const bool &i) : - NodeValue(NodeValue::kBoolean, i) + value_t(const QVector3D &vec) : + value_t(TYPE_DOUBLE, size_t(3)) { + data_[0] = double(vec.x()); + data_[1] = double(vec.y()); + data_[2] = double(vec.z()); } - NodeValue(const Color &i) : - NodeValue(NodeValue::kColor, i) + value_t(const QVector4D &vec) : + value_t(TYPE_DOUBLE, size_t(4)) { + data_[0] = double(vec.x()); + data_[1] = double(vec.y()); + data_[2] = double(vec.z()); + data_[3] = double(vec.w()); } - NodeValue(const QString &i) : - NodeValue(NodeValue::kText, i) + value_t(const double &d) : + value_t(TYPE_DOUBLE, d) { } - NodeValue(const rational &i) : - NodeValue(NodeValue::kRational, i) + value_t(const int64_t &i) : + value_t(TYPE_INTEGER, i) { } - Type type() const + value_t(const uint64_t &i) : + value_t(TYPE_INTEGER, int64_t(i)) { - return type_; } - template - T value() const + value_t(const int &i) : + value_t(TYPE_INTEGER, int64_t(i)) { - return data_.value(); } - template - void set_value(const T &v) + value_t(const bool &i) : + value_t(TYPE_INTEGER, int64_t(i)) { - data_ = QVariant::fromValue(v); } - const QVariant &data() const { return data_; } - - template - bool canConvert() const + value_t(const Color &i) : + value_t(TYPE_DOUBLE, size_t(4)) { - return data_.canConvert(); + data_[0] = double(i.red()); + data_[1] = double(i.green()); + data_[2] = double(i.blue()); + data_[3] = double(i.alpha()); } - bool array() const + value_t(const QString &i) : + value_t(TYPE_STRING, i) { - return array_; } - bool operator==(const NodeValue& rhs) const + value_t(const char *i) : + value_t(TYPE_STRING, QString::fromUtf8(i)) { - return type_ == rhs.type_ && data_ == rhs.data_; } - operator bool() const + value_t(const rational &i) : + value_t(TYPE_RATIONAL, i) { - return !data_.isNull(); } - static QString GetPrettyDataTypeName(Type type); - - static QString GetDataTypeName(Type type); - static NodeValue::Type GetDataTypeFromName(const QString &n); - - static QString ValueToString(Type data_type, const QVariant& value, bool value_is_a_key_track); - static QString ValueToString(const NodeValue &v, bool value_is_a_key_track) + value_t(const QByteArray &i) : + value_t(TYPE_BINARY, i) { - return ValueToString(v.type_, v.data_, value_is_a_key_track); } - static QVariant StringToValue(Type data_type, const QString &string, bool value_is_a_key_track); - - static QVector split_normal_value_into_track_values(Type type, const QVariant &value); - - static QVariant combine_track_values_into_normal_value(Type type, const QVector& split); - - SplitValue to_split_value() const + value_t(const NodeValueArray &i) : + value_t(TYPE_ARRAY, i) { - return split_normal_value_into_track_values(type_, data_); } - /** - * @brief Returns whether a data type can be interpolated or not - */ - static bool type_can_be_interpolated(NodeValue::Type type) + value_t(const QMatrix4x4 &i) : + value_t(TYPE_MATRIX, i) { - return type == kFloat - || type == kVec2 - || type == kVec3 - || type == kVec4 - || type == kBezier - || type == kColor - || type == kRational; } - static bool type_is_numeric(NodeValue::Type type) + const type_t &type() const { - return type == kFloat - || type == kInt - || type == kRational; + return type_; } - static bool type_is_vector(NodeValue::Type type) + component_t at(size_t channel) const { - return type == kVec2 - || type == kVec3 - || type == kVec4; + if (channel < data_.size()) { + return data_[channel]; + } + return component_t(); } - static bool type_is_buffer(NodeValue::Type type) + component_t &operator[](size_t i) { return data_[i]; } + const component_t &operator[](size_t i) const { return data_[i]; } + + template + T value(size_t channel = 0) const { - return type == kTexture - || type == kSamples; + return at(channel).get(); } - static int get_number_of_keyframe_tracks(Type type); + size_t size() const { return data_.size(); } - static void ValidateVectorString(QStringList* list, int count); + std::vector &data() { return data_; } + const std::vector &data() const { return data_; } - TexturePtr toTexture() const { return value(); } - SampleBuffer toSamples() const { return value(); } - bool toBool() const { return value(); } + bool isValid() const { return type_ != TYPE_NONE; } + + bool toBool() const { return toInt(); } double toDouble() const { return value(); } int64_t toInt() const { return value(); } rational toRational() const { return value(); } QString toString() const { return value(); } - Color toColor() const { return value(); } + Color toColor() const { return Color(value(0), value(1), value(2), value(3)); } + QVector2D toVec2() const { return QVector2D(value(0), value(1)); } + QVector3D toVec3() const { return QVector3D(value(0), value(1), value(2)); } + QVector4D toVec4() const { return QVector4D(value(0), value(1), value(2), value(3)); } QMatrix4x4 toMatrix() const { return value(); } - VideoParams toVideoParams() const { return value(); } - AudioParams toAudioParams() const { return value(); } - QVector2D toVec2() const { return value(); } - QVector3D toVec3() const { return value(); } - QVector4D toVec4() const { return value(); } - Bezier toBezier() const { return value(); } + QByteArray toBinary() const { return value(); } NodeValueArray toArray() const { return value(); } + TexturePtr toTexture() const { return value(); } + SampleBuffer toSamples() const { return value(); } + + static value_t fromSerializedString(type_t target_type, const QString &s); + QString toSerializedString() const; + + value_t converted(type_t to) const; + + typedef component_t(*Converter_t)(const component_t &v); + static void registerConverter(const type_t &from, const type_t &to, Converter_t converter); + static void registerDefaultConverters(); private: - Type type_; - QVariant data_; - bool array_; + type_t type_; + std::vector data_; + + static std::map > converters_; }; } -Q_DECLARE_METATYPE(olive::NodeValue) +Q_DECLARE_METATYPE(olive::value_t) #endif // NODEVALUE_H diff --git a/app/render/job/acceleratedjob.h b/app/render/job/acceleratedjob.h index d5667e5470..214c8b8e72 100644 --- a/app/render/job/acceleratedjob.h +++ b/app/render/job/acceleratedjob.h @@ -28,7 +28,7 @@ namespace olive { class AcceleratedJob { public: - using NodeValueRow = QHash; + using NodeValueRow = QHash; AcceleratedJob() = default; @@ -44,7 +44,7 @@ class AcceleratedJob id_ = id; } - NodeValue Get(const QString& input) const + value_t Get(const QString& input) const { return value_map_.value(input); } @@ -54,7 +54,7 @@ class AcceleratedJob value_map_.insert(input, row.value(input)); } - void Insert(const QString& input, const NodeValue& value) + void Insert(const QString& input, const value_t& value) { value_map_.insert(input, value); } diff --git a/app/render/job/cachejob.h b/app/render/job/cachejob.h index 4d7800ce12..b156f4fe05 100644 --- a/app/render/job/cachejob.h +++ b/app/render/job/cachejob.h @@ -33,7 +33,7 @@ class CacheJob : public AcceleratedJob { public: CacheJob() = default; - CacheJob(const QString &filename, const NodeValue &fallback = NodeValue()) + CacheJob(const QString &filename, const value_t &fallback = value_t()) { filename_ = filename; } @@ -41,13 +41,13 @@ class CacheJob : public AcceleratedJob const QString &GetFilename() const { return filename_; } void SetFilename(const QString &s) { filename_ = s; } - const NodeValue &GetFallback() const { return fallback_; } - void SetFallback(const NodeValue &val) { fallback_ = val; } + const value_t &GetFallback() const { return fallback_; } + void SetFallback(const value_t &val) { fallback_ = val; } private: QString filename_; - NodeValue fallback_; + value_t fallback_; }; diff --git a/app/render/job/colortransformjob.h b/app/render/job/colortransformjob.h index 2b55cfb8f8..003a7fdb18 100644 --- a/app/render/job/colortransformjob.h +++ b/app/render/job/colortransformjob.h @@ -62,12 +62,12 @@ class ColorTransformJob : public AcceleratedJob void SetOverrideID(const QString &id) { id_ = id; } - const NodeValue &GetInputTexture() const { return input_texture_; } - void SetInputTexture(const NodeValue &tex) { input_texture_ = tex; } + const value_t &GetInputTexture() const { return input_texture_; } + void SetInputTexture(const value_t &tex) { input_texture_ = tex; } void SetInputTexture(TexturePtr tex) { Q_ASSERT(!tex->IsDummy()); - input_texture_ = NodeValue(NodeValue::kTexture, tex); + input_texture_ = tex; } ColorProcessorPtr GetColorProcessor() const { return processor_; } @@ -95,7 +95,7 @@ class ColorTransformJob : public AcceleratedJob ColorProcessorPtr processor_; QString id_; - NodeValue input_texture_; + value_t input_texture_; ShaderJob::GetShaderCodeFunction_t custom_shader_; diff --git a/app/render/opengl/openglrenderer.cpp b/app/render/opengl/openglrenderer.cpp index b7451395f9..143d85d6f0 100644 --- a/app/render/opengl/openglrenderer.cpp +++ b/app/render/opengl/openglrenderer.cpp @@ -419,58 +419,30 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video } // This variable is used in the shader, let's set it - const NodeValue& value = it.value(); + const value_t& value = it.value(); - // Arrays are not currently supported in this system - if (value.array()) { - continue; - } - - switch (value.type()) { - case NodeValue::kInt: + if (value.type() == TYPE_INTEGER) { // kInt technically specifies a LongLong, but OpenGL doesn't support those. This may lead to // over/underflows if the number is large enough, but the likelihood of that is quite low. functions_->glUniform1i(variable_location, value.toInt()); - break; - case NodeValue::kFloat: - // kFloat technically specifies a double but as above, OpenGL doesn't support those. - functions_->glUniform1f(variable_location, value.toDouble()); - break; - case NodeValue::kVec2: - { - QVector2D v = value.toVec2(); - functions_->glUniform2fv(variable_location, 1, reinterpret_cast(&v)); - break; - } - case NodeValue::kVec3: - { - QVector3D v = value.toVec3(); - functions_->glUniform3fv(variable_location, 1, reinterpret_cast(&v)); - break; - } - case NodeValue::kVec4: - { - QVector4D v = value.toVec4(); - functions_->glUniform4fv(variable_location, 1, reinterpret_cast(&v)); - break; - } - case NodeValue::kMatrix: + } else if (value.type() == TYPE_DOUBLE) { + switch (value.size()) { + case 1: + functions_->glUniform1f(variable_location, value.value(0)); + break; + case 2: + functions_->glUniform2f(variable_location, value.value(0), value.value(1)); + break; + case 3: + functions_->glUniform3f(variable_location, value.value(0), value.value(1), value.value(2)); + break; + case 4: + functions_->glUniform4f(variable_location, value.value(0), value.value(1), value.value(2), value.value(3)); + break; + } + } else if (value.type() == TYPE_MATRIX) { functions_->glUniformMatrix4fv(variable_location, 1, false, value.toMatrix().constData()); - break; - case NodeValue::kCombo: - functions_->glUniform1i(variable_location, value.toInt()); - break; - case NodeValue::kColor: - { - Color color = value.toColor(); - functions_->glUniform4f(variable_location, color.red(), color.green(), color.blue(), color.alpha()); - break; - } - case NodeValue::kBoolean: - functions_->glUniform1i(variable_location, value.toBool()); - break; - case NodeValue::kTexture: - { + } else if (value.type() == TYPE_TEXTURE) { TexturePtr texture = value.toTexture(); // Set value to bound texture @@ -486,21 +458,6 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video if (enable_param_location > -1) { functions_->glUniform1i(enable_param_location, tex_id > 0); } - break; - } - case NodeValue::kSamples: - case NodeValue::kText: - case NodeValue::kRational: - case NodeValue::kFont: - case NodeValue::kFile: - case NodeValue::kVideoParams: - case NodeValue::kAudioParams: - case NodeValue::kSubtitleParams: - case NodeValue::kBezier: - case NodeValue::kBinary: - case NodeValue::kNone: - case NodeValue::kDataTypeCount: - break; } } diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index 47ad6340f4..2703c12368 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -24,6 +24,7 @@ #include #include "codec/conformmanager.h" +#include "common/qtutils.h" #include "node/inputdragger.h" #include "node/project.h" #include "render/diskmanager.h" diff --git a/app/render/renderer.cpp b/app/render/renderer.cpp index ebe7550e98..0ca0cadc12 100644 --- a/app/render/renderer.cpp +++ b/app/render/renderer.cpp @@ -108,9 +108,9 @@ TexturePtr Renderer::InterlaceTexture(TexturePtr top, TexturePtr bottom, const V color_cache_mutex_.unlock(); ShaderJob job; - job.Insert(QStringLiteral("top_tex_in"), NodeValue(NodeValue::kTexture, QVariant::fromValue(top))); - job.Insert(QStringLiteral("bottom_tex_in"), NodeValue(NodeValue::kTexture, QVariant::fromValue(bottom))); - job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, QVector2D(params.effective_width(), params.effective_height()))); + job.Insert(QStringLiteral("top_tex_in"), top); + job.Insert(QStringLiteral("bottom_tex_in"), bottom); + job.Insert(QStringLiteral("resolution_in"), QVector2D(params.effective_width(), params.effective_height())); TexturePtr output = CreateTexture(params); @@ -293,17 +293,17 @@ void Renderer::BlitColorManaged(const ColorTransformJob &color_job, Texture *des ShaderJob job; job.Insert(QStringLiteral("ove_maintex"), color_job.GetInputTexture()); - job.Insert(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, color_job.GetTransformMatrix())); - job.Insert(QStringLiteral("ove_cropmatrix"), NodeValue(NodeValue::kMatrix, color_job.GetCropMatrix().inverted())); - job.Insert(QStringLiteral("ove_maintex_alpha"), NodeValue(NodeValue::kInt, int(color_job.GetInputAlphaAssociation()))); + job.Insert(QStringLiteral("ove_mvpmat"), color_job.GetTransformMatrix()); + job.Insert(QStringLiteral("ove_cropmatrix"), color_job.GetCropMatrix().inverted()); + job.Insert(QStringLiteral("ove_maintex_alpha"), color_job.GetInputAlphaAssociation()); job.Insert(color_job.GetValues()); foreach (const ColorContext::LUT& l, color_ctx.lut3d_textures) { - job.Insert(l.name, NodeValue(NodeValue::kTexture, QVariant::fromValue(l.texture))); + job.Insert(l.name, l.texture); job.SetInterpolation(l.name, l.interpolation); } foreach (const ColorContext::LUT& l, color_ctx.lut1d_textures) { - job.Insert(l.name, NodeValue(NodeValue::kTexture, QVariant::fromValue(l.texture))); + job.Insert(l.name, l.texture); job.SetInterpolation(l.name, l.interpolation); } diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index d974bce75e..484bf25b32 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -26,6 +26,7 @@ #include #include "audio/audioprocessor.h" +#include "common/qtutils.h" #include "node/block/clip/clip.h" #include "node/block/transition/transition.h" #include "node/project.h" @@ -45,7 +46,7 @@ TexturePtr RenderProcessor::GenerateTexture(const rational &time, const rational { TimeRange range = TimeRange(time, time + frame_length); - NodeValue tex_val; + value_t tex_val; if (Node* node = QtUtils::ValueToPtr(ticket_->property("node"))) { ValueParams vp(GetCacheVideoParams(), GetCacheAudioParams(), range, ticket_->property("output").toString(), LoopMode::kLoopModeOff, GetCancelPointer()); tex_val = node->Value(vp); @@ -216,7 +217,7 @@ void RenderProcessor::Run() { TimeRange time = ticket_->property("time").value(); - NodeValue sample_val; + value_t sample_val; if (Node* node = QtUtils::ValueToPtr(ticket_->property("node"))) { ValueParams vp(GetCacheVideoParams(), GetCacheAudioParams(), time, ticket_->property("output").toString(), LoopMode::kLoopModeOff, GetCancelPointer()); sample_val = node->Value(vp); @@ -529,29 +530,29 @@ bool RenderProcessor::UseCache() const return static_cast(ticket_->property("mode").toInt()) == RenderMode::kOffline; } -void RenderProcessor::ResolveJobs(NodeValue &val) +void RenderProcessor::ResolveJobs(value_t &val) { - if (val.type() == NodeValue::kTexture) { + if (val.type() == TYPE_TEXTURE) { if (TexturePtr job_tex = val.toTexture()) { if (AcceleratedJob *base_job = job_tex->job()) { if (resolved_texture_cache_.contains(job_tex.get())) { - val.set_value(resolved_texture_cache_.value(job_tex.get())); + val = resolved_texture_cache_.value(job_tex.get()); } else { // Resolve any sub-jobs for (auto it=base_job->GetValues().begin(); it!=base_job->GetValues().end(); it++) { // Jobs will almost always be submitted with one of these types - NodeValue &subval = it.value(); + value_t &subval = it.value(); ResolveJobs(subval); } if (CacheJob *cj = dynamic_cast(base_job)) { TexturePtr tex = ProcessVideoCacheJob(cj); if (tex) { - val.set_value(tex); + val = tex; } else { - val.set_value(cj->GetFallback()); + val = cj->GetFallback(); } } else if (ColorTransformJob *ctj = dynamic_cast(base_job)) { @@ -563,13 +564,13 @@ void RenderProcessor::ResolveJobs(NodeValue &val) TexturePtr dest = CreateTexture(ctj_params); // Resolve input texture - NodeValue v = ctj->GetInputTexture(); + value_t v = ctj->GetInputTexture(); ResolveJobs(v); ctj->SetInputTexture(v); ProcessColorTransform(dest, ctj); - val.set_value(dest); + val = dest; } else if (ShaderJob *sj = dynamic_cast(base_job)) { @@ -579,7 +580,7 @@ void RenderProcessor::ResolveJobs(NodeValue &val) ProcessShader(tex, sj); - val.set_value(tex); + val = tex; } else if (GenerateJob *gj = dynamic_cast(base_job)) { @@ -602,7 +603,7 @@ void RenderProcessor::ResolveJobs(NodeValue &val) tex = dest; } - val.set_value(tex); + val = tex; } else if (FootageJob *fj = dynamic_cast(base_job)) { @@ -621,7 +622,7 @@ void RenderProcessor::ResolveJobs(NodeValue &val) ProcessVideoFootage(tex, fj, footage_time); } - val.set_value(tex); + val = tex; } @@ -631,15 +632,16 @@ void RenderProcessor::ResolveJobs(NodeValue &val) } } - } else if (val.type() == NodeValue::kSamples) { + } else if (val.type() == TYPE_SAMPLES) { - if (val.canConvert()) { + qDebug() << "disabled handling of samplejobs and audio footagejobs..."; + /*if (val.canConvert()) { SampleJob job = val.value(); for (auto it=job.GetValues().begin(); it!=job.GetValues().end(); it++) { // Jobs will almost always be submitted with one of these types - NodeValue &subval = it.value(); + value_t &subval = it.value(); ResolveJobs(subval); } @@ -654,7 +656,7 @@ void RenderProcessor::ResolveJobs(NodeValue &val) ProcessAudioFootage(buffer, &job, job.time()); val.set_value(buffer); - } + }*/ } } diff --git a/app/render/renderprocessor.h b/app/render/renderprocessor.h index 8c1bf30d29..28435b9898 100644 --- a/app/render/renderprocessor.h +++ b/app/render/renderprocessor.h @@ -92,7 +92,7 @@ class RenderProcessor DecoderPtr ResolveDecoderFromInput(const QString &decoder_id, const Decoder::CodecStream& stream); - void ResolveJobs(NodeValue &value); + void ResolveJobs(value_t &value); const VideoParams &GetCacheVideoParams() const { return vparam_; } const AudioParams &GetCacheAudioParams() const { return aparam_; } diff --git a/app/task/export/export.cpp b/app/task/export/export.cpp index a171df1ce0..78c2807d38 100644 --- a/app/task/export/export.cpp +++ b/app/task/export/export.cpp @@ -20,6 +20,7 @@ #include "export.h" +#include "common/qtutils.h" #include "node/color/colormanager/colormanager.h" namespace olive { diff --git a/app/task/render/render.cpp b/app/task/render/render.cpp index 0dfdfcc8b7..993f7d9e1b 100644 --- a/app/task/render/render.cpp +++ b/app/task/render/render.cpp @@ -20,6 +20,7 @@ #include "render.h" +#include "common/qtutils.h" #include "node/project/sequence/sequence.h" #include "render/rendermanager.h" diff --git a/app/widget/colorbutton/colorbutton.cpp b/app/widget/colorbutton/colorbutton.cpp index 798a41db82..6e9144c147 100644 --- a/app/widget/colorbutton/colorbutton.cpp +++ b/app/widget/colorbutton/colorbutton.cpp @@ -20,6 +20,7 @@ #include "colorbutton.h" +#include "common/qtutils.h" #include "dialog/color/colordialog.h" namespace olive { diff --git a/app/widget/colorwheel/colorgradientwidget.cpp b/app/widget/colorwheel/colorgradientwidget.cpp index e4cec9e7c7..31306f6f4a 100644 --- a/app/widget/colorwheel/colorgradientwidget.cpp +++ b/app/widget/colorwheel/colorgradientwidget.cpp @@ -23,6 +23,7 @@ #include #include "common/lerp.h" +#include "common/qtutils.h" #include "node/node.h" namespace olive { diff --git a/app/widget/colorwheel/colorwheelwidget.cpp b/app/widget/colorwheel/colorwheelwidget.cpp index 78d591978e..17b44276a8 100644 --- a/app/widget/colorwheel/colorwheelwidget.cpp +++ b/app/widget/colorwheel/colorwheelwidget.cpp @@ -23,6 +23,7 @@ #include #include +#include "common/qtutils.h" #include "node/node.h" namespace olive { diff --git a/app/widget/curvewidget/curveview.cpp b/app/widget/curvewidget/curveview.cpp index a49482057a..30c9a3a356 100644 --- a/app/widget/curvewidget/curveview.cpp +++ b/app/widget/curvewidget/curveview.cpp @@ -435,7 +435,7 @@ void CurveView::KeyframeDragMove(QMouseEvent *event, QString &tip) FloatSlider::DisplayType display = GetFloatDisplayTypeFromKeyframe(key); Node* node = key->parent(); - double original_val = FloatSlider::TransformValueToDisplay(drag_keyframe_values_.at(i).toDouble(), display); + double original_val = FloatSlider::TransformValueToDisplay(drag_keyframe_values_.at(i).get(), display); const QString& input = key->input(); double new_val = FloatSlider::TransformDisplayToValue(original_val - scaled_diff, display); double limited = new_val; @@ -457,13 +457,13 @@ void CurveView::KeyframeDragMove(QMouseEvent *event, QString &tip) for (size_t i=0; iset_value(FloatSlider::TransformDisplayToValue(FloatSlider::TransformValueToDisplay(drag_keyframe_values_.at(i).toDouble(), display) - scaled_diff, display)); + key->set_value(FloatSlider::TransformDisplayToValue(FloatSlider::TransformValueToDisplay(drag_keyframe_values_.at(i).get(), display) - scaled_diff, display)); } NodeKeyframe *tip_item = GetSelectedKeyframes().front(); bool ok; - double num_value = tip_item->value().toDouble(&ok); + double num_value = tip_item->value().get(&ok); if (ok) { tip = QStringLiteral("%1\n"); @@ -475,7 +475,7 @@ void CurveView::KeyframeDragRelease(QMouseEvent *event, MultiUndoCommand *comman { for (size_t i=0; ivalue().toDouble(), drag_keyframe_values_.at(i).toDouble())) { + if (!qFuzzyCompare(k->value().get(), drag_keyframe_values_.at(i).get())) { command->add_child(new NodeParamSetKeyframeValueCommand(k, k->value(), drag_keyframe_values_.at(i))); } } @@ -582,7 +582,7 @@ qreal CurveView::GetItemYFromKeyframeValue(NodeKeyframe *key) qreal CurveView::GetUnscaledItemYFromKeyframeValue(NodeKeyframe *key) { - double val = key->value().toDouble(); + double val = key->value().get(); val = FloatSlider::TransformValueToDisplay(val, GetFloatDisplayTypeFromKeyframe(key)); @@ -615,12 +615,9 @@ double CurveView::GetOffsetFromKeyframe(NodeKeyframe *key) Node *node = key->parent(); const QString &input = key->input(); if (node->HasInputProperty(input, QStringLiteral("offset"))) { - QVariant v = node->GetInputProperty(input, QStringLiteral("offset")); + value_t v = node->GetInputProperty(input, QStringLiteral("offset")).value(); - // NOTE: Implement getting correct offset for the track based on the data type - QVector track_vals = NodeValue::split_normal_value_into_track_values(node->GetInputDataType(input), v); - - return track_vals.at(key->track()).toDouble(); + return v.at(key->track()).get(); } return 0; diff --git a/app/widget/curvewidget/curveview.h b/app/widget/curvewidget/curveview.h index a01598548f..f6bafd9be8 100644 --- a/app/widget/curvewidget/curveview.h +++ b/app/widget/curvewidget/curveview.h @@ -118,7 +118,7 @@ public slots: QPointF dragging_bezier_point_opposing_start_; QPointF drag_start_; - QVector drag_keyframe_values_; + QVector drag_keyframe_values_; }; diff --git a/app/widget/curvewidget/curvewidget.cpp b/app/widget/curvewidget/curvewidget.cpp index 836984a0c6..78bbae7c28 100644 --- a/app/widget/curvewidget/curvewidget.cpp +++ b/app/widget/curvewidget/curvewidget.cpp @@ -258,8 +258,8 @@ void CurveWidget::ConnectInput(Node *node, const QString &input, int element) void CurveWidget::ConnectInputInternal(Node *node, const QString &input, int element) { NodeInput input_ref(node, input, element); - int track_count = NodeValue::get_number_of_keyframe_tracks(input_ref.GetDataType()); - for (int i=0; iConnectInput(track_ref); selected_tracks_.append(track_ref); diff --git a/app/widget/nodeparamview/CMakeLists.txt b/app/widget/nodeparamview/CMakeLists.txt index 1b80edebc2..ad455bb8e4 100644 --- a/app/widget/nodeparamview/CMakeLists.txt +++ b/app/widget/nodeparamview/CMakeLists.txt @@ -14,6 +14,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +add_subdirectory(paramwidget) + set(OLIVE_SOURCES ${OLIVE_SOURCES} widget/nodeparamview/nodeparamview.cpp diff --git a/app/widget/nodeparamview/nodeparamviewitem.cpp b/app/widget/nodeparamview/nodeparamviewitem.cpp index 76e22da891..134dd2874b 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.cpp +++ b/app/widget/nodeparamview/nodeparamviewitem.cpp @@ -350,8 +350,10 @@ void NodeParamViewItemBody::UpdateUIForEdgeConnection(const NodeInput& input) bool is_connected = NodeGroup::ResolveInput(input).IsConnected(); - foreach (QWidget* w, ui_objects.widget_bridge->widgets()) { - w->setVisible(!is_connected); + if (ui_objects.widget_bridge->has_widgets()) { + foreach (QWidget* w, ui_objects.widget_bridge->widgets()) { + w->setVisible(!is_connected); + } } // Show/hide connection label @@ -370,21 +372,23 @@ void NodeParamViewItemBody::UpdateUIForEdgeConnection(const NodeInput& input) void NodeParamViewItemBody::PlaceWidgetsFromBridge(QGridLayout* layout, NodeParamViewWidgetBridge *bridge, int row) { - // Add widgets for this parameter to the layout - for (int i=0; iwidgets().size(); i++) { - QWidget* w = bridge->widgets().at(i); - - int col = i+kWidgetStartColumn; + if (bridge->has_widgets()) { + // Add widgets for this parameter to the layout + for (size_t i=0; iwidgets().size(); i++) { + QWidget* w = bridge->widgets().at(i); + + int col = i+kWidgetStartColumn; + + int colspan; + if (i == bridge->widgets().size()-1) { + // Span this widget among remaining columns + colspan = kMaxWidgetColumn - col; + } else { + colspan = 1; + } - int colspan; - if (i == bridge->widgets().size()-1) { - // Span this widget among remaining columns - colspan = kMaxWidgetColumn - col; - } else { - colspan = 1; + layout->addWidget(w, row, col, 1, colspan); } - - layout->addWidget(w, row, col, 1, colspan); } } @@ -413,7 +417,9 @@ void NodeParamViewItemBody::InputArraySizeChangedInternal(Node *node, const QStr // Our UI count is larger than the size, delete InputUI input_ui = input_ui_map_.take({node, input, i}); delete input_ui.main_label; - qDeleteAll(input_ui.widget_bridge->widgets()); + if (input_ui.widget_bridge->has_widgets()) { + qDeleteAll(input_ui.widget_bridge->widgets()); + } delete input_ui.widget_bridge; delete input_ui.connected_label; delete input_ui.key_control; diff --git a/app/widget/nodeparamview/nodeparamviewkeyframecontrol.cpp b/app/widget/nodeparamview/nodeparamviewkeyframecontrol.cpp index 8d02dad740..e0e9dd66cd 100644 --- a/app/widget/nodeparamview/nodeparamviewkeyframecontrol.cpp +++ b/app/widget/nodeparamview/nodeparamviewkeyframecontrol.cpp @@ -235,9 +235,9 @@ void NodeParamViewKeyframeControl::KeyframeEnableBtnClicked(bool e) command->add_child(new NodeParamSetKeyframingCommand(input_, true)); // Create one keyframe across all tracks here - const QVector& key_vals = input_.node()->GetSplitStandardValue(input_); + const value_t& key_vals = input_.node()->GetStandardValue(input_); - for (int i=0;i& stored_vals = input_.node()->GetSplitValueAtTime(input_, GetCurrentTimeAsNodeTime()); + const value_t& stored_vals = input_.node()->GetValueAtTime(input_, GetCurrentTimeAsNodeTime()); // Delete all keyframes foreach (const NodeKeyframeTrack& track, input_.node()->GetKeyframeTracks(input_)) { @@ -267,7 +267,7 @@ void NodeParamViewKeyframeControl::KeyframeEnableBtnClicked(bool e) } // Update standard value - for (int i=0;iadd_child(new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(input_, i), stored_vals.at(i))); } diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index 585f2526e6..fafd7f0230 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -35,17 +35,25 @@ #include "nodeparamviewarraywidget.h" #include "nodeparamviewtextedit.h" #include "undo/undostack.h" -#include "widget/bezier/bezierwidget.h" #include "widget/colorbutton/colorbutton.h" #include "widget/filefield/filefield.h" -#include "widget/slider/floatslider.h" -#include "widget/slider/integerslider.h" -#include "widget/slider/rationalslider.h" +#include "widget/nodeparamview/paramwidget/arrayparamwidget.h" +#include "widget/nodeparamview/paramwidget/bezierparamwidget.h" +#include "widget/nodeparamview/paramwidget/boolparamwidget.h" +#include "widget/nodeparamview/paramwidget/colorparamwidget.h" +#include "widget/nodeparamview/paramwidget/comboparamwidget.h" +#include "widget/nodeparamview/paramwidget/fileparamwidget.h" +#include "widget/nodeparamview/paramwidget/floatsliderparamwidget.h" +#include "widget/nodeparamview/paramwidget/fontparamwidget.h" +#include "widget/nodeparamview/paramwidget/integersliderparamwidget.h" +#include "widget/nodeparamview/paramwidget/rationalsliderparamwidget.h" +#include "widget/nodeparamview/paramwidget/textparamwidget.h" namespace olive { NodeParamViewWidgetBridge::NodeParamViewWidgetBridge(NodeInput input, QObject *parent) : - QObject(parent) + QObject(parent), + widget_(nullptr) { do { input_hierarchy_.append(input); @@ -58,133 +66,79 @@ NodeParamViewWidgetBridge::NodeParamViewWidgetBridge(NodeInput input, QObject *p CreateWidgets(); } -int GetSliderCount(NodeValue::Type type) -{ - return NodeValue::get_number_of_keyframe_tracks(type); -} - void NodeParamViewWidgetBridge::CreateWidgets() { QWidget *parent = dynamic_cast(this->parent()); if (GetInnerInput().IsArray() && GetInnerInput().element() == -1) { - NodeParamViewArrayWidget* w = new NodeParamViewArrayWidget(GetInnerInput().node(), GetInnerInput().input(), parent); - connect(w, &NodeParamViewArrayWidget::DoubleClicked, this, &NodeParamViewWidgetBridge::ArrayWidgetDoubleClicked); - widgets_.append(w); + widget_ = new ArrayParamWidget(GetInnerInput().node(), GetInnerInput().input(), parent); + connect(static_cast(widget_), &ArrayParamWidget::DoubleClicked, this, &NodeParamViewWidgetBridge::ArrayWidgetDoubleClicked); } else { // We assume the first data type is the "primary" type - NodeValue::Type t = GetDataType(); - switch (t) { - // None of these inputs have applicable UI widgets - case NodeValue::kNone: - case NodeValue::kTexture: - case NodeValue::kMatrix: - case NodeValue::kSamples: - case NodeValue::kVideoParams: - case NodeValue::kAudioParams: - case NodeValue::kSubtitleParams: - case NodeValue::kBinary: - case NodeValue::kDataTypeCount: - break; - case NodeValue::kInt: - { - CreateSliders(1, parent); - break; - } - case NodeValue::kRational: - { - CreateSliders(1, parent); - break; - } - case NodeValue::kFloat: - case NodeValue::kVec2: - case NodeValue::kVec3: - case NodeValue::kVec4: - { - CreateSliders(GetSliderCount(t), parent); - break; - } - case NodeValue::kCombo: - { - QComboBox* combobox = new QComboBox(parent); - - QStringList items = GetInnerInput().GetComboBoxStrings(); - foreach (const QString& s, items) { - combobox->addItem(s); + type_t t = GetDataType(); + if (t == TYPE_INTEGER) { + QString type = GetOuterInput().GetProperty(QStringLiteral("subtype")).toString(); + + if (type == QStringLiteral("bool")) { + widget_ = new BoolParamWidget(this); + } else if (type == QStringLiteral("combo")) { + widget_ = new ComboParamWidget(this); + } else { + widget_ = new IntegerSliderParamWidget(this); + } + } else if (t == TYPE_RATIONAL) { + widget_ = new RationalSliderParamWidget(this); + } else if (t == TYPE_DOUBLE) { + QString type; + + if (type == QStringLiteral("color")) { + widget_ = new ColorParamWidget(this); + } else if (type == QStringLiteral("bezier")) { + widget_ = new BezierParamWidget(this); + } else { + widget_ = new FloatSliderParamWidget(this); } + } else if (t == TYPE_STRING) { + QString type; - widgets_.append(combobox); - connect(combobox, static_cast(&QComboBox::currentIndexChanged), this, &NodeParamViewWidgetBridge::WidgetCallback); - break; - } - case NodeValue::kFile: - { - FileField* file_field = new FileField(parent); - widgets_.append(file_field); - connect(file_field, &FileField::FilenameChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); - break; - } - case NodeValue::kColor: - { - ColorButton* color_button = new ColorButton(GetInnerInput().node()->project()->color_manager(), parent); - widgets_.append(color_button); - connect(color_button, &ColorButton::ColorChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); - break; - } - case NodeValue::kText: - { - NodeParamViewTextEdit* line_edit = new NodeParamViewTextEdit(parent); - widgets_.append(line_edit); - connect(line_edit, &NodeParamViewTextEdit::textEdited, this, &NodeParamViewWidgetBridge::WidgetCallback); - connect(line_edit, &NodeParamViewTextEdit::RequestEditInViewer, this, &NodeParamViewWidgetBridge::RequestEditTextInViewer); - break; - } - case NodeValue::kBoolean: - { - QCheckBox* check_box = new QCheckBox(parent); - widgets_.append(check_box); - connect(check_box, &QCheckBox::clicked, this, &NodeParamViewWidgetBridge::WidgetCallback); - break; - } - case NodeValue::kFont: - { - QFontComboBox* font_combobox = new QFontComboBox(parent); - widgets_.append(font_combobox); - connect(font_combobox, &QFontComboBox::currentFontChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); - break; - } - case NodeValue::kBezier: - { - BezierWidget *bezier = new BezierWidget(parent); - widgets_.append(bezier); - - connect(bezier->x_slider(), &FloatSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); - connect(bezier->y_slider(), &FloatSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); - connect(bezier->cp1_x_slider(), &FloatSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); - connect(bezier->cp1_y_slider(), &FloatSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); - connect(bezier->cp2_x_slider(), &FloatSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); - connect(bezier->cp2_y_slider(), &FloatSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); - break; - } + if (type == QStringLiteral("file")) { + widget_ = new FileParamWidget(this); + } else if (type == QStringLiteral("font")) { + widget_ = new FontParamWidget(this); + } else { + widget_ = new TextParamWidget(this); + connect(static_cast(widget_), &TextParamWidget::RequestEditInViewer, this, &NodeParamViewWidgetBridge::RequestEditTextInViewer); + } } - // Check all properties - UpdateProperties(); + } - UpdateWidgetValues(); + if (widget_) { + // Whatever we created, initialize it + widget_->Initialize(parent, GetChannelCount()); // Install event filter to disable widgets picking up scroll events - foreach (QWidget* w, widgets_) { + foreach (QWidget* w, widget_->GetWidgets()) { w->installEventFilter(&scroll_filter_); } + // Connect signals + connect(widget_, &AbstractParamWidget::SliderDragged, this, &NodeParamViewWidgetBridge::ProcessSlider); + connect(widget_, &AbstractParamWidget::ChannelValueChanged, this, &NodeParamViewWidgetBridge::ValueChanged); + + // Check all properties + UpdateProperties(); + + UpdateWidgetValues(); + } else { + qWarning() << "Failed to find matching parameter widget for" << GetInnerInput().node()->id() << GetInnerInput().input(); } } -void NodeParamViewWidgetBridge::SetInputValue(const QVariant &value, int track) +void NodeParamViewWidgetBridge::SetInputValue(const value_t::component_t &value, size_t track) { MultiUndoCommand* command = new MultiUndoCommand(); @@ -193,13 +147,15 @@ void NodeParamViewWidgetBridge::SetInputValue(const QVariant &value, int track) Core::instance()->undo_stack()->push(command, GetCommandName()); } -void NodeParamViewWidgetBridge::SetInputValueInternal(const QVariant &value, int track, MultiUndoCommand *command, bool insert_on_all_tracks_if_no_key) +void NodeParamViewWidgetBridge::SetInputValueInternal(const value_t::component_t &value, size_t track, MultiUndoCommand *command, bool insert_on_all_tracks_if_no_key) { Node::SetValueAtTime(GetInnerInput(), GetCurrentTimeAsNodeTime(), value, track, command, insert_on_all_tracks_if_no_key); } -void NodeParamViewWidgetBridge::ProcessSlider(NumericSliderBase *slider, int slider_track, const QVariant &value) +void NodeParamViewWidgetBridge::ProcessSlider(NumericSliderBase *slider, size_t slider_track) { + value_t::component_t value = slider->GetValueInternal().at(0); + if (slider->IsDragging()) { // While we're dragging, we block the input's normal signalling and create our own @@ -228,173 +184,9 @@ void NodeParamViewWidgetBridge::ProcessSlider(NumericSliderBase *slider, int sli } } -void NodeParamViewWidgetBridge::WidgetCallback() +void NodeParamViewWidgetBridge::ValueChanged(size_t track, const value_t::component_t &val) { - switch (GetDataType()) { - // None of these inputs have applicable UI widgets - case NodeValue::kNone: - case NodeValue::kTexture: - case NodeValue::kMatrix: - case NodeValue::kSamples: - case NodeValue::kVideoParams: - case NodeValue::kAudioParams: - case NodeValue::kSubtitleParams: - case NodeValue::kBinary: - case NodeValue::kDataTypeCount: - break; - case NodeValue::kInt: - { - // Widget is a IntegerSlider - IntegerSlider* slider = static_cast(sender()); - - ProcessSlider(slider, QVariant::fromValue(slider->GetValue())); - break; - } - case NodeValue::kFloat: - { - // Widget is a FloatSlider - FloatSlider* slider = static_cast(sender()); - - ProcessSlider(slider, slider->GetValue()); - break; - } - case NodeValue::kRational: - { - // Widget is a RationalSlider - RationalSlider* slider = static_cast(sender()); - ProcessSlider(slider, QVariant::fromValue(slider->GetValue())); - break; - } - case NodeValue::kVec2: - { - // Widget is a FloatSlider - FloatSlider* slider = static_cast(sender()); - - ProcessSlider(slider, slider->GetValue()); - break; - } - case NodeValue::kVec3: - { - // Widget is a FloatSlider - FloatSlider* slider = static_cast(sender()); - - ProcessSlider(slider, slider->GetValue()); - break; - } - case NodeValue::kVec4: - { - // Widget is a FloatSlider - FloatSlider* slider = static_cast(sender()); - - ProcessSlider(slider, slider->GetValue()); - break; - } - case NodeValue::kFile: - { - SetInputValue(static_cast(sender())->GetFilename(), 0); - break; - } - case NodeValue::kColor: - { - // Sender is a ColorButton - ManagedColor c = static_cast(sender())->GetColor(); - - MultiUndoCommand* command = new MultiUndoCommand(); - - SetInputValueInternal(c.red(), 0, command, false); - SetInputValueInternal(c.green(), 1, command, false); - SetInputValueInternal(c.blue(), 2, command, false); - SetInputValueInternal(c.alpha(), 3, command, false); - - Node* n = GetInnerInput().node(); - n->blockSignals(true); - n->SetInputProperty(GetInnerInput().input(), QStringLiteral("col_input"), c.color_input()); - n->SetInputProperty(GetInnerInput().input(), QStringLiteral("col_display"), c.color_output().display()); - n->SetInputProperty(GetInnerInput().input(), QStringLiteral("col_view"), c.color_output().view()); - n->SetInputProperty(GetInnerInput().input(), QStringLiteral("col_look"), c.color_output().look()); - n->blockSignals(false); - - Core::instance()->undo_stack()->push(command, GetCommandName()); - break; - } - case NodeValue::kText: - { - // Sender is a NodeParamViewRichText - SetInputValue(static_cast(sender())->text(), 0); - break; - } - case NodeValue::kBoolean: - { - // Widget is a QCheckBox - SetInputValue(static_cast(sender())->isChecked(), 0); - break; - } - case NodeValue::kFont: - { - // Widget is a QFontComboBox - SetInputValue(static_cast(sender())->currentFont().family(), 0); - break; - } - case NodeValue::kCombo: - { - // Widget is a QComboBox - QComboBox* cb = static_cast(widgets_.first()); - int index = cb->currentIndex(); - - // Subtract any splitters up until this point - for (int i=index-1; i>=0; i--) { - if (cb->itemData(i, Qt::AccessibleDescriptionRole).toString() == QStringLiteral("separator")) { - index--; - } - - } - - SetInputValue(index, 0); - break; - } - case NodeValue::kBezier: - { - // Widget is a FloatSlider (child of BezierWidget) - BezierWidget *bw = static_cast(widgets_.first()); - FloatSlider *fs = static_cast(sender()); - - int index = -1; - if (fs == bw->x_slider()) { - index = 0; - } else if (fs == bw->y_slider()) { - index = 1; - } else if (fs == bw->cp1_x_slider()) { - index = 2; - } else if (fs == bw->cp1_y_slider()) { - index = 3; - } else if (fs == bw->cp2_x_slider()) { - index = 4; - } else if (fs == bw->cp2_y_slider()) { - index = 5; - } - - if (index != -1) { - ProcessSlider(fs, index, fs->GetValue()); - } - break; - } - } -} - -template -void NodeParamViewWidgetBridge::CreateSliders(int count, QWidget *parent) -{ - for (int i=0;iSliderBase::SetDefaultValue(GetInnerInput().GetSplitDefaultValueForTrack(i)); - fs->SetLadderElementCount(2); - - // HACK: Force some spacing between sliders - fs->setContentsMargins(0, 0, QtUtils::QFontMetricsWidth(fs->fontMetrics(), QStringLiteral(" ")), 0); - - widgets_.append(fs); - connect(fs, &T::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); - } + SetInputValue(val, track); } void NodeParamViewWidgetBridge::UpdateWidgetValues() @@ -403,123 +195,13 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() return; } - rational node_time; - if (GetInnerInput().IsKeyframing()) { - node_time = GetCurrentTimeAsNodeTime(); - } - - // We assume the first data type is the "primary" type - switch (GetDataType()) { - // None of these inputs have applicable UI widgets - case NodeValue::kNone: - case NodeValue::kTexture: - case NodeValue::kMatrix: - case NodeValue::kSamples: - case NodeValue::kVideoParams: - case NodeValue::kAudioParams: - case NodeValue::kSubtitleParams: - case NodeValue::kBinary: - case NodeValue::kDataTypeCount: - break; - case NodeValue::kInt: - { - static_cast(widgets_.first())->SetValue(GetInnerInput().GetValueAtTime(node_time).toLongLong()); - break; - } - case NodeValue::kFloat: - { - static_cast(widgets_.first())->SetValue(GetInnerInput().GetValueAtTime(node_time).toDouble()); - break; - } - case NodeValue::kRational: - { - static_cast(widgets_.first())->SetValue(GetInnerInput().GetValueAtTime(node_time).value()); - break; - } - case NodeValue::kVec2: - { - QVector2D vec2 = GetInnerInput().GetValueAtTime(node_time).value(); - - static_cast(widgets_.at(0))->SetValue(static_cast(vec2.x())); - static_cast(widgets_.at(1))->SetValue(static_cast(vec2.y())); - break; - } - case NodeValue::kVec3: - { - QVector3D vec3 = GetInnerInput().GetValueAtTime(node_time).value(); - - static_cast(widgets_.at(0))->SetValue(static_cast(vec3.x())); - static_cast(widgets_.at(1))->SetValue(static_cast(vec3.y())); - static_cast(widgets_.at(2))->SetValue(static_cast(vec3.z())); - break; - } - case NodeValue::kVec4: - { - QVector4D vec4 = GetInnerInput().GetValueAtTime(node_time).value(); - - static_cast(widgets_.at(0))->SetValue(static_cast(vec4.x())); - static_cast(widgets_.at(1))->SetValue(static_cast(vec4.y())); - static_cast(widgets_.at(2))->SetValue(static_cast(vec4.z())); - static_cast(widgets_.at(3))->SetValue(static_cast(vec4.w())); - break; - } - case NodeValue::kFile: - { - FileField* ff = static_cast(widgets_.first()); - ff->SetFilename(GetInnerInput().GetValueAtTime(node_time).toString()); - break; - } - case NodeValue::kColor: - { - ManagedColor mc = GetInnerInput().GetValueAtTime(node_time).value(); - - mc.set_color_input(GetInnerInput().GetProperty("col_input").toString()); - - QString d = GetInnerInput().GetProperty("col_display").toString(); - QString v = GetInnerInput().GetProperty("col_view").toString(); - QString l = GetInnerInput().GetProperty("col_look").toString(); - - mc.set_color_output(ColorTransform(d, v, l)); - - static_cast(widgets_.first())->SetColor(mc); - break; - } - case NodeValue::kText: - { - NodeParamViewTextEdit* e = static_cast(widgets_.first()); - e->setTextPreservingCursor(GetInnerInput().GetValueAtTime(node_time).toString()); - break; - } - case NodeValue::kBoolean: - static_cast(widgets_.first())->setChecked(GetInnerInput().GetValueAtTime(node_time).toBool()); - break; - case NodeValue::kFont: - { - QFontComboBox* fc = static_cast(widgets_.first()); - fc->blockSignals(true); - fc->setCurrentFont(GetInnerInput().GetValueAtTime(node_time).toString()); - fc->blockSignals(false); - break; - } - case NodeValue::kCombo: - { - QComboBox* cb = static_cast(widgets_.first()); - cb->blockSignals(true); - int index = GetInnerInput().GetValueAtTime(node_time).toInt(); - for (int i=0; icount(); i++) { - if (cb->itemData(i).toInt() == index) { - cb->setCurrentIndex(i); - } + if (widget_) { + rational node_time; + if (GetInnerInput().IsKeyframing()) { + node_time = GetCurrentTimeAsNodeTime(); } - cb->blockSignals(false); - break; - } - case NodeValue::kBezier: - { - BezierWidget* bw = static_cast(widgets_.first()); - bw->SetValue(GetInnerInput().GetValueAtTime(node_time).value()); - break; - } + + widget_->SetValue(GetInnerInput().GetValueAtTime(node_time)); } } @@ -540,8 +222,8 @@ QString NodeParamViewWidgetBridge::GetCommandName() const void NodeParamViewWidgetBridge::SetTimebase(const rational& timebase) { - if (GetDataType() == NodeValue::kRational) { - static_cast(widgets_.first())->SetTimebase(timebase); + if (widget_) { + widget_->SetTimebase(timebase); } } @@ -566,262 +248,18 @@ void NodeParamViewWidgetBridge::InputValueChanged(const NodeInput &input, const } } -void NodeParamViewWidgetBridge::SetProperty(const QString &key, const QVariant &value) +void NodeParamViewWidgetBridge::SetProperty(const QString &key, const value_t &value) { - NodeValue::Type data_type = GetDataType(); - - // Parameters for all types - bool key_is_disable = key.startsWith(QStringLiteral("disable")); - if (key_is_disable || key.startsWith(QStringLiteral("enabled"))) { - - bool e = value.toBool(); - if (key_is_disable) { - e = !e; - } - - if (key.size() == 7) { // just the word "disable" or "enabled" - for (int i=0; isetEnabled(e); - } - } else { // set specific track/widget - bool ok; - int element = key.mid(7).toInt(&ok); - int tracks = NodeValue::get_number_of_keyframe_tracks(data_type); - - if (ok && element >= 0 && element < tracks) { - widgets_.at(element)->setEnabled(e); - } - } - - } - - if (key == QStringLiteral("tooltip")) { - for (int i = 0; i < widgets_.size(); i++) { - widgets_.at(i)->setToolTip(value.toString()); - } - } - - // Parameters for integers, floats, and vectors - if (NodeValue::type_is_numeric(data_type) || NodeValue::type_is_vector(data_type)) { - if (key == QStringLiteral("min")) { - switch (data_type) { - case NodeValue::kInt: - static_cast(widgets_.first())->SetMinimum(value.value()); - break; - case NodeValue::kFloat: - static_cast(widgets_.first())->SetMinimum(value.toDouble()); - break; - case NodeValue::kRational: - static_cast(widgets_.first())->SetMinimum(value.value()); - break; - case NodeValue::kVec2: - { - QVector2D min = value.value(); - static_cast(widgets_.at(0))->SetMinimum(min.x()); - static_cast(widgets_.at(1))->SetMinimum(min.y()); - break; - } - case NodeValue::kVec3: - { - QVector3D min = value.value(); - static_cast(widgets_.at(0))->SetMinimum(min.x()); - static_cast(widgets_.at(1))->SetMinimum(min.y()); - static_cast(widgets_.at(2))->SetMinimum(min.z()); - break; - } - case NodeValue::kVec4: - { - QVector4D min = value.value(); - static_cast(widgets_.at(0))->SetMinimum(min.x()); - static_cast(widgets_.at(1))->SetMinimum(min.y()); - static_cast(widgets_.at(2))->SetMinimum(min.z()); - static_cast(widgets_.at(3))->SetMinimum(min.w()); - break; - } - default: - break; - } - } else if (key == QStringLiteral("max")) { - switch (data_type) { - case NodeValue::kInt: - static_cast(widgets_.first())->SetMaximum(value.value()); - break; - case NodeValue::kFloat: - static_cast(widgets_.first())->SetMaximum(value.toDouble()); - break; - case NodeValue::kRational: - static_cast(widgets_.first())->SetMaximum(value.value()); - break; - case NodeValue::kVec2: - { - QVector2D max = value.value(); - static_cast(widgets_.at(0))->SetMaximum(max.x()); - static_cast(widgets_.at(1))->SetMaximum(max.y()); - break; - } - case NodeValue::kVec3: - { - QVector3D max = value.value(); - static_cast(widgets_.at(0))->SetMaximum(max.x()); - static_cast(widgets_.at(1))->SetMaximum(max.y()); - static_cast(widgets_.at(2))->SetMaximum(max.z()); - break; - } - case NodeValue::kVec4: - { - QVector4D max = value.value(); - static_cast(widgets_.at(0))->SetMaximum(max.x()); - static_cast(widgets_.at(1))->SetMaximum(max.y()); - static_cast(widgets_.at(2))->SetMaximum(max.z()); - static_cast(widgets_.at(3))->SetMaximum(max.w()); - break; - } - default: - break; - } - } else if (key == QStringLiteral("offset")) { - - int tracks = NodeValue::get_number_of_keyframe_tracks(data_type); - - QVector offsets = NodeValue::split_normal_value_into_track_values(data_type, value); - - for (int i=0; i(widgets_.at(i))->SetOffset(offsets.at(i)); - } - - UpdateWidgetValues(); - - } else if (key.startsWith(QStringLiteral("color"))) { - - QColor c(value.toString()); - - int tracks = NodeValue::get_number_of_keyframe_tracks(data_type); - - if (key.size() == 5) { - // Set for all tracks - for (int i=0; i(widgets_.at(i))->SetColor(c); - } - } else { - bool ok; - int element = key.mid(5).toInt(&ok); - if (ok && element >= 0 && element < tracks) { - static_cast(widgets_.at(element))->SetColor(c); - } - } - - } else if (key == QStringLiteral("base")) { - - double d = value.toDouble(); - for (int i=0; i(widgets_.at(i))->SetDragMultiplier(d); - } - - } - } - - // ComboBox strings changing - if (data_type == NodeValue::kCombo) { - if (key == QStringLiteral("combo_str")) { - QComboBox* cb = static_cast(widgets_.first()); - - int old_index = cb->currentIndex(); - - // Block the combobox changed signals since we anticipate the index will be the same and not require a re-render - cb->blockSignals(true); - - cb->clear(); - - QStringList items = value.toStringList(); - int index = 0; - foreach (const QString& s, items) { - if (s.isEmpty()) { - cb->insertSeparator(cb->count()); - cb->setItemData(cb->count()-1, -1); - } else { - cb->addItem(s, index); - index++; - } - } - - cb->setCurrentIndex(old_index); - - cb->blockSignals(false); - - // In case the amount of items is LESS and the previous index cannot be set, NOW we trigger a re-cache since the - // value has changed - if (cb->currentIndex() != old_index) { - WidgetCallback(); - } - } - } - - // Parameters for floats and vectors only - if (data_type == NodeValue::kFloat || NodeValue::type_is_vector(data_type)) { - if (key == QStringLiteral("view")) { - FloatSlider::DisplayType display_type = static_cast(value.toInt()); - - foreach (QWidget* w, widgets_) { - static_cast(w)->SetDisplayType(display_type); - } - } else if (key == QStringLiteral("decimalplaces")) { - int dec_places = value.toInt(); - - foreach (QWidget* w, widgets_) { - static_cast(w)->SetDecimalPlaces(dec_places); - } - } else if (key == QStringLiteral("autotrim")) { - bool autotrim = value.toBool(); - - foreach (QWidget* w, widgets_) { - static_cast(w)->SetAutoTrimDecimalPlaces(autotrim); - } - } - } - - if (data_type == NodeValue::kRational) { - if (key == QStringLiteral("view")) { - RationalSlider::DisplayType display_type = static_cast(value.toInt()); - - foreach (QWidget* w, widgets_) { - static_cast(w)->SetDisplayType(display_type); - } - } else if (key == QStringLiteral("viewlock")) { - bool locked = value.toBool(); - - foreach (QWidget* w, widgets_) { - static_cast(w)->SetLockDisplayType(locked); - } - } - } - - // Parameters for files - if (data_type == NodeValue::kFile) { - FileField* ff = static_cast(widgets_.first()); - - if (key == QStringLiteral("placeholder")) { - ff->SetPlaceholder(value.toString()); - } else if (key == QStringLiteral("directory")) { - ff->SetDirectoryMode(value.toBool()); - } - } - - // Parameters for text - if (data_type == NodeValue::kText) { - NodeParamViewTextEdit *tex = static_cast(widgets_.first()); - - if (key == QStringLiteral("vieweronly")) { - tex->SetEditInViewerOnlyMode(value.toBool()); - } + if (widget_) { + widget_->SetProperty(key, value); } } -void NodeParamViewWidgetBridge::InputDataTypeChanged(const QString &input, NodeValue::Type type) +void NodeParamViewWidgetBridge::InputDataTypeChanged(const QString &input, type_t type) { if (sender() == GetOuterInput().node() && input == GetOuterInput().input()) { // Delete all widgets - qDeleteAll(widgets_); - widgets_.clear(); + delete widget_; // Create new widgets CreateWidgets(); @@ -831,7 +269,7 @@ void NodeParamViewWidgetBridge::InputDataTypeChanged(const QString &input, NodeV } } -void NodeParamViewWidgetBridge::PropertyChanged(const QString &input, const QString &key, const QVariant &value) +void NodeParamViewWidgetBridge::PropertyChanged(const QString &input, const QString &key, const value_t &value) { bool found = false; diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h index cfea84c3f2..1ad48a4796 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h @@ -24,7 +24,7 @@ #include #include "node/inputdragger.h" -#include "widget/slider/base/numericsliderbase.h" +#include "widget/nodeparamview/paramwidget/abstractparamwidget.h" #include "widget/timetarget/timetarget.h" namespace olive { @@ -42,11 +42,13 @@ class NodeParamViewWidgetBridge : public QObject, public TimeTargetObject public: NodeParamViewWidgetBridge(NodeInput input, QObject* parent); - const QVector& widgets() const + const std::vector& widgets() const { - return widgets_; + return widget_->GetWidgets(); } + bool has_widgets() const { return widget_; } + // Set the timebase of certain Timebased widgets void SetTimebase(const rational& timebase); @@ -64,20 +66,11 @@ class NodeParamViewWidgetBridge : public QObject, public TimeTargetObject private: void CreateWidgets(); - void SetInputValue(const QVariant& value, int track); - - void SetInputValueInternal(const QVariant& value, int track, MultiUndoCommand *command, bool insert_on_all_tracks_if_no_key); - - void ProcessSlider(NumericSliderBase* slider, int slider_track, const QVariant& value); - void ProcessSlider(NumericSliderBase* slider, const QVariant& value) - { - ProcessSlider(slider, widgets_.indexOf(slider), value); - } + void SetInputValue(const value_t::component_t &value, size_t track); - void SetProperty(const QString &key, const QVariant &value); + void SetInputValueInternal(const value_t::component_t &value, size_t track, MultiUndoCommand *command, bool insert_on_all_tracks_if_no_key); - template - void CreateSliders(int count, QWidget *parent); + void SetProperty(const QString &key, const value_t &value); void UpdateWidgetValues(); @@ -95,29 +88,36 @@ class NodeParamViewWidgetBridge : public QObject, public TimeTargetObject QString GetCommandName() const; - NodeValue::Type GetDataType() const + type_t GetDataType() const { return GetOuterInput().GetDataType(); } + size_t GetChannelCount() const + { + return GetOuterInput().GetChannelCount(); + } + void UpdateProperties(); QVector input_hierarchy_; - QVector widgets_; + AbstractParamWidget *widget_; NodeInputDragger dragger_; NodeParamViewScrollBlocker scroll_filter_; private slots: - void WidgetCallback(); - void InputValueChanged(const NodeInput& input, const TimeRange& range); - void InputDataTypeChanged(const QString& input, NodeValue::Type type); + void InputDataTypeChanged(const QString& input, type_t type); + + void PropertyChanged(const QString &input, const QString &key, const value_t &value); + + void ProcessSlider(NumericSliderBase* slider, size_t track); - void PropertyChanged(const QString &input, const QString &key, const QVariant &value); + void ValueChanged(size_t track, const value_t::component_t &val); }; diff --git a/app/widget/nodeparamview/paramwidget/CMakeLists.txt b/app/widget/nodeparamview/paramwidget/CMakeLists.txt new file mode 100644 index 0000000000..4e4db6f250 --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/CMakeLists.txt @@ -0,0 +1,46 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2022 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + widget/nodeparamview/paramwidget/abstractparamwidget.cpp + widget/nodeparamview/paramwidget/abstractparamwidget.h + widget/nodeparamview/paramwidget/arrayparamwidget.cpp + widget/nodeparamview/paramwidget/arrayparamwidget.h + widget/nodeparamview/paramwidget/bezierparamwidget.cpp + widget/nodeparamview/paramwidget/bezierparamwidget.h + widget/nodeparamview/paramwidget/boolparamwidget.cpp + widget/nodeparamview/paramwidget/boolparamwidget.h + widget/nodeparamview/paramwidget/colorparamwidget.cpp + widget/nodeparamview/paramwidget/colorparamwidget.h + widget/nodeparamview/paramwidget/comboparamwidget.cpp + widget/nodeparamview/paramwidget/comboparamwidget.h + widget/nodeparamview/paramwidget/fileparamwidget.cpp + widget/nodeparamview/paramwidget/fileparamwidget.h + widget/nodeparamview/paramwidget/floatsliderparamwidget.cpp + widget/nodeparamview/paramwidget/floatsliderparamwidget.h + widget/nodeparamview/paramwidget/fontparamwidget.cpp + widget/nodeparamview/paramwidget/fontparamwidget.h + widget/nodeparamview/paramwidget/integersliderparamwidget.cpp + widget/nodeparamview/paramwidget/integersliderparamwidget.h + widget/nodeparamview/paramwidget/numericsliderparamwidget.cpp + widget/nodeparamview/paramwidget/numericsliderparamwidget.h + widget/nodeparamview/paramwidget/rationalsliderparamwidget.cpp + widget/nodeparamview/paramwidget/rationalsliderparamwidget.h + widget/nodeparamview/paramwidget/textparamwidget.cpp + widget/nodeparamview/paramwidget/textparamwidget.h + PARENT_SCOPE +) diff --git a/app/widget/nodeparamview/paramwidget/abstractparamwidget.cpp b/app/widget/nodeparamview/paramwidget/abstractparamwidget.cpp new file mode 100644 index 0000000000..c4a44ee4fb --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/abstractparamwidget.cpp @@ -0,0 +1,83 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "abstractparamwidget.h" + +#include + +namespace olive { + +AbstractParamWidget::AbstractParamWidget(QObject *parent) : + QObject{parent} +{ + +} + +AbstractParamWidget::~AbstractParamWidget() +{ + qDeleteAll(widgets_); +} + +void AbstractParamWidget::SetProperty(const QString &key, const value_t &value) +{ + if (key == QStringLiteral("tooltip")) { + for (size_t i = 0; i < widgets_.size(); i++) { + widgets_.at(i)->setToolTip(value.toString()); + } + } else { + bool key_is_disable = key.startsWith(QStringLiteral("disable")); + if (key_is_disable || key.startsWith(QStringLiteral("enabled"))) { + + bool e = value.toBool(); + if (key_is_disable) { + e = !e; + } + + if (key.size() == 7) { // just the word "disable" or "enabled" + for (size_t i=0; isetEnabled(e); + } + } else { // set specific track/widget + bool ok; + size_t element = key.midRef(7).toInt(&ok); + + if (ok && element < widgets_.size()) { + widgets_.at(element)->setEnabled(e); + } + } + + } + } +} + +void AbstractParamWidget::ArbitrateSliders() +{ + NumericSliderBase *w = static_cast(sender()); + + for (size_t i = 0; i < GetWidgets().size(); i++) { + if (GetWidgets().at(i) == w) { + //emit ChannelValueChanged(i, w->GetValue()); + emit SliderDragged(w, i); + break; + } + } +} + +} diff --git a/app/widget/nodeparamview/paramwidget/abstractparamwidget.h b/app/widget/nodeparamview/paramwidget/abstractparamwidget.h new file mode 100644 index 0000000000..9ed906cf3b --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/abstractparamwidget.h @@ -0,0 +1,69 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef ABSTRACTPARAMWIDGET_H +#define ABSTRACTPARAMWIDGET_H + +#include + +#include "node/value.h" +#include "widget/slider/base/numericsliderbase.h" + +namespace olive { + +class AbstractParamWidget : public QObject +{ + Q_OBJECT +public: + explicit AbstractParamWidget(QObject *parent = nullptr); + + virtual ~AbstractParamWidget() override; + + const std::vector &GetWidgets() const { return widgets_; } + + virtual void Initialize(QWidget *parent, size_t channels) = 0; + + virtual void SetValue(const value_t &val) = 0; + + virtual void SetProperty(const QString &key, const value_t &value); + + virtual void SetTimebase(const rational &timebase){} + +signals: + void ValueChanged(const olive::value_t &val); + + void ChannelValueChanged(size_t channel, const olive::value_t::component_t &val); + + void SliderDragged(NumericSliderBase *slider, size_t track); + +protected: + void AddWidget(QWidget *w) { widgets_.push_back(w); } + +protected slots: + void ArbitrateSliders(); + +private: + std::vector widgets_; + +}; + +} + +#endif // ABSTRACTPARAMWIDGET_H diff --git a/app/widget/nodeparamview/paramwidget/arrayparamwidget.cpp b/app/widget/nodeparamview/paramwidget/arrayparamwidget.cpp new file mode 100644 index 0000000000..cd0a3a5a8d --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/arrayparamwidget.cpp @@ -0,0 +1,42 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2022 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "arrayparamwidget.h" + +#include "widget/nodeparamview/nodeparamviewarraywidget.h" + +namespace olive { + +ArrayParamWidget::ArrayParamWidget(Node *node, const QString &input, QObject *parent) + : AbstractParamWidget{parent}, + node_(node), + input_(input) +{ + +} + +void ArrayParamWidget::Initialize(QWidget *parent, size_t channels) +{ + NodeParamViewArrayWidget* w = new NodeParamViewArrayWidget(node_, input_, parent); + connect(w, &NodeParamViewArrayWidget::DoubleClicked, this, &ArrayParamWidget::DoubleClicked); + AddWidget(w); +} + +} diff --git a/app/widget/nodeparamview/paramwidget/arrayparamwidget.h b/app/widget/nodeparamview/paramwidget/arrayparamwidget.h new file mode 100644 index 0000000000..403e06eca1 --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/arrayparamwidget.h @@ -0,0 +1,49 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2022 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef ARRAYPARAMWIDGET_H +#define ARRAYPARAMWIDGET_H + +#include "abstractparamwidget.h" + +namespace olive { + +class ArrayParamWidget : public AbstractParamWidget +{ + Q_OBJECT +public: + explicit ArrayParamWidget(Node *node, const QString &input, QObject *parent = nullptr); + + virtual void Initialize(QWidget *parent, size_t channels) override; + + virtual void SetValue(const value_t &val) override {} + +signals: + void DoubleClicked(); + +private: + Node *node_; + QString input_; + +}; + +} + +#endif // ARRAYPARAMWIDGET_H diff --git a/app/widget/nodeparamview/paramwidget/bezierparamwidget.cpp b/app/widget/nodeparamview/paramwidget/bezierparamwidget.cpp new file mode 100644 index 0000000000..4e138186b6 --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/bezierparamwidget.cpp @@ -0,0 +1,81 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "bezierparamwidget.h" + +#include "widget/bezier/bezierwidget.h" + +namespace olive { + +BezierParamWidget::BezierParamWidget(QObject *parent) + : AbstractParamWidget{parent} +{ + +} + +void BezierParamWidget::Initialize(QWidget *parent, size_t channels) +{ + Q_ASSERT(channels == 6); + + BezierWidget *bezier = new BezierWidget(parent); + AddWidget(bezier); + + connect(bezier->x_slider(), &FloatSlider::ValueChanged, this, &BezierParamWidget::Arbitrate); + connect(bezier->y_slider(), &FloatSlider::ValueChanged, this, &BezierParamWidget::Arbitrate); + connect(bezier->cp1_x_slider(), &FloatSlider::ValueChanged, this, &BezierParamWidget::Arbitrate); + connect(bezier->cp1_y_slider(), &FloatSlider::ValueChanged, this, &BezierParamWidget::Arbitrate); + connect(bezier->cp2_x_slider(), &FloatSlider::ValueChanged, this, &BezierParamWidget::Arbitrate); + connect(bezier->cp2_y_slider(), &FloatSlider::ValueChanged, this, &BezierParamWidget::Arbitrate); +} + +void BezierParamWidget::SetValue(const value_t &val) +{ + Bezier b(val.value(0), val.value(1), val.value(2), val.value(3), val.value(4), val.value(5)); + BezierWidget* bw = static_cast(GetWidgets().at(0)); + bw->SetValue(b); +} + +void BezierParamWidget::Arbitrate() +{ + // Widget is a FloatSlider (child of BezierWidget) + BezierWidget *bw = static_cast(GetWidgets().at(0)); + FloatSlider *fs = static_cast(sender()); + + int index = -1; + if (fs == bw->x_slider()) { + index = 0; + } else if (fs == bw->y_slider()) { + index = 1; + } else if (fs == bw->cp1_x_slider()) { + index = 2; + } else if (fs == bw->cp1_y_slider()) { + index = 3; + } else if (fs == bw->cp2_x_slider()) { + index = 4; + } else if (fs == bw->cp2_y_slider()) { + index = 5; + } + + if (index != -1) { + emit SliderDragged(fs, index); + } +} + +} diff --git a/app/widget/nodeparamview/paramwidget/bezierparamwidget.h b/app/widget/nodeparamview/paramwidget/bezierparamwidget.h new file mode 100644 index 0000000000..967a50d9b0 --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/bezierparamwidget.h @@ -0,0 +1,45 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef BEZIERPARAMWIDGET_H +#define BEZIERPARAMWIDGET_H + +#include "abstractparamwidget.h" + +namespace olive { + +class BezierParamWidget : public AbstractParamWidget +{ + Q_OBJECT +public: + BezierParamWidget(QObject *parent = nullptr); + + virtual void Initialize(QWidget *parent, size_t channels) override; + + virtual void SetValue(const value_t &val) override; + +private slots: + void Arbitrate(); + +}; + +} + +#endif // BEZIERPARAMWIDGET_H diff --git a/app/widget/nodeparamview/paramwidget/boolparamwidget.cpp b/app/widget/nodeparamview/paramwidget/boolparamwidget.cpp new file mode 100644 index 0000000000..509a9e9c44 --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/boolparamwidget.cpp @@ -0,0 +1,61 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "boolparamwidget.h" + +#include + +namespace olive { + +BoolParamWidget::BoolParamWidget(QObject *parent) + : AbstractParamWidget{parent} +{ + +} + +void BoolParamWidget::Initialize(QWidget *parent, size_t channels) +{ + for (size_t i = 0; i < channels; i++) { + QCheckBox *c = new QCheckBox(parent); + connect(c, &QCheckBox::clicked, this, &BoolParamWidget::Arbitrate); + AddWidget(c); + } +} + +void BoolParamWidget::SetValue(const value_t &val) +{ + for (size_t i = 0; i < val.size() && i < GetWidgets().size(); i++) { + static_cast(GetWidgets().at(i))->setChecked(val.value(i)); + } +} + +void BoolParamWidget::Arbitrate() +{ + QCheckBox *w = static_cast(sender()); + + for (size_t i = 0; i < GetWidgets().size(); i++) { + if (GetWidgets().at(i) == w) { + emit ChannelValueChanged(i, int64_t(w->isChecked())); + break; + } + } +} + +} diff --git a/app/node/splitvalue.h b/app/widget/nodeparamview/paramwidget/boolparamwidget.h similarity index 61% rename from app/node/splitvalue.h rename to app/widget/nodeparamview/paramwidget/boolparamwidget.h index 2a1d2a49cc..37b984f57f 100644 --- a/app/node/splitvalue.h +++ b/app/widget/nodeparamview/paramwidget/boolparamwidget.h @@ -1,7 +1,7 @@ /*** Olive - Non-Linear Video Editor - Copyright (C) 2022 Olive Team + Copyright (C) 2023 Olive Studios LLC 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 @@ -18,16 +18,28 @@ ***/ -#ifndef SPLITVALUE_H -#define SPLITVALUE_H +#ifndef BOOLPARAMWIDGET_H +#define BOOLPARAMWIDGET_H -#include -#include +#include "abstractparamwidget.h" namespace olive { -using SplitValue = QVector; +class BoolParamWidget : public AbstractParamWidget +{ + Q_OBJECT +public: + explicit BoolParamWidget(QObject *parent = nullptr); + + virtual void Initialize(QWidget *parent, size_t channels) override; + + virtual void SetValue(const value_t &val) override; + +private slots: + void Arbitrate(); + +}; } -#endif // SPLITVALUE_H +#endif // BOOLPARAMWIDGET_H diff --git a/app/widget/nodeparamview/paramwidget/colorparamwidget.cpp b/app/widget/nodeparamview/paramwidget/colorparamwidget.cpp new file mode 100644 index 0000000000..c7b90b6700 --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/colorparamwidget.cpp @@ -0,0 +1,89 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "colorparamwidget.h" + +#include "widget/colorbutton/colorbutton.h" + +namespace olive { + +ColorParamWidget::ColorParamWidget(QObject *parent) + : AbstractParamWidget{parent} +{ + +} + +void ColorParamWidget::Initialize(QWidget *parent, size_t channels) +{ + qDebug() << "CPW::Initialize - stub"; + /* + for (size_t i = 0; i < channels; i++) { + ColorButton* color_button = new ColorButton(project()->color_manager(), parent); + AddWidget(color_button); + connect(color_button, &ColorButton::ColorChanged, this, &ColorParamWidget::Arbitrate); + } + */ +} + +void ColorParamWidget::SetValue(const value_t &val) +{ + qDebug() << "CPW::SetValue - stub"; + /* + ManagedColor mc = val.value(); + + mc.set_color_input(GetInnerInput().GetProperty("col_input").toString()); + + QString d = GetInnerInput().GetProperty("col_display").toString(); + QString v = GetInnerInput().GetProperty("col_view").toString(); + QString l = GetInnerInput().GetProperty("col_look").toString(); + + mc.set_color_output(ColorTransform(d, v, l)); + + static_cast(GetWidgets().at(0))->SetColor(mc); + */ +} + +void ColorParamWidget::Arbitrate() +{ + qDebug() << "CPW::Arbitrate - stub"; + /* + // Sender is a ColorButton + ManagedColor c = static_cast(sender())->GetColor(); + + MultiUndoCommand* command = new MultiUndoCommand(); + + SetInputValueInternal(c.red(), 0, command, false); + SetInputValueInternal(c.green(), 1, command, false); + SetInputValueInternal(c.blue(), 2, command, false); + SetInputValueInternal(c.alpha(), 3, command, false); + + Node* n = GetInnerInput().node(); + n->blockSignals(true); + n->SetInputProperty(GetInnerInput().input(), QStringLiteral("col_input"), c.color_input()); + n->SetInputProperty(GetInnerInput().input(), QStringLiteral("col_display"), c.color_output().display()); + n->SetInputProperty(GetInnerInput().input(), QStringLiteral("col_view"), c.color_output().view()); + n->SetInputProperty(GetInnerInput().input(), QStringLiteral("col_look"), c.color_output().look()); + n->blockSignals(false); + + Core::instance()->undo_stack()->push(command, GetCommandName()); + */ +} + +} diff --git a/app/widget/nodeparamview/paramwidget/colorparamwidget.h b/app/widget/nodeparamview/paramwidget/colorparamwidget.h new file mode 100644 index 0000000000..061fce311a --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/colorparamwidget.h @@ -0,0 +1,45 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef COLORPARAMWIDGET_H +#define COLORPARAMWIDGET_H + +#include "abstractparamwidget.h" + +namespace olive { + +class ColorParamWidget : public AbstractParamWidget +{ + Q_OBJECT +public: + explicit ColorParamWidget(QObject *parent = nullptr); + + virtual void Initialize(QWidget *parent, size_t channels) override; + + virtual void SetValue(const value_t &val) override; + +private slots: + void Arbitrate(); + +}; + +} + +#endif // COLORPARAMWIDGET_H diff --git a/app/widget/nodeparamview/paramwidget/comboparamwidget.cpp b/app/widget/nodeparamview/paramwidget/comboparamwidget.cpp new file mode 100644 index 0000000000..a8a8a78262 --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/comboparamwidget.cpp @@ -0,0 +1,127 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "comboparamwidget.h" + +#include + +namespace olive { + +#define super AbstractParamWidget + +ComboParamWidget::ComboParamWidget(QObject *parent) + : super{parent} +{ +} + +void ComboParamWidget::Initialize(QWidget *parent, size_t channels) +{ + for (size_t i = 0; i < channels; i++) { + QComboBox* combobox = new QComboBox(parent); + AddWidget(combobox); + connect(combobox, static_cast(&QComboBox::currentIndexChanged), this, &ComboParamWidget::ArbitrateCombo); + } +} + +void ComboParamWidget::SetValue(const value_t &val) +{ + for (size_t j = 0; j < val.size() && j < GetWidgets().size(); j++) { + QComboBox* cb = static_cast(GetWidgets().at(j)); + cb->blockSignals(true); + int index = val.value(j); + for (int i=0; icount(); i++) { + if (cb->itemData(i).toInt() == index) { + cb->setCurrentIndex(i); + } + } + cb->blockSignals(false); + } +} + +void ComboParamWidget::SetProperty(const QString &key, const value_t &val) +{ + if (key == QStringLiteral("combo_str")) { + for (size_t j = 0; j < val.size() && j < GetWidgets().size(); j++) { + QComboBox* cb = static_cast(GetWidgets().at(j)); + + int old_index = cb->currentIndex(); + + // Block the combobox changed signals since we anticipate the index will be the same and not require a re-render + cb->blockSignals(true); + + cb->clear(); + + QStringList items = val.value(j); + int index = 0; + foreach (const QString& s, items) { + if (s.isEmpty()) { + cb->insertSeparator(cb->count()); + cb->setItemData(cb->count()-1, -1); + } else { + cb->addItem(s, index); + index++; + } + } + + cb->setCurrentIndex(old_index); + + cb->blockSignals(false); + + // In case the amount of items is LESS and the previous index cannot be set, NOW we trigger a re-cache since the + // value has changed + if (cb->currentIndex() != old_index) { + ArbitrateSpecificCombo(cb); + } + } + } else { + super::SetProperty(key, val); + } +} + +void ComboParamWidget::ArbitrateSpecificCombo(QComboBox *cb) +{ + size_t channel = 0; + for (size_t i = 0; i < GetWidgets().size(); i++) { + if (GetWidgets().at(i) == cb) { + channel = i; + break; + } + } + + int64_t index = cb->currentIndex(); + + // Subtract any splitters up until this point + for (int i=index-1; i>=0; i--) { + if (cb->itemData(i, Qt::AccessibleDescriptionRole).toString() == QStringLiteral("separator")) { + index--; + } + } + + emit ChannelValueChanged(channel, index); +} + +void ComboParamWidget::ArbitrateCombo() +{ + // Widget is a QComboBox + QComboBox* cb = static_cast(sender()); + ArbitrateSpecificCombo(cb); +} + +} diff --git a/app/widget/nodeparamview/paramwidget/comboparamwidget.h b/app/widget/nodeparamview/paramwidget/comboparamwidget.h new file mode 100644 index 0000000000..31cfce67bb --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/comboparamwidget.h @@ -0,0 +1,52 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef COMBOPARAMWIDGET_H +#define COMBOPARAMWIDGET_H + +#include + +#include "abstractparamwidget.h" + +namespace olive { + +class ComboParamWidget : public AbstractParamWidget +{ + Q_OBJECT +public: + explicit ComboParamWidget(QObject *parent = nullptr); + + virtual void Initialize(QWidget *parent, size_t channels) override; + + virtual void SetValue(const value_t &val) override; + + virtual void SetProperty(const QString &key, const value_t &val) override; + +private: + void ArbitrateSpecificCombo(QComboBox *b); + +private slots: + void ArbitrateCombo(); + +}; + +} + +#endif // COMBOPARAMWIDGET_H diff --git a/app/widget/nodeparamview/paramwidget/fileparamwidget.cpp b/app/widget/nodeparamview/paramwidget/fileparamwidget.cpp new file mode 100644 index 0000000000..7260c7d998 --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/fileparamwidget.cpp @@ -0,0 +1,79 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "fileparamwidget.h" + +#include "widget/filefield/filefield.h" + +namespace olive { + +#define super AbstractParamWidget + +FileParamWidget::FileParamWidget(QObject *parent) + : super{parent} +{ + +} + +void FileParamWidget::Initialize(QWidget *parent, size_t channels) +{ + for (size_t i = 0; i < channels; i++) { + FileField* file_field = new FileField(parent); + AddWidget(file_field); + connect(file_field, &FileField::FilenameChanged, this, &FileParamWidget::Arbitrate); + } +} + +void FileParamWidget::SetValue(const value_t &val) +{ + for (size_t i = 0; i < val.size() && i < GetWidgets().size(); i++) { + FileField* ff = static_cast(GetWidgets().at(i)); + ff->SetFilename(val.value(i)); + } +} + +void FileParamWidget::SetProperty(const QString &key, const value_t &val) +{ + if (key == QStringLiteral("placeholder")) { + for (size_t i = 0; i < val.size() && i < GetWidgets().size(); i++) { + static_cast(GetWidgets().at(i))->SetPlaceholder(val.value(i)); + } + } else if (key == QStringLiteral("directory")) { + for (size_t i = 0; i < val.size() && i < GetWidgets().size(); i++) { + static_cast(GetWidgets().at(i))->SetDirectoryMode(val.value(i)); + } + } else { + super::SetProperty(key, val); + } +} + +void FileParamWidget::Arbitrate() +{ + FileField* ff = static_cast(sender()); + + for (size_t i = 0; i < GetWidgets().size(); i++) { + if (GetWidgets().at(i) == ff) { + emit ChannelValueChanged(i, ff->GetFilename()); + break; + } + } +} + +} diff --git a/app/widget/nodeparamview/paramwidget/fileparamwidget.h b/app/widget/nodeparamview/paramwidget/fileparamwidget.h new file mode 100644 index 0000000000..2eed3465d7 --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/fileparamwidget.h @@ -0,0 +1,47 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef FILEPARAMWIDGET_H +#define FILEPARAMWIDGET_H + +#include "abstractparamwidget.h" + +namespace olive { + +class FileParamWidget : public AbstractParamWidget +{ + Q_OBJECT +public: + explicit FileParamWidget(QObject *parent = nullptr); + + virtual void Initialize(QWidget *parent, size_t channels) override; + + virtual void SetValue(const value_t &val) override; + + virtual void SetProperty(const QString &key, const value_t &val) override; + +private slots: + void Arbitrate(); + +}; + +} + +#endif // FILEPARAMWIDGET_H diff --git a/app/widget/nodeparamview/paramwidget/floatsliderparamwidget.cpp b/app/widget/nodeparamview/paramwidget/floatsliderparamwidget.cpp new file mode 100644 index 0000000000..fc8c306bc6 --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/floatsliderparamwidget.cpp @@ -0,0 +1,83 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "floatsliderparamwidget.h" + +#include "widget/slider/floatslider.h" + +namespace olive { + +#define super NumericSliderParamWidget + +FloatSliderParamWidget::FloatSliderParamWidget(QObject *parent) + : super{parent} +{ +} + +void FloatSliderParamWidget::Initialize(QWidget *parent, size_t channels) +{ + CreateSliders(parent, channels); +} + +void FloatSliderParamWidget::SetValue(const value_t &val) +{ + for (size_t i = 0; i < val.size() && i < GetWidgets().size(); i++) { + static_cast(GetWidgets().at(i))->SetValue(val.value(i)); + } +} + +void FloatSliderParamWidget::SetProperty(const QString &key, const value_t &val) +{ + if (key == QStringLiteral("min")) { + for (size_t i = 0; i < val.size() && i < GetWidgets().size(); i++) { + static_cast(GetWidgets().at(i))->SetMinimum(val.value(i)); + } + } else if (key == QStringLiteral("max")) { + for (size_t i = 0; i < val.size() && i < GetWidgets().size(); i++) { + static_cast(GetWidgets().at(i))->SetValue(val.value(i)); + } + } else if (key == QStringLiteral("view")) { + FloatSlider::DisplayType display_type = static_cast(val.toInt()); + + foreach (QWidget* w, GetWidgets()) { + static_cast(w)->SetDisplayType(display_type); + } + } else if (key == QStringLiteral("decimalplaces")) { + int dec_places = val.toInt(); + + foreach (QWidget* w, GetWidgets()) { + static_cast(w)->SetDecimalPlaces(dec_places); + } + } else if (key == QStringLiteral("autotrim")) { + bool autotrim = val.toBool(); + + foreach (QWidget* w, GetWidgets()) { + static_cast(w)->SetAutoTrimDecimalPlaces(autotrim); + } + } else if (key == QStringLiteral("offset")) { + for (size_t i=0; i(GetWidgets().at(i))->SetOffset(val.value(i)); + } + } else { + super::SetProperty(key, val); + } +} + +} diff --git a/app/widget/nodeparamview/paramwidget/floatsliderparamwidget.h b/app/widget/nodeparamview/paramwidget/floatsliderparamwidget.h new file mode 100644 index 0000000000..ebe6a26332 --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/floatsliderparamwidget.h @@ -0,0 +1,44 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef FLOATSLIDERPARAMWIDGET_H +#define FLOATSLIDERPARAMWIDGET_H + +#include "numericsliderparamwidget.h" + +namespace olive { + +class FloatSliderParamWidget : public NumericSliderParamWidget +{ + Q_OBJECT +public: + explicit FloatSliderParamWidget(QObject *parent = nullptr); + + virtual void Initialize(QWidget *parent, size_t channels) override; + + virtual void SetValue(const value_t &val) override; + + virtual void SetProperty(const QString &key, const value_t &val) override; + +}; + +} + +#endif // FLOATSLIDERPARAMWIDGET_H diff --git a/app/widget/nodeparamview/paramwidget/fontparamwidget.cpp b/app/widget/nodeparamview/paramwidget/fontparamwidget.cpp new file mode 100644 index 0000000000..3aae0c2292 --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/fontparamwidget.cpp @@ -0,0 +1,64 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "fontparamwidget.h" + +#include + +namespace olive { + +FontParamWidget::FontParamWidget(QObject *parent) + : AbstractParamWidget{parent} +{ + +} + +void FontParamWidget::Initialize(QWidget *parent, size_t channels) +{ + for (size_t i = 0; i < channels; i++) { + QFontComboBox* font_combobox = new QFontComboBox(parent); + AddWidget(font_combobox); + connect(font_combobox, &QFontComboBox::currentFontChanged, this, &FontParamWidget::Arbitrate); + } +} + +void FontParamWidget::SetValue(const value_t &val) +{ + for (size_t i = 0; i < val.size() && i < GetWidgets().size(); i++) { + QFontComboBox* fc = static_cast(GetWidgets().at(i)); + fc->blockSignals(true); + fc->setCurrentFont(val.value(i)); + fc->blockSignals(false); + } +} + +void FontParamWidget::Arbitrate() +{ + QFontComboBox* ff = static_cast(sender()); + + for (size_t i = 0; i < GetWidgets().size(); i++) { + if (GetWidgets().at(i) == ff) { + emit ChannelValueChanged(i, ff->currentFont().toString()); + break; + } + } +} + +} diff --git a/app/widget/nodeparamview/paramwidget/fontparamwidget.h b/app/widget/nodeparamview/paramwidget/fontparamwidget.h new file mode 100644 index 0000000000..8294387908 --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/fontparamwidget.h @@ -0,0 +1,45 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef FONTPARAMWIDGET_H +#define FONTPARAMWIDGET_H + +#include "abstractparamwidget.h" + +namespace olive { + +class FontParamWidget : public AbstractParamWidget +{ + Q_OBJECT +public: + explicit FontParamWidget(QObject *parent = nullptr); + + virtual void Initialize(QWidget *parent, size_t channels) override; + + virtual void SetValue(const value_t &val) override; + +private slots: + void Arbitrate(); + +}; + +} + +#endif // FONTPARAMWIDGET_H diff --git a/app/widget/nodeparamview/paramwidget/integersliderparamwidget.cpp b/app/widget/nodeparamview/paramwidget/integersliderparamwidget.cpp new file mode 100644 index 0000000000..79abab0e41 --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/integersliderparamwidget.cpp @@ -0,0 +1,65 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "integersliderparamwidget.h" + +#include "widget/slider/integerslider.h" + +namespace olive { + +#define super NumericSliderParamWidget + +IntegerSliderParamWidget::IntegerSliderParamWidget(QObject *parent) : + super(parent) +{ +} + +void IntegerSliderParamWidget::Initialize(QWidget *parent, size_t channels) +{ + CreateSliders(parent, channels); +} + +void IntegerSliderParamWidget::SetValue(const value_t &val) +{ + for (size_t i = 0; i < val.size() && i < GetWidgets().size(); i++) { + static_cast(GetWidgets().at(i))->SetValue(val.value(i)); + } +} + +void IntegerSliderParamWidget::SetProperty(const QString &key, const value_t &val) +{ + if (key == QStringLiteral("min")) { + for (size_t i = 0; i < val.size() && i < GetWidgets().size(); i++) { + static_cast(GetWidgets().at(i))->SetMinimum(val.value(i)); + } + } else if (key == QStringLiteral("max")) { + for (size_t i = 0; i < val.size() && i < GetWidgets().size(); i++) { + static_cast(GetWidgets().at(i))->SetValue(val.value(i)); + } + } else if (key == QStringLiteral("offset")) { + for (size_t i=0; i(GetWidgets().at(i))->SetOffset(val.value(i)); + } + } else { + super::SetProperty(key, val); + } +} + +} diff --git a/app/widget/nodeparamview/paramwidget/integersliderparamwidget.h b/app/widget/nodeparamview/paramwidget/integersliderparamwidget.h new file mode 100644 index 0000000000..4a351ee58e --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/integersliderparamwidget.h @@ -0,0 +1,44 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef INTEGERSLIDERPARAMWIDGET_H +#define INTEGERSLIDERPARAMWIDGET_H + +#include "numericsliderparamwidget.h" + +namespace olive { + +class IntegerSliderParamWidget : public NumericSliderParamWidget +{ + Q_OBJECT +public: + IntegerSliderParamWidget(QObject *parent = nullptr); + + virtual void Initialize(QWidget *parent, size_t channels) override; + + virtual void SetValue(const value_t &val) override; + + virtual void SetProperty(const QString &key, const value_t &val) override; + +}; + +} + +#endif // INTEGERSLIDERPARAMWIDGET_H diff --git a/app/widget/nodeparamview/paramwidget/numericsliderparamwidget.cpp b/app/widget/nodeparamview/paramwidget/numericsliderparamwidget.cpp new file mode 100644 index 0000000000..cdfda95887 --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/numericsliderparamwidget.cpp @@ -0,0 +1,62 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "numericsliderparamwidget.h" + +#include "widget/slider/base/numericsliderbase.h" + +namespace olive { + +#define super AbstractParamWidget + +NumericSliderParamWidget::NumericSliderParamWidget(QObject *parent) + : AbstractParamWidget{parent} +{ + +} + +void NumericSliderParamWidget::SetProperty(const QString &key, const value_t &val) +{ + if (key == QStringLiteral("color")) { + QColor c(val.toString()); + + if (key.size() == 5) { + // Set for all tracks + for (size_t i=0; i(GetWidgets().at(i))->SetColor(c); + } + } else { + bool ok; + size_t element = key.midRef(5).toInt(&ok); + if (ok && element < GetWidgets().size()) { + static_cast(GetWidgets().at(element))->SetColor(c); + } + } + } else if (key == QStringLiteral("base")) { + double d = val.toDouble(); + for (size_t i=0; i(GetWidgets().at(i))->SetDragMultiplier(d); + } + } else { + super::SetProperty(key, val); + } +} + +} diff --git a/app/widget/nodeparamview/paramwidget/numericsliderparamwidget.h b/app/widget/nodeparamview/paramwidget/numericsliderparamwidget.h new file mode 100644 index 0000000000..7084504f07 --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/numericsliderparamwidget.h @@ -0,0 +1,62 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef NUMERICSLIDERPARAMWIDGET_H +#define NUMERICSLIDERPARAMWIDGET_H + +#include "abstractparamwidget.h" +#include "common/qtutils.h" + +namespace olive { + +class NumericSliderParamWidget : public AbstractParamWidget +{ + Q_OBJECT +public: + explicit NumericSliderParamWidget(QObject *parent = nullptr); + + virtual void SetProperty(const QString &key, const value_t &val) override; + +protected: + template + void CreateSliders(QWidget *parent, size_t channels) + { + for (size_t i = 0; i < channels; i++) { + T *s = new T(parent); + + qDebug() << "no default value set"; + //s->SetDefaultValue(GetDefaultValue().at(i)); + + s->SetLadderElementCount(2); + + // HACK: Force some spacing between sliders + s->setContentsMargins(0, 0, QtUtils::QFontMetricsWidth(s->fontMetrics(), QStringLiteral(" ")), 0); + + connect(s, &T::ValueChanged, this, &NumericSliderParamWidget::ArbitrateSliders); + + AddWidget(s); + } + } + +}; + +} + +#endif // NUMERICSLIDERPARAMWIDGET_H diff --git a/app/widget/nodeparamview/paramwidget/rationalsliderparamwidget.cpp b/app/widget/nodeparamview/paramwidget/rationalsliderparamwidget.cpp new file mode 100644 index 0000000000..fa1957fa73 --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/rationalsliderparamwidget.cpp @@ -0,0 +1,73 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "rationalsliderparamwidget.h" + +#include "widget/slider/rationalslider.h" + +namespace olive { + +#define super NumericSliderParamWidget + +RationalSliderParamWidget::RationalSliderParamWidget(QObject *parent) + : NumericSliderParamWidget{parent} +{ + +} + +void RationalSliderParamWidget::Initialize(QWidget *parent, size_t channels) +{ + CreateSliders(parent, channels); +} + +void RationalSliderParamWidget::SetValue(const value_t &val) +{ + for (size_t i = 0; i < val.size() && i < GetWidgets().size(); i++) { + static_cast(GetWidgets().at(i))->SetValue(val.value(i)); + } +} + +void RationalSliderParamWidget::SetProperty(const QString &key, const value_t &val) +{ + if (key == QStringLiteral("view")) { + RationalSlider::DisplayType display_type = static_cast(val.toInt()); + + foreach (QWidget* w, GetWidgets()) { + static_cast(w)->SetDisplayType(display_type); + } + } else if (key == QStringLiteral("viewlock")) { + bool locked = val.toBool(); + + foreach (QWidget* w, GetWidgets()) { + static_cast(w)->SetLockDisplayType(locked); + } + } else { + super::SetProperty(key, val); + } +} + +void RationalSliderParamWidget::SetTimebase(const rational &timebase) +{ + for (QWidget *w : GetWidgets()) { + static_cast(w)->SetTimebase(timebase); + } +} + +} diff --git a/app/widget/nodeparamview/paramwidget/rationalsliderparamwidget.h b/app/widget/nodeparamview/paramwidget/rationalsliderparamwidget.h new file mode 100644 index 0000000000..c272a9b642 --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/rationalsliderparamwidget.h @@ -0,0 +1,45 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef RATIONALSLIDERPARAMWIDGET_H +#define RATIONALSLIDERPARAMWIDGET_H + +#include "numericsliderparamwidget.h" + +namespace olive { + +class RationalSliderParamWidget : public NumericSliderParamWidget +{ +public: + explicit RationalSliderParamWidget(QObject *parent = nullptr); + + virtual void Initialize(QWidget *parent, size_t channels) override; + + virtual void SetValue(const value_t &val) override; + + virtual void SetProperty(const QString &key, const value_t &val) override; + + virtual void SetTimebase(const rational &timebase) override; + +}; + +} + +#endif // RATIONALSLIDERPARAMWIDGET_H diff --git a/app/widget/nodeparamview/paramwidget/textparamwidget.cpp b/app/widget/nodeparamview/paramwidget/textparamwidget.cpp new file mode 100644 index 0000000000..f557622286 --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/textparamwidget.cpp @@ -0,0 +1,76 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "textparamwidget.h" + +#include "widget/nodeparamview/nodeparamviewtextedit.h" + +namespace olive { + +#define super AbstractParamWidget + +TextParamWidget::TextParamWidget(QObject *parent) + : super{parent} +{ + +} + +void TextParamWidget::Initialize(QWidget *parent, size_t channels) +{ + for (size_t i = 0; i < channels; i++) { + NodeParamViewTextEdit* line_edit = new NodeParamViewTextEdit(parent); + AddWidget(line_edit); + connect(line_edit, &NodeParamViewTextEdit::textEdited, this, &TextParamWidget::Arbitrate); + connect(line_edit, &NodeParamViewTextEdit::RequestEditInViewer, this, &TextParamWidget::RequestEditInViewer); + } +} + +void TextParamWidget::SetValue(const value_t &val) +{ + for (size_t i = 0; i < val.size() && i < GetWidgets().size(); i++) { + NodeParamViewTextEdit* e = static_cast(GetWidgets().at(i)); + e->setTextPreservingCursor(val.value(i)); + } +} + +void TextParamWidget::SetProperty(const QString &key, const value_t &val) +{ + if (key == QStringLiteral("vieweronly")) { + for (size_t i = 0; i < val.size() && i < GetWidgets().size(); i++) { + static_cast(GetWidgets().at(i))->SetEditInViewerOnlyMode(val.value(i)); + } + } else { + super::SetProperty(key, val); + } +} + +void TextParamWidget::Arbitrate() +{ + NodeParamViewTextEdit* ff = static_cast(sender()); + + for (size_t i = 0; i < GetWidgets().size(); i++) { + if (GetWidgets().at(i) == ff) { + emit ChannelValueChanged(i, ff->text()); + break; + } + } +} + +} diff --git a/app/widget/nodeparamview/paramwidget/textparamwidget.h b/app/widget/nodeparamview/paramwidget/textparamwidget.h new file mode 100644 index 0000000000..68a594ffb4 --- /dev/null +++ b/app/widget/nodeparamview/paramwidget/textparamwidget.h @@ -0,0 +1,50 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef TEXTPARAMWIDGET_H +#define TEXTPARAMWIDGET_H + +#include "abstractparamwidget.h" + +namespace olive { + +class TextParamWidget : public AbstractParamWidget +{ + Q_OBJECT +public: + explicit TextParamWidget(QObject *parent = nullptr); + + virtual void Initialize(QWidget *parent, size_t channels) override; + + virtual void SetValue(const value_t &val) override; + + virtual void SetProperty(const QString &key, const value_t &val) override; + +signals: + void RequestEditInViewer(); + +private slots: + void Arbitrate(); + +}; + +} + +#endif // TEXTPARAMWIDGET_H diff --git a/app/widget/nodetreeview/nodetreeview.cpp b/app/widget/nodetreeview/nodetreeview.cpp index b298bab615..bc898fc7e9 100644 --- a/app/widget/nodetreeview/nodetreeview.cpp +++ b/app/widget/nodetreeview/nodetreeview.cpp @@ -22,6 +22,8 @@ #include +#include "common/qtutils.h" + namespace olive { NodeTreeView::NodeTreeView(QWidget *parent) : @@ -171,7 +173,7 @@ QTreeWidgetItem* NodeTreeView::CreateItem(QTreeWidgetItem *parent, const NodeKey QString item_name; if (ref.track() == -1 - || NodeValue::get_number_of_keyframe_tracks(ref.input().GetDataType()) == 1 + || ref.input().GetChannelCount() == 1 || (ref.input().IsArray() && ref.input().element() == -1)) { if (ref.input().element() == -1) { item_name = ref.input().name(); @@ -222,7 +224,7 @@ void NodeTreeView::CreateItemsForTracks(QTreeWidgetItem *parent, const NodeInput bool NodeTreeView::UseRGBAOverXYZW(const NodeKeyframeTrackReference &ref) { - return ref.input().GetDataType() == NodeValue::kColor; + return ref.input().GetProperty(QStringLiteral("subtype")).toString() == QStringLiteral("color"); } void NodeTreeView::ItemCheckStateChanged(QTreeWidgetItem *item, int column) diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index 778695949f..d765b3d248 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -1014,7 +1014,7 @@ void NodeView::ProcessMovingAttachedNodes(const QPoint &pos) if (new_drop_edge) { drop_input_.Reset(); - NodeValue::Type drop_edge_data_type = new_drop_edge->input().GetDataType(); + type_t drop_edge_data_type = new_drop_edge->input().GetDataType(); // Determine best input to connect to our new node if (attached_node->GetEffectInput().IsValid()) { diff --git a/app/widget/nodeview/nodeviewcontext.cpp b/app/widget/nodeview/nodeviewcontext.cpp index 661c53bb89..ebabb6c035 100644 --- a/app/widget/nodeview/nodeviewcontext.cpp +++ b/app/widget/nodeview/nodeviewcontext.cpp @@ -7,6 +7,7 @@ #include #include +#include "common/qtutils.h" #include "core.h" #include "node/block/block.h" #include "node/group/group.h" diff --git a/app/widget/resizablescrollbar/resizabletimelinescrollbar.cpp b/app/widget/resizablescrollbar/resizabletimelinescrollbar.cpp index a1648f50b0..c0b997c6ca 100644 --- a/app/widget/resizablescrollbar/resizabletimelinescrollbar.cpp +++ b/app/widget/resizablescrollbar/resizabletimelinescrollbar.cpp @@ -25,6 +25,7 @@ #include #include +#include "common/qtutils.h" #include "ui/colorcoding.h" namespace olive { diff --git a/app/widget/scope/histogram/histogram.cpp b/app/widget/scope/histogram/histogram.cpp index 0cda42142e..633bf18c7e 100644 --- a/app/widget/scope/histogram/histogram.cpp +++ b/app/widget/scope/histogram/histogram.cpp @@ -71,8 +71,8 @@ void HistogramScope::DrawScope(TexturePtr managed_tex, QVariant pipeline) ShaderJob shader_job; shader_job.Insert(QStringLiteral("viewport"), QVector2D(width(), height())); - shader_job.Insert(QStringLiteral("histogram_scale"), NodeValue(NodeValue::kFloat, histogram_scale)); - shader_job.Insert(QStringLiteral("histogram_power"), NodeValue(NodeValue::kFloat, histogram_power)); + shader_job.Insert(QStringLiteral("histogram_scale"), histogram_scale); + shader_job.Insert(QStringLiteral("histogram_power"), histogram_power); if (!texture_row_sums_ || texture_row_sums_->width() != this->width() diff --git a/app/widget/scope/scopebase/scopebase.cpp b/app/widget/scope/scopebase/scopebase.cpp index d758f0b42e..666096002b 100644 --- a/app/widget/scope/scopebase/scopebase.cpp +++ b/app/widget/scope/scopebase/scopebase.cpp @@ -50,7 +50,7 @@ void ScopeBase::DrawScope(TexturePtr managed_tex, QVariant pipeline) { ShaderJob job; - job.Insert(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(managed_tex))); + job.Insert(QStringLiteral("ove_maintex"), managed_tex); renderer()->Blit(pipeline, job, GetViewportParams()); } diff --git a/app/widget/scope/waveform/waveform.cpp b/app/widget/scope/waveform/waveform.cpp index 2f8f464071..a08a944779 100644 --- a/app/widget/scope/waveform/waveform.cpp +++ b/app/widget/scope/waveform/waveform.cpp @@ -54,23 +54,19 @@ void WaveformScope::DrawScope(TexturePtr managed_tex, QVariant pipeline) ShaderJob job; // Set viewport size - job.Insert(QStringLiteral("viewport"), - NodeValue(NodeValue::kVec2, QVector2D(width(), height()))); + job.Insert(QStringLiteral("viewport"), QVector2D(width(), height())); // Set luma coefficients double luma_coeffs[3] = {0.0f, 0.0f, 0.0f}; color_manager()->GetDefaultLumaCoefs(luma_coeffs); - job.Insert(QStringLiteral("luma_coeffs"), - NodeValue(NodeValue::kVec3, QVector3D(luma_coeffs[0], luma_coeffs[1], luma_coeffs[2]))); + job.Insert(QStringLiteral("luma_coeffs"), QVector3D(luma_coeffs[0], luma_coeffs[1], luma_coeffs[2])); // Scale of the waveform relative to the viewport surface. - job.Insert(QStringLiteral("waveform_scale"), - NodeValue(NodeValue::kFloat, waveform_scale)); + job.Insert(QStringLiteral("waveform_scale"), waveform_scale); // Insert source texture - job.Insert(QStringLiteral("ove_maintex"), - NodeValue(NodeValue::kTexture, QVariant::fromValue(managed_tex))); + job.Insert(QStringLiteral("ove_maintex"), managed_tex); renderer()->Blit(pipeline, job, GetViewportParams()); diff --git a/app/widget/slider/base/numericsliderbase.cpp b/app/widget/slider/base/numericsliderbase.cpp index d0a3e78f25..3ca583945e 100644 --- a/app/widget/slider/base/numericsliderbase.cpp +++ b/app/widget/slider/base/numericsliderbase.cpp @@ -72,13 +72,13 @@ void NumericSliderBase::LadderDragged(int value, double multiplier) dragged_diff_ += value * multiplier; // Store current value to try and prevent any unnecessary signalling if the value doesn't change - QVariant pre_set_value = GetValueInternal(); + InternalType pre_set_value = GetValueInternal(); setting_drag_value_ = true; SetValueInternal(AdjustDragDistanceInternal(drag_start_value_, dragged_diff_)); setting_drag_value_ = false; - if (GetValueInternal() != pre_set_value) { + if (!Equals(GetValueInternal(), pre_set_value)) { // We retrieve the value instead of storing it ourselves because SetValueInternal may do extra // processing (such as clamping). drag_ladder_->SetValue(GetFormattedValueToString()); @@ -142,7 +142,7 @@ bool NumericSliderBase::UsingLadders() const return ladder_element_count_ > 0 && OLIVE_CONFIG("UseSliderLadders").toBool(); } -QVariant NumericSliderBase::AdjustValue(const QVariant &value) const +NumericSliderBase::InternalType NumericSliderBase::AdjustValue(const InternalType &value) const { // Clamps between min/max if (has_min_ && ValueLessThan(value, min_value_)) { @@ -154,19 +154,19 @@ QVariant NumericSliderBase::AdjustValue(const QVariant &value) const return value; } -void NumericSliderBase::SetOffset(const QVariant &v) +void NumericSliderBase::SetOffset(const InternalType &v) { offset_ = v; UpdateLabel(); } -QVariant NumericSliderBase::AdjustDragDistanceInternal(const QVariant &start, const double &drag) const +NumericSliderBase::InternalType NumericSliderBase::AdjustDragDistanceInternal(const InternalType &start, const double &drag) const { - return start.toDouble() + drag; + return start.value() + drag; } -void NumericSliderBase::SetMinimumInternal(const QVariant &v) +void NumericSliderBase::SetMinimumInternal(const InternalType &v) { min_value_ = v; has_min_ = true; @@ -177,7 +177,7 @@ void NumericSliderBase::SetMinimumInternal(const QVariant &v) } } -void NumericSliderBase::SetMaximumInternal(const QVariant &v) +void NumericSliderBase::SetMaximumInternal(const InternalType &v) { max_value_ = v; has_max_ = true; @@ -188,14 +188,14 @@ void NumericSliderBase::SetMaximumInternal(const QVariant &v) } } -bool NumericSliderBase::ValueGreaterThan(const QVariant &lhs, const QVariant &rhs) const +bool NumericSliderBase::ValueGreaterThan(const InternalType &lhs, const InternalType &rhs) const { - return lhs.toDouble() > rhs.toDouble(); + return lhs.value() > rhs.value(); } -bool NumericSliderBase::ValueLessThan(const QVariant &lhs, const QVariant &rhs) const +bool NumericSliderBase::ValueLessThan(const InternalType &lhs, const InternalType &rhs) const { - return lhs.toDouble() < rhs.toDouble(); + return lhs.value() < rhs.value(); } bool NumericSliderBase::CanSetValue() const diff --git a/app/widget/slider/base/numericsliderbase.h b/app/widget/slider/base/numericsliderbase.h index 71f2557d1b..9407453b22 100644 --- a/app/widget/slider/base/numericsliderbase.h +++ b/app/widget/slider/base/numericsliderbase.h @@ -38,32 +38,32 @@ class NumericSliderBase : public SliderBase void SetDragMultiplier(const double& d); - void SetOffset(const QVariant& v); + void SetOffset(const InternalType& v); bool IsDragging() const; protected: - const QVariant& GetOffset() const + const InternalType& GetOffset() const { return offset_; } - virtual QVariant AdjustDragDistanceInternal(const QVariant &start, const double &drag) const; + virtual InternalType AdjustDragDistanceInternal(const InternalType &start, const double &drag) const; - void SetMinimumInternal(const QVariant& v); + void SetMinimumInternal(const InternalType& v); - void SetMaximumInternal(const QVariant& v); + void SetMaximumInternal(const InternalType& v); - virtual bool ValueGreaterThan(const QVariant& lhs, const QVariant& rhs) const; + virtual bool ValueGreaterThan(const InternalType& lhs, const InternalType& rhs) const; - virtual bool ValueLessThan(const QVariant& lhs, const QVariant& rhs) const; + virtual bool ValueLessThan(const InternalType& lhs, const InternalType& rhs) const; virtual bool CanSetValue() const override; private: bool UsingLadders() const; - virtual QVariant AdjustValue(const QVariant& value) const override; + virtual InternalType AdjustValue(const InternalType& value) const override; SliderLadder* drag_ladder_; @@ -72,16 +72,16 @@ class NumericSliderBase : public SliderBase bool dragged_; bool has_min_; - QVariant min_value_; + InternalType min_value_; bool has_max_; - QVariant max_value_; + InternalType max_value_; double dragged_diff_; - QVariant drag_start_value_; + InternalType drag_start_value_; - QVariant offset_; + InternalType offset_; double drag_multiplier_; diff --git a/app/widget/slider/base/sliderbase.cpp b/app/widget/slider/base/sliderbase.cpp index ef58ce8ed2..49d41865a5 100644 --- a/app/widget/slider/base/sliderbase.cpp +++ b/app/widget/slider/base/sliderbase.cpp @@ -71,12 +71,12 @@ void SliderBase::SetTristate() UpdateLabel(); } -const QVariant &SliderBase::GetValueInternal() const +const SliderBase::InternalType &SliderBase::GetValueInternal() const { return value_; } -void SliderBase::SetValueInternal(const QVariant &v) +void SliderBase::SetValueInternal(const InternalType &v) { if (!CanSetValue()) { return; @@ -90,7 +90,7 @@ void SliderBase::SetValueInternal(const QVariant &v) UpdateLabel(); } -void SliderBase::SetDefaultValue(const QVariant &v) +void SliderBase::SetDefaultValue(const InternalType &v) { default_value_ = v; } @@ -103,10 +103,10 @@ void SliderBase::changeEvent(QEvent *e) super::changeEvent(e); } -bool SliderBase::GetLabelSubstitution(const QVariant &v, QString *out) const +bool SliderBase::GetLabelSubstitution(const InternalType &v, QString *out) const { for (auto it=label_substitutions_.constBegin(); it!=label_substitutions_.constEnd(); it++) { - if (it->first == v) { + if (Equals(it->first, v)) { *out = it->second; return true; } @@ -130,7 +130,7 @@ void SliderBase::UpdateLabel() label_->setText(s); } -QVariant SliderBase::AdjustValue(const QVariant &value) const +SliderBase::InternalType SliderBase::AdjustValue(const InternalType &value) const { return value; } @@ -140,7 +140,7 @@ bool SliderBase::CanSetValue() const return true; } -void SliderBase::ValueSignalEvent(const QVariant &value) +void SliderBase::ValueSignalEvent(const InternalType &value) { Q_UNUSED(value) } @@ -162,7 +162,7 @@ void SliderBase::ShowEditor() void SliderBase::LineEditConfirmed() { bool is_valid = true; - QVariant test_val = StringToValue(editor_->text(), &is_valid); + InternalType test_val = StringToValue(editor_->text(), &is_valid); // Ensure editor doesn't signal that the focus is lost editor_->blockSignals(true); @@ -241,7 +241,7 @@ QString SliderBase::GetFormattedValueToString() const return GetFormattedValueToString(GetValueInternal()); } -QString SliderBase::GetFormattedValueToString(const QVariant &v) const +QString SliderBase::GetFormattedValueToString(const InternalType &v) const { if (format_plural_) { return tr(GetFormat().toUtf8().constData(), nullptr, v.toInt()); diff --git a/app/widget/slider/base/sliderbase.h b/app/widget/slider/base/sliderbase.h index 9828bb8489..fcd7c22f42 100644 --- a/app/widget/slider/base/sliderbase.h +++ b/app/widget/slider/base/sliderbase.h @@ -23,6 +23,7 @@ #include +#include "node/value.h" #include "sliderlabel.h" #include "sliderladder.h" #include "widget/focusablelineedit/focusablelineedit.h" @@ -33,6 +34,8 @@ class SliderBase : public QStackedWidget { Q_OBJECT public: + using InternalType = value_t; //QVariant; + SliderBase(QWidget* parent = nullptr); void SetAlignment(Qt::Alignment alignment); @@ -45,11 +48,11 @@ class SliderBase : public QStackedWidget bool IsFormatPlural() const; - void SetDefaultValue(const QVariant& v); + void SetDefaultValue(const InternalType& v); - QString GetFormattedValueToString(const QVariant& v) const; + QString GetFormattedValueToString(const InternalType& v) const; - void InsertLabelSubstitution(const QVariant &value, const QString &label) + void InsertLabelSubstitution(const InternalType &value, const QString &label) { label_substitutions_.append({value, label}); UpdateLabel(); @@ -60,6 +63,8 @@ class SliderBase : public QStackedWidget label_->SetColor(c); } + const InternalType& GetValueInternal() const; + public slots: void ShowEditor(); @@ -67,9 +72,7 @@ protected slots: void UpdateLabel(); protected: - const QVariant& GetValueInternal() const; - - void SetValueInternal(const QVariant& v); + void SetValueInternal(const InternalType& v); QString GetFormat() const; @@ -77,27 +80,29 @@ protected slots: SliderLabel* label() { return label_; } - virtual QString ValueToString(const QVariant &v) const = 0; + virtual QString ValueToString(const InternalType &v) const = 0; - virtual QVariant StringToValue(const QString& s, bool* ok) const = 0; + virtual InternalType StringToValue(const QString& s, bool* ok) const = 0; - virtual QVariant AdjustValue(const QVariant& value) const; + virtual InternalType AdjustValue(const InternalType& value) const; virtual bool CanSetValue() const; - virtual void ValueSignalEvent(const QVariant& value) = 0; + virtual void ValueSignalEvent(const InternalType& value) = 0; virtual void changeEvent(QEvent* e) override; + virtual bool Equals(const InternalType &a, const InternalType &b) const = 0; + private: - bool GetLabelSubstitution(const QVariant &v, QString *out) const; + bool GetLabelSubstitution(const InternalType &v, QString *out) const; SliderLabel* label_; FocusableLineEdit* editor_; - QVariant value_; - QVariant default_value_; + InternalType value_; + InternalType default_value_; bool tristate_; @@ -105,7 +110,7 @@ protected slots: bool format_plural_; - QVector > label_substitutions_; + QVector > label_substitutions_; private slots: void LineEditConfirmed(); diff --git a/app/widget/slider/floatslider.cpp b/app/widget/slider/floatslider.cpp index b03995170b..d7640e41f3 100644 --- a/app/widget/slider/floatslider.cpp +++ b/app/widget/slider/floatslider.cpp @@ -38,7 +38,7 @@ FloatSlider::FloatSlider(QWidget *parent) : double FloatSlider::GetValue() const { - return GetValueInternal().toDouble(); + return GetValueInternal().value(); } void FloatSlider::SetValue(const double &d) @@ -120,12 +120,15 @@ QString FloatSlider::ValueToString(double val, FloatSlider::DisplayType display, return FloatToString(TransformValueToDisplay(val, display), decimal_places, autotrim_decimal_places); } -QString FloatSlider::ValueToString(const QVariant &v) const +QString FloatSlider::ValueToString(const InternalType &v) const { - return ValueToString(v.toDouble() + GetOffset().toDouble(), display_type_, GetDecimalPlaces(), GetAutoTrimDecimalPlaces()); + double d = v.converted(TYPE_DOUBLE).value(); + double o = GetOffset().value(); + + return ValueToString(d + o, display_type_, GetDecimalPlaces(), GetAutoTrimDecimalPlaces()); } -QVariant FloatSlider::StringToValue(const QString &s, bool *ok) const +FloatSlider::InternalType FloatSlider::StringToValue(const QString &s, bool *ok) const { bool valid; double val = s.toDouble(&valid); @@ -141,10 +144,10 @@ QVariant FloatSlider::StringToValue(const QString &s, bool *ok) const } // Return un-offset value - return val - GetOffset().toDouble(); + return val - GetOffset().value(); } -QVariant FloatSlider::AdjustDragDistanceInternal(const QVariant &start, const double &drag) const +FloatSlider::InternalType FloatSlider::AdjustDragDistanceInternal(const InternalType &start, const double &drag) const { switch (display_type_) { case kNormal: @@ -152,7 +155,7 @@ QVariant FloatSlider::AdjustDragDistanceInternal(const QVariant &start, const do break; case kDecibel: { - double current_db = Decibel::fromLinear(start.toDouble()); + double current_db = Decibel::fromLinear(start.value()); current_db += drag; double adjusted_linear = Decibel::toLinear(current_db); @@ -165,9 +168,14 @@ QVariant FloatSlider::AdjustDragDistanceInternal(const QVariant &start, const do return super::AdjustDragDistanceInternal(start, drag); } -void FloatSlider::ValueSignalEvent(const QVariant &value) +void FloatSlider::ValueSignalEvent(const InternalType &value) +{ + emit ValueChanged(value.value()); +} + +bool FloatSlider::Equals(const InternalType &a, const InternalType &b) const { - emit ValueChanged(value.toDouble()); + return qFuzzyCompare(a.value(), b.value()); } } diff --git a/app/widget/slider/floatslider.h b/app/widget/slider/floatslider.h index eee55db410..94bef4b8b3 100644 --- a/app/widget/slider/floatslider.h +++ b/app/widget/slider/floatslider.h @@ -56,13 +56,15 @@ class FloatSlider : public DecimalSliderBase static QString ValueToString(double val, DisplayType display, int decimal_places, bool autotrim_decimal_places); protected: - virtual QString ValueToString(const QVariant& v) const override; + virtual QString ValueToString(const InternalType& v) const override; - virtual QVariant StringToValue(const QString& s, bool* ok) const override; + virtual InternalType StringToValue(const QString& s, bool* ok) const override; - virtual QVariant AdjustDragDistanceInternal(const QVariant &start, const double &drag) const override; + virtual InternalType AdjustDragDistanceInternal(const InternalType &start, const double &drag) const override; - virtual void ValueSignalEvent(const QVariant &value) override; + virtual void ValueSignalEvent(const InternalType &value) override; + + virtual bool Equals(const InternalType &a, const InternalType &b) const override; signals: void ValueChanged(double); diff --git a/app/widget/slider/integerslider.cpp b/app/widget/slider/integerslider.cpp index 1ce992b905..3f71d4f458 100644 --- a/app/widget/slider/integerslider.cpp +++ b/app/widget/slider/integerslider.cpp @@ -32,35 +32,35 @@ IntegerSlider::IntegerSlider(QWidget* parent) : int64_t IntegerSlider::GetValue() { - return GetValueInternal().toLongLong(); + return GetValueInternal().value(); } void IntegerSlider::SetValue(const int64_t &v) { - SetValueInternal(QVariant::fromValue(v)); + SetValueInternal(v); } void IntegerSlider::SetMinimum(const int64_t &d) { - SetMinimumInternal(QVariant::fromValue(d)); + SetMinimumInternal(d); } void IntegerSlider::SetMaximum(const int64_t &d) { - SetMaximumInternal(QVariant::fromValue(d)); + SetMaximumInternal(d); } void IntegerSlider::SetDefaultValue(const int64_t &d) { - super::SetDefaultValue(QVariant::fromValue(d)); + super::SetDefaultValue(d); } -QString IntegerSlider::ValueToString(const QVariant &v) const +QString IntegerSlider::ValueToString(const InternalType &v) const { - return QString::number(v.toLongLong() + GetOffset().toLongLong()); + return QString::number(v.value() + GetOffset().value()); } -QVariant IntegerSlider::StringToValue(const QString &s, bool *ok) const +IntegerSlider::InternalType IntegerSlider::StringToValue(const QString &s, bool *ok) const { bool valid; @@ -71,24 +71,29 @@ QVariant IntegerSlider::StringToValue(const QString &s, bool *ok) const *ok = valid; } - decimal_val -= GetOffset().toLongLong(); + decimal_val -= GetOffset().value(); if (valid) { // But for an integer, we round it return qRound(decimal_val); } - return QVariant(); + return InternalType(); } -void IntegerSlider::ValueSignalEvent(const QVariant &value) +void IntegerSlider::ValueSignalEvent(const InternalType &value) { - emit ValueChanged(value.toInt()); + emit ValueChanged(value.value()); } -QVariant IntegerSlider::AdjustDragDistanceInternal(const QVariant &start, const double &drag) const +IntegerSlider::InternalType IntegerSlider::AdjustDragDistanceInternal(const InternalType &start, const double &drag) const { - return qRound64(super::AdjustDragDistanceInternal(start, drag).toDouble()); + return int64_t(super::AdjustDragDistanceInternal(start, drag).value()); +} + +bool IntegerSlider::Equals(const InternalType &a, const InternalType &b) const +{ + return a.value() == b.value(); } } diff --git a/app/widget/slider/integerslider.h b/app/widget/slider/integerslider.h index 452f41a68a..dce8a4d77c 100644 --- a/app/widget/slider/integerslider.h +++ b/app/widget/slider/integerslider.h @@ -42,13 +42,15 @@ class IntegerSlider : public NumericSliderBase void SetDefaultValue(const int64_t& d); protected: - virtual QString ValueToString(const QVariant& v) const override; + virtual QString ValueToString(const InternalType& v) const override; - virtual QVariant StringToValue(const QString& s, bool* ok) const override; + virtual InternalType StringToValue(const QString& s, bool* ok) const override; - virtual void ValueSignalEvent(const QVariant &value) override; + virtual void ValueSignalEvent(const InternalType &value) override; - virtual QVariant AdjustDragDistanceInternal(const QVariant &start, const double &drag) const override; + virtual InternalType AdjustDragDistanceInternal(const InternalType &start, const double &drag) const override; + + virtual bool Equals(const InternalType &a, const InternalType &b) const override; signals: void ValueChanged(int64_t); diff --git a/app/widget/slider/rationalslider.cpp b/app/widget/slider/rationalslider.cpp index bcad1f4932..1335748c56 100644 --- a/app/widget/slider/rationalslider.cpp +++ b/app/widget/slider/rationalslider.cpp @@ -20,6 +20,7 @@ #include "rationalslider.h" +#include "common/qtutils.h" #include "core.h" #include "widget/menu/menu.h" #include "widget/menu/menushared.h" @@ -47,22 +48,22 @@ rational RationalSlider::GetValue() void RationalSlider::SetValue(const rational &d) { - SetValueInternal(QVariant::fromValue(d)); + SetValueInternal(d); } void RationalSlider::SetDefaultValue(const rational &r) { - super::SetDefaultValue(QVariant::fromValue(r)); + super::SetDefaultValue(r); } void RationalSlider::SetMinimum(const rational &d) { - SetMinimumInternal(QVariant::fromValue(d)); + SetMinimumInternal(d); } void RationalSlider::SetMaximum(const rational &d) { - SetMaximumInternal(QVariant::fromValue(d)); + SetMaximumInternal(d); } void RationalSlider::SetTimebase(const rational &timebase) @@ -95,7 +96,7 @@ void RationalSlider::DisableDisplayType(RationalSlider::DisplayType type) disabled_.append(type); } -QString RationalSlider::ValueToString(const QVariant &v) const +QString RationalSlider::ValueToString(const InternalType &v) const { rational r = v.value(); @@ -110,14 +111,13 @@ QString RationalSlider::ValueToString(const QVariant &v) const case kFloat: return FloatToString(val, GetDecimalPlaces(), GetAutoTrimDecimalPlaces()); case kRational: + default: return QString::fromStdString(v.value().toString()); } - - return v.toString(); } } -QVariant RationalSlider::StringToValue(const QString &s, bool *ok) const +RationalSlider::InternalType RationalSlider::StringToValue(const QString &s, bool *ok) const { rational r; *ok = false; @@ -145,31 +145,35 @@ QVariant RationalSlider::StringToValue(const QString &s, bool *ok) const break; } - //return QVariant::fromValue(r - GetOffset().value()); - return QVariant::fromValue(r); + return r - GetOffset().value(); } -QVariant RationalSlider::AdjustDragDistanceInternal(const QVariant &start, const double &drag) const +RationalSlider::InternalType RationalSlider::AdjustDragDistanceInternal(const InternalType &start, const double &drag) const { // Assume we want smallest increment to be timebase or 1 frame - return QVariant::fromValue(start.value() + rational::fromDouble(drag)*timebase_); + return start.value() + rational::fromDouble(drag)*timebase_; } -void RationalSlider::ValueSignalEvent(const QVariant &v) +void RationalSlider::ValueSignalEvent(const InternalType &v) { emit ValueChanged(v.value()); } -bool RationalSlider::ValueGreaterThan(const QVariant &lhs, const QVariant &rhs) const +bool RationalSlider::ValueGreaterThan(const InternalType &lhs, const InternalType &rhs) const { return lhs.value() > rhs.value(); } -bool RationalSlider::ValueLessThan(const QVariant &lhs, const QVariant &rhs) const +bool RationalSlider::ValueLessThan(const InternalType &lhs, const InternalType &rhs) const { return lhs.value() < rhs.value(); } +bool RationalSlider::Equals(const InternalType &a, const InternalType &b) const +{ + return a.value() == b.value(); +} + void RationalSlider::ShowDisplayTypeMenu() { Menu m(this); diff --git a/app/widget/slider/rationalslider.h b/app/widget/slider/rationalslider.h index 4da25b96ac..5f817656a3 100644 --- a/app/widget/slider/rationalslider.h +++ b/app/widget/slider/rationalslider.h @@ -103,17 +103,19 @@ public slots: void SetValue(const rational& d); protected: - virtual QString ValueToString(const QVariant& v) const override; + virtual QString ValueToString(const InternalType& v) const override; - virtual QVariant StringToValue(const QString& s, bool* ok) const override; + virtual InternalType StringToValue(const QString& s, bool* ok) const override; - virtual QVariant AdjustDragDistanceInternal(const QVariant &start, const double &drag) const override; + virtual InternalType AdjustDragDistanceInternal(const InternalType &start, const double &drag) const override; - virtual void ValueSignalEvent(const QVariant& v) override; + virtual void ValueSignalEvent(const InternalType& v) override; - virtual bool ValueGreaterThan(const QVariant& lhs, const QVariant& rhs) const override; + virtual bool ValueGreaterThan(const InternalType& lhs, const InternalType& rhs) const override; - virtual bool ValueLessThan(const QVariant& lhs, const QVariant& rhs) const override; + virtual bool ValueLessThan(const InternalType& lhs, const InternalType& rhs) const override; + + virtual bool Equals(const InternalType &a, const InternalType &b) const override; signals: void ValueChanged(rational); diff --git a/app/widget/slider/stringslider.cpp b/app/widget/slider/stringslider.cpp index fcac6cf820..725d9641a3 100644 --- a/app/widget/slider/stringslider.cpp +++ b/app/widget/slider/stringslider.cpp @@ -34,7 +34,7 @@ StringSlider::StringSlider(QWidget* parent) : QString StringSlider::GetValue() const { - return GetValueInternal().toString(); + return GetValueInternal().value(); } void StringSlider::SetValue(const QString &v) @@ -47,21 +47,26 @@ void StringSlider::SetDefaultValue(const QString &v) super::SetDefaultValue(v); } -QString StringSlider::ValueToString(const QVariant &v) const +QString StringSlider::ValueToString(const InternalType &v) const { - QString vstr = v.toString(); + QString vstr = v.value(); return (vstr.isEmpty()) ? tr("(none)") : vstr; } -QVariant StringSlider::StringToValue(const QString &s, bool *ok) const +StringSlider::InternalType StringSlider::StringToValue(const QString &s, bool *ok) const { *ok = true; return s; } -void StringSlider::ValueSignalEvent(const QVariant &value) +void StringSlider::ValueSignalEvent(const InternalType &value) { - emit ValueChanged(value.toString()); + emit ValueChanged(value.value()); +} + +bool StringSlider::Equals(const InternalType &a, const InternalType &b) const +{ + return a.value() == b.value(); } } diff --git a/app/widget/slider/stringslider.h b/app/widget/slider/stringslider.h index 0e61c48f51..552e5e1d37 100644 --- a/app/widget/slider/stringslider.h +++ b/app/widget/slider/stringslider.h @@ -43,11 +43,13 @@ class StringSlider : public SliderBase void ValueChanged(const QString& str); protected: - virtual QString ValueToString(const QVariant& value) const override; + virtual QString ValueToString(const InternalType& value) const override; - virtual QVariant StringToValue(const QString &s, bool *ok) const override; + virtual InternalType StringToValue(const QString &s, bool *ok) const override; - virtual void ValueSignalEvent(const QVariant &value) override; + virtual void ValueSignalEvent(const InternalType &value) override; + + virtual bool Equals(const InternalType &a, const InternalType &b) const override; }; diff --git a/app/widget/standardcombos/frameratecombobox.h b/app/widget/standardcombos/frameratecombobox.h index c0d63d24df..d0ca9cd603 100644 --- a/app/widget/standardcombos/frameratecombobox.h +++ b/app/widget/standardcombos/frameratecombobox.h @@ -27,6 +27,7 @@ #include #include +#include "common/qtutils.h" #include "render/videoparams.h" namespace olive { diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 8d6101655c..36cfab5afe 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -1319,9 +1319,9 @@ void TimelineWidget::ShowContextMenu() Menu *thumbnail_menu = new Menu(tr("Show Thumbnails"), &menu); menu.addMenu(thumbnail_menu); - thumbnail_menu->AddActionWithData(tr("Disabled"), Timeline::kThumbnailOff, OLIVE_CONFIG("TimelineThumbnailMode")); - thumbnail_menu->AddActionWithData(tr("Only At In Points"), Timeline::kThumbnailInOut, OLIVE_CONFIG("TimelineThumbnailMode")); - thumbnail_menu->AddActionWithData(tr("Enabled"), Timeline::kThumbnailOn, OLIVE_CONFIG("TimelineThumbnailMode")); + thumbnail_menu->AddActionWithData(tr("Disabled"), Timeline::kThumbnailOff, Timeline::ThumbnailMode(OLIVE_CONFIG("TimelineThumbnailMode").toInt())); + thumbnail_menu->AddActionWithData(tr("Only At In Points"), Timeline::kThumbnailInOut, Timeline::ThumbnailMode(OLIVE_CONFIG("TimelineThumbnailMode").toInt())); + thumbnail_menu->AddActionWithData(tr("Enabled"), Timeline::kThumbnailOn, Timeline::ThumbnailMode(OLIVE_CONFIG("TimelineThumbnailMode").toInt())); connect(thumbnail_menu, &Menu::triggered, this, &TimelineWidget::SetViewThumbnailsEnabled); } @@ -1390,7 +1390,7 @@ void TimelineWidget::SetViewWaveformsEnabled(bool e) void TimelineWidget::SetViewThumbnailsEnabled(QAction *action) { - OLIVE_CONFIG("TimelineThumbnailMode") = action->data(); + OLIVE_CONFIG("TimelineThumbnailMode") = action->data().toInt(); UpdateViewports(); } diff --git a/app/widget/timelinewidget/view/timelineview.cpp b/app/widget/timelinewidget/view/timelineview.cpp index 306a82f21a..766e460824 100644 --- a/app/widget/timelinewidget/view/timelineview.cpp +++ b/app/widget/timelinewidget/view/timelineview.cpp @@ -484,7 +484,7 @@ void TimelineView::DrawBlock(QPainter *painter, bool foreground, Block *block, q painter->setRenderHint(QPainter::SmoothPixmapTransform); painter->setClipRect(preview_rect); - if (OLIVE_CONFIG("TimelineThumbnailMode") == Timeline::kThumbnailOn) { + if (OLIVE_CONFIG("TimelineThumbnailMode").toInt() == Timeline::kThumbnailOn) { Sequence *s = clip->track()->sequence(); int width = s->GetVideoParams().width(); diff --git a/app/widget/timelinewidget/view/timelineviewghostitem.h b/app/widget/timelinewidget/view/timelineviewghostitem.h index 40a936ab45..0b17238b02 100644 --- a/app/widget/timelinewidget/view/timelineviewghostitem.h +++ b/app/widget/timelinewidget/view/timelineviewghostitem.h @@ -23,6 +23,7 @@ #include +#include "common/qtutils.h" #include "node/block/clip/clip.h" #include "node/block/transition/transition.h" #include "node/output/track/track.h" diff --git a/app/widget/viewer/footageviewer.cpp b/app/widget/viewer/footageviewer.cpp index 8bcc2b9b50..c33980dfcf 100644 --- a/app/widget/viewer/footageviewer.cpp +++ b/app/widget/viewer/footageviewer.cpp @@ -64,9 +64,6 @@ void FootageViewerWidget::StartFootageDragInternal(bool enable_video, bool enabl return; } - QDrag* drag = new QDrag(this); - QMimeData* mimedata = new QMimeData(); - QByteArray encoded_data; QDataStream data_stream(&encoded_data, QIODevice::WriteOnly); @@ -86,6 +83,9 @@ void FootageViewerWidget::StartFootageDragInternal(bool enable_video, bool enabl } if (!streams.isEmpty()) { + QDrag* drag = new QDrag(this); + QMimeData* mimedata = new QMimeData(); + data_stream << streams << reinterpret_cast(GetConnectedNode()); mimedata->setData(Project::kItemMimeType, encoded_data); diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index fea0ad0fad..11df290184 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -515,7 +515,7 @@ void ViewerWidget::UpdateAudioProcessor() ap.set_format(ViewerOutput::kDefaultSampleFormat); AudioParams packed(OLIVE_CONFIG("AudioOutputSampleRate").toInt(), - OLIVE_CONFIG("AudioOutputChannelLayout").toULongLong(), + OLIVE_CONFIG("AudioOutputChannelLayout").toInt(), SampleFormat::from_string(OLIVE_CONFIG("AudioOutputSampleFormat").toString().toStdString())); audio_processor_.Open(ap, packed, (playback_speed_ == 0) ? 1 : std::abs(playback_speed_)); @@ -694,12 +694,12 @@ MultiCamNode *ViewerWidget::DetectMulticamNode(const rational &time) } } } - } - if (multicam) { - multicam_panel_->SetMulticamNode(GetConnectedNode(), multicam, clip, time); - } else { - multicam_panel_->SetMulticamNode(nullptr, nullptr, nullptr, time); + if (multicam) { + multicam_panel_->SetMulticamNode(GetConnectedNode(), multicam, clip, time); + } else { + multicam_panel_->SetMulticamNode(nullptr, nullptr, nullptr, time); + } } return multicam; @@ -1225,7 +1225,7 @@ void ViewerWidget::ContextMenuSetPlaybackRes(QAction *action) auto vp = GetConnectedNode()->GetVideoParams(); vp.set_divider(div); - auto c = new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(NodeInput(GetConnectedNode(), ViewerOutput::kVideoParamsInput, 0)), QVariant::fromValue(vp)); + auto c = new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(NodeInput(GetConnectedNode(), ViewerOutput::kVideoParamsInput, 0)), vp); Core::instance()->undo_stack()->push(c, tr("Changed Playback Resolution")); } @@ -1568,7 +1568,7 @@ void ViewerWidget::Play(bool in_to_out_only) ); AudioParams ap(OLIVE_CONFIG("AudioRecordingSampleRate").toInt(), - OLIVE_CONFIG("AudioRecordingChannelLayout").toULongLong(), + OLIVE_CONFIG("AudioRecordingChannelLayout").toInt(), SampleFormat::from_string(OLIVE_CONFIG("AudioRecordingSampleFormat").toString().toStdString())); EncodingParams encode_param; diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index 26c076bba2..c9a02dd9b2 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -422,8 +422,8 @@ void ViewerDisplayWidget::OnPaint() } ShaderJob job; - job.Insert(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, QVector2D(texture_to_draw->width(), texture_to_draw->height()))); - job.Insert(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture_to_draw))); + job.Insert(QStringLiteral("resolution_in"), QVector2D(texture_to_draw->width(), texture_to_draw->height())); + job.Insert(QStringLiteral("ove_maintex"), texture_to_draw); renderer()->BlitToTexture(deinterlace_shader_, job, deinterlace_texture_.get()); @@ -1257,8 +1257,8 @@ void ViewerDisplayWidget::DrawBlank(const VideoParams &device_params) } ShaderJob job; - job.Insert(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, combined_matrix_flipped_)); - job.Insert(QStringLiteral("ove_cropmatrix"), NodeValue(NodeValue::kMatrix, crop_matrix_)); + job.Insert(QStringLiteral("ove_mvpmat"), combined_matrix_flipped_); + job.Insert(QStringLiteral("ove_cropmatrix"), crop_matrix_); renderer()->Blit(blank_shader_, job, device_params, false); } From 8153ce7297f0824c3a71be5bad5519a8df13a57f Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Mon, 3 Apr 2023 18:09:40 -0700 Subject: [PATCH 20/71] corrected various straggling issues --- app/codec/ffmpeg/ffmpegdecoder.cpp | 2 -- app/node/audio/volume/volume.cpp | 3 ++- app/node/node.cpp | 2 +- app/node/value.cpp | 2 -- app/node/value.h | 2 +- app/render/renderprocessor.cpp | 15 ++++++--------- .../nodeparamview/nodeparamviewwidgetbridge.cpp | 10 +++++----- .../paramwidget/abstractparamwidget.h | 2 ++ .../paramwidget/floatsliderparamwidget.cpp | 7 +++++++ .../paramwidget/floatsliderparamwidget.h | 2 ++ .../paramwidget/integersliderparamwidget.cpp | 7 +++++++ .../paramwidget/integersliderparamwidget.h | 2 ++ .../paramwidget/numericsliderparamwidget.h | 3 --- .../paramwidget/rationalsliderparamwidget.cpp | 7 +++++++ .../paramwidget/rationalsliderparamwidget.h | 2 ++ app/widget/slider/integerslider.cpp | 10 ++++++++++ app/widget/slider/integerslider.h | 4 ++++ 17 files changed, 58 insertions(+), 24 deletions(-) diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index 4289ab389b..fdfae1c570 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -179,8 +179,6 @@ TexturePtr FFmpegDecoder::ProcessFrameIntoTexture(AVFramePtr f, const RetrieveVi job.Insert(QStringLiteral("yuv_cgv"), yuv_coeffs[3]/65536.0); job.Insert(QStringLiteral("yuv_cbu"), yuv_coeffs[1]/65536.0); - qDebug() << "sending job with keys:" << job.GetValues().keys(); - tex = p.renderer->CreateTexture(vp); p.renderer->BlitToTexture(Yuv2RgbShader, job, tex.get(), false); break; diff --git a/app/node/audio/volume/volume.cpp b/app/node/audio/volume/volume.cpp index d5e7547731..639daa807c 100644 --- a/app/node/audio/volume/volume.cpp +++ b/app/node/audio/volume/volume.cpp @@ -63,7 +63,8 @@ QString VolumeNode::Description() const value_t VolumeNode::Value(const ValueParams &p) const { - return ValueInternal(kOpMultiply, kPairSampleNumber, kSamplesInput, GetInputValue(p, kSamplesInput), kVolumeInput, GetInputValue(p, kVolumeInput), p); + return GetInputValue(p, kSamplesInput); + //return ValueInternal(kOpMultiply, kPairSampleNumber, kSamplesInput, GetInputValue(p, kSamplesInput), kVolumeInput, GetInputValue(p, kVolumeInput), p); } void VolumeNode::Retranslate() diff --git a/app/node/node.cpp b/app/node/node.cpp index 60a121b15c..4c97a4f214 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -418,7 +418,7 @@ value_t Node::GetValueAtTime(const QString &input, const rational &time, int ele value_t v(GetInputDataType(input), GetNumberOfKeyframeTracks(input)); for (size_t i = 0; i < v.data().size(); i++) { - v.data()[0] = GetSplitValueAtTimeOnTrack(input, time, i, element); + v.data()[i] = GetSplitValueAtTimeOnTrack(input, time, i, element); } return v; diff --git a/app/node/value.cpp b/app/node/value.cpp index 2852833977..c7920dd50b 100644 --- a/app/node/value.cpp +++ b/app/node/value.cpp @@ -93,8 +93,6 @@ value_t value_t::converted(type_t to) const v.data_[0] = c(data_[0]); } - qDebug() << "converted" << this->type().toString() << "to" << to.toString(); - return v; } diff --git a/app/node/value.h b/app/node/value.h index a6ee97e2fa..f40359590e 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -100,7 +100,7 @@ class value_t T get(bool *ok = nullptr) const { if (data_.has_value()) { - qDebug() << "getting any type" << data_.type().name() << "attempting cast to" << typeid(T).name(); + //qDebug() << "getting any type" << data_.type().name() << "attempting cast to" << typeid(T).name(); return std::any_cast(data_); } else { return T(); diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 484bf25b32..a8d73897b2 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -634,9 +634,7 @@ void RenderProcessor::ResolveJobs(value_t &val) } else if (val.type() == TYPE_SAMPLES) { - qDebug() << "disabled handling of samplejobs and audio footagejobs..."; - /*if (val.canConvert()) { - + try { SampleJob job = val.value(); for (auto it=job.GetValues().begin(); it!=job.GetValues().end(); it++) { @@ -647,16 +645,15 @@ void RenderProcessor::ResolveJobs(value_t &val) SampleBuffer output_buffer = CreateSampleBuffer(job.audio_params(), job.sample_count()); ProcessSamples(output_buffer, job); - val.set_value(QVariant::fromValue(output_buffer)); - - } else if (val.canConvert()) { + val = value_t(TYPE_SAMPLES, output_buffer); + } catch (std::bad_any_cast &e) {} + try { FootageJob job = val.value(); SampleBuffer buffer = CreateSampleBuffer(GetCacheAudioParams(), job.time().length()); ProcessAudioFootage(buffer, &job, job.time()); - val.set_value(buffer); - - }*/ + val = value_t(TYPE_SAMPLES, buffer); + } catch (std::bad_any_cast &e) {} } } diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index fafd7f0230..1123fc038f 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -120,6 +120,11 @@ void NodeParamViewWidgetBridge::CreateWidgets() // Whatever we created, initialize it widget_->Initialize(parent, GetChannelCount()); + value_t def = GetInnerInput().GetDefaultValue(); + if (def.isValid()) { + widget_->SetDefaultValue(def); + } + // Install event filter to disable widgets picking up scroll events foreach (QWidget* w, widget_->GetWidgets()) { w->installEventFilter(&scroll_filter_); @@ -133,8 +138,6 @@ void NodeParamViewWidgetBridge::CreateWidgets() UpdateProperties(); UpdateWidgetValues(); - } else { - qWarning() << "Failed to find matching parameter widget for" << GetInnerInput().node()->id() << GetInnerInput().input(); } } @@ -169,9 +172,6 @@ void NodeParamViewWidgetBridge::ProcessSlider(NumericSliderBase *slider, size_t } else if (dragger_.IsStarted()) { - // We were dragging and just stopped - dragger_.Drag(value); - MultiUndoCommand *command = new MultiUndoCommand(); dragger_.End(command); Core::instance()->undo_stack()->push(command, GetCommandName()); diff --git a/app/widget/nodeparamview/paramwidget/abstractparamwidget.h b/app/widget/nodeparamview/paramwidget/abstractparamwidget.h index 9ed906cf3b..da9dd80445 100644 --- a/app/widget/nodeparamview/paramwidget/abstractparamwidget.h +++ b/app/widget/nodeparamview/paramwidget/abstractparamwidget.h @@ -42,6 +42,8 @@ class AbstractParamWidget : public QObject virtual void SetValue(const value_t &val) = 0; + virtual void SetDefaultValue(const value_t &val) {} + virtual void SetProperty(const QString &key, const value_t &value); virtual void SetTimebase(const rational &timebase){} diff --git a/app/widget/nodeparamview/paramwidget/floatsliderparamwidget.cpp b/app/widget/nodeparamview/paramwidget/floatsliderparamwidget.cpp index fc8c306bc6..0b6e73c8ca 100644 --- a/app/widget/nodeparamview/paramwidget/floatsliderparamwidget.cpp +++ b/app/widget/nodeparamview/paramwidget/floatsliderparamwidget.cpp @@ -43,6 +43,13 @@ void FloatSliderParamWidget::SetValue(const value_t &val) } } +void FloatSliderParamWidget::SetDefaultValue(const value_t &val) +{ + for (size_t i = 0; i < val.size() && i < GetWidgets().size(); i++) { + static_cast(GetWidgets().at(i))->SetDefaultValue(val.value(i)); + } +} + void FloatSliderParamWidget::SetProperty(const QString &key, const value_t &val) { if (key == QStringLiteral("min")) { diff --git a/app/widget/nodeparamview/paramwidget/floatsliderparamwidget.h b/app/widget/nodeparamview/paramwidget/floatsliderparamwidget.h index ebe6a26332..78ca46f183 100644 --- a/app/widget/nodeparamview/paramwidget/floatsliderparamwidget.h +++ b/app/widget/nodeparamview/paramwidget/floatsliderparamwidget.h @@ -35,6 +35,8 @@ class FloatSliderParamWidget : public NumericSliderParamWidget virtual void SetValue(const value_t &val) override; + virtual void SetDefaultValue(const value_t &val) override; + virtual void SetProperty(const QString &key, const value_t &val) override; }; diff --git a/app/widget/nodeparamview/paramwidget/integersliderparamwidget.cpp b/app/widget/nodeparamview/paramwidget/integersliderparamwidget.cpp index 79abab0e41..ea26849a05 100644 --- a/app/widget/nodeparamview/paramwidget/integersliderparamwidget.cpp +++ b/app/widget/nodeparamview/paramwidget/integersliderparamwidget.cpp @@ -43,6 +43,13 @@ void IntegerSliderParamWidget::SetValue(const value_t &val) } } +void IntegerSliderParamWidget::SetDefaultValue(const value_t &val) +{ + for (size_t i = 0; i < val.size() && i < GetWidgets().size(); i++) { + static_cast(GetWidgets().at(i))->SetDefaultValue(val.value(i)); + } +} + void IntegerSliderParamWidget::SetProperty(const QString &key, const value_t &val) { if (key == QStringLiteral("min")) { diff --git a/app/widget/nodeparamview/paramwidget/integersliderparamwidget.h b/app/widget/nodeparamview/paramwidget/integersliderparamwidget.h index 4a351ee58e..18b4e3b644 100644 --- a/app/widget/nodeparamview/paramwidget/integersliderparamwidget.h +++ b/app/widget/nodeparamview/paramwidget/integersliderparamwidget.h @@ -35,6 +35,8 @@ class IntegerSliderParamWidget : public NumericSliderParamWidget virtual void SetValue(const value_t &val) override; + virtual void SetDefaultValue(const value_t &val) override; + virtual void SetProperty(const QString &key, const value_t &val) override; }; diff --git a/app/widget/nodeparamview/paramwidget/numericsliderparamwidget.h b/app/widget/nodeparamview/paramwidget/numericsliderparamwidget.h index 7084504f07..3dc72dab81 100644 --- a/app/widget/nodeparamview/paramwidget/numericsliderparamwidget.h +++ b/app/widget/nodeparamview/paramwidget/numericsliderparamwidget.h @@ -41,9 +41,6 @@ class NumericSliderParamWidget : public AbstractParamWidget for (size_t i = 0; i < channels; i++) { T *s = new T(parent); - qDebug() << "no default value set"; - //s->SetDefaultValue(GetDefaultValue().at(i)); - s->SetLadderElementCount(2); // HACK: Force some spacing between sliders diff --git a/app/widget/nodeparamview/paramwidget/rationalsliderparamwidget.cpp b/app/widget/nodeparamview/paramwidget/rationalsliderparamwidget.cpp index fa1957fa73..95d36ad4a5 100644 --- a/app/widget/nodeparamview/paramwidget/rationalsliderparamwidget.cpp +++ b/app/widget/nodeparamview/paramwidget/rationalsliderparamwidget.cpp @@ -44,6 +44,13 @@ void RationalSliderParamWidget::SetValue(const value_t &val) } } +void RationalSliderParamWidget::SetDefaultValue(const value_t &val) +{ + for (size_t i = 0; i < val.size() && i < GetWidgets().size(); i++) { + static_cast(GetWidgets().at(i))->SetDefaultValue(val.value(i)); + } +} + void RationalSliderParamWidget::SetProperty(const QString &key, const value_t &val) { if (key == QStringLiteral("view")) { diff --git a/app/widget/nodeparamview/paramwidget/rationalsliderparamwidget.h b/app/widget/nodeparamview/paramwidget/rationalsliderparamwidget.h index c272a9b642..fee889e764 100644 --- a/app/widget/nodeparamview/paramwidget/rationalsliderparamwidget.h +++ b/app/widget/nodeparamview/paramwidget/rationalsliderparamwidget.h @@ -34,6 +34,8 @@ class RationalSliderParamWidget : public NumericSliderParamWidget virtual void SetValue(const value_t &val) override; + virtual void SetDefaultValue(const value_t &val) override; + virtual void SetProperty(const QString &key, const value_t &val) override; virtual void SetTimebase(const rational &timebase) override; diff --git a/app/widget/slider/integerslider.cpp b/app/widget/slider/integerslider.cpp index 3f71d4f458..dcf8004d9e 100644 --- a/app/widget/slider/integerslider.cpp +++ b/app/widget/slider/integerslider.cpp @@ -96,4 +96,14 @@ bool IntegerSlider::Equals(const InternalType &a, const InternalType &b) const return a.value() == b.value(); } +bool IntegerSlider::ValueGreaterThan(const InternalType &lhs, const InternalType &rhs) const +{ + return lhs.value() > rhs.value(); +} + +bool IntegerSlider::ValueLessThan(const InternalType &lhs, const InternalType &rhs) const +{ + return lhs.value() < rhs.value(); +} + } diff --git a/app/widget/slider/integerslider.h b/app/widget/slider/integerslider.h index dce8a4d77c..ddea3b4e0e 100644 --- a/app/widget/slider/integerslider.h +++ b/app/widget/slider/integerslider.h @@ -52,6 +52,10 @@ class IntegerSlider : public NumericSliderBase virtual bool Equals(const InternalType &a, const InternalType &b) const override; + virtual bool ValueGreaterThan(const InternalType& lhs, const InternalType& rhs) const override; + + virtual bool ValueLessThan(const InternalType& lhs, const InternalType& rhs) const override; + signals: void ValueChanged(int64_t); From 7ef50abc2e41d9a66cac2d1be977287a95d6eaa0 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 5 Apr 2023 12:09:42 -0700 Subject: [PATCH 21/71] began math node rewrite --- app/node/audio/volume/volume.cpp | 6 +- app/node/audio/volume/volume.h | 4 +- app/node/effect/opacity/opacityeffect.cpp | 6 - app/node/math/math/CMakeLists.txt | 6 +- app/node/math/math/math.cpp | 112 +++- app/node/math/math/math.h | 33 +- app/node/math/math/mathbase.cpp | 597 ---------------------- app/node/math/math/mathbase.h | 111 ---- app/node/math/math/mathfunctions.cpp | 124 +++++ app/node/math/math/mathfunctions.h | 50 ++ 10 files changed, 297 insertions(+), 752 deletions(-) delete mode 100644 app/node/math/math/mathbase.cpp delete mode 100644 app/node/math/math/mathbase.h create mode 100644 app/node/math/math/mathfunctions.cpp create mode 100644 app/node/math/math/mathfunctions.h diff --git a/app/node/audio/volume/volume.cpp b/app/node/audio/volume/volume.cpp index 639daa807c..2c7e3232ac 100644 --- a/app/node/audio/volume/volume.cpp +++ b/app/node/audio/volume/volume.cpp @@ -20,6 +20,7 @@ #include "volume.h" +#include "node/math/math/mathfunctions.h" #include "widget/slider/floatslider.h" namespace olive { @@ -27,7 +28,7 @@ namespace olive { const QString VolumeNode::kSamplesInput = QStringLiteral("samples_in"); const QString VolumeNode::kVolumeInput = QStringLiteral("volume_in"); -#define super MathNodeBase +#define super Node VolumeNode::VolumeNode() { @@ -63,8 +64,7 @@ QString VolumeNode::Description() const value_t VolumeNode::Value(const ValueParams &p) const { - return GetInputValue(p, kSamplesInput); - //return ValueInternal(kOpMultiply, kPairSampleNumber, kSamplesInput, GetInputValue(p, kSamplesInput), kVolumeInput, GetInputValue(p, kVolumeInput), p); + return Math::MultiplySamplesDouble(GetInputValue(p, kSamplesInput), GetInputValue(p, kVolumeInput)); } void VolumeNode::Retranslate() diff --git a/app/node/audio/volume/volume.h b/app/node/audio/volume/volume.h index 8d7262d814..01eeba9fea 100644 --- a/app/node/audio/volume/volume.h +++ b/app/node/audio/volume/volume.h @@ -21,11 +21,11 @@ #ifndef VOLUMENODE_H #define VOLUMENODE_H -#include "node/math/math/mathbase.h" +#include "node/node.h" namespace olive { -class VolumeNode : public MathNodeBase +class VolumeNode : public Node { Q_OBJECT public: diff --git a/app/node/effect/opacity/opacityeffect.cpp b/app/node/effect/opacity/opacityeffect.cpp index f503bbeb2b..a1105c5aac 100644 --- a/app/node/effect/opacity/opacityeffect.cpp +++ b/app/node/effect/opacity/opacityeffect.cpp @@ -12,12 +12,6 @@ const QString OpacityEffect::kValueInput = QStringLiteral("opacity_in"); OpacityEffect::OpacityEffect() { - MathNode *math = new MathNode(); - - math->SetOperation(MathNode::kOpMultiply); - - SetNodePositionInContext(math, QPointF(0, 0)); - AddInput(kTextureInput, TYPE_TEXTURE, kInputFlagNotKeyframable); AddInput(kValueInput, TYPE_DOUBLE, 1.0); diff --git a/app/node/math/math/CMakeLists.txt b/app/node/math/math/CMakeLists.txt index 8837a6e9d4..5f3a7c4df9 100644 --- a/app/node/math/math/CMakeLists.txt +++ b/app/node/math/math/CMakeLists.txt @@ -16,9 +16,9 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} - node/math/math/math.h node/math/math/math.cpp - node/math/math/mathbase.h - node/math/math/mathbase.cpp + node/math/math/math.h + node/math/math/mathfunctions.cpp + node/math/math/mathfunctions.h PARENT_SCOPE ) diff --git a/app/node/math/math/math.cpp b/app/node/math/math/math.cpp index 4455f6b2f2..fcf9fe09c8 100644 --- a/app/node/math/math/math.cpp +++ b/app/node/math/math/math.cpp @@ -1,7 +1,7 @@ /*** Olive - Non-Linear Video Editor - Copyright (C) 2022 Olive Team + Copyright (C) 2023 Olive Studios LLC 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 @@ -20,6 +20,8 @@ #include "math.h" +#include "mathfunctions.h" + namespace olive { const QString MathNode::kMethodIn = QStringLiteral("method_in"); @@ -27,7 +29,9 @@ const QString MathNode::kParamAIn = QStringLiteral("param_a_in"); const QString MathNode::kParamBIn = QStringLiteral("param_b_in"); const QString MathNode::kParamCIn = QStringLiteral("param_c_in"); -#define super MathNodeBase +#define super Node + +std::map > > MathNode::operations_; MathNode::MathNode() { @@ -40,15 +44,19 @@ MathNode::MathNode() AddInput(kParamBIn, TYPE_DOUBLE, 0.0); SetInputProperty(kParamBIn, QStringLiteral("decimalplaces"), 8); SetInputProperty(kParamBIn, QStringLiteral("autotrim"), true); + + if (operations_.empty()) { + PopulateOperations(); + } } QString MathNode::Name() const { // Default to naming after the operation if (parent()) { - QString op_name = GetOperationName(GetOperation()); - if (!op_name.isEmpty()) { - return op_name; + QString name = GetOperationName(static_cast(GetStandardValue(kMethodIn).toInt())); + if (!name.isEmpty()) { + return name; } } @@ -88,6 +96,35 @@ void MathNode::Retranslate() SetComboBoxStrings(kMethodIn, operations); } +void ProcessSamplesSamples(const void *context, const SampleJob &job, SampleBuffer &mixed_samples) +{ + const SampleBuffer samples_a = job.Get(QStringLiteral("a")).toSamples(); + const SampleBuffer samples_b = job.Get(QStringLiteral("b")).toSamples(); + const MathNode::Operation operation = static_cast(job.Get(QStringLiteral("operation")).toInt()); + + size_t max_samples = qMax(samples_a.sample_count(), samples_b.sample_count()); + size_t min_samples = qMin(samples_a.sample_count(), samples_b.sample_count()); + + for (int i=0;i(operation, samples_a.data(i)[j], samples_b.data(i)[j]); + } + } + + if (max_samples > min_samples) { + size_t remainder = max_samples - min_samples; + + const SampleBuffer &larger_buffer = (max_samples == samples_a.sample_count()) ? samples_a : samples_b; + + for (int i=0;i(GetInputValue(p, kMethodIn).toInt()); + + if (aval.type() == TYPE_SAMPLES || bval.type() == TYPE_SAMPLES) { + if (aval.type() == TYPE_SAMPLES && bval.type() == TYPE_SAMPLES) { + SampleJob job(p); + + job.Insert(QStringLiteral("a"), aval); + job.Insert(QStringLiteral("b"), bval); + job.Insert(QStringLiteral("operation"), int(operation)); + + job.set_function(ProcessSamplesSamples, this); + + return job; + } + + + } else if (aval.type() == TYPE_TEXTURE || bval.type() == TYPE_TEXTURE) { + // FIXME: Requires a more complex shader job + } else { + // Operation can be done entirely here + operation_t func = operations_[operation][aval.type()][bval.type()]; + if (func) { + return func(aval, bval); + } + } + + return aval; +} + +QString MathNode::GetOperationName(Operation o) +{ + switch (o) { + case kOpAdd: return tr("Add"); + case kOpSubtract: return tr("Subtract"); + case kOpMultiply: return tr("Multiply"); + case kOpDivide: return tr("Divide"); + case kOpPower: return tr("Power"); + } + + return QString(); +} + +void MathNode::PopulateOperations() +{ + operations_[kOpAdd][TYPE_INTEGER][TYPE_INTEGER] = Math::AddIntegerInteger; - // Do nothing if no pairing was found - if (calc.FoundMostLikelyPairing()) { - return ValueInternal(GetOperation(), - calc.GetMostLikelyPairing(), - kParamAIn, - calc.GetMostLikelyValueA(), - kParamBIn, - calc.GetMostLikelyValueB(), - p); - }*/ + operations_[kOpAdd][TYPE_DOUBLE][TYPE_DOUBLE] = Math::AddDoubleDouble; + operations_[kOpSubtract][TYPE_DOUBLE][TYPE_DOUBLE] = Math::SubtractDoubleDouble; + operations_[kOpMultiply][TYPE_DOUBLE][TYPE_DOUBLE] = Math::MultiplyDoubleDouble; + operations_[kOpDivide][TYPE_DOUBLE][TYPE_DOUBLE] = Math::DivideDoubleDouble; + operations_[kOpPower][TYPE_DOUBLE][TYPE_DOUBLE] = Math::PowerDoubleDouble; - return value_t(); + operations_[kOpAdd][TYPE_MATRIX][TYPE_MATRIX] = Math::AddMatrixMatrix; + operations_[kOpSubtract][TYPE_MATRIX][TYPE_MATRIX] = Math::SubtractMatrixMatrix; + operations_[kOpMultiply][TYPE_MATRIX][TYPE_MATRIX] = Math::MultiplyMatrixMatrix; } } diff --git a/app/node/math/math/math.h b/app/node/math/math/math.h index bf8fec1615..e4b0518eae 100644 --- a/app/node/math/math/math.h +++ b/app/node/math/math/math.h @@ -1,7 +1,7 @@ /*** Olive - Non-Linear Video Editor - Copyright (C) 2022 Olive Team + Copyright (C) 2023 Olive Studios LLC 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 @@ -21,16 +21,24 @@ #ifndef MATHNODE_H #define MATHNODE_H -#include "mathbase.h" +#include "node/node.h" namespace olive { -class MathNode : public MathNodeBase +class MathNode : public Node { Q_OBJECT public: MathNode(); + enum Operation { + kOpAdd, + kOpSubtract, + kOpMultiply, + kOpDivide, + kOpPower + }; + NODE_DEFAULT_FUNCTIONS(MathNode) virtual QString Name() const override; @@ -40,23 +48,22 @@ class MathNode : public MathNodeBase virtual void Retranslate() override; - Operation GetOperation() const - { - return static_cast(GetStandardValue(kMethodIn).toInt()); - } - - void SetOperation(Operation o) - { - SetStandardValue(kMethodIn, o); - } - virtual value_t Value(const ValueParams &p) const override; + static QString GetOperationName(Operation o); + static const QString kMethodIn; static const QString kParamAIn; static const QString kParamBIn; static const QString kParamCIn; +private: + typedef value_t (*operation_t)(const value_t &a, const value_t &b); + + static std::map > > operations_; + + static void PopulateOperations(); + }; } diff --git a/app/node/math/math/mathbase.cpp b/app/node/math/math/mathbase.cpp deleted file mode 100644 index 005bbd7aff..0000000000 --- a/app/node/math/math/mathbase.cpp +++ /dev/null @@ -1,597 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2022 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#include "math.h" - -#include -#include - -#include "common/tohex.h" -#include "node/distort/transform/transformdistortnode.h" - -namespace olive { - -static const QString param_a = QStringLiteral("param_a"); -static const QString param_b = QStringLiteral("param_b"); - -ShaderCode MathNodeBase::GetShaderCode(const QString &shader_id) -{ - QStringList code_id = shader_id.split('.'); - - Operation op = static_cast(code_id.at(0).toInt()); - Pairing pairing = static_cast(code_id.at(1).toInt()); - type_t type_a = code_id.at(2).toUtf8().constData(); - type_t type_b = code_id.at(3).toUtf8().constData(); - size_t size_a = code_id.at(4).toULongLong(); - size_t size_b = code_id.at(5).toULongLong(); - - QString operation, frag, vert; - - if (pairing == kPairTextureMatrix && op == kOpMultiply) { - - // Override the operation for this operation since we multiply texture COORDS by the matrix rather than - const QString& tex_in = (type_a == TYPE_TEXTURE) ? param_a : param_b; - const QString& mat_in = (type_a == TYPE_TEXTURE) ? param_b : param_a; - - // No-op frag shader (can we return QString() instead?) - operation = QStringLiteral("texture(%1, ove_texcoord)").arg(tex_in); - - vert = QStringLiteral("uniform mat4 %1;\n" - "\n" - "in vec4 a_position;\n" - "in vec2 a_texcoord;\n" - "\n" - "out vec2 ove_texcoord;\n" - "\n" - "void main() {\n" - " gl_Position = %1 * a_position;\n" - " ove_texcoord = a_texcoord;\n" - "}\n").arg(mat_in); - - } else { - switch (op) { - case kOpAdd: - operation = QStringLiteral("%1 + %2"); - break; - case kOpSubtract: - operation = QStringLiteral("%1 - %2"); - break; - case kOpMultiply: - operation = QStringLiteral("%1 * %2"); - break; - case kOpDivide: - operation = QStringLiteral("%1 / %2"); - break; - case kOpPower: - if (pairing == kPairTextureNumber) { - // The "number" in this operation has to be declared a vec4 - if (type_a == TYPE_INTEGER || type_a == TYPE_DOUBLE || type_a == TYPE_RATIONAL) { - operation = QStringLiteral("pow(%2, vec4(%1))"); - } else { - operation = QStringLiteral("pow(%1, vec4(%2))"); - } - } else { - operation = QStringLiteral("pow(%1, %2)"); - } - break; - } - - operation = operation.arg(GetShaderVariableCall(param_a, type_a), - GetShaderVariableCall(param_b, type_b)); - } - - frag = QStringLiteral("uniform %1 %3;\n" - "uniform %2 %4;\n" - "\n" - "in vec2 ove_texcoord;\n" - "out vec4 frag_color;\n" - "\n" - "void main(void) {\n" - " vec4 c = %5;\n" - " c.a = clamp(c.a, 0.0, 1.0);\n" // Ensure alpha is between 0.0 and 1.0 - " frag_color = c;\n" - "}\n").arg(GetShaderUniformType(type_a, size_a), - GetShaderUniformType(type_b, size_b), - param_a, - param_b, - operation); - - return ShaderCode(frag, vert); -} - -QString MathNodeBase::GetShaderUniformType(const olive::type_t &type, size_t channels) -{ - if (type == TYPE_TEXTURE) { - return QStringLiteral("sampler2D"); - } else if (type == TYPE_MATRIX) { - return QStringLiteral("mat4"); - } else if (type == TYPE_DOUBLE) { - switch (channels) { - case 1: return QStringLiteral("float"); - case 2: return QStringLiteral("vec2"); - case 3: return QStringLiteral("vec3"); - case 4: return QStringLiteral("vec4"); - } - } - - // Fallback (shouldn't ever get here) - return QString(); -} - -QString MathNodeBase::GetShaderVariableCall(const QString &input_id, const type_t &type, const QString& coord_op) -{ - if (type == TYPE_TEXTURE) { - return QStringLiteral("texture(%1, ove_texcoord%2)").arg(input_id, coord_op); - } - - return input_id; -} - -QVector4D MathNodeBase::RetrieveVector(const value_t &val) -{ - return val.toVec4(); -} - -value_t MathNodeBase::PushVector(size_t channels, const QVector4D &vec) const -{ - switch (channels) { - case 2: return vec.toVector2D(); - case 3: return vec.toVector3D(); - case 4: return vec; - } - - return value_t(); -} - -QString MathNodeBase::GetOperationName(Operation o) -{ - switch (o) { - case kOpAdd: return tr("Add"); - case kOpSubtract: return tr("Subtract"); - case kOpMultiply: return tr("Multiply"); - case kOpDivide: return tr("Divide"); - case kOpPower: return tr("Power"); - } - - return QString(); -} - -void MathNodeBase::PerformAllOnFloatBuffer(Operation operation, const float *input, float *output, float b, size_t start, size_t end) -{ - for (size_t j=start;j(context); - - const ValueParams &p = job.value_params(); - const SampleBuffer input = job.Get(QStringLiteral("samples")).toSamples(); - const QString number_in = job.Get(QStringLiteral("number")).toString(); - const Operation operation = static_cast(job.Get(QStringLiteral("operation")).toInt()); - - if (n->IsInputStatic(number_in)) { - auto f = n->GetStandardValue(number_in).toDouble(); - - for (int i=0;iGetInputValue(p.time_transformed(this_sample_range), number_in).toDouble(); - - for (int i=0;i(operation, input.data(i)[j], v); - } - } - } -} - -value_t MathNodeBase::ValueInternal(Operation operation, Pairing pairing, const QString& param_a_in, const value_t& val_a, const QString& param_b_in, const value_t& val_b, const ValueParams &p) const -{ - /* - switch (pairing) { - - case kPairNumberNumber: - { - if (val_a.type() == TYPE_RATIONAL && val_b.type() == TYPE_RATIONAL && operation != kOpPower) { - // Preserve rationals - return PerformAddSubMultDiv(operation, val_a.toRational(), val_b.toRational()); - } else { - return PerformAll(operation, RetrieveNumber(val_a), RetrieveNumber(val_b)); - } - } - - case kPairVecVec: - { - // We convert all vectors to QVector4D just for simplicity and exploit the fact that kVec4 is higher than kVec2 in - // the enum to find the largest data type - return PushVector(qMax(val_a.type(), val_b.type()), - PerformAddSubMultDiv(operation, RetrieveVector(val_a), RetrieveVector(val_b))); - } - - case kPairMatrixVec: - { - QMatrix4x4 matrix = (val_a.type() == TYPE_MATRIX) ? val_a.toMatrix() : val_b.toMatrix(); - QVector4D vec = (val_a.type() == TYPE_MATRIX) ? RetrieveVector(val_b) : RetrieveVector(val_a); - - // Only valid operation is multiply - return PushVector(qMax(val_a.type(), val_b.type()), - PerformMult(operation, vec, matrix)); - } - - case kPairVecNumber: - { - QVector4D vec = (NodeValue::type_is_vector(val_a.type()) ? RetrieveVector(val_a) : RetrieveVector(val_b)); - float number = RetrieveNumber((val_a.type() & TYPE_MATRIX) ? val_b : val_a); - - // Only multiply and divide are valid operations - return PushVector(val_a.type(), PerformMultDiv(operation, vec, number)); - } - - case kPairMatrixMatrix: - { - QMatrix4x4 mat_a = val_a.toMatrix(); - QMatrix4x4 mat_b = val_b.toMatrix(); - return PerformAddSubMult(operation, mat_a, mat_b); - } - - case kPairColorColor: - { - Color col_a = val_a.toColor(); - Color col_b = val_b.toColor(); - - // Only add and subtract are valid operations - return PerformAddSub(operation, col_a, col_b); - } - - - case kPairNumberColor: - { - Color col = (val_a.type() == NodeValue::kColor) ? val_a.toColor() : val_b.toColor(); - float num = (val_a.type() == NodeValue::kColor) ? val_b.toDouble() : val_a.toDouble(); - - // Only multiply and divide are valid operations - return PerformMult(operation, col, num); - } - - case kPairSampleSample: - { - SampleJob job(p); - - job.Insert(QStringLiteral("a"), val_a); - job.Insert(QStringLiteral("b"), val_b); - job.Insert(QStringLiteral("operation"), int(operation)); - - job.set_function(MathNodeBase::ProcessSamplesSamples, this); - - return job; - } - - case kPairTextureColor: - case kPairTextureNumber: - case kPairTextureTexture: - case kPairTextureMatrix: - { - ShaderJob job; - job.set_function(MathNodeBase::GetShaderCode); - job.SetShaderID(QStringLiteral("%1.%2.%3.%4.%5.%6").arg(QString::number(operation), - QString::number(pairing), - val_a.type(), - val_b.type(), - QString::number(val_a.size()), - QString::number(val_b.size()))); - - job.Insert(param_a, val_a); - job.Insert(param_b, val_b); - - bool operation_is_noop = false; - - const Value& number_val = val_a.type() == NodeValue::TYPE_TEXTURE ? val_b : val_a; - const Value& texture_val = val_a.type() == NodeValue::TYPE_TEXTURE ? val_a : val_b; - TexturePtr texture = texture_val.toTexture(); - - if (!texture) { - operation_is_noop = true; - } else if (pairing == kPairTextureNumber) { - if (NumberIsNoOp(operation, RetrieveNumber(number_val))) { - operation_is_noop = true; - } - } else if (pairing == kPairTextureMatrix) { - // Only allow matrix multiplication - const QVector2D &sequence_res = p.nonsquare_resolution(); - QVector2D texture_res(texture->params().width() * texture->pixel_aspect_ratio().toDouble(), texture->params().height()); - - QMatrix4x4 adjusted_matrix = TransformDistortNode::AdjustMatrixByResolutions(number_val.toMatrix(), - sequence_res, - texture->params().offset(), - texture_res); - - if (operation != kOpMultiply || adjusted_matrix.isIdentity()) { - operation_is_noop = true; - } else { - // Replace with adjusted matrix - job.Insert(val_a.type() == NodeValue::kTexture ? param_b_in : param_a_in, adjusted_matrix); - } - } - - if (operation_is_noop) { - // Just push texture as-is - return texture_val; - } else { - // Push shader job - return Texture::Job(p.vparams(), job); - } - break; - } - - case kPairSampleNumber: - { - // Queue a sample job - const Value& number_val = val_a.type() == NodeValue::kSamples ? val_b : val_a; - const QString& number_param = val_a.type() == NodeValue::kSamples ? param_b_in : param_a_in; - - float number = RetrieveNumber(number_val); - - const Value &sample_val = val_a.type() == NodeValue::kSamples ? val_a : val_b; - - if (IsInputStatic(number_param) && NumberIsNoOp(operation, number)) { - return sample_val; - } else { - SampleJob job(p); - - job.Insert(QStringLiteral("samples"), sample_val); - job.Insert(QStringLiteral("number"), val_a.type() == NodeValue::kSamples ? param_b_in : param_a_in); - job.Insert(QStringLiteral("operation"), int(operation)); - - job.set_function(MathNodeBase::ProcessSamplesNumber, this); - - return job; - } - break; - } - - case kPairNone: - case kPairCount: - break; - } - - return Value(); - */ - return value_t(); -} - -void MathNodeBase::ProcessSamplesSamples(const void *context, const SampleJob &job, SampleBuffer &mixed_samples) -{ - const SampleBuffer samples_a = job.Get(QStringLiteral("a")).toSamples(); - const SampleBuffer samples_b = job.Get(QStringLiteral("b")).toSamples(); - const Operation operation = static_cast(job.Get(QStringLiteral("operation")).toInt()); - - size_t max_samples = qMax(samples_a.sample_count(), samples_b.sample_count()); - size_t min_samples = qMin(samples_a.sample_count(), samples_b.sample_count()); - - for (int i=0;i(operation, samples_a.data(i)[j], samples_b.data(i)[j]); - } - } - - if (max_samples > min_samples) { - size_t remainder = max_samples - min_samples; - - const SampleBuffer &larger_buffer = (max_samples == samples_a.sample_count()) ? samples_a : samples_b; - - for (int i=0;i -T MathNodeBase::PerformAll(Operation operation, T a, U b) -{ - switch (operation) { - case kOpAdd: - return a + b; - case kOpSubtract: - return a - b; - case kOpMultiply: - return a * b; - case kOpDivide: - return a / b; - case kOpPower: - return std::pow(a, b); - } - - return a; -} - -template -T MathNodeBase::PerformMultDiv(Operation operation, T a, U b) -{ - switch (operation) { - case kOpMultiply: - return a * b; - case kOpDivide: - return a / b; - case kOpAdd: - case kOpSubtract: - case kOpPower: - break; - } - - return a; -} - -template -T MathNodeBase::PerformAddSub(Operation operation, T a, U b) -{ - switch (operation) { - case kOpAdd: - return a + b; - case kOpSubtract: - return a - b; - case kOpMultiply: - case kOpDivide: - case kOpPower: - break; - } - - return a; -} - -template -T MathNodeBase::PerformMult(Operation operation, T a, U b) -{ - switch (operation) { - case kOpMultiply: - return a * b; - case kOpAdd: - case kOpSubtract: - case kOpDivide: - case kOpPower: - break; - } - - return a; -} - -template -T MathNodeBase::PerformAddSubMult(Operation operation, T a, U b) -{ - switch (operation) { - case kOpAdd: - return a + b; - case kOpSubtract: - return a - b; - case kOpMultiply: - return a * b; - case kOpDivide: - case kOpPower: - break; - } - - return a; -} - -template -T MathNodeBase::PerformAddSubMultDiv(Operation operation, T a, U b) -{ - switch (operation) { - case kOpAdd: - return a + b; - case kOpSubtract: - return a - b; - case kOpMultiply: - return a * b; - case kOpDivide: - return a / b; - case kOpPower: - break; - } - - return a; -} - -} diff --git a/app/node/math/math/mathbase.h b/app/node/math/math/mathbase.h deleted file mode 100644 index 0e39c03003..0000000000 --- a/app/node/math/math/mathbase.h +++ /dev/null @@ -1,111 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2022 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#ifndef MATHNODEBASE_H -#define MATHNODEBASE_H - -#include "node/node.h" - -namespace olive { - -class MathNodeBase : public Node -{ -public: - MathNodeBase() = default; - - enum Operation { - kOpAdd, - kOpSubtract, - kOpMultiply, - kOpDivide, - kOpPower - }; - - static QString GetOperationName(Operation o); - - static ShaderCode GetShaderCode(const QString &shader_id); - static void ProcessSamplesNumber(const void *context, const SampleJob &job, SampleBuffer &mixed_samples); - static void ProcessSamplesSamples(const void *context, const SampleJob &job, SampleBuffer &mixed_samples); - -protected: - enum Pairing { - kPairNone = -1, - - kPairNumberNumber, - kPairVecVec, - kPairMatrixMatrix, - kPairColorColor, - kPairTextureTexture, - - kPairVecNumber, - kPairMatrixVec, - kPairNumberColor, - kPairTextureNumber, - kPairTextureColor, - kPairTextureMatrix, - kPairSampleSample, - kPairSampleNumber, - - kPairCount - }; - - template - static T PerformAll(Operation operation, T a, U b); - - template - static T PerformMultDiv(Operation operation, T a, U b); - - template - static T PerformAddSub(Operation operation, T a, U b); - - template - static T PerformMult(Operation operation, T a, U b); - - template - static T PerformAddSubMult(Operation operation, T a, U b); - - template - static T PerformAddSubMultDiv(Operation operation, T a, U b); - - static void PerformAllOnFloatBuffer(Operation operation, const float *input, float *output, float b, size_t start, size_t end); - -#if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM) - static void PerformAllOnFloatBufferSSE(Operation operation, const float *input, float *output, float b, size_t start, size_t end); -#endif - - static QString GetShaderUniformType(const type_t& type, size_t channels); - - static QString GetShaderVariableCall(const QString& input_id, const type_t& type, const QString &coord_op = QString()); - - static QVector4D RetrieveVector(const value_t& val); - - static float RetrieveNumber(const value_t& val); - - static bool NumberIsNoOp(const Operation& op, const float& number); - - value_t PushVector(size_t channels, const QVector4D& vec) const; - - value_t ValueInternal(Operation operation, Pairing pairing, const QString& param_a_in, const value_t &val_a, const QString& param_b_in, const value_t& val_b, const ValueParams &p) const; - -}; - -} - -#endif // MATHNODEBASE_H diff --git a/app/node/math/math/mathfunctions.cpp b/app/node/math/math/mathfunctions.cpp new file mode 100644 index 0000000000..18c001525c --- /dev/null +++ b/app/node/math/math/mathfunctions.cpp @@ -0,0 +1,124 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "mathfunctions.h" + +namespace olive { + +value_t Math::AddIntegerInteger(const value_t &a, const value_t &b) +{ + value_t v(TYPE_INTEGER, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = a.value(i) + b.value(i); + } + return v; +} + +value_t Math::AddDoubleDouble(const value_t &a, const value_t &b) +{ + value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = a.value(i) + b.value(i); + } + return v; +} + +value_t Math::SubtractDoubleDouble(const value_t &a, const value_t &b) +{ + value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = a.value(i) - b.value(i); + } + return v; +} + +value_t Math::MultiplyDoubleDouble(const value_t &a, const value_t &b) +{ + value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = a.value(i) * b.value(i); + } + return v; +} + +value_t Math::DivideDoubleDouble(const value_t &a, const value_t &b) +{ + value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = a.value(i) / b.value(i); + } + return v; +} + +value_t Math::PowerDoubleDouble(const value_t &a, const value_t &b) +{ + value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::pow(a.value(i), b.value(i)); + } + return v; +} + +value_t Math::AddMatrixMatrix(const value_t &a, const value_t &b) +{ + value_t v(TYPE_MATRIX, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = a.value(i) + b.value(i); + } + return v; +} + +value_t Math::SubtractMatrixMatrix(const value_t &a, const value_t &b) +{ + value_t v(TYPE_MATRIX, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = a.value(i) - b.value(i); + } + return v; +} + +value_t Math::MultiplyMatrixMatrix(const value_t &a, const value_t &b) +{ + value_t v(TYPE_MATRIX, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = a.value(i) * b.value(i); + } + return v; +} + +value_t Math::MultiplySamplesDouble(const value_t &a, const value_t &b) +{ + SampleJob job(p); + + job.Insert(QStringLiteral("samples"), a); + job.Insert(QStringLiteral("number"), val_a.type() == NodeValue::kSamples ? param_b_in : param_a_in); + job.Insert(QStringLiteral("operation"), int(operation)); + + job.set_function(MathNodeBase::ProcessSamplesNumber, this); + + return job; +} + +value_t Math::MultiplyDoubleSamples(const value_t &a, const value_t &b) +{ + return MultiplySamplesDouble(b, a); +} + +} diff --git a/app/node/math/math/mathfunctions.h b/app/node/math/math/mathfunctions.h new file mode 100644 index 0000000000..a1117ded5f --- /dev/null +++ b/app/node/math/math/mathfunctions.h @@ -0,0 +1,50 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef MATHFUNCTIONS_H +#define MATHFUNCTIONS_H + +#include "node/value.h" + +namespace olive { + +class Math +{ +public: + static value_t AddIntegerInteger(const value_t &a, const value_t &b); + + static value_t AddDoubleDouble(const value_t &a, const value_t &b); + static value_t SubtractDoubleDouble(const value_t &a, const value_t &b); + static value_t MultiplyDoubleDouble(const value_t &a, const value_t &b); + static value_t DivideDoubleDouble(const value_t &a, const value_t &b); + static value_t PowerDoubleDouble(const value_t &a, const value_t &b); + + static value_t AddMatrixMatrix(const value_t &a, const value_t &b); + static value_t SubtractMatrixMatrix(const value_t &a, const value_t &b); + static value_t MultiplyMatrixMatrix(const value_t &a, const value_t &b); + + static value_t MultiplySamplesDouble(const value_t &a, const value_t &b); + static value_t MultiplyDoubleSamples(const value_t &a, const value_t &b); + +}; + +} + +#endif // MATHFUNCTIONS_H From 73227fdb90c70b62c06389df5e6371044bf03ad7 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Thu, 27 Apr 2023 14:59:37 -0700 Subject: [PATCH 22/71] use SSE in all sample operations --- app/node/audio/volume/volume.cpp | 12 +- app/node/math/math/math.cpp | 177 ++++++++++++++++++++++++++- app/node/math/math/math.h | 7 ++ app/node/math/math/mathfunctions.cpp | 18 --- 4 files changed, 188 insertions(+), 26 deletions(-) diff --git a/app/node/audio/volume/volume.cpp b/app/node/audio/volume/volume.cpp index 2c7e3232ac..fa6418f4e2 100644 --- a/app/node/audio/volume/volume.cpp +++ b/app/node/audio/volume/volume.cpp @@ -20,7 +20,7 @@ #include "volume.h" -#include "node/math/math/mathfunctions.h" +#include "node/math/math/math.h" #include "widget/slider/floatslider.h" namespace olive { @@ -64,7 +64,15 @@ QString VolumeNode::Description() const value_t VolumeNode::Value(const ValueParams &p) const { - return Math::MultiplySamplesDouble(GetInputValue(p, kSamplesInput), GetInputValue(p, kVolumeInput)); + SampleJob job(p); + + job.Insert(QStringLiteral("samples"), GetInputValue(p, kSamplesInput)); + job.Insert(QStringLiteral("number"), kVolumeInput); + job.Insert(QStringLiteral("operation"), MathNode::kOpMultiply); + + job.set_function(MathNode::ProcessSamplesDouble, this); + + return job; } void VolumeNode::Retranslate() diff --git a/app/node/math/math/math.cpp b/app/node/math/math/math.cpp index fcf9fe09c8..6c731a4dec 100644 --- a/app/node/math/math/math.cpp +++ b/app/node/math/math/math.cpp @@ -96,7 +96,7 @@ void MathNode::Retranslate() SetComboBoxStrings(kMethodIn, operations); } -void ProcessSamplesSamples(const void *context, const SampleJob &job, SampleBuffer &mixed_samples) +void MathNode::ProcessSamplesSamples(const void *context, const SampleJob &job, SampleBuffer &mixed_samples) { const SampleBuffer samples_a = job.Get(QStringLiteral("a")).toSamples(); const SampleBuffer samples_b = job.Get(QStringLiteral("b")).toSamples(); @@ -106,10 +106,7 @@ void ProcessSamplesSamples(const void *context, const SampleJob &job, SampleBuff size_t min_samples = qMin(samples_a.sample_count(), samples_b.sample_count()); for (int i=0;i(operation, samples_a.data(i)[j], samples_b.data(i)[j]); - } + OperateSampleSample(operation, samples_a.data(i), mixed_samples.data(i), samples_b.data(i), 0, std::min(samples_a.sample_count(), samples_b.sample_count())); } if (max_samples > min_samples) { @@ -125,6 +122,166 @@ void ProcessSamplesSamples(const void *context, const SampleJob &job, SampleBuff } } +#if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM) +#define USE_SSE +#endif + +void MathNode::OperateSampleNumber(Operation operation, const float *input, float *output, float b, size_t start, size_t end) +{ + size_t end_divisible_4 = +#ifdef USE_SSE + (end / 4) * 4 +#else + 0 +#endif + ; + + // Load number to multiply by into buffer + __m128 mult = _mm_load1_ps(&b); + + switch (operation) { + case kOpAdd: +#ifdef USE_SSE + for(size_t j = 0; j < end_divisible_4; j+=4) { + _mm_storeu_ps(output + start + j, _mm_add_ps(_mm_loadu_ps(input + start + j), mult)); + } +#endif + for (size_t j = end_divisible_4; j < end; j++) { + output[start + j] = input[start + j] + b; + } + break; + case kOpSubtract: +#ifdef USE_SSE + for(size_t j = 0; j < end_divisible_4; j+=4) { + _mm_storeu_ps(output + start + j, _mm_sub_ps(_mm_loadu_ps(input + start + j), mult)); + } +#endif + for (size_t j = end_divisible_4; j < end; j++) { + output[start + j] = input[start + j] - b; + } + break; + case kOpMultiply: +#ifdef USE_SSE + for(size_t j = 0; j < end_divisible_4; j+=4) { + _mm_storeu_ps(output + start + j, _mm_mul_ps(_mm_loadu_ps(input + start + j), mult)); + } +#endif + for (size_t j = end_divisible_4; j < end; j++) { + output[start + j] = input[start + j] * b; + } + break; + case kOpDivide: +#ifdef USE_SSE + for(size_t j = 0; j < end_divisible_4; j+=4) { + _mm_storeu_ps(output + start + j, _mm_div_ps(_mm_loadu_ps(input + start + j), mult)); + } +#endif + for (size_t j = end_divisible_4; j < end; j++) { + output[start + j] = input[start + j] / b; + } + break; + case kOpPower: + // Can't do pow in SSE, do it manually here instead + end_divisible_4 = 0; + for (size_t j = end_divisible_4; j < end; j++) { + output[start + j] = std::pow(input[start + j], b); + } + break; + } +} + +void MathNode::OperateSampleSample(Operation operation, const float *input, float *output, const float *input2, size_t start, size_t end) +{ + size_t end_divisible_4 = +#ifdef USE_SSE + (end / 4) * 4 +#else + 0 +#endif + ; + + switch (operation) { + case kOpAdd: +#ifdef USE_SSE + for(size_t j = 0; j < end_divisible_4; j+=4) { + _mm_storeu_ps(output + start + j, _mm_add_ps(_mm_loadu_ps(input + start + j), _mm_loadu_ps(input2 + start + j))); + } +#endif + for (size_t j = end_divisible_4; j < end; j++) { + output[start + j] = input[start + j] + input2[start + j]; + } + break; + case kOpSubtract: +#ifdef USE_SSE + for(size_t j = 0; j < end_divisible_4; j+=4) { + _mm_storeu_ps(output + start + j, _mm_sub_ps(_mm_loadu_ps(input + start + j), _mm_loadu_ps(input2 + start + j))); + } +#endif + for (size_t j = end_divisible_4; j < end; j++) { + output[start + j] = input[start + j] - input2[start + j]; + } + break; + case kOpMultiply: +#ifdef USE_SSE + for(size_t j = 0; j < end_divisible_4; j+=4) { + _mm_storeu_ps(output + start + j, _mm_mul_ps(_mm_loadu_ps(input + start + j), _mm_loadu_ps(input2 + start + j))); + } +#endif + for (size_t j = end_divisible_4; j < end; j++) { + output[start + j] = input[start + j] * input2[start + j]; + } + break; + case kOpDivide: +#ifdef USE_SSE + for(size_t j = 0; j < end_divisible_4; j+=4) { + _mm_storeu_ps(output + start + j, _mm_div_ps(_mm_loadu_ps(input + start + j), _mm_loadu_ps(input2 + start + j))); + } +#endif + for (size_t j = end_divisible_4; j < end; j++) { + output[start + j] = input[start + j] / input2[start + j]; + } + break; + case kOpPower: + // Can't do pow in SSE, do it manually here instead + end_divisible_4 = 0; + for (size_t j = end_divisible_4; j < end; j++) { + output[start + j] = std::pow(input[start + j], input2[start + j]); + } + break; + } +} + +void MathNode::ProcessSamplesDouble(const void *context, const SampleJob &job, SampleBuffer &output) +{ + const Node *n = static_cast(context); + + const ValueParams &p = job.value_params(); + const SampleBuffer input = job.Get(QStringLiteral("samples")).toSamples(); + const QString number_in = job.Get(QStringLiteral("number")).toString(); + const Operation operation = static_cast(job.Get(QStringLiteral("operation")).toInt()); + + if (n->IsInputStatic(number_in)) { + auto f = n->GetStandardValue(number_in).toDouble(); + + for (int i=0;i values(input.sample_count()); + + for (size_t i = 0; i < values.size(); i++) { + rational this_sample_time = p.time().in() + rational(i, input.audio_params().sample_rate()); + TimeRange this_sample_range(this_sample_time, this_sample_time + input.audio_params().sample_rate_as_time_base()); + auto v = n->GetInputValue(p.time_transformed(this_sample_range), number_in).toDouble(); + values[i] = v; + } + + for (int i=0;i Date: Fri, 19 May 2023 21:06:50 -0700 Subject: [PATCH 23/71] implement basic channel swizzling --- app/node/CMakeLists.txt | 1 + app/node/inputimmediate.cpp | 4 + app/node/node.cpp | 42 ++- app/node/node.h | 10 +- .../project/serializer/serializer210528.cpp | 4 +- app/node/swizzlemap.h | 61 ++++ app/node/value.cpp | 18 +- app/node/value.h | 14 +- app/task/precache/precachetask.cpp | 4 +- app/widget/nodeparamview/nodeparamview.cpp | 7 + app/widget/nodeparamview/nodeparamview.h | 2 + .../nodeparamviewconnectedlabel.h | 2 + .../nodeparamview/nodeparamviewitem.cpp | 29 ++ app/widget/nodeparamview/nodeparamviewitem.h | 6 + .../nodeparamviewwidgetbridge.cpp | 8 +- app/widget/nodetreeview/CMakeLists.txt | 2 +- app/widget/nodevaluetree/CMakeLists.txt | 2 + app/widget/nodevaluetree/nodevaluetree.cpp | 67 +++- app/widget/nodevaluetree/nodevaluetree.h | 9 + .../nodevaluetree/valueswizzlewidget.cpp | 323 ++++++++++++++++++ app/widget/nodevaluetree/valueswizzlewidget.h | 120 +++++++ app/widget/nodeview/CMakeLists.txt | 2 + app/widget/nodeview/curvedconnectoritem.cpp | 195 +++++++++++ app/widget/nodeview/curvedconnectoritem.h | 93 +++++ app/widget/nodeview/nodeviewedge.cpp | 177 +--------- app/widget/nodeview/nodeviewedge.h | 86 +---- app/widget/timelinewidget/tool/import.cpp | 7 +- 27 files changed, 1024 insertions(+), 271 deletions(-) create mode 100644 app/node/swizzlemap.h create mode 100644 app/widget/nodevaluetree/valueswizzlewidget.cpp create mode 100644 app/widget/nodevaluetree/valueswizzlewidget.h create mode 100644 app/widget/nodeview/curvedconnectoritem.cpp create mode 100644 app/widget/nodeview/curvedconnectoritem.h diff --git a/app/node/CMakeLists.txt b/app/node/CMakeLists.txt index e2d1a682ee..ca075085e6 100644 --- a/app/node/CMakeLists.txt +++ b/app/node/CMakeLists.txt @@ -50,6 +50,7 @@ set(OLIVE_SOURCES node/param.h node/project.cpp node/project.h + node/swizzlemap.h node/value.cpp node/value.h PARENT_SCOPE diff --git a/app/node/inputimmediate.cpp b/app/node/inputimmediate.cpp index e3afa1ba61..779814c247 100644 --- a/app/node/inputimmediate.cpp +++ b/app/node/inputimmediate.cpp @@ -211,6 +211,10 @@ NodeKeyframe *NodeInputImmediate::get_latest_keyframe() const void NodeInputImmediate::insert_keyframe(NodeKeyframe* key) { + if (key->track() >= keyframe_tracks_.size()) { + keyframe_tracks_.resize(key->track() + 1); + } + NodeKeyframeTrack& key_track = keyframe_tracks_[key->track()]; int insert_index = key_track.size(); diff --git a/app/node/node.cpp b/app/node/node.cpp index 4c97a4f214..84357ef953 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -884,7 +884,29 @@ value_t Node::GetFakeConnectedValue(const ValueParams &g, Node *output, const QS while (output) { if (output->is_enabled()) { - return output->Value(g.time_transformed(adjusted_time).output_edited(this->GetValueHintForInput(input, element).tag())); + Node::ValueHint vh = this->GetValueHintForInput(input, element); + value_t v = output->Value(g.time_transformed(adjusted_time).output_edited(vh.tag())); + + // Perform swizzle if requested + const SwizzleMap &swizzle = vh.swizzle(); + if (!swizzle.empty()) { + value_t swiz(v.type(), v.size()); + for (auto it = swizzle.cbegin(); it != swizzle.cend(); it++) { + size_t from = it->second; + size_t to = it->first; + + if (from < v.size()) { + if (to >= swiz.size()) { + swiz.resize(to + 1); + } + + swiz[to] = v[from]; + } + } + v = swiz; + } + + return v; } else { output = output->GetConnectedOutput(output->GetEffectInput()); } @@ -1649,7 +1671,7 @@ void Node::SaveImmediate(QXmlStreamWriter *writer, const QString &input, int ele value_t value = this->GetStandardValue(input, element); for (size_t i = 0; i < value.size(); i++) { - value_t::component_t v; + const value_t::component_t &v = value.at(i); writer->writeStartElement(QStringLiteral("track")); @@ -1805,20 +1827,22 @@ NodeInputImmediate *Node::CreateImmediate(const QString &input) void Node::ArrayResizeInternal(const QString &id, size_t size) { - Input* imm = GetInternalInputData(id); + Input* data = GetInternalInputData(id); - if (!imm) { + if (!data) { ReportInvalidInput("set array size", id, -1); return; } - if (imm->array_size != size) { + if (data->array_size != size) { // Update array size - if (imm->array_size < size) { + if (data->array_size < size) { // Size is larger, create any immediates that don't exist QVector& subinputs = array_immediates_[id]; for (size_t i=subinputs.size(); iset_split_standard_value(data->default_value); + subinputs.append(imm); } // Note that we do not delete any immediates when decreasing size since the user might still @@ -1826,8 +1850,8 @@ void Node::ArrayResizeInternal(const QString &id, size_t size) // equal subinputs_.size() } - int old_sz = imm->array_size; - imm->array_size = size; + int old_sz = data->array_size; + data->array_size = size; emit InputArraySizeChanged(id, old_sz, size); ParameterValueChanged(id, -1, TimeRange(RATIONAL_MIN, RATIONAL_MAX)); } diff --git a/app/node/node.h b/app/node/node.h index 64bba7a0d4..3041ba0a02 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -35,6 +35,7 @@ #include "node/keyframe.h" #include "node/inputimmediate.h" #include "node/param.h" +#include "node/swizzlemap.h" #include "render/audioplaybackcache.h" #include "render/audiowaveformcache.h" #include "render/framehashcache.h" @@ -563,19 +564,20 @@ class Node : public QObject class ValueHint { public: - ValueHint(const QString &tag = QString()) : - tag_(tag) - { - } + ValueHint() = default; const QString& tag() const { return tag_; } void set_tag(const QString &tag) { tag_ = tag; } + const SwizzleMap &swizzle() const { return swizzle_; } + void set_swizzle(const SwizzleMap &m) { swizzle_ = m; } + bool load(QXmlStreamReader *reader); void save(QXmlStreamWriter *writer) const; private: QString tag_; + SwizzleMap swizzle_; }; diff --git a/app/node/project/serializer/serializer210528.cpp b/app/node/project/serializer/serializer210528.cpp index 4f12d51b9f..768b08b97a 100644 --- a/app/node/project/serializer/serializer210528.cpp +++ b/app/node/project/serializer/serializer210528.cpp @@ -597,7 +597,9 @@ void ProjectSerializer210528::PostConnect(const XMLNodeData &xml_node_data) cons foreach (const XMLNodeData::SerializedConnection& con, xml_node_data.desired_connections) { if (Node *out = xml_node_data.node_ptrs.value(con.output_node)) { // Use output param as hint tag since we grandfathered those in - Node::ValueHint hint(con.output_param); + Node::ValueHint hint; + + hint.set_tag(con.output_param); Node::ConnectEdge(out, con.input); diff --git a/app/node/swizzlemap.h b/app/node/swizzlemap.h new file mode 100644 index 0000000000..9f22ab479f --- /dev/null +++ b/app/node/swizzlemap.h @@ -0,0 +1,61 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef SWIZZLE_H +#define SWIZZLE_H + +#include +#include + +namespace olive { + +class SwizzleMap +{ +public: + SwizzleMap() = default; + + bool empty() const { return map_.empty(); } + + void clear() { map_.clear(); } + + void insert(size_t to, size_t from) + { + map_[to] = from; + } + + void remove(size_t to) + { + map_.erase(to); + } + + bool operator==(const SwizzleMap &m) const { return map_ == m.map_; } + bool operator!=(const SwizzleMap &m) const { return map_ != m.map_; } + + std::map::const_iterator cbegin() const { return map_.cbegin(); } + std::map::const_iterator cend() const { return map_.cend(); } + +private: + std::map map_; + +}; + +} + +#endif // SWIZZLE_H diff --git a/app/node/value.cpp b/app/node/value.cpp index c7920dd50b..075d88a7f8 100644 --- a/app/node/value.cpp +++ b/app/node/value.cpp @@ -67,21 +67,28 @@ QString value_t::toSerializedString() const } return l.join(CHANNEL_SPLITTER); } else { - value_t stringified = this->converted(TYPE_STRING); - return stringified.toSerializedString(); + bool ok; + value_t stringified = this->converted(TYPE_STRING, &ok); + if (ok) { + return stringified.toSerializedString(); + } else { + return QString(); + } } } -value_t value_t::converted(type_t to) const +value_t value_t::converted(type_t to, bool *ok) const { // No-op if type is already requested if (this->type() == to) { + if (ok) *ok = true; return *this; } // Find converter, no-op if none found Converter_t c = converters_[this->type()][to]; if (!c) { + if (ok) *ok = false; return *this; } @@ -93,6 +100,7 @@ value_t value_t::converted(type_t to) const v.data_[0] = c(data_[0]); } + if (ok) *ok = true; return v; } @@ -198,12 +206,14 @@ void value_t::registerDefaultConverters() registerConverter(TYPE_RATIONAL, TYPE_DOUBLE, converter_RationalToDouble); } -value_t::component_t value_t::component_t::converted(type_t from, type_t to) const +value_t::component_t value_t::component_t::converted(type_t from, type_t to, bool *ok) const { if (Converter_t c = converters_[from][to]) { + if (ok) *ok = true; return c(*this); } + if (ok) *ok = false; return *this; } diff --git a/app/node/value.h b/app/node/value.h index f40359590e..e037cec808 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -99,12 +99,13 @@ class value_t template T get(bool *ok = nullptr) const { + T t = T(); + if (data_.has_value()) { - //qDebug() << "getting any type" << data_.type().name() << "attempting cast to" << typeid(T).name(); - return std::any_cast(data_); - } else { - return T(); + t = std::any_cast(data_); } + + return t; } template @@ -115,7 +116,7 @@ class value_t const std::any &data() const { return data_; } - component_t converted(type_t from, type_t to) const; + component_t converted(type_t from, type_t to, bool *ok = nullptr) const; static component_t fromSerializedString(type_t to, const QString &s); QString toSerializedString(type_t from) const; @@ -265,6 +266,7 @@ class value_t return at(channel).get(); } + void resize(size_t s) { data_.resize(s); } size_t size() const { return data_.size(); } std::vector &data() { return data_; } @@ -290,7 +292,7 @@ class value_t static value_t fromSerializedString(type_t target_type, const QString &s); QString toSerializedString() const; - value_t converted(type_t to) const; + value_t converted(type_t to, bool *ok = nullptr) const; typedef component_t(*Converter_t)(const component_t &v); static void registerConverter(const type_t &from, const type_t &to, Converter_t converter); diff --git a/app/task/precache/precachetask.cpp b/app/task/precache/precachetask.cpp index e8e4d134a2..5caa62c07d 100644 --- a/app/task/precache/precachetask.cpp +++ b/app/task/precache/precachetask.cpp @@ -48,7 +48,9 @@ PreCacheTask::PreCacheTask(Footage *footage, int index, Sequence* sequence) Node::CopyInputs(footage, footage_, false); Node::ConnectEdge(footage_, NodeInput(viewer(), ViewerOutput::kTextureInput)); - viewer()->SetValueHintForInput(ViewerOutput::kTextureInput, Track::Reference(Track::kVideo, index).ToString()); + Node::ValueHint hint; + hint.set_tag(Track::Reference(Track::kVideo, index).ToString()); + viewer()->SetValueHintForInput(ViewerOutput::kTextureInput, hint); SetTitle(tr("Pre-caching %1:%2").arg(footage_->filename(), QString::number(index))); } diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index a4aada805e..8008aaad6b 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -381,6 +381,8 @@ void NodeParamView::DeleteSelected() { if (keyframe_view_ && keyframe_view_->hasFocus()) { keyframe_view_->DeleteSelected(); + } else if (DeleteInsideWidgets()) { + return; } else if (!selected_nodes_.isEmpty()) { MultiUndoCommand *c = new MultiUndoCommand(); @@ -784,6 +786,11 @@ void NodeParamView::SortItemsInContext(NodeParamViewContext *context_item) } } +bool NodeParamView::DeleteInsideWidgets() +{ + return focused_node_ && focused_node_->DeleteSelected(); +} + NodeParamViewContext *NodeParamView::GetContextItemFromContext(Node *ctx) { Track::Type ctx_type = Track::kCount; diff --git a/app/widget/nodeparamview/nodeparamview.h b/app/widget/nodeparamview/nodeparamview.h index 3899f4e664..b610ff092d 100644 --- a/app/widget/nodeparamview/nodeparamview.h +++ b/app/widget/nodeparamview/nodeparamview.h @@ -123,6 +123,8 @@ public slots: void SortItemsInContext(NodeParamViewContext *context); + bool DeleteInsideWidgets(); + NodeParamViewContext *GetContextItemFromContext(Node *context); bool IsGroupMode() const diff --git a/app/widget/nodeparamview/nodeparamviewconnectedlabel.h b/app/widget/nodeparamview/nodeparamviewconnectedlabel.h index 16a7428af1..74fe0917a0 100644 --- a/app/widget/nodeparamview/nodeparamviewconnectedlabel.h +++ b/app/widget/nodeparamview/nodeparamviewconnectedlabel.h @@ -34,6 +34,8 @@ class NodeParamViewConnectedLabel : public QWidget { void SetViewerNode(ViewerOutput *viewer); + bool DeleteSelected(); + signals: void RequestSelectNode(Node *n); diff --git a/app/widget/nodeparamview/nodeparamviewitem.cpp b/app/widget/nodeparamview/nodeparamviewitem.cpp index 134dd2874b..6b12160ee2 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.cpp +++ b/app/widget/nodeparamview/nodeparamviewitem.cpp @@ -117,6 +117,15 @@ void NodeParamViewItem::SetInputChecked(const NodeInput &input, bool e) body_->SetInputChecked(input, e); } +bool NodeParamViewItem::DeleteSelected() +{ + if (body_) { + return body_->DeleteSelected(); + } + + return false; +} + NodeParamViewItemBody::NodeParamViewItemBody(Node* node, NodeParamViewCheckBoxBehavior create_checkboxes, QWidget *parent) : QWidget(parent), node_(node), @@ -177,6 +186,15 @@ NodeParamViewItemBody::NodeParamViewItemBody(Node* node, NodeParamViewCheckBoxBe } } +NodeParamViewItemBody::~NodeParamViewItemBody() +{ + for (auto it = input_ui_map_.cbegin(); it != input_ui_map_.cend(); it++) { + const InputUI &u = *it; + + delete u.widget_bridge; + } +} + void NodeParamViewItemBody::CreateWidgets(QGridLayout* layout, Node *node, const QString &input, int element, int row) { NodeInput input_ref(node, input, element); @@ -534,6 +552,17 @@ void NodeParamViewItemBody::SetInputChecked(const NodeInput &input, bool e) } } +bool NodeParamViewItemBody::DeleteSelected() +{ + for (auto it = input_ui_map_.cbegin(); it != input_ui_map_.cend(); it++) { + if (it->connected_label && it->connected_label->DeleteSelected()) { + return true; + } + } + + return false; +} + void NodeParamViewItemBody::ReplaceWidgets(const NodeInput &input) { InputUI ui = input_ui_map_.value(input); diff --git a/app/widget/nodeparamview/nodeparamviewitem.h b/app/widget/nodeparamview/nodeparamviewitem.h index 56149064c3..0b6e3a2f4f 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.h +++ b/app/widget/nodeparamview/nodeparamviewitem.h @@ -51,6 +51,8 @@ class NodeParamViewItemBody : public QWidget { public: NodeParamViewItemBody(Node* node, NodeParamViewCheckBoxBehavior create_checkboxes, QWidget* parent = nullptr); + virtual ~NodeParamViewItemBody() override; + void SetTimeTarget(ViewerOutput *target); void Retranslate(); @@ -62,6 +64,8 @@ class NodeParamViewItemBody : public QWidget { void SetInputChecked(const NodeInput &input, bool e); + bool DeleteSelected(); + signals: void RequestSelectNode(Node *node); @@ -211,6 +215,8 @@ class NodeParamViewItem : public NodeParamViewItemBase keyframe_connections_ = c; } + bool DeleteSelected(); + signals: void RequestSelectNode(Node *node); diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index 1123fc038f..b8a582fc92 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -79,9 +79,9 @@ void NodeParamViewWidgetBridge::CreateWidgets() // We assume the first data type is the "primary" type type_t t = GetDataType(); - if (t == TYPE_INTEGER) { - QString type = GetOuterInput().GetProperty(QStringLiteral("subtype")).toString(); + QString type = GetOuterInput().GetProperty(QStringLiteral("subtype")).toString(); + if (t == TYPE_INTEGER) { if (type == QStringLiteral("bool")) { widget_ = new BoolParamWidget(this); } else if (type == QStringLiteral("combo")) { @@ -92,8 +92,6 @@ void NodeParamViewWidgetBridge::CreateWidgets() } else if (t == TYPE_RATIONAL) { widget_ = new RationalSliderParamWidget(this); } else if (t == TYPE_DOUBLE) { - QString type; - if (type == QStringLiteral("color")) { widget_ = new ColorParamWidget(this); } else if (type == QStringLiteral("bezier")) { @@ -102,8 +100,6 @@ void NodeParamViewWidgetBridge::CreateWidgets() widget_ = new FloatSliderParamWidget(this); } } else if (t == TYPE_STRING) { - QString type; - if (type == QStringLiteral("file")) { widget_ = new FileParamWidget(this); } else if (type == QStringLiteral("font")) { diff --git a/app/widget/nodetreeview/CMakeLists.txt b/app/widget/nodetreeview/CMakeLists.txt index a8b5b6dce8..e24083c666 100644 --- a/app/widget/nodetreeview/CMakeLists.txt +++ b/app/widget/nodetreeview/CMakeLists.txt @@ -16,7 +16,7 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} - widget/nodetreeview/nodetreeview.h widget/nodetreeview/nodetreeview.cpp + widget/nodetreeview/nodetreeview.h PARENT_SCOPE ) diff --git a/app/widget/nodevaluetree/CMakeLists.txt b/app/widget/nodevaluetree/CMakeLists.txt index 44ed10d5b0..296f90af61 100644 --- a/app/widget/nodevaluetree/CMakeLists.txt +++ b/app/widget/nodevaluetree/CMakeLists.txt @@ -18,5 +18,7 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} widget/nodevaluetree/nodevaluetree.cpp widget/nodevaluetree/nodevaluetree.h + widget/nodevaluetree/valueswizzlewidget.cpp + widget/nodevaluetree/valueswizzlewidget.h PARENT_SCOPE ) diff --git a/app/widget/nodevaluetree/nodevaluetree.cpp b/app/widget/nodevaluetree/nodevaluetree.cpp index d0989944e7..6e6ecadd7f 100644 --- a/app/widget/nodevaluetree/nodevaluetree.cpp +++ b/app/widget/nodevaluetree/nodevaluetree.cpp @@ -1,6 +1,7 @@ #include "nodevaluetree.h" #include +#include #include "core.h" #include "node/nodeundo.h" @@ -27,12 +28,21 @@ NodeValueTree::NodeValueTree(QWidget *parent) : void NodeValueTree::SetNode(const NodeInput &input) { + if (input_ == input) { + return; + } + + if (Node *n = input_.node()) { + disconnect(n, &Node::InputValueHintChanged, this, &NodeValueTree::ValueHintChanged); + } + clear(); input_ = input; if (Node *connected_node = input.GetConnectedOutput()) { connected_node->Retranslate(); + connect(connected_node, &Node::InputValueHintChanged, this, &NodeValueTree::ValueHintChanged); const QVector &outputs = connected_node->outputs(); @@ -54,8 +64,29 @@ void NodeValueTree::SetNode(const NodeInput &input) } setItemWidget(item, 0, radio); + + QTreeWidgetItem *swizzler_items = new QTreeWidgetItem(item); + swizzler_items->setFlags(Qt::NoItemFlags); + + ValueSwizzleWidget *b = new ValueSwizzleWidget(); + b->set_channels(4, 4); + b->set(vh.swizzle()); + connect(b, &ValueSwizzleWidget::value_changed, this, &NodeValueTree::SwizzleChanged); + this->setItemWidget(swizzler_items, 1, b); + } + } +} + +bool NodeValueTree::DeleteSelected() +{ + for (int i = 0; i < topLevelItemCount(); i++) { + ValueSwizzleWidget *w = GetSwizzleWidgetFromTopLevelItem(i); + if (w->delete_selected()) { + return true; } } + + return false; } void NodeValueTree::changeEvent(QEvent *event) @@ -72,13 +103,20 @@ void NodeValueTree::Retranslate() setHeaderLabels({QString(), tr("Output")}); } +ValueSwizzleWidget *NodeValueTree::GetSwizzleWidgetFromTopLevelItem(int i) +{ + return static_cast(itemWidget(topLevelItem(i)->child(0), 1)); +} + void NodeValueTree::RadioButtonChecked(bool e) { if (e) { QRadioButton *btn = static_cast(sender()); - Node::ValueHint hint = btn->property("output").toString(); + QString tag = btn->property("output").toString(); NodeInput input = btn->property("input").value(); + Node::ValueHint hint = input.node()->GetValueHintForInput(input.input(), input.element()); + hint.set_tag(tag); Core::instance()->undo_stack()->push(new NodeSetValueHintCommand(input, hint), tr("Switched Connected Output Parameter")); } } @@ -88,4 +126,31 @@ void NodeValueTree::Update() SetNode(input_); } +void NodeValueTree::SwizzleChanged(const SwizzleMap &map) +{ + if (input_.IsValid()) { + Node::ValueHint hint = input_.node()->GetValueHintForInput(input_.input(), input_.element()); + hint.set_swizzle(map); + Core::instance()->undo_stack()->push(new NodeSetValueHintCommand(input_, hint), tr("Edited Channel Swizzle For Input")); + } +} + +void NodeValueTree::ValueHintChanged(const NodeInput &input) +{ + if (input == input_) { + Node::ValueHint vh = input.node()->GetValueHintForInput(input.input(), input.element()); + for (int i = 0; i < this->topLevelItemCount(); i++) { + QTreeWidgetItem *item = this->topLevelItem(i); + QRadioButton *rb = static_cast(this->itemWidget(item, 0)); + + if (rb->property("output").toString() == vh.tag()) { + rb->setChecked(true); + + ValueSwizzleWidget *b = GetSwizzleWidgetFromTopLevelItem(i); + b->set(vh.swizzle()); + } + } + } +} + } diff --git a/app/widget/nodevaluetree/nodevaluetree.h b/app/widget/nodevaluetree/nodevaluetree.h index 58275d4351..11e6bb65f2 100644 --- a/app/widget/nodevaluetree/nodevaluetree.h +++ b/app/widget/nodevaluetree/nodevaluetree.h @@ -5,6 +5,7 @@ #include #include "node/node.h" +#include "valueswizzlewidget.h" namespace olive { @@ -16,12 +17,16 @@ class NodeValueTree : public QTreeWidget void SetNode(const NodeInput &input); + bool DeleteSelected(); + protected: virtual void changeEvent(QEvent *event) override; private: void Retranslate(); + ValueSwizzleWidget *GetSwizzleWidgetFromTopLevelItem(int i); + NodeInput input_; private slots: @@ -29,6 +34,10 @@ private slots: void Update(); + void SwizzleChanged(const SwizzleMap &map); + + void ValueHintChanged(const NodeInput &input); + }; } diff --git a/app/widget/nodevaluetree/valueswizzlewidget.cpp b/app/widget/nodevaluetree/valueswizzlewidget.cpp new file mode 100644 index 0000000000..a4df9725f3 --- /dev/null +++ b/app/widget/nodevaluetree/valueswizzlewidget.cpp @@ -0,0 +1,323 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "valueswizzlewidget.h" + +#include +#include +#include + +namespace olive { + +#define super QGraphicsView + +ValueSwizzleWidget::ValueSwizzleWidget(QWidget *parent) : + super{parent} +{ + setFrameShape(QFrame::NoFrame); + setFrameShadow(QFrame::Plain); + setRenderHint(QPainter::Antialiasing); + setBackgroundRole(QPalette::Base); + setAlignment(Qt::AlignLeft | Qt::AlignTop); + setDragMode(RubberBandDrag); + + scene_ = new QGraphicsScene(this); + setScene(scene_); + + new_item_ = nullptr; + //labels_ = kRGBALabels; + labels_ = kXYZWLabels; + + channel_height_ = this->fontMetrics().height() * 3 / 2; + channel_width_ = channel_height_ * 2; + from_count_ = 0; + to_count_ = 0; + + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); +} + +bool ValueSwizzleWidget::delete_selected() +{ + auto sel = scene_->selectedItems(); + if (sel.isEmpty()) { + return false; + } + + auto map = get_map_from_connectors(); + + for (auto s : sel) { + auto conn = static_cast(s); + map.remove(conn->to()); + } + + set(map); + emit value_changed(map); + + return true; +} + +void ValueSwizzleWidget::set(const SwizzleMap &map) +{ + clear_all(); + + cached_map_ = map; + + if (cached_map_.empty()) { + // Connect everything directly for empty maps + size_t lim = std::min(from_count_, to_count_); + for (size_t i = 0; i < lim; i++) { + make_item(i, i); + } + } else { + for (auto it = cached_map_.cbegin(); it != cached_map_.cend(); it++) { + make_item(it->second, it->first); + } + } + + adjust_all(); +} + +void ValueSwizzleWidget::set_channels(size_t from, size_t to) +{ + from_count_ = from; + to_count_ = to; + setFixedHeight(std::max(from, to) * channel_height_); + + set(cached_map_); +} + +void ValueSwizzleWidget::drawBackground(QPainter *p, const QRectF &r) +{ + for (size_t i = 0; i < from_count_; i++) { + draw_channel(p, i, 0); + } + + for (size_t i = 0; i < to_count_; i++) { + draw_channel(p, i, width() - channel_width_ - 1); + } +} + +void ValueSwizzleWidget::mousePressEvent(QMouseEvent *e) +{ + if (is_inside_bounds(e->x())) { + drag_from_ = channel_is_from(e->x()); + drag_index_ = get_channel_index_from_y(e->y()); + drag_start_ = get_connect_point_of_channel(drag_from_, drag_index_); + new_item_connected_ = false; + + if (!drag_from_) { + // "to"s can only have one connection, so if there's already a connection here, we'll remove it + for (auto it = connectors_.cbegin(); it != connectors_.cend(); it++) { + SwizzleConnectorItem *s = *it; + if (s->to() == drag_index_) { + // Grab this item + new_item_ = s; + + // Flip so we're dragging the connector from its old position + drag_from_ = true; + drag_index_ = new_item_->from(); + drag_start_ = get_connect_point_of_channel(true, drag_index_); + new_item_connected_ = true; + + // Remove from list because we'll delete this later + connectors_.erase(it); + break; + } + } + } + + if (!new_item_) { + new_item_ = new SwizzleConnectorItem(); + if (drag_from_) { + new_item_->set_from(drag_index_); + } else { + new_item_->set_to(drag_index_); + } + scene_->addItem(new_item_); + } + } else { + super::mousePressEvent(e); + } +} + +void ValueSwizzleWidget::mouseMoveEvent(QMouseEvent *e) +{ + if (new_item_) { + QPoint end_point; + + new_item_connected_ = false; + + if (is_inside_bounds(e->x())) { + bool is_from = channel_is_from(e->x()); + if (drag_from_ != is_from) { + size_t index = get_channel_index_from_y(e->y()); + if ((is_from && index < from_count_) || (!is_from && index < to_count_)) { + end_point = get_connect_point_of_channel(is_from, index); + new_item_connected_ = true; + if (drag_from_) { + new_item_->set_to(index); + } else { + new_item_->set_from(index); + } + } + } + } + + if (!new_item_connected_) { + end_point = e->pos(); + } + new_item_->SetConnected(new_item_connected_); + + new_item_->SetPoints(drag_start_, end_point); + } else { + super::mouseMoveEvent(e); + } +} + +void ValueSwizzleWidget::mouseReleaseEvent(QMouseEvent *e) +{ + if (new_item_) { + SwizzleMap map = get_map_from_connectors(); + + if (new_item_connected_) { + map.insert(new_item_->to(), new_item_->from()); + } + + delete new_item_; + new_item_ = nullptr; + + set(map); + emit value_changed(map); + } else { + super::mouseReleaseEvent(e); + } +} + +void ValueSwizzleWidget::resizeEvent(QResizeEvent *e) +{ + this->setSceneRect(this->viewport()->rect()); + + adjust_all(); + + super::resizeEvent(e); +} + +void ValueSwizzleWidget::draw_channel(QPainter *p, size_t i, int x) +{ + static const size_t kChannelColorCount = 4; + static const QColor kDefaultColor = QColor(32, 32, 32); + const QColor kChannelColors[kChannelColorCount] = { + QColor(160, 32, 32), + QColor(32, 160, 32), + QColor(32, 32, 160), + QColor(64, 64, 64) + }; + + QRect r(x, i * channel_height_, channel_width_, channel_height_); + + p->setPen(Qt::black); + p->setBrush(i < kChannelColorCount ? kChannelColors[i] : kDefaultColor); + p->drawRect(r); + + p->setPen(Qt::white); + p->drawText(r, Qt::AlignCenter, get_label_text(i)); +} + +QPoint ValueSwizzleWidget::get_connect_point_of_channel(bool from, size_t index) +{ + QPoint p; + + if (from) { + p.setX(channel_width_); + } else { + p.setX(get_right_channel_bound()); + } + p.setY(index * channel_height_ + channel_height_ / 2); + + return p; +} + +void ValueSwizzleWidget::adjust_all() +{ + for (auto it = connectors_.cbegin(); it != connectors_.cend(); it++) { + SwizzleConnectorItem *s = *it; + s->SetPoints(get_connect_point_of_channel(true, s->from()), get_connect_point_of_channel(false, s->to())); + } +} + +void ValueSwizzleWidget::make_item(size_t from, size_t to) +{ + SwizzleConnectorItem *s = new SwizzleConnectorItem(); + s->set_from(from); + s->set_to(to); + s->SetConnected(true); + scene_->addItem(s); + connectors_.push_back(s); +} + +void ValueSwizzleWidget::clear_all() +{ + qDeleteAll(connectors_); + connectors_.clear(); +} + +SwizzleMap ValueSwizzleWidget::get_map_from_connectors() const +{ + SwizzleMap map; + for (auto it = connectors_.cbegin(); it != connectors_.cend(); it++) { + SwizzleConnectorItem *s = *it; + map.insert(s->to(), s->from()); + } + return map; +} + +QString ValueSwizzleWidget::get_label_text(size_t index) const +{ + switch (labels_) { + case kNumberLabels: + break; + case kXYZWLabels: + switch (index) { + case 0: return tr("X"); + case 1: return tr("Y"); + case 2: return tr("Z"); + case 3: return tr("W"); + } + break; + case kRGBALabels: + switch (index) { + case 0: return tr("R"); + case 1: return tr("G"); + case 2: return tr("B"); + case 3: return tr("A"); + } + break; + } + + return QString::number(index); +} + +SwizzleConnectorItem::SwizzleConnectorItem(QGraphicsItem *parent) : + CurvedConnectorItem(parent) +{ +} + +} diff --git a/app/widget/nodevaluetree/valueswizzlewidget.h b/app/widget/nodevaluetree/valueswizzlewidget.h new file mode 100644 index 0000000000..f1bce8ae07 --- /dev/null +++ b/app/widget/nodevaluetree/valueswizzlewidget.h @@ -0,0 +1,120 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef VALUESWIZZLEWIDGET_H +#define VALUESWIZZLEWIDGET_H + +#include +#include +#include +#include + +#include "node/swizzlemap.h" +#include "widget/nodeview/curvedconnectoritem.h" + +namespace olive { + +class SwizzleConnectorItem : public CurvedConnectorItem +{ +public: + SwizzleConnectorItem(QGraphicsItem *parent = nullptr); + + size_t from() const { return from_index_; } + size_t to() const { return to_index_; } + void set_from(size_t f) { from_index_ = f; } + void set_to(size_t t) { to_index_ = t; } + +private: + size_t from_index_; + size_t to_index_; + +}; + +class ValueSwizzleWidget : public QGraphicsView +{ + Q_OBJECT +public: + enum Labels + { + kNumberLabels, + kXYZWLabels, + kRGBALabels, + }; + + explicit ValueSwizzleWidget(QWidget *parent = nullptr); + + bool delete_selected(); + + void set(const SwizzleMap &map); + void set_channels(size_t from, size_t to); + + void set_label_type(Labels l) { labels_ = l; } + +protected: + virtual void drawBackground(QPainter *p, const QRectF &r) override; + + virtual void mousePressEvent(QMouseEvent *e) override; + virtual void mouseMoveEvent(QMouseEvent *e) override; + virtual void mouseReleaseEvent(QMouseEvent *e) override; + + virtual void resizeEvent(QResizeEvent *e) override; + +signals: + void value_changed(const SwizzleMap &map); + +private: + QString get_label_text(size_t index) const; + + void draw_channel(QPainter *p, size_t i, int x); + inline bool channel_is_from(int x) const { return x < get_left_channel_bound(); } + inline bool channel_is_to(int x) const { return x >= get_right_channel_bound(); } + inline bool is_inside_bounds(int x) const { return channel_is_from(x) || channel_is_to(x); } + inline int get_left_channel_bound() const { return channel_width_; } + inline int get_right_channel_bound() const { return viewport()->width() - channel_width_ - 1; } + inline size_t get_channel_index_from_y(int y) const { return y / channel_height_; } + + QPoint get_connect_point_of_channel(bool from, size_t index); + + void adjust_all(); + void make_item(size_t from, size_t to); + void clear_all(); + SwizzleMap get_map_from_connectors() const; + + QGraphicsScene *scene_; + int channel_width_; + int channel_height_; + size_t from_count_; + size_t to_count_; + Labels labels_; + bool drag_from_; + size_t drag_index_; + bool new_item_connected_; + + SwizzleMap cached_map_; + SwizzleConnectorItem *new_item_; + QPoint drag_start_; + + std::vector connectors_; + +}; + +} + +#endif // VALUESWIZZLEWIDGET_H diff --git a/app/widget/nodeview/CMakeLists.txt b/app/widget/nodeview/CMakeLists.txt index 184ef1190c..60d6738ba8 100644 --- a/app/widget/nodeview/CMakeLists.txt +++ b/app/widget/nodeview/CMakeLists.txt @@ -16,6 +16,8 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} + widget/nodeview/curvedconnectoritem.cpp + widget/nodeview/curvedconnectoritem.h widget/nodeview/nodeview.cpp widget/nodeview/nodeview.h widget/nodeview/nodeviewcommon.h diff --git a/app/widget/nodeview/curvedconnectoritem.cpp b/app/widget/nodeview/curvedconnectoritem.cpp new file mode 100644 index 0000000000..3b73128242 --- /dev/null +++ b/app/widget/nodeview/curvedconnectoritem.cpp @@ -0,0 +1,195 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "curvedconnectoritem.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "common/lerp.h" + +namespace olive { + +using namespace core; + +CurvedConnectorItem::CurvedConnectorItem(QGraphicsItem* parent) : + QGraphicsPathItem(parent) +{ + connected_ = false; + highlighted_ = false; + curved_ = true; + + setFlag(QGraphicsItem::ItemIsSelectable); + + // Ensures this UI object is drawn behind other objects + setZValue(-1); + + // Use font metrics to set edge width for basic high DPI support + edge_width_ = QFontMetrics(QFont()).height() / 12; +} + +void CurvedConnectorItem::SetConnected(bool c) +{ + connected_ = c; + + update(); +} + +void CurvedConnectorItem::SetHighlighted(bool e) +{ + highlighted_ = e; + + update(); +} + +void CurvedConnectorItem::SetPoints(const QPointF &start, const QPointF &end) +{ + cached_start_ = start; + cached_end_ = end; + + UpdateCurve(); +} + +void CurvedConnectorItem::SetCurved(bool e) +{ + curved_ = e; + + UpdateCurve(); +} + +void CurvedConnectorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) +{ + QPalette::ColorGroup group; + QPalette::ColorRole role; + + if (connected_) { + group = QPalette::Active; + } else { + group = QPalette::Disabled; + } + + if (highlighted_ != bool(option->state & QStyle::State_Selected)) { + role = QPalette::Highlight; + } else { + role = QPalette::Text; + } + + // Draw main path + QColor edge_color = qApp->palette().color(group, role); + + painter->setPen(QPen(edge_color, edge_width_)); + painter->setBrush(Qt::NoBrush); + painter->drawPath(path()); +} + +NodeViewCommon::FlowDirection CurvedConnectorItem::GetFromDirection() const +{ + return NodeViewCommon::kLeftToRight; +} + +NodeViewCommon::FlowDirection CurvedConnectorItem::GetToDirection() const +{ + return NodeViewCommon::kLeftToRight; +} + +void CurvedConnectorItem::UpdateCurve() +{ + const QPointF &start = cached_start_; + const QPointF &end = cached_end_; + + QPainterPath path; + path.moveTo(start); + + double angle = std::atan2(end.y() - start.y(), end.x() - start.x()); + + if (curved_) { + + double half_x = lerp(start.x(), end.x(), 0.5); + double half_y = lerp(start.y(), end.y(), 0.5); + + QPointF cp1, cp2; + + NodeViewCommon::FlowDirection from_flow = GetFromDirection(); + NodeViewCommon::FlowDirection to_flow = GetToDirection(); + + if (from_flow == NodeViewCommon::kInvalidDirection && to_flow == NodeViewCommon::kInvalidDirection) { + // This is a technically unsupported scenario, but to avoid issues, we'll use a fallback + from_flow = NodeViewCommon::kLeftToRight; + to_flow = NodeViewCommon::kLeftToRight; + } else if (from_flow == NodeViewCommon::kInvalidDirection) { + from_flow = to_flow; + } else if (to_flow == NodeViewCommon::kInvalidDirection) { + to_flow = from_flow; + } + + if (NodeViewCommon::GetFlowOrientation(from_flow) == Qt::Horizontal) { + cp1 = QPointF(half_x, start.y()); + } else { + cp1 = QPointF(start.x(), half_y); + } + + if (NodeViewCommon::GetFlowOrientation(to_flow) == Qt::Horizontal) { + cp2 = QPointF(half_x, end.y()); + } else { + cp2 = QPointF(end.x(), half_y); + } + + path.cubicTo(cp1, cp2, end); + + if (!qFuzzyCompare(start.x(), end.x())) { + double continue_x = end.x() - std::cos(angle); + + double x1 = start.x(); + double x2 = cp1.x(); + double x3 = cp2.x(); + double x4 = end.x(); + double y1 = start.y(); + double y2 = cp1.y(); + double y3 = cp2.y(); + double y4 = end.y(); + + if (start.x() >= end.x()) { + std::swap(x1, x4); + std::swap(x2, x3); + std::swap(y1, y4); + std::swap(y2, y3); + } + + double t = Bezier::CubicXtoT(continue_x, x1, x2, x3, x4); + double y = Bezier::CubicTtoY(y1, y2, y3, y4, t); + + angle = std::atan2(end.y() - y, end.x() - continue_x); + } + + } else { + + path.lineTo(end); + + } + + setPath(mapFromScene(path)); +} + +} diff --git a/app/widget/nodeview/curvedconnectoritem.h b/app/widget/nodeview/curvedconnectoritem.h new file mode 100644 index 0000000000..24f54608c7 --- /dev/null +++ b/app/widget/nodeview/curvedconnectoritem.h @@ -0,0 +1,93 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef CURVEDCONNECTORITEM_H +#define CURVEDCONNECTORITEM_H + +#include + +#include "nodeviewcommon.h" + +namespace olive { + +class CurvedConnectorItem : public QGraphicsPathItem +{ +public: + CurvedConnectorItem(QGraphicsItem* parent = nullptr); + + /** + * @brief Set the connected state of this line + * + * When the edge is not connected, it visually depicts this by coloring the line grey. When an edge is connected or + * a potential connection is valid, the line is colored white. This function sets whether the line should be grey + * (false) or white (true). + * + * Using SetEdge() automatically sets this to true. Under most circumstances this should be left alone, and only + * be set when an edge is being created/dragged. + */ + void SetConnected(bool c); + + bool IsConnected() const + { + return connected_; + } + + /** + * @brief Set points to create curve from + */ + void SetPoints(const QPointF& start, const QPointF& end); + + /** + * @brief Set highlighted state + * + * Changes color of edge. + */ + void SetHighlighted(bool e); + + /** + * @brief Set whether edges should be drawn as curved or as straight lines + */ + void SetCurved(bool e); + +protected: + virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; + + virtual NodeViewCommon::FlowDirection GetFromDirection() const; + virtual NodeViewCommon::FlowDirection GetToDirection() const; + +private: + void UpdateCurve(); + + bool connected_; + + bool highlighted_; + + bool curved_; + + int edge_width_; + + QPointF cached_start_; + QPointF cached_end_; + +}; + +} + +#endif // CURVEDCONNECTORITEM_H diff --git a/app/widget/nodeview/nodeviewedge.cpp b/app/widget/nodeview/nodeviewedge.cpp index fd182fb8fd..b5a63b400c 100644 --- a/app/widget/nodeview/nodeviewedge.cpp +++ b/app/widget/nodeview/nodeviewedge.cpp @@ -22,42 +22,38 @@ #include #include -#include -#include -#include "common/lerp.h" #include "nodeview.h" #include "nodeviewitem.h" #include "nodeviewscene.h" namespace olive { -#define super QGraphicsPathItem +#define super CurvedConnectorItem + +NodeViewEdge::NodeViewEdge(QGraphicsItem *parent) : + super(parent), + from_item_(nullptr), + to_item_(nullptr) +{ +} NodeViewEdge::NodeViewEdge(Node *output, const NodeInput &input, NodeViewItem* from_item, NodeViewItem* to_item, QGraphicsItem* parent) : - super(parent), - output_(output), - input_(input), - from_item_(from_item), - to_item_(to_item) + NodeViewEdge(parent) { - Init(); + output_ = output; + input_ = input; + from_item_ = from_item; + to_item_ = to_item; + SetConnected(true); from_item_->AddEdge(this); to_item_->AddEdge(this); } -NodeViewEdge::NodeViewEdge(QGraphicsItem *parent) : - QGraphicsPathItem(parent), - from_item_(nullptr), - to_item_(nullptr) -{ - Init(); -} - NodeViewEdge::~NodeViewEdge() { if (from_item_) { @@ -105,151 +101,14 @@ void NodeViewEdge::Adjust() SetPoints(from_item()->GetOutputPoint(), to_item()->GetInputPoint()); } -void NodeViewEdge::SetConnected(bool c) -{ - connected_ = c; - - update(); -} - -void NodeViewEdge::SetHighlighted(bool e) -{ - highlighted_ = e; - - update(); -} - -void NodeViewEdge::SetPoints(const QPointF &start, const QPointF &end) -{ - cached_start_ = start; - cached_end_ = end; - - UpdateCurve(); -} - -void NodeViewEdge::SetCurved(bool e) +NodeViewCommon::FlowDirection NodeViewEdge::GetFromDirection() const { - curved_ = e; - - UpdateCurve(); + return from_item_ ? from_item_->GetFlowDirection() : NodeViewCommon::kInvalidDirection; } -void NodeViewEdge::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) +NodeViewCommon::FlowDirection NodeViewEdge::GetToDirection() const { - QPalette::ColorGroup group; - QPalette::ColorRole role; - - if (connected_) { - group = QPalette::Active; - } else { - group = QPalette::Disabled; - } - - if (highlighted_ != bool(option->state & QStyle::State_Selected)) { - role = QPalette::Highlight; - } else { - role = QPalette::Text; - } - - // Draw main path - QColor edge_color = qApp->palette().color(group, role); - - painter->setPen(QPen(edge_color, edge_width_)); - painter->setBrush(Qt::NoBrush); - painter->drawPath(path()); -} - -void NodeViewEdge::Init() -{ - connected_ = false; - highlighted_ = false; - curved_ = true; - - setFlag(QGraphicsItem::ItemIsSelectable); - - // Ensures this UI object is drawn behind other objects - setZValue(-1); - - // Use font metrics to set edge width for basic high DPI support - edge_width_ = QFontMetrics(QFont()).height() / 12; -} - -void NodeViewEdge::UpdateCurve() -{ - const QPointF &start = cached_start_; - const QPointF &end = cached_end_; - - QPainterPath path; - path.moveTo(start); - - double angle = std::atan2(end.y() - start.y(), end.x() - start.x()); - - if (curved_) { - - double half_x = lerp(start.x(), end.x(), 0.5); - double half_y = lerp(start.y(), end.y(), 0.5); - - QPointF cp1, cp2; - - NodeViewCommon::FlowDirection from_flow = from_item_ ? from_item_->GetFlowDirection() : NodeViewCommon::kInvalidDirection; - NodeViewCommon::FlowDirection to_flow = to_item_ ? to_item_->GetFlowDirection() : NodeViewCommon::kInvalidDirection; - - if (from_flow == NodeViewCommon::kInvalidDirection && to_flow == NodeViewCommon::kInvalidDirection) { - // This is a technically unsupported scenario, but to avoid issues, we'll use a fallback - from_flow = NodeViewCommon::kLeftToRight; - to_flow = NodeViewCommon::kLeftToRight; - } else if (from_flow == NodeViewCommon::kInvalidDirection) { - from_flow = to_flow; - } else if (to_flow == NodeViewCommon::kInvalidDirection) { - to_flow = from_flow; - } - - if (NodeViewCommon::GetFlowOrientation(from_flow) == Qt::Horizontal) { - cp1 = QPointF(half_x, start.y()); - } else { - cp1 = QPointF(start.x(), half_y); - } - - if (NodeViewCommon::GetFlowOrientation(to_flow) == Qt::Horizontal) { - cp2 = QPointF(half_x, end.y()); - } else { - cp2 = QPointF(end.x(), half_y); - } - - path.cubicTo(cp1, cp2, end); - - if (!qFuzzyCompare(start.x(), end.x())) { - double continue_x = end.x() - std::cos(angle); - - double x1 = start.x(); - double x2 = cp1.x(); - double x3 = cp2.x(); - double x4 = end.x(); - double y1 = start.y(); - double y2 = cp1.y(); - double y3 = cp2.y(); - double y4 = end.y(); - - if (start.x() >= end.x()) { - std::swap(x1, x4); - std::swap(x2, x3); - std::swap(y1, y4); - std::swap(y2, y3); - } - - double t = Bezier::CubicXtoT(continue_x, x1, x2, x3, x4); - double y = Bezier::CubicTtoY(y1, y2, y3, y4, t); - - angle = std::atan2(end.y() - y, end.x() - continue_x); - } - - } else { - - path.lineTo(end); - - } - - setPath(mapFromScene(path)); + return to_item_ ? to_item_->GetFlowDirection() : NodeViewCommon::kInvalidDirection; } } diff --git a/app/widget/nodeview/nodeviewedge.h b/app/widget/nodeview/nodeviewedge.h index 6e17dea307..8abad088bd 100644 --- a/app/widget/nodeview/nodeviewedge.h +++ b/app/widget/nodeview/nodeviewedge.h @@ -24,7 +24,7 @@ #include #include -#include "nodeviewcommon.h" +#include "curvedconnectoritem.h" #include "node/node.h" namespace olive { @@ -36,7 +36,7 @@ class NodeViewItem; * * A fairly simple line widget use to visualize a connection between two node parameters (a NodeEdge). */ -class NodeViewEdge : public QGraphicsPathItem +class NodeViewEdge : public CurvedConnectorItem { public: NodeViewEdge(Node *output, const NodeInput& input, @@ -47,79 +47,22 @@ class NodeViewEdge : public QGraphicsPathItem virtual ~NodeViewEdge() override; - Node *output() const - { - return output_; - } - - const NodeInput& input() const - { - return input_; - } - - int element() const - { - return element_; - } - - NodeViewItem* from_item() const - { - return from_item_; - } - - NodeViewItem* to_item() const - { - return to_item_; - } + Node *output() const { return output_; } + const NodeInput& input() const { return input_; } + int element() const { return element_; } + NodeViewItem* from_item() const { return from_item_; } + NodeViewItem* to_item() const { return to_item_; } void set_from_item(NodeViewItem *i); - void set_to_item(NodeViewItem *i); void Adjust(); - /** - * @brief Set the connected state of this line - * - * When the edge is not connected, it visually depicts this by coloring the line grey. When an edge is connected or - * a potential connection is valid, the line is colored white. This function sets whether the line should be grey - * (false) or white (true). - * - * Using SetEdge() automatically sets this to true. Under most circumstances this should be left alone, and only - * be set when an edge is being created/dragged. - */ - void SetConnected(bool c); - - bool IsConnected() const - { - return connected_; - } - - /** - * @brief Set highlighted state - * - * Changes color of edge. - */ - void SetHighlighted(bool e); - - /** - * @brief Set points to create curve from - */ - void SetPoints(const QPointF& start, const QPointF& end); - - /** - * @brief Set whether edges should be drawn as curved or as straight lines - */ - void SetCurved(bool e); - protected: - virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; + virtual NodeViewCommon::FlowDirection GetFromDirection() const override; + virtual NodeViewCommon::FlowDirection GetToDirection() const override; private: - void Init(); - - void UpdateCurve(); - Node *output_; NodeInput input_; @@ -130,17 +73,6 @@ class NodeViewEdge : public QGraphicsPathItem NodeViewItem* to_item_; - int edge_width_; - - bool connected_; - - bool highlighted_; - - bool curved_; - - QPointF cached_start_; - QPointF cached_end_; - }; } diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index 5f0921c21c..7d77ab5778 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -448,13 +448,16 @@ void ImportTool::DropGhosts(bool insert, MultiUndoCommand *parent_command) dep_pos++; + Node::ValueHint hint; + hint.set_tag(footage_stream.output); + switch (Track::Reference::TypeFromString(footage_stream.output)) { case Track::kVideo: { TransformDistortNode* transform = new TransformDistortNode(); command->add_child(new NodeAddCommand(dst_graph, transform)); - command->add_child(new NodeSetValueHintCommand(transform, TransformDistortNode::kTextureInput, -1, footage_stream.output)); + command->add_child(new NodeSetValueHintCommand(transform, TransformDistortNode::kTextureInput, -1, hint)); command->add_child(new NodeEdgeAddCommand(footage_stream.footage, NodeInput(transform, TransformDistortNode::kTextureInput))); command->add_child(new NodeEdgeAddCommand(transform, NodeInput(clip, ClipBlock::kBufferIn))); @@ -466,7 +469,7 @@ void ImportTool::DropGhosts(bool insert, MultiUndoCommand *parent_command) VolumeNode* volume_node = new VolumeNode(); command->add_child(new NodeAddCommand(dst_graph, volume_node)); - command->add_child(new NodeSetValueHintCommand(volume_node, VolumeNode::kSamplesInput, -1, footage_stream.output)); + command->add_child(new NodeSetValueHintCommand(volume_node, VolumeNode::kSamplesInput, -1, hint)); command->add_child(new NodeEdgeAddCommand(footage_stream.footage, NodeInput(volume_node, VolumeNode::kSamplesInput))); command->add_child(new NodeEdgeAddCommand(volume_node, NodeInput(clip, ClipBlock::kBufferIn))); From a6c5cf9074d86045e945b139e0a03b82990e4095 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Fri, 19 May 2023 21:07:20 -0700 Subject: [PATCH 24/71] show "not connected" label for inputs without widgets --- .../nodeparamviewconnectedlabel.cpp | 32 ++++++++++++------- .../nodeparamviewconnectedlabel.h | 5 +++ .../nodeparamview/nodeparamviewitem.cpp | 2 +- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp b/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp index 1ce061c87f..2f2bb569b0 100644 --- a/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp +++ b/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp @@ -26,7 +26,6 @@ #include "core.h" #include "node/node.h" #include "node/nodeundo.h" -#include "widget/collapsebutton/collapsebutton.h" #include "widget/menu/menu.h" namespace olive { @@ -51,11 +50,12 @@ NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(const NodeInput &input, label_layout->setContentsMargins(0, 0, 0, 0); layout->addLayout(label_layout); - CollapseButton *collapse_btn = new CollapseButton(this); - collapse_btn->setChecked(false); - label_layout->addWidget(collapse_btn); + collapse_btn_ = new CollapseButton(this); + collapse_btn_->setChecked(false); + label_layout->addWidget(collapse_btn_); - label_layout->addWidget(new QLabel(tr("Connected to"), this)); + prefix_lbl_ = new QLabel(this); + label_layout->addWidget(prefix_lbl_); connected_to_lbl_ = new ClickableLabel(this); connected_to_lbl_->setCursor(Qt::PointingHandCursor); @@ -83,7 +83,7 @@ NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(const NodeInput &input, // Creating the tree is expensive, hold off until the user specifically requests it value_tree_ = nullptr; - connect(collapse_btn, &CollapseButton::toggled, this, &NodeParamViewConnectedLabel::SetValueTreeVisible); + connect(collapse_btn_, &CollapseButton::toggled, this, &NodeParamViewConnectedLabel::SetValueTreeVisible); } void NodeParamViewConnectedLabel::SetViewerNode(ViewerOutput *viewer) @@ -100,6 +100,15 @@ void NodeParamViewConnectedLabel::SetViewerNode(ViewerOutput *viewer) } } +bool NodeParamViewConnectedLabel::DeleteSelected() +{ + if (value_tree_) { + return value_tree_->DeleteSelected(); + } + + return false; +} + void NodeParamViewConnectedLabel::CreateTree() { // Set up table area @@ -152,15 +161,16 @@ void NodeParamViewConnectedLabel::ConnectionClicked() void NodeParamViewConnectedLabel::UpdateLabel() { - QString s; + collapse_btn_->setVisible(connected_node_); + connected_to_lbl_->setVisible(connected_node_); if (connected_node_) { - s = connected_node_->Name(); + prefix_lbl_->setText(tr("Connected to")); + connected_to_lbl_->setText(connected_node_->GetLabelAndName()); + prefix_lbl_->setForegroundRole(QPalette::Text); } else { - s = tr("Nothing"); + prefix_lbl_->setText(tr("(Not Connected)")); } - - connected_to_lbl_->setText(s); } void NodeParamViewConnectedLabel::UpdateValueTree() diff --git a/app/widget/nodeparamview/nodeparamviewconnectedlabel.h b/app/widget/nodeparamview/nodeparamviewconnectedlabel.h index 74fe0917a0..5b2b885e14 100644 --- a/app/widget/nodeparamview/nodeparamviewconnectedlabel.h +++ b/app/widget/nodeparamview/nodeparamviewconnectedlabel.h @@ -23,6 +23,7 @@ #include "node/param.h" #include "widget/clickablelabel/clickablelabel.h" +#include "widget/collapsebutton/collapsebutton.h" #include "widget/nodevaluetree/nodevaluetree.h" namespace olive { @@ -55,6 +56,10 @@ private slots: void CreateTree(); + CollapseButton *collapse_btn_; + + QLabel *prefix_lbl_; + ClickableLabel* connected_to_lbl_; NodeInput input_; diff --git a/app/widget/nodeparamview/nodeparamviewitem.cpp b/app/widget/nodeparamview/nodeparamviewitem.cpp index 6b12160ee2..6f84c45fb8 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.cpp +++ b/app/widget/nodeparamview/nodeparamviewitem.cpp @@ -375,7 +375,7 @@ void NodeParamViewItemBody::UpdateUIForEdgeConnection(const NodeInput& input) } // Show/hide connection label - ui_objects.connected_label->setVisible(is_connected); + ui_objects.connected_label->setVisible(is_connected || !ui_objects.widget_bridge->has_widgets()); if (ui_objects.key_control) { ui_objects.key_control->setVisible(!is_connected); From 77e6fdd2fdb2c8a0017760e4002372e2efdf0aef Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Fri, 19 May 2023 21:07:26 -0700 Subject: [PATCH 25/71] fix polygon node --- app/node/generator/polygon/polygon.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/app/node/generator/polygon/polygon.cpp b/app/node/generator/polygon/polygon.cpp index 247eb95760..5d2288ae8e 100644 --- a/app/node/generator/polygon/polygon.cpp +++ b/app/node/generator/polygon/polygon.cpp @@ -36,15 +36,15 @@ PolygonGenerator::PolygonGenerator() AddInput(kColorInput, TYPE_COLOR, Color(1.0, 1.0, 1.0)); - const int kMiddleX = 135; - const int kMiddleY = 45; - const int kBottomX = 90; - const int kBottomY = 120; - const int kTopY = 135; + const double kMiddleX = 135; + const double kMiddleY = 45; + const double kBottomX = 90; + const double kBottomY = 120; + const double kTopY = 135; // The Default Pentagon(tm) InputArrayResize(kPointsInput, 5); - SetSplitStandardValueOnTrack(kPointsInput, 0, 0, 0); + SetSplitStandardValueOnTrack(kPointsInput, 0, 0.0, 0); SetSplitStandardValueOnTrack(kPointsInput, 1, -kTopY, 0); SetSplitStandardValueOnTrack(kPointsInput, 0, kMiddleX, 1); SetSplitStandardValueOnTrack(kPointsInput, 1, -kMiddleY, 1); @@ -210,9 +210,8 @@ void PolygonGenerator::UpdateGizmoPositions(const ValueParams &p) bez_gizmo2->SetSmaller(true); } - int pts_sz = InputArraySize(kPointsInput); if (!points.empty()) { - for (int i=0; iSetPath(GeneratePath(points, pts_sz).translated(QPointF(half_res.x, half_res.y))); + poly_gizmo_->SetPath(GeneratePath(points, points.size()).translated(QPointF(half_res.x, half_res.y))); } void PolygonGenerator::GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) From b3381992fe9c8c3bfab1c894b57f5af6c658f812 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Fri, 19 May 2023 21:07:35 -0700 Subject: [PATCH 26/71] fix minor qt issue --- app/common/html.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/common/html.cpp b/app/common/html.cpp index 74cc805f91..bcc802aa3a 100644 --- a/app/common/html.cpp +++ b/app/common/html.cpp @@ -248,7 +248,7 @@ void Html::WriteCharFormat(QString *style, const QTextCharFormat &fmt) } if (fmt.foreground().style() != Qt::NoBrush) { - const QColor &color = fmt.foreground().color(); + QColor color = fmt.foreground().color(); QString cs; if (color.alpha() == 255) { From 0299f07e5462113f59550316f77d495759aa6f1b Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sat, 20 May 2023 13:28:43 -0700 Subject: [PATCH 27/71] fixed some node issues --- app/node/distort/ripple/rippledistortnode.cpp | 6 +++--- app/node/distort/swirl/swirldistortnode.cpp | 6 +++--- app/node/distort/tile/tiledistortnode.cpp | 2 +- app/node/distort/wave/wavedistortnode.cpp | 6 +++--- app/node/effect/opacity/opacityeffect.cpp | 9 ++++++--- app/node/filter/mosaic/mosaicfilternode.cpp | 4 ++-- app/node/generator/noise/noise.cpp | 2 +- app/node/generator/shape/shapenodebase.cpp | 8 ++++---- app/node/generator/text/textv3.cpp | 2 +- app/node/value.h | 5 +++++ 10 files changed, 29 insertions(+), 21 deletions(-) diff --git a/app/node/distort/ripple/rippledistortnode.cpp b/app/node/distort/ripple/rippledistortnode.cpp index 9b91e95acb..d114f3d528 100644 --- a/app/node/distort/ripple/rippledistortnode.cpp +++ b/app/node/distort/ripple/rippledistortnode.cpp @@ -35,10 +35,10 @@ RippleDistortNode::RippleDistortNode() { AddInput(kTextureInput, TYPE_TEXTURE, kInputFlagNotKeyframable); - AddInput(kEvolutionInput, TYPE_DOUBLE, 0); - AddInput(kIntensityInput, TYPE_DOUBLE, 100); + AddInput(kEvolutionInput, TYPE_DOUBLE, 0.0); + AddInput(kIntensityInput, TYPE_DOUBLE, 100.0); - AddInput(kFrequencyInput, TYPE_DOUBLE, 1); + AddInput(kFrequencyInput, TYPE_DOUBLE, 1.0); SetInputProperty(kFrequencyInput, QStringLiteral("base"), 0.01); AddInput(kPositionInput, TYPE_VEC2, QVector2D(0, 0)); diff --git a/app/node/distort/swirl/swirldistortnode.cpp b/app/node/distort/swirl/swirldistortnode.cpp index 09eaad3747..8e8d808ded 100644 --- a/app/node/distort/swirl/swirldistortnode.cpp +++ b/app/node/distort/swirl/swirldistortnode.cpp @@ -33,10 +33,10 @@ SwirlDistortNode::SwirlDistortNode() { AddInput(kTextureInput, TYPE_TEXTURE, kInputFlagNotKeyframable); - AddInput(kRadiusInput, TYPE_DOUBLE, 200); - SetInputProperty(kRadiusInput, QStringLiteral("min"), 0); + AddInput(kRadiusInput, TYPE_DOUBLE, 200.0); + SetInputProperty(kRadiusInput, QStringLiteral("min"), 0.0); - AddInput(kAngleInput, TYPE_DOUBLE, 10); + AddInput(kAngleInput, TYPE_DOUBLE, 10.0); SetInputProperty(kAngleInput, QStringLiteral("base"), 0.1); AddInput(kPositionInput, TYPE_VEC2, QVector2D(0, 0)); diff --git a/app/node/distort/tile/tiledistortnode.cpp b/app/node/distort/tile/tiledistortnode.cpp index 79f796a41a..70d530f545 100644 --- a/app/node/distort/tile/tiledistortnode.cpp +++ b/app/node/distort/tile/tiledistortnode.cpp @@ -38,7 +38,7 @@ TileDistortNode::TileDistortNode() AddInput(kTextureInput, TYPE_TEXTURE, kInputFlagNotKeyframable); AddInput(kScaleInput, TYPE_DOUBLE, 0.5); - SetInputProperty(kScaleInput, QStringLiteral("min"), 0); + SetInputProperty(kScaleInput, QStringLiteral("min"), 0.0); SetInputProperty(kScaleInput, QStringLiteral("view"), FloatSlider::kPercentage); AddInput(kPositionInput, TYPE_VEC2, QVector2D(0, 0)); diff --git a/app/node/distort/wave/wavedistortnode.cpp b/app/node/distort/wave/wavedistortnode.cpp index 5679b4b552..3d974376bc 100644 --- a/app/node/distort/wave/wavedistortnode.cpp +++ b/app/node/distort/wave/wavedistortnode.cpp @@ -34,9 +34,9 @@ WaveDistortNode::WaveDistortNode() { AddInput(kTextureInput, TYPE_TEXTURE, kInputFlagNotKeyframable); - AddInput(kFrequencyInput, TYPE_DOUBLE, 10); - AddInput(kIntensityInput, TYPE_DOUBLE, 10); - AddInput(kEvolutionInput, TYPE_DOUBLE, 0); + AddInput(kFrequencyInput, TYPE_DOUBLE, 10.0); + AddInput(kIntensityInput, TYPE_DOUBLE, 10.0); + AddInput(kEvolutionInput, TYPE_DOUBLE, 0.0); AddInput(kVerticalInput, TYPE_COMBO, false); diff --git a/app/node/effect/opacity/opacityeffect.cpp b/app/node/effect/opacity/opacityeffect.cpp index a1105c5aac..9fd3e65f92 100644 --- a/app/node/effect/opacity/opacityeffect.cpp +++ b/app/node/effect/opacity/opacityeffect.cpp @@ -48,12 +48,15 @@ value_t OpacityEffect::Value(const ValueParams &p) const value_t value = GetInputValue(p, kValueInput); if (TexturePtr tex = texture.toTexture()) { - if (TexturePtr opacity_tex = value.toTexture()) { + try { + TexturePtr opacity_tex = value.toTexture(); ShaderJob job = CreateShaderJob(p, GetRGBShaderCode); job.SetShaderID(QStringLiteral("rgbmult")); return tex->toJob(job); - } else if (!qFuzzyCompare(value.toDouble(), 1.0)) { - return tex->toJob(CreateShaderJob(p, GetNumberShaderCode)); + } catch (std::bad_any_cast &e) { + if (!qFuzzyCompare(value.toDouble(), 1.0)) { + return tex->toJob(CreateShaderJob(p, GetNumberShaderCode)); + } } } diff --git a/app/node/filter/mosaic/mosaicfilternode.cpp b/app/node/filter/mosaic/mosaicfilternode.cpp index b90d14bb54..6f7161ca95 100644 --- a/app/node/filter/mosaic/mosaicfilternode.cpp +++ b/app/node/filter/mosaic/mosaicfilternode.cpp @@ -62,8 +62,8 @@ value_t MosaicFilterNode::Value(const ValueParams &p) const if (TexturePtr texture = tex_meta.toTexture()) { if (texture - && GetInputValue(p, kHorizInput).toInt() != texture->width() - && GetInputValue(p, kVertInput).toInt() != texture->height()) { + && std::floor(GetInputValue(p, kHorizInput).toDouble()) != texture->width() + && std::floor(GetInputValue(p, kVertInput).toDouble()) != texture->height()) { ShaderJob job = CreateShaderJob(p, GetShaderCode); // Mipmapping makes this look weird, so we just use bilinear for finding the color of each block diff --git a/app/node/generator/noise/noise.cpp b/app/node/generator/noise/noise.cpp index a4fca19157..2fd1f99764 100644 --- a/app/node/generator/noise/noise.cpp +++ b/app/node/generator/noise/noise.cpp @@ -36,7 +36,7 @@ NoiseGeneratorNode::NoiseGeneratorNode() AddInput(kStrengthInput, TYPE_DOUBLE, 0.2); SetInputProperty(kStrengthInput, QStringLiteral("view"), FloatSlider::kPercentage); - SetInputProperty(kStrengthInput, QStringLiteral("min"), 0); + SetInputProperty(kStrengthInput, QStringLiteral("min"), 0.0); AddInput(kColorInput, TYPE_BOOL, false); diff --git a/app/node/generator/shape/shapenodebase.cpp b/app/node/generator/shape/shapenodebase.cpp index 067009afce..80b6df4c81 100644 --- a/app/node/generator/shape/shapenodebase.cpp +++ b/app/node/generator/shape/shapenodebase.cpp @@ -241,10 +241,10 @@ void ShapeNodeBase::GizmoDragMove(double x, double y, const Qt::KeyboardModifier new_pos = GenerateGizmoAnchor(gizmo_pos_start, gizmo_sz_start, gizmo) + using_size / 2; } - x_drag.Drag(new_pos.x()); - y_drag.Drag(new_pos.y()); - w_drag.Drag(new_size.x()); - h_drag.Drag(new_size.y()); + x_drag.Drag(double(new_pos.x())); + y_drag.Drag(double(new_pos.y())); + w_drag.Drag(double(new_size.x())); + h_drag.Drag(double(new_size.y())); } } diff --git a/app/node/generator/text/textv3.cpp b/app/node/generator/text/textv3.cpp index 728bbda3c2..0cfeb27e21 100644 --- a/app/node/generator/text/textv3.cpp +++ b/app/node/generator/text/textv3.cpp @@ -160,7 +160,7 @@ value_t TextGeneratorV3::Value(const ValueParams &p) const } } - value_t base_val = GetInputValue(p, kTextInput); + value_t base_val = GetInputValue(p, kBaseInput); if (!text.isEmpty()) { TexturePtr base = base_val.toTexture(); diff --git a/app/node/value.h b/app/node/value.h index e037cec808..bab0efb562 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -180,6 +180,11 @@ class value_t data_[3] = double(vec.w()); } + value_t(const float &f) : + value_t(TYPE_DOUBLE, double(f)) + { + } + value_t(const double &d) : value_t(TYPE_DOUBLE, d) { From 31a108ebbdb36f6b7b2a3a7cc7d951c5e0d81560 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sat, 20 May 2023 13:29:31 -0700 Subject: [PATCH 28/71] use exception as primary means of detecting bad cast --- app/node/value.h | 2 +- app/widget/curvewidget/curveview.cpp | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/node/value.h b/app/node/value.h index bab0efb562..e44e284688 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -97,7 +97,7 @@ class value_t component_t(const std::vector &t) { abort(); } template - T get(bool *ok = nullptr) const + T get() const { T t = T(); diff --git a/app/widget/curvewidget/curveview.cpp b/app/widget/curvewidget/curveview.cpp index 30c9a3a356..43bd8fe59f 100644 --- a/app/widget/curvewidget/curveview.cpp +++ b/app/widget/curvewidget/curveview.cpp @@ -462,13 +462,11 @@ void CurveView::KeyframeDragMove(QMouseEvent *event, QString &tip) NodeKeyframe *tip_item = GetSelectedKeyframes().front(); - bool ok; - double num_value = tip_item->value().get(&ok); - - if (ok) { + try { + double num_value = tip_item->value().get(); tip = QStringLiteral("%1\n"); tip.append(FloatSlider::ValueToString(num_value + GetOffsetFromKeyframe(tip_item), GetFloatDisplayTypeFromKeyframe(tip_item), 2, true)); - } + } catch (std::bad_any_cast &e) {} } void CurveView::KeyframeDragRelease(QMouseEvent *event, MultiUndoCommand *command) From 645c33ea885e2c13cc67ecf767d11f5284e181ce Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 21 May 2023 14:53:29 -0700 Subject: [PATCH 29/71] added two methods of receiving values from value_t --- .../cornerpin/cornerpindistortnode.cpp | 4 +-- app/node/distort/crop/cropdistortnode.cpp | 2 +- app/node/distort/ripple/rippledistortnode.cpp | 4 +-- app/node/distort/swirl/swirldistortnode.cpp | 4 +-- app/node/distort/tile/tiledistortnode.cpp | 4 +-- .../transform/transformdistortnode.cpp | 14 ++++---- app/node/effect/opacity/opacityeffect.cpp | 11 +++--- app/node/filter/blur/blur.cpp | 4 +-- app/node/generator/polygon/polygon.cpp | 4 +-- app/node/generator/shape/shapenodebase.cpp | 10 +++--- app/node/inputdragger.cpp | 4 +-- app/node/node.cpp | 16 ++++----- app/node/value.cpp | 34 +++++++++---------- app/node/value.h | 29 ++++++++++++++-- app/render/renderprocessor.cpp | 23 ++++++------- app/widget/curvewidget/curveview.cpp | 17 +++++----- 16 files changed, 102 insertions(+), 82 deletions(-) diff --git a/app/node/distort/cornerpin/cornerpindistortnode.cpp b/app/node/distort/cornerpin/cornerpindistortnode.cpp index b10d603c8b..e72f6141ea 100644 --- a/app/node/distort/cornerpin/cornerpindistortnode.cpp +++ b/app/node/distort/cornerpin/cornerpindistortnode.cpp @@ -143,8 +143,8 @@ void CornerPinDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardM DraggableGizmo *gizmo = static_cast(sender()); if (gizmo != gizmo_whole_rect_) { - gizmo->GetDraggers()[0].Drag(gizmo->GetDraggers()[0].GetStartValue().get() + x); - gizmo->GetDraggers()[1].Drag(gizmo->GetDraggers()[1].GetStartValue().get() + y); + gizmo->GetDraggers()[0].Drag(gizmo->GetDraggers()[0].GetStartValue().value() + x); + gizmo->GetDraggers()[1].Drag(gizmo->GetDraggers()[1].GetStartValue().value() + y); } } diff --git a/app/node/distort/crop/cropdistortnode.cpp b/app/node/distort/crop/cropdistortnode.cpp index fdb700a2f1..18baaebf0b 100644 --- a/app/node/distort/crop/cropdistortnode.cpp +++ b/app/node/distort/crop/cropdistortnode.cpp @@ -135,7 +135,7 @@ void CropDistortNode::GizmoDragMove(double x_diff, double y_diff, const Qt::Keyb for (int j=0; jGetDraggers().size(); j++) { NodeInputDragger& i = gizmo->GetDraggers()[j]; - double s = i.GetStartValue().get(); + double s = i.GetStartValue().value(); if (i.GetInput().input().input() == kLeftInput) { i.Drag(s + x_diff); } else if (i.GetInput().input().input() == kTopInput) { diff --git a/app/node/distort/ripple/rippledistortnode.cpp b/app/node/distort/ripple/rippledistortnode.cpp index d114f3d528..a939faf41f 100644 --- a/app/node/distort/ripple/rippledistortnode.cpp +++ b/app/node/distort/ripple/rippledistortnode.cpp @@ -135,8 +135,8 @@ void RippleDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardModi NodeInputDragger &x_drag = gizmo_->GetDraggers()[0]; NodeInputDragger &y_drag = gizmo_->GetDraggers()[1]; - x_drag.Drag(x_drag.GetStartValue().get() + x); - y_drag.Drag(y_drag.GetStartValue().get() + y); + x_drag.Drag(x_drag.GetStartValue().value() + x); + y_drag.Drag(y_drag.GetStartValue().value() + y); } } diff --git a/app/node/distort/swirl/swirldistortnode.cpp b/app/node/distort/swirl/swirldistortnode.cpp index 8e8d808ded..b7854a8562 100644 --- a/app/node/distort/swirl/swirldistortnode.cpp +++ b/app/node/distort/swirl/swirldistortnode.cpp @@ -115,8 +115,8 @@ void SwirlDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardModif NodeInputDragger &x_drag = gizmo_->GetDraggers()[0]; NodeInputDragger &y_drag = gizmo_->GetDraggers()[1]; - x_drag.Drag(x_drag.GetStartValue().get() + x); - y_drag.Drag(y_drag.GetStartValue().get() + y); + x_drag.Drag(x_drag.GetStartValue().value() + x); + y_drag.Drag(y_drag.GetStartValue().value() + y); } } diff --git a/app/node/distort/tile/tiledistortnode.cpp b/app/node/distort/tile/tiledistortnode.cpp index 70d530f545..0c55fa6964 100644 --- a/app/node/distort/tile/tiledistortnode.cpp +++ b/app/node/distort/tile/tiledistortnode.cpp @@ -157,8 +157,8 @@ void TileDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardModifi NodeInputDragger &x_drag = gizmo_->GetDraggers()[0]; NodeInputDragger &y_drag = gizmo_->GetDraggers()[1]; - x_drag.Drag(x_drag.GetStartValue().get() + x); - y_drag.Drag(y_drag.GetStartValue().get() + y); + x_drag.Drag(x_drag.GetStartValue().value() + x); + y_drag.Drag(y_drag.GetStartValue().value() + y); } } diff --git a/app/node/distort/transform/transformdistortnode.cpp b/app/node/distort/transform/transformdistortnode.cpp index 689e979165..3ce0e7610a 100644 --- a/app/node/distort/transform/transformdistortnode.cpp +++ b/app/node/distort/transform/transformdistortnode.cpp @@ -196,8 +196,8 @@ void TransformDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardM NodeInputDragger &x_drag = gizmo->GetDraggers()[0]; NodeInputDragger &y_drag = gizmo->GetDraggers()[1]; - x_drag.Drag(x_drag.GetStartValue().get() + x); - y_drag.Drag(y_drag.GetStartValue().get() + y); + x_drag.Drag(x_drag.GetStartValue().value() + x); + y_drag.Drag(y_drag.GetStartValue().value() + y); } else if (gizmo == anchor_gizmo_) { @@ -208,10 +208,10 @@ void TransformDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardM QPointF inverted_movement(gizmo_inverted_transform_.map(QPointF(x, y))); - x_anchor_drag.Drag(x_anchor_drag.GetStartValue().get() + inverted_movement.x()); - y_anchor_drag.Drag(y_anchor_drag.GetStartValue().get() + inverted_movement.y()); - x_pos_drag.Drag(x_pos_drag.GetStartValue().get() + x); - y_pos_drag.Drag(y_pos_drag.GetStartValue().get() + y); + x_anchor_drag.Drag(x_anchor_drag.GetStartValue().value() + inverted_movement.x()); + y_anchor_drag.Drag(y_anchor_drag.GetStartValue().value() + inverted_movement.y()); + x_pos_drag.Drag(x_pos_drag.GetStartValue().value() + x); + y_pos_drag.Drag(y_pos_drag.GetStartValue().value() + y); } else if (gizmo == rotation_gizmo_) { @@ -248,7 +248,7 @@ void TransformDistortNode::GizmoDragMove(double x, double y, const Qt::KeyboardM double rotation_difference = (current_angle - gizmo_start_angle_) * 57.2958; NodeInputDragger &d = gizmo->GetDraggers()[0]; - d.Drag(d.GetStartValue().get() + rotation_difference); + d.Drag(d.GetStartValue().value() + rotation_difference); } else if (IsAScaleGizmo(gizmo)) { diff --git a/app/node/effect/opacity/opacityeffect.cpp b/app/node/effect/opacity/opacityeffect.cpp index 9fd3e65f92..73348593e4 100644 --- a/app/node/effect/opacity/opacityeffect.cpp +++ b/app/node/effect/opacity/opacityeffect.cpp @@ -48,15 +48,14 @@ value_t OpacityEffect::Value(const ValueParams &p) const value_t value = GetInputValue(p, kValueInput); if (TexturePtr tex = texture.toTexture()) { - try { - TexturePtr opacity_tex = value.toTexture(); + TexturePtr opacity_tex; + + if (value.get(&opacity_tex)) { ShaderJob job = CreateShaderJob(p, GetRGBShaderCode); job.SetShaderID(QStringLiteral("rgbmult")); return tex->toJob(job); - } catch (std::bad_any_cast &e) { - if (!qFuzzyCompare(value.toDouble(), 1.0)) { - return tex->toJob(CreateShaderJob(p, GetNumberShaderCode)); - } + } else if (!qFuzzyCompare(value.toDouble(), 1.0)) { + return tex->toJob(CreateShaderJob(p, GetNumberShaderCode)); } } diff --git a/app/node/filter/blur/blur.cpp b/app/node/filter/blur/blur.cpp index 50da0e3f53..40c65a1128 100644 --- a/app/node/filter/blur/blur.cpp +++ b/app/node/filter/blur/blur.cpp @@ -192,8 +192,8 @@ void BlurFilterNode::GizmoDragMove(double x, double y, const Qt::KeyboardModifie NodeInputDragger &x_drag = gizmo->GetDraggers()[0]; NodeInputDragger &y_drag = gizmo->GetDraggers()[1]; - x_drag.Drag(x_drag.GetStartValue().get() + x); - y_drag.Drag(y_drag.GetStartValue().get() + y); + x_drag.Drag(x_drag.GetStartValue().value() + x); + y_drag.Drag(y_drag.GetStartValue().value() + y); } } diff --git a/app/node/generator/polygon/polygon.cpp b/app/node/generator/polygon/polygon.cpp index 5d2288ae8e..f1a91f6696 100644 --- a/app/node/generator/polygon/polygon.cpp +++ b/app/node/generator/polygon/polygon.cpp @@ -239,8 +239,8 @@ void PolygonGenerator::GizmoDragMove(double x, double y, const Qt::KeyboardModif } else { NodeInputDragger &x_drag = gizmo->GetDraggers()[0]; NodeInputDragger &y_drag = gizmo->GetDraggers()[1]; - x_drag.Drag(x_drag.GetStartValue().get() + x); - y_drag.Drag(y_drag.GetStartValue().get() + y); + x_drag.Drag(x_drag.GetStartValue().value() + x); + y_drag.Drag(y_drag.GetStartValue().value() + y); } } diff --git a/app/node/generator/shape/shapenodebase.cpp b/app/node/generator/shape/shapenodebase.cpp index 80b6df4c81..aff091faa2 100644 --- a/app/node/generator/shape/shapenodebase.cpp +++ b/app/node/generator/shape/shapenodebase.cpp @@ -126,8 +126,8 @@ void ShapeNodeBase::GizmoDragMove(double x, double y, const Qt::KeyboardModifier NodeInputDragger &y_drag = gizmo->GetDraggers()[1]; if (gizmo == poly_gizmo_) { - x_drag.Drag(x_drag.GetStartValue().get() + x); - y_drag.Drag(y_drag.GetStartValue().get() + y); + x_drag.Drag(x_drag.GetStartValue().value() + x); + y_drag.Drag(y_drag.GetStartValue().value() + y); } else { bool from_center = modifiers & Qt::AltModifier; bool keep_ratio = modifiers & Qt::ShiftModifier; @@ -135,8 +135,8 @@ void ShapeNodeBase::GizmoDragMove(double x, double y, const Qt::KeyboardModifier NodeInputDragger &w_drag = gizmo->GetDraggers()[2]; NodeInputDragger &h_drag = gizmo->GetDraggers()[3]; - QVector2D gizmo_sz_start(w_drag.GetStartValue().get(), h_drag.GetStartValue().get()); - QVector2D gizmo_pos_start(x_drag.GetStartValue().get(), y_drag.GetStartValue().get()); + QVector2D gizmo_sz_start(w_drag.GetStartValue().value(), h_drag.GetStartValue().value()); + QVector2D gizmo_pos_start(x_drag.GetStartValue().value(), y_drag.GetStartValue().value()); QVector2D gizmo_half_res = gizmo->GetGlobals().square_resolution()/2; QVector2D adjusted_pt(x, y); QVector2D new_size; @@ -147,7 +147,7 @@ void ShapeNodeBase::GizmoDragMove(double x, double y, const Qt::KeyboardModifier double original_ratio; if (keep_ratio) { - original_ratio = w_drag.GetStartValue().get() / h_drag.GetStartValue().get(); + original_ratio = w_drag.GetStartValue().value() / h_drag.GetStartValue().value(); } // Calculate new size diff --git a/app/node/inputdragger.cpp b/app/node/inputdragger.cpp index 83758f2ca5..c9cb7c36aa 100644 --- a/app/node/inputdragger.cpp +++ b/app/node/inputdragger.cpp @@ -94,7 +94,7 @@ void NodeInputDragger::Drag(value_t::component_t value) if (node->HasInputProperty(input, QStringLiteral("min"))) { // Assumes the value is a double of some kind double min = node->GetInputProperty(input, QStringLiteral("min")).converted(TYPE_DOUBLE).toDouble(); - double v = value.converted(type, TYPE_DOUBLE).get(); + double v = value.converted(type, TYPE_DOUBLE).value(); if (v < min) { value = value_t::component_t(min).converted(TYPE_DOUBLE, type); } @@ -102,7 +102,7 @@ void NodeInputDragger::Drag(value_t::component_t value) if (node->HasInputProperty(input, QStringLiteral("max"))) { double max = node->GetInputProperty(input, QStringLiteral("max")).converted(TYPE_DOUBLE).toDouble(); - double v = value.converted(type, TYPE_DOUBLE).get(); + double v = value.converted(type, TYPE_DOUBLE).value(); if (v > max) { value = value_t::component_t(max).converted(TYPE_DOUBLE, type); } diff --git a/app/node/node.cpp b/app/node/node.cpp index 84357ef953..d340ceba94 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -481,14 +481,14 @@ value_t::component_t Node::GetSplitValueAtTimeOnTrack(const QString &input, cons double before_val, after_val, interpolated; if (type == TYPE_RATIONAL) { - before_val = before->value().get().toDouble(); - after_val = after->value().get().toDouble(); + before_val = before->value().value().toDouble(); + after_val = after->value().value().toDouble(); } else if (type == TYPE_INTEGER) { - before_val = before->value().get(); - after_val = after->value().get(); + before_val = before->value().value(); + after_val = after->value().value(); } else { - before_val = before->value().get(); - after_val = after->value().get(); + before_val = before->value().value(); + after_val = after->value().value(); } if (before->type() == NodeKeyframe::kBezier && after->type() == NodeKeyframe::kBezier) { @@ -1678,9 +1678,9 @@ void Node::SaveImmediate(QXmlStreamWriter *writer, const QString &input, int ele // FIXME: We now have converters for custom types, so this should be handled with those instead. // It'll probably require bumping the project version though... if (data_type == ViewerOutput::TYPE_VPARAM) { - v.get().Save(writer); + v.value().Save(writer); } else if (data_type == ViewerOutput::TYPE_APARAM) { - TypeSerializer::SaveAudioParams(writer, v.get()); + TypeSerializer::SaveAudioParams(writer, v.value()); } else { writer->writeCharacters(v.toSerializedString(data_type)); } diff --git a/app/node/value.cpp b/app/node/value.cpp index 075d88a7f8..56e275e6c0 100644 --- a/app/node/value.cpp +++ b/app/node/value.cpp @@ -63,7 +63,7 @@ QString value_t::toSerializedString() const if (this->type() == TYPE_STRING) { QStringList l; for (auto it = data_.cbegin(); it != data_.cend(); it++) { - l.append(it->get()); + l.append(it->value()); } return l.join(CHANNEL_SPLITTER); } else { @@ -106,72 +106,72 @@ value_t value_t::converted(type_t to, bool *ok) const value_t::component_t converter_IntegerToString(const value_t::component_t &v) { - return QString::number(v.get()); + return QString::number(v.value()); } value_t::component_t converter_StringToInteger(const value_t::component_t &v) { - return int64_t(v.get().toLongLong()); + return int64_t(v.value().toLongLong()); } value_t::component_t converter_DoubleToString(const value_t::component_t &v) { - return QString::number(v.get()); + return QString::number(v.value()); } value_t::component_t converter_StringToDouble(const value_t::component_t &v) { - return v.get().toDouble(); + return v.value().toDouble(); } value_t::component_t converter_RationalToString(const value_t::component_t &v) { - return QString::fromStdString(v.get().toString()); + return QString::fromStdString(v.value().toString()); } value_t::component_t converter_StringToRational(const value_t::component_t &v) { - return rational::fromString(v.get().toStdString()); + return rational::fromString(v.value().toStdString()); } value_t::component_t converter_BinaryToString(const value_t::component_t &v) { - return QString::fromLatin1(v.get().toBase64()); + return QString::fromLatin1(v.value().toBase64()); } value_t::component_t converter_StringToBinary(const value_t::component_t &v) { - return QByteArray::fromBase64(v.get().toLatin1()); + return QByteArray::fromBase64(v.value().toLatin1()); } value_t::component_t converter_IntegerToDouble(const value_t::component_t &v) { - return double(v.get()); + return double(v.value()); } value_t::component_t converter_IntegerToRational(const value_t::component_t &v) { - return rational(v.get()); + return rational(v.value()); } value_t::component_t converter_DoubleToInteger(const value_t::component_t &v) { - return int64_t(v.get()); + return int64_t(v.value()); } value_t::component_t converter_DoubleToRational(const value_t::component_t &v) { - return rational::fromDouble(v.get()); + return rational::fromDouble(v.value()); } value_t::component_t converter_RationalToInteger(const value_t::component_t &v) { - return int64_t(v.get().toDouble()); + return int64_t(v.value().toDouble()); } value_t::component_t converter_RationalToDouble(const value_t::component_t &v) { - return v.get().toDouble(); + return v.value().toDouble(); } void value_t::registerConverter(const type_t &from, const type_t &to, Converter_t converter) @@ -229,10 +229,10 @@ value_t::component_t value_t::component_t::fromSerializedString(type_t to, const QString value_t::component_t::toSerializedString(type_t from) const { if (from == TYPE_STRING) { - return get(); + return value(); } else { component_t c = this->converted(from, TYPE_STRING); - return c.get(); + return c.value(); } } diff --git a/app/node/value.h b/app/node/value.h index e44e284688..4a476b1411 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -97,12 +97,29 @@ class value_t component_t(const std::vector &t) { abort(); } template - T get() const + bool get(T *out) const + { + if (data_.has_value()) { + try { + *out = std::any_cast(data_); + return true; + } catch (std::bad_any_cast &e) {} + } + + return false; + } + + template + T value() const { T t = T(); if (data_.has_value()) { - t = std::any_cast(data_); + try { + t = std::any_cast(data_); + } catch (std::bad_any_cast &e) { + qCritical() << "Failed to cast" << data_.type().name() << "to" << typeid(T).name() << e.what(); + } } return t; @@ -265,10 +282,16 @@ class value_t component_t &operator[](size_t i) { return data_[i]; } const component_t &operator[](size_t i) const { return data_[i]; } + template + bool get(T *out, size_t channel = 0) const + { + return at(channel).get(out); + } + template T value(size_t channel = 0) const { - return at(channel).get(); + return at(channel).value(); } void resize(size_t s) { data_.resize(s); } diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index a8d73897b2..3ef485a64c 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -634,27 +634,24 @@ void RenderProcessor::ResolveJobs(value_t &val) } else if (val.type() == TYPE_SAMPLES) { - try { - SampleJob job = val.value(); + SampleJob sjob; + FootageJob fjob; - for (auto it=job.GetValues().begin(); it!=job.GetValues().end(); it++) { + if (val.get(&sjob)) { + for (auto it=sjob.GetValues().begin(); it!=sjob.GetValues().end(); it++) { // Jobs will almost always be submitted with one of these types value_t &subval = it.value(); ResolveJobs(subval); } - SampleBuffer output_buffer = CreateSampleBuffer(job.audio_params(), job.sample_count()); - ProcessSamples(output_buffer, job); + SampleBuffer output_buffer = CreateSampleBuffer(sjob.audio_params(), sjob.sample_count()); + ProcessSamples(output_buffer, sjob); val = value_t(TYPE_SAMPLES, output_buffer); - } catch (std::bad_any_cast &e) {} - - try { - FootageJob job = val.value(); - SampleBuffer buffer = CreateSampleBuffer(GetCacheAudioParams(), job.time().length()); - ProcessAudioFootage(buffer, &job, job.time()); + } else if (val.get(&fjob)) { + SampleBuffer buffer = CreateSampleBuffer(GetCacheAudioParams(), fjob.time().length()); + ProcessAudioFootage(buffer, &fjob, fjob.time()); val = value_t(TYPE_SAMPLES, buffer); - } catch (std::bad_any_cast &e) {} - + } } } diff --git a/app/widget/curvewidget/curveview.cpp b/app/widget/curvewidget/curveview.cpp index 43bd8fe59f..36c921cc5e 100644 --- a/app/widget/curvewidget/curveview.cpp +++ b/app/widget/curvewidget/curveview.cpp @@ -435,7 +435,7 @@ void CurveView::KeyframeDragMove(QMouseEvent *event, QString &tip) FloatSlider::DisplayType display = GetFloatDisplayTypeFromKeyframe(key); Node* node = key->parent(); - double original_val = FloatSlider::TransformValueToDisplay(drag_keyframe_values_.at(i).get(), display); + double original_val = FloatSlider::TransformValueToDisplay(drag_keyframe_values_.at(i).value(), display); const QString& input = key->input(); double new_val = FloatSlider::TransformDisplayToValue(original_val - scaled_diff, display); double limited = new_val; @@ -457,23 +457,24 @@ void CurveView::KeyframeDragMove(QMouseEvent *event, QString &tip) for (size_t i=0; iset_value(FloatSlider::TransformDisplayToValue(FloatSlider::TransformValueToDisplay(drag_keyframe_values_.at(i).get(), display) - scaled_diff, display)); + key->set_value(FloatSlider::TransformDisplayToValue(FloatSlider::TransformValueToDisplay(drag_keyframe_values_.at(i).value(), display) - scaled_diff, display)); } NodeKeyframe *tip_item = GetSelectedKeyframes().front(); - try { - double num_value = tip_item->value().get(); + double num_value; + + if (tip_item->value().get(&num_value)) { tip = QStringLiteral("%1\n"); tip.append(FloatSlider::ValueToString(num_value + GetOffsetFromKeyframe(tip_item), GetFloatDisplayTypeFromKeyframe(tip_item), 2, true)); - } catch (std::bad_any_cast &e) {} + } } void CurveView::KeyframeDragRelease(QMouseEvent *event, MultiUndoCommand *command) { for (size_t i=0; ivalue().get(), drag_keyframe_values_.at(i).get())) { + if (!qFuzzyCompare(k->value().value(), drag_keyframe_values_.at(i).value())) { command->add_child(new NodeParamSetKeyframeValueCommand(k, k->value(), drag_keyframe_values_.at(i))); } } @@ -580,7 +581,7 @@ qreal CurveView::GetItemYFromKeyframeValue(NodeKeyframe *key) qreal CurveView::GetUnscaledItemYFromKeyframeValue(NodeKeyframe *key) { - double val = key->value().get(); + double val = key->value().value(); val = FloatSlider::TransformValueToDisplay(val, GetFloatDisplayTypeFromKeyframe(key)); @@ -615,7 +616,7 @@ double CurveView::GetOffsetFromKeyframe(NodeKeyframe *key) if (node->HasInputProperty(input, QStringLiteral("offset"))) { value_t v = node->GetInputProperty(input, QStringLiteral("offset")).value(); - return v.at(key->track()).get(); + return v.at(key->track()).value(); } return 0; From 79a23dd73dbce8a504a320708caf7c085ae65811 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 21 May 2023 14:53:36 -0700 Subject: [PATCH 30/71] implement auto conversion --- app/node/node.cpp | 12 ++++++++++++ app/node/param.h | 1 + 2 files changed, 13 insertions(+) diff --git a/app/node/node.cpp b/app/node/node.cpp index d340ceba94..99fae24611 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -906,6 +906,18 @@ value_t Node::GetFakeConnectedValue(const ValueParams &g, Node *output, const QS v = swiz; } + // Perform conversion if necessary + type_t expected_type = this->GetInputDataType(input); + if (expected_type != TYPE_NONE && expected_type != v.type() && !(this->GetInputFlags(input) & kInputFlagDontAutoConvert)) { + bool ok; + v = v.converted(expected_type, &ok); + if (!ok) { + // Return null value instead of converted value because node is probably not set up to + // handle this type (unless kInputFlagDontAutoConvert is specified of course) + v = value_t(); + } + } + return v; } else { output = output->GetConnectedOutput(output->GetEffectInput()); diff --git a/app/node/param.h b/app/node/param.h index 707032fa26..882d469dc5 100644 --- a/app/node/param.h +++ b/app/node/param.h @@ -82,6 +82,7 @@ static const InputFlag kInputFlagNotKeyframable = InputFlag(0x2); static const InputFlag kInputFlagNotConnectable = InputFlag(0x4); static const InputFlag kInputFlagHidden = InputFlag(0x8); static const InputFlag kInputFlagIgnoreInvalidations = InputFlag(0x10); +static const InputFlag kInputFlagDontAutoConvert = InputFlag(0x20); static const InputFlag kInputFlagStatic = kInputFlagNotKeyframable | kInputFlagNotConnectable; struct NodeInputPair From a87bf80a58a2611808f5d9f04502fa6c8ffcdd86 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 21 May 2023 14:53:55 -0700 Subject: [PATCH 31/71] fixed color param widget --- .../nodeparamviewwidgetbridge.cpp | 2 +- .../paramwidget/abstractparamwidget.h | 2 - .../paramwidget/colorparamwidget.cpp | 57 ++++++++----------- .../paramwidget/colorparamwidget.h | 6 +- 4 files changed, 29 insertions(+), 38 deletions(-) diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index b8a582fc92..d35e5fd451 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -93,7 +93,7 @@ void NodeParamViewWidgetBridge::CreateWidgets() widget_ = new RationalSliderParamWidget(this); } else if (t == TYPE_DOUBLE) { if (type == QStringLiteral("color")) { - widget_ = new ColorParamWidget(this); + widget_ = new ColorParamWidget(GetInnerInput(), this); } else if (type == QStringLiteral("bezier")) { widget_ = new BezierParamWidget(this); } else { diff --git a/app/widget/nodeparamview/paramwidget/abstractparamwidget.h b/app/widget/nodeparamview/paramwidget/abstractparamwidget.h index da9dd80445..8f31a2fdc9 100644 --- a/app/widget/nodeparamview/paramwidget/abstractparamwidget.h +++ b/app/widget/nodeparamview/paramwidget/abstractparamwidget.h @@ -49,8 +49,6 @@ class AbstractParamWidget : public QObject virtual void SetTimebase(const rational &timebase){} signals: - void ValueChanged(const olive::value_t &val); - void ChannelValueChanged(size_t channel, const olive::value_t::component_t &val); void SliderDragged(NumericSliderBase *slider, size_t track); diff --git a/app/widget/nodeparamview/paramwidget/colorparamwidget.cpp b/app/widget/nodeparamview/paramwidget/colorparamwidget.cpp index c7b90b6700..f38d467e8e 100644 --- a/app/widget/nodeparamview/paramwidget/colorparamwidget.cpp +++ b/app/widget/nodeparamview/paramwidget/colorparamwidget.cpp @@ -20,70 +20,59 @@ #include "colorparamwidget.h" +#include "node/project.h" #include "widget/colorbutton/colorbutton.h" namespace olive { -ColorParamWidget::ColorParamWidget(QObject *parent) - : AbstractParamWidget{parent} +ColorParamWidget::ColorParamWidget(const NodeInput &input, QObject *parent) + : AbstractParamWidget{parent}, + input_(input) { } void ColorParamWidget::Initialize(QWidget *parent, size_t channels) { - qDebug() << "CPW::Initialize - stub"; - /* - for (size_t i = 0; i < channels; i++) { - ColorButton* color_button = new ColorButton(project()->color_manager(), parent); - AddWidget(color_button); - connect(color_button, &ColorButton::ColorChanged, this, &ColorParamWidget::Arbitrate); - } - */ + Q_ASSERT(channels == 4); + + ColorButton* color_button = new ColorButton(input_.node()->project()->color_manager(), parent); + AddWidget(color_button); + connect(color_button, &ColorButton::ColorChanged, this, &ColorParamWidget::Arbitrate); } void ColorParamWidget::SetValue(const value_t &val) { - qDebug() << "CPW::SetValue - stub"; - /* - ManagedColor mc = val.value(); + ManagedColor mc = val.toColor(); - mc.set_color_input(GetInnerInput().GetProperty("col_input").toString()); + mc.set_color_input(input_.GetProperty("col_input").toString()); - QString d = GetInnerInput().GetProperty("col_display").toString(); - QString v = GetInnerInput().GetProperty("col_view").toString(); - QString l = GetInnerInput().GetProperty("col_look").toString(); + QString d = input_.GetProperty("col_display").toString(); + QString v = input_.GetProperty("col_view").toString(); + QString l = input_.GetProperty("col_look").toString(); mc.set_color_output(ColorTransform(d, v, l)); static_cast(GetWidgets().at(0))->SetColor(mc); - */ } void ColorParamWidget::Arbitrate() { - qDebug() << "CPW::Arbitrate - stub"; - /* // Sender is a ColorButton ManagedColor c = static_cast(sender())->GetColor(); - MultiUndoCommand* command = new MultiUndoCommand(); - - SetInputValueInternal(c.red(), 0, command, false); - SetInputValueInternal(c.green(), 1, command, false); - SetInputValueInternal(c.blue(), 2, command, false); - SetInputValueInternal(c.alpha(), 3, command, false); + emit ChannelValueChanged(0, double(c.red())); + emit ChannelValueChanged(1, double(c.green())); + emit ChannelValueChanged(2, double(c.blue())); + emit ChannelValueChanged(3, double(c.alpha())); - Node* n = GetInnerInput().node(); + Node* n = input_.node(); n->blockSignals(true); - n->SetInputProperty(GetInnerInput().input(), QStringLiteral("col_input"), c.color_input()); - n->SetInputProperty(GetInnerInput().input(), QStringLiteral("col_display"), c.color_output().display()); - n->SetInputProperty(GetInnerInput().input(), QStringLiteral("col_view"), c.color_output().view()); - n->SetInputProperty(GetInnerInput().input(), QStringLiteral("col_look"), c.color_output().look()); + n->SetInputProperty(input_.input(), QStringLiteral("col_input"), c.color_input()); + n->SetInputProperty(input_.input(), QStringLiteral("col_display"), c.color_output().display()); + n->SetInputProperty(input_.input(), QStringLiteral("col_view"), c.color_output().view()); + n->SetInputProperty(input_.input(), QStringLiteral("col_look"), c.color_output().look()); n->blockSignals(false); - - Core::instance()->undo_stack()->push(command, GetCommandName()); - */ } } diff --git a/app/widget/nodeparamview/paramwidget/colorparamwidget.h b/app/widget/nodeparamview/paramwidget/colorparamwidget.h index 061fce311a..310ef93185 100644 --- a/app/widget/nodeparamview/paramwidget/colorparamwidget.h +++ b/app/widget/nodeparamview/paramwidget/colorparamwidget.h @@ -22,6 +22,7 @@ #define COLORPARAMWIDGET_H #include "abstractparamwidget.h" +#include "node/color/colormanager/colormanager.h" namespace olive { @@ -29,12 +30,15 @@ class ColorParamWidget : public AbstractParamWidget { Q_OBJECT public: - explicit ColorParamWidget(QObject *parent = nullptr); + explicit ColorParamWidget(const NodeInput &input, QObject *parent = nullptr); virtual void Initialize(QWidget *parent, size_t channels) override; virtual void SetValue(const value_t &val) override; +private: + NodeInput input_; + private slots: void Arbitrate(); From ed938cd44e56edca4d82ebd43074091b66833a30 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 21 May 2023 14:54:02 -0700 Subject: [PATCH 32/71] fixed slider issues --- app/widget/slider/base/numericsliderbase.cpp | 2 +- app/widget/slider/integerslider.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/widget/slider/base/numericsliderbase.cpp b/app/widget/slider/base/numericsliderbase.cpp index 3ca583945e..88fb6142dd 100644 --- a/app/widget/slider/base/numericsliderbase.cpp +++ b/app/widget/slider/base/numericsliderbase.cpp @@ -163,7 +163,7 @@ void NumericSliderBase::SetOffset(const InternalType &v) NumericSliderBase::InternalType NumericSliderBase::AdjustDragDistanceInternal(const InternalType &start, const double &drag) const { - return start.value() + drag; + return start.converted(TYPE_DOUBLE).value() + drag; } void NumericSliderBase::SetMinimumInternal(const InternalType &v) diff --git a/app/widget/slider/integerslider.cpp b/app/widget/slider/integerslider.cpp index dcf8004d9e..cc62d76ff4 100644 --- a/app/widget/slider/integerslider.cpp +++ b/app/widget/slider/integerslider.cpp @@ -88,7 +88,7 @@ void IntegerSlider::ValueSignalEvent(const InternalType &value) IntegerSlider::InternalType IntegerSlider::AdjustDragDistanceInternal(const InternalType &start, const double &drag) const { - return int64_t(super::AdjustDragDistanceInternal(start, drag).value()); + return int64_t(super::AdjustDragDistanceInternal(start, drag).value()); } bool IntegerSlider::Equals(const InternalType &a, const InternalType &b) const From 8c244482128cc2d2e50c416d585923f5f912cf30 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 21 May 2023 14:54:13 -0700 Subject: [PATCH 33/71] added some math functions --- app/node/math/math/math.cpp | 333 ++++++++++++++++++++++++--- app/node/math/math/math.h | 34 ++- app/node/math/math/mathfunctions.cpp | 131 +++++++++-- app/node/math/math/mathfunctions.h | 36 ++- 4 files changed, 472 insertions(+), 62 deletions(-) diff --git a/app/node/math/math/math.cpp b/app/node/math/math/math.cpp index 6c731a4dec..de9ccb2c99 100644 --- a/app/node/math/math/math.cpp +++ b/app/node/math/math/math.cpp @@ -31,30 +31,36 @@ const QString MathNode::kParamCIn = QStringLiteral("param_c_in"); #define super Node -std::map > > MathNode::operations_; +std::map> > > MathNode::operations_; MathNode::MathNode() { AddInput(kMethodIn, TYPE_COMBO, kInputFlagNotConnectable | kInputFlagNotKeyframable); - AddInput(kParamAIn, TYPE_DOUBLE, 0.0); + AddInput(kParamAIn, TYPE_DOUBLE, 0.0, kInputFlagDontAutoConvert); SetInputProperty(kParamAIn, QStringLiteral("decimalplaces"), 8); SetInputProperty(kParamAIn, QStringLiteral("autotrim"), true); - AddInput(kParamBIn, TYPE_DOUBLE, 0.0); + AddInput(kParamBIn, TYPE_DOUBLE, 0.0, kInputFlagDontAutoConvert); SetInputProperty(kParamBIn, QStringLiteral("decimalplaces"), 8); SetInputProperty(kParamBIn, QStringLiteral("autotrim"), true); + AddInput(kParamCIn, TYPE_DOUBLE, 0.0, kInputFlagDontAutoConvert); + SetInputProperty(kParamCIn, QStringLiteral("decimalplaces"), 8); + SetInputProperty(kParamCIn, QStringLiteral("autotrim"), true); + if (operations_.empty()) { PopulateOperations(); } + + UpdateInputVisibility(); } QString MathNode::Name() const { // Default to naming after the operation if (parent()) { - QString name = GetOperationName(static_cast(GetStandardValue(kMethodIn).toInt())); + QString name = GetOperationName(GetOperation()); if (!name.isEmpty()) { return name; } @@ -83,15 +89,40 @@ void MathNode::Retranslate() super::Retranslate(); SetInputName(kMethodIn, tr("Method")); + SetInputName(kParamAIn, tr("Value")); - SetInputName(kParamBIn, tr("Value")); + if (GetOperation() == kOpClamp) { + SetInputName(kParamBIn, tr("Minimum")); + SetInputName(kParamCIn, tr("Maximum")); + } else { + SetInputName(kParamBIn, tr("Value")); + SetInputName(kParamCIn, tr("Value")); + } - QStringList operations = {GetOperationName(kOpAdd), - GetOperationName(kOpSubtract), - GetOperationName(kOpMultiply), - GetOperationName(kOpDivide), - QString(), - GetOperationName(kOpPower)}; + QStringList operations = { + GetOperationName(kOpAdd), + GetOperationName(kOpSubtract), + GetOperationName(kOpMultiply), + GetOperationName(kOpDivide), + QString(), + GetOperationName(kOpPower), + QString(), + GetOperationName(kOpSine), + GetOperationName(kOpCosine), + GetOperationName(kOpTangent), + QString(), + GetOperationName(kOpArcSine), + GetOperationName(kOpArcCosine), + GetOperationName(kOpArcTangent), + QString(), + GetOperationName(kOpHypSine), + GetOperationName(kOpHypCosine), + GetOperationName(kOpHypTangent), + QString(), + GetOperationName(kOpMin), + GetOperationName(kOpMax), + GetOperationName(kOpClamp), + }; SetComboBoxStrings(kMethodIn, operations); } @@ -187,6 +218,39 @@ void MathNode::OperateSampleNumber(Operation operation, const float *input, floa output[start + j] = std::pow(input[start + j], b); } break; + case kOpMin: +#ifdef USE_SSE + for(size_t j = 0; j < end_divisible_4; j+=4) { + _mm_storeu_ps(output + start + j, _mm_min_ps(_mm_loadu_ps(input + start + j), mult)); + } +#endif + for (size_t j = end_divisible_4; j < end; j++) { + output[start + j] = std::min(input[start + j], b); + } + break; + case kOpMax: +#ifdef USE_SSE + for(size_t j = 0; j < end_divisible_4; j+=4) { + _mm_storeu_ps(output + start + j, _mm_max_ps(_mm_loadu_ps(input + start + j), mult)); + } +#endif + for (size_t j = end_divisible_4; j < end; j++) { + output[start + j] = std::max(input[start + j], b); + } + break; + + // These operations use either more or less than 2 operands and are thus invalid for this function + case kOpSine: + case kOpCosine: + case kOpTangent: + case kOpArcSine: + case kOpArcCosine: + case kOpArcTangent: + case kOpHypSine: + case kOpHypCosine: + case kOpHypTangent: + case kOpClamp: + break; } } @@ -248,9 +312,83 @@ void MathNode::OperateSampleSample(Operation operation, const float *input, floa output[start + j] = std::pow(input[start + j], input2[start + j]); } break; + case kOpMin: +#ifdef USE_SSE + for(size_t j = 0; j < end_divisible_4; j+=4) { + _mm_storeu_ps(output + start + j, _mm_min_ps(_mm_loadu_ps(input + start + j), _mm_loadu_ps(input2 + start + j))); + } +#endif + for (size_t j = end_divisible_4; j < end; j++) { + output[start + j] = std::min(input[start + j], input2[start + j]); + } + break; + case kOpMax: +#ifdef USE_SSE + for(size_t j = 0; j < end_divisible_4; j+=4) { + _mm_storeu_ps(output + start + j, _mm_max_ps(_mm_loadu_ps(input + start + j), _mm_loadu_ps(input2 + start + j))); + } +#endif + for (size_t j = end_divisible_4; j < end; j++) { + output[start + j] = std::max(input[start + j], input2[start + j]); + } + break; + + // These operations use either more or less than 2 operands and are thus invalid for this function + case kOpSine: + case kOpCosine: + case kOpTangent: + case kOpArcSine: + case kOpArcCosine: + case kOpArcTangent: + case kOpHypSine: + case kOpHypCosine: + case kOpHypTangent: + case kOpClamp: + break; } } +int MathNode::GetNumberOfOperands(Operation o) +{ + switch (o) { + case kOpAdd: return 2; + case kOpSubtract: return 2; + case kOpMultiply: return 2; + case kOpDivide: return 2; + case kOpPower: return 2; + case kOpSine: return 1; + case kOpCosine: return 1; + case kOpTangent: return 1; + case kOpArcSine: return 1; + case kOpArcCosine: return 1; + case kOpArcTangent: return 1; + case kOpHypSine: return 1; + case kOpHypCosine: return 1; + case kOpHypTangent: return 1; + case kOpMin: return 2; + case kOpMax: return 2; + case kOpClamp: return 3; + } + + return 0; +} + +MathNode::Operation MathNode::GetOperation() const +{ + return static_cast(GetStandardValue(kMethodIn).toInt()); +} + +void MathNode::UpdateInputVisibility() +{ + int count = GetNumberOfOperands(GetOperation()); + + SetInputFlag(kParamAIn, kInputFlagHidden, 0 >= count); + SetInputFlag(kParamBIn, kInputFlagHidden, 1 >= count); + SetInputFlag(kParamCIn, kInputFlagHidden, 2 >= count); + + Retranslate(); +} + void MathNode::ProcessSamplesDouble(const void *context, const SampleJob &job, SampleBuffer &output) { const Node *n = static_cast(context); @@ -282,20 +420,122 @@ void MathNode::ProcessSamplesDouble(const void *context, const SampleJob &job, S } } +void MathNode::InputValueChangedEvent(const QString &input, int element) +{ + if (input == kMethodIn) { + UpdateInputVisibility(); + } + + super::InputValueChangedEvent(input, element); +} + +QString GetShaderUniformType(const type_t &type) +{ + if (type == TYPE_TEXTURE) { + return QStringLiteral("sampler2D"); + } else { + return QStringLiteral("float"); + } +} + +QString GetShaderVariableCall(const QString &input_id, const type_t &type, const QString& coord_op = QString()) +{ + if (type == TYPE_TEXTURE) { + return QStringLiteral("texture(%1, ove_texcoord%2)").arg(input_id, coord_op); + } + + return input_id; +} + +ShaderCode MathNode::GetShaderCode(const QString &id) +{ + QStringList j = id.split(':'); + if (j.size() != 3) { + return ShaderCode(); + } + + Operation op = static_cast(j.at(0).toInt()); + type_t atype = type_t::fromString(j.at(1)); + type_t btype = type_t::fromString(j.at(2)); + + QString line; + + switch (op) { + case kOpAdd: line = QStringLiteral("%1 + %2"); break; + case kOpSubtract: line = QStringLiteral("%1 - %2"); break; + case kOpMultiply: line = QStringLiteral("%1 * %2"); break; + case kOpDivide: line = QStringLiteral("%1 / %2"); break; + case kOpPower: + case kOpMin: + case kOpMax: + if (op == kOpPower) { + line = QStringLiteral("pow"); + } else if (op == kOpMin) { + line = QStringLiteral("min"); + } else if (op == kOpMax) { + line = QStringLiteral("max"); + } + + if (atype == TYPE_DOUBLE) { + // The "number" in this operation has to be declared a vec4 + line.append("(%2, vec4(%1))"); + } else if (btype == TYPE_DOUBLE) { + line.append("(%1, vec4(%2))"); + } else { + line.append("(%1, %2)"); + } + break; + case kOpSine: line = QStringLiteral("sin(%1)"); break; + case kOpCosine: line = QStringLiteral("cos(%1)"); break; + case kOpTangent: line = QStringLiteral("tan(%1)"); break; + case kOpArcSine: line = QStringLiteral("asin(%1)"); break; + case kOpArcCosine: line = QStringLiteral("acos(%1)"); break; + case kOpArcTangent: line = QStringLiteral("atan(%1)"); break; + case kOpHypSine: line = QStringLiteral("sinh(%1)"); break; + case kOpHypCosine: line = QStringLiteral("cosh(%1)"); break; + case kOpHypTangent: line = QStringLiteral("tanh(%1)"); break; + case kOpClamp: + qWarning() << "texture clamping is currently a stub!"; + break; + } + + line = line.arg(GetShaderVariableCall(kParamAIn, atype), + GetShaderVariableCall(kParamBIn, btype)); + + static const QString stub = + QStringLiteral("uniform %1 %3;\n" + "uniform %2 %4;\n" + "\n" + "in vec2 ove_texcoord;\n" + "out vec4 frag_color;\n" + "\n" + "void main(void) {\n" + " vec4 c = %5;\n" + " c.a = clamp(c.a, 0.0, 1.0);\n" // Ensure alpha is between 0.0 and 1.0 + " frag_color = c;\n" + "}\n"); + + return stub.arg(GetShaderUniformType(atype), + GetShaderUniformType(btype), + kParamAIn, + kParamBIn, + line); +} + value_t MathNode::Value(const ValueParams &p) const { // Auto-detect what values to operate with auto aval = GetInputValue(p, kParamAIn); auto bval = GetInputValue(p, kParamBIn); - - if (!bval.isValid()) { - return aval; - } - if (!aval.isValid()) { - return bval; - } + auto cval = GetInputValue(p, kParamCIn); Operation operation = static_cast(GetInputValue(p, kMethodIn).toInt()); + int count = GetNumberOfOperands(operation); + + // Null values if we aren't listening so the function-lookup is more reliable + if (count < 1) { aval = value_t(); } + if (count < 2) { bval = value_t(); } + if (count < 3) { cval = value_t(); } if (aval.type() == TYPE_SAMPLES || bval.type() == TYPE_SAMPLES) { if (aval.type() == TYPE_SAMPLES && bval.type() == TYPE_SAMPLES) { @@ -320,12 +560,15 @@ value_t MathNode::Value(const ValueParams &p) const return job; } } else if (aval.type() == TYPE_TEXTURE || bval.type() == TYPE_TEXTURE) { - // FIXME: Requires a more complex shader job + // Produce shader job + ShaderJob job = CreateShaderJob(p, GetShaderCode); + job.SetShaderID(QStringLiteral("%1:%2:%3").arg(QString::number(operation), aval.type().toString(), bval.type().toString())); + return Texture::Job(aval.type() == TYPE_TEXTURE ? aval.toTexture()->params() : bval.toTexture()->params(), job); } else { // Operation can be done entirely here - operation_t func = operations_[operation][aval.type()][bval.type()]; + operation_t func = operations_[operation][aval.type()][bval.type()][cval.type()]; if (func) { - return func(aval, bval); + return func(aval, bval, cval); } } @@ -340,6 +583,18 @@ QString MathNode::GetOperationName(Operation o) case kOpMultiply: return tr("Multiply"); case kOpDivide: return tr("Divide"); case kOpPower: return tr("Power"); + case kOpSine: return tr("Sine"); + case kOpCosine: return tr("Cosine"); + case kOpTangent: return tr("Tangent"); + case kOpArcSine: return tr("Inverse Sine"); + case kOpArcCosine: return tr("Inverse Cosine"); + case kOpArcTangent: return tr("Inverse Tangent"); + case kOpHypSine: return tr("Hyperbolic Sine"); + case kOpHypCosine: return tr("Hyperbolic Cosine"); + case kOpHypTangent: return tr("Hyperbolic Tangent"); + case kOpMin: return tr("Minimum"); + case kOpMax: return tr("Maximum"); + case kOpClamp: return tr("Clamp"); } return QString(); @@ -347,17 +602,31 @@ QString MathNode::GetOperationName(Operation o) void MathNode::PopulateOperations() { - operations_[kOpAdd][TYPE_INTEGER][TYPE_INTEGER] = Math::AddIntegerInteger; - - operations_[kOpAdd][TYPE_DOUBLE][TYPE_DOUBLE] = Math::AddDoubleDouble; - operations_[kOpSubtract][TYPE_DOUBLE][TYPE_DOUBLE] = Math::SubtractDoubleDouble; - operations_[kOpMultiply][TYPE_DOUBLE][TYPE_DOUBLE] = Math::MultiplyDoubleDouble; - operations_[kOpDivide][TYPE_DOUBLE][TYPE_DOUBLE] = Math::DivideDoubleDouble; - operations_[kOpPower][TYPE_DOUBLE][TYPE_DOUBLE] = Math::PowerDoubleDouble; - - operations_[kOpAdd][TYPE_MATRIX][TYPE_MATRIX] = Math::AddMatrixMatrix; - operations_[kOpSubtract][TYPE_MATRIX][TYPE_MATRIX] = Math::SubtractMatrixMatrix; - operations_[kOpMultiply][TYPE_MATRIX][TYPE_MATRIX] = Math::MultiplyMatrixMatrix; + operations_[kOpAdd][TYPE_DOUBLE][TYPE_DOUBLE][TYPE_NONE] = Math::AddDoubleDouble; + operations_[kOpSubtract][TYPE_DOUBLE][TYPE_DOUBLE][TYPE_NONE] = Math::SubtractDoubleDouble; + operations_[kOpMultiply][TYPE_DOUBLE][TYPE_DOUBLE][TYPE_NONE] = Math::MultiplyDoubleDouble; + operations_[kOpDivide][TYPE_DOUBLE][TYPE_DOUBLE][TYPE_NONE] = Math::DivideDoubleDouble; + operations_[kOpPower][TYPE_DOUBLE][TYPE_DOUBLE][TYPE_NONE] = Math::PowerDoubleDouble; + + operations_[kOpSine][TYPE_DOUBLE][TYPE_NONE][TYPE_NONE] = Math::SineDouble; + operations_[kOpCosine][TYPE_DOUBLE][TYPE_NONE][TYPE_NONE] = Math::CosineDouble; + operations_[kOpTangent][TYPE_DOUBLE][TYPE_NONE][TYPE_NONE] = Math::TangentDouble; + + operations_[kOpArcSine][TYPE_DOUBLE][TYPE_NONE][TYPE_NONE] = Math::ArcSineDouble; + operations_[kOpArcCosine][TYPE_DOUBLE][TYPE_NONE][TYPE_NONE] = Math::ArcCosineDouble; + operations_[kOpArcTangent][TYPE_DOUBLE][TYPE_NONE][TYPE_NONE] = Math::ArcTangentDouble; + + operations_[kOpHypSine][TYPE_DOUBLE][TYPE_NONE][TYPE_NONE] = Math::HypSineDouble; + operations_[kOpHypCosine][TYPE_DOUBLE][TYPE_NONE][TYPE_NONE] = Math::HypCosineDouble; + operations_[kOpHypTangent][TYPE_DOUBLE][TYPE_NONE][TYPE_NONE] = Math::HypTangentDouble; + + operations_[kOpMin][TYPE_DOUBLE][TYPE_DOUBLE][TYPE_NONE] = Math::MinDoubleDouble; + operations_[kOpMax][TYPE_DOUBLE][TYPE_DOUBLE][TYPE_NONE] = Math::MaxDoubleDouble; + operations_[kOpClamp][TYPE_DOUBLE][TYPE_DOUBLE][TYPE_DOUBLE] = Math::ClampDoubleDoubleDouble; + + operations_[kOpAdd][TYPE_MATRIX][TYPE_MATRIX][TYPE_NONE] = Math::AddMatrixMatrix; + operations_[kOpSubtract][TYPE_MATRIX][TYPE_MATRIX][TYPE_NONE] = Math::SubtractMatrixMatrix; + operations_[kOpMultiply][TYPE_MATRIX][TYPE_MATRIX][TYPE_NONE] = Math::MultiplyMatrixMatrix; } } diff --git a/app/node/math/math/math.h b/app/node/math/math/math.h index 90860d9950..33456f5441 100644 --- a/app/node/math/math/math.h +++ b/app/node/math/math/math.h @@ -36,7 +36,24 @@ class MathNode : public Node kOpSubtract, kOpMultiply, kOpDivide, - kOpPower + + kOpPower, + + kOpSine, + kOpCosine, + kOpTangent, + + kOpArcSine, + kOpArcCosine, + kOpArcTangent, + + kOpHypSine, + kOpHypCosine, + kOpHypTangent, + + kOpMin, + kOpMax, + kOpClamp }; NODE_DEFAULT_FUNCTIONS(MathNode) @@ -52,6 +69,8 @@ class MathNode : public Node static QString GetOperationName(Operation o); + static ShaderCode GetShaderCode(const QString &id); + static const QString kMethodIn; static const QString kParamAIn; static const QString kParamBIn; @@ -59,10 +78,13 @@ class MathNode : public Node static void ProcessSamplesDouble(const void *context, const SampleJob &job, SampleBuffer &mixed_samples); +protected: + virtual void InputValueChangedEvent(const QString& input, int element) override; + private: - typedef value_t (*operation_t)(const value_t &a, const value_t &b); + typedef value_t (*operation_t)(const value_t &a, const value_t &b, const value_t &c); - static std::map > > operations_; + static std::map> > > operations_; static void PopulateOperations(); @@ -71,6 +93,12 @@ class MathNode : public Node static void OperateSampleNumber(Operation operation, const float *input, float *output, float b, size_t start, size_t end); static void OperateSampleSample(Operation operation, const float *input, float *output, const float *input2, size_t start, size_t end); + static int GetNumberOfOperands(Operation o); + + Operation GetOperation() const; + + void UpdateInputVisibility(); + }; } diff --git a/app/node/math/math/mathfunctions.cpp b/app/node/math/math/mathfunctions.cpp index 4720b6c5c4..ef1f08134e 100644 --- a/app/node/math/math/mathfunctions.cpp +++ b/app/node/math/math/mathfunctions.cpp @@ -22,61 +22,160 @@ namespace olive { -value_t Math::AddIntegerInteger(const value_t &a, const value_t &b) +value_t Math::AddDoubleDouble(const value_t &a, const value_t &b, const value_t &c) { - value_t v(TYPE_INTEGER, std::max(a.size(), b.size())); + value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); for (size_t i = 0; i < v.size(); i++) { - v[i] = a.value(i) + b.value(i); + v[i] = a.value(i) + b.value(i); } return v; } -value_t Math::AddDoubleDouble(const value_t &a, const value_t &b) +value_t Math::SubtractDoubleDouble(const value_t &a, const value_t &b, const value_t &c) { value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); for (size_t i = 0; i < v.size(); i++) { - v[i] = a.value(i) + b.value(i); + v[i] = a.value(i) - b.value(i); } return v; } -value_t Math::SubtractDoubleDouble(const value_t &a, const value_t &b) +value_t Math::MultiplyDoubleDouble(const value_t &a, const value_t &b, const value_t &c) { value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); for (size_t i = 0; i < v.size(); i++) { - v[i] = a.value(i) - b.value(i); + v[i] = a.value(i) * b.value(i); } return v; } -value_t Math::MultiplyDoubleDouble(const value_t &a, const value_t &b) +value_t Math::DivideDoubleDouble(const value_t &a, const value_t &b, const value_t &c) { value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); for (size_t i = 0; i < v.size(); i++) { - v[i] = a.value(i) * b.value(i); + v[i] = a.value(i) / b.value(i); } return v; } -value_t Math::DivideDoubleDouble(const value_t &a, const value_t &b) +value_t Math::PowerDoubleDouble(const value_t &a, const value_t &b, const value_t &c) { value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); for (size_t i = 0; i < v.size(); i++) { - v[i] = a.value(i) / b.value(i); + v[i] = std::pow(a.value(i), b.value(i)); } return v; } -value_t Math::PowerDoubleDouble(const value_t &a, const value_t &b) +value_t Math::SineDouble(const value_t &a, const value_t &b, const value_t &c) { value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); for (size_t i = 0; i < v.size(); i++) { - v[i] = std::pow(a.value(i), b.value(i)); + v[i] = std::sin(a.value(i)); + } + return v; +} + +value_t Math::CosineDouble(const value_t &a, const value_t &b, const value_t &c) +{ + value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::cos(a.value(i)); + } + return v; +} + +value_t Math::TangentDouble(const value_t &a, const value_t &b, const value_t &c) +{ + value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::tan(a.value(i)); + } + return v; +} + +value_t Math::ArcSineDouble(const value_t &a, const value_t &b, const value_t &c) +{ + value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::asin(a.value(i)); + } + return v; +} + +value_t Math::ArcCosineDouble(const value_t &a, const value_t &b, const value_t &c) +{ + value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::acos(a.value(i)); + } + return v; +} + +value_t Math::ArcTangentDouble(const value_t &a, const value_t &b, const value_t &c) +{ + value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::atan(a.value(i)); + } + return v; +} + +value_t Math::HypSineDouble(const value_t &a, const value_t &b, const value_t &c) +{ + value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::sinh(a.value(i)); + } + return v; +} + +value_t Math::HypCosineDouble(const value_t &a, const value_t &b, const value_t &c) +{ + value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::cosh(a.value(i)); + } + return v; +} + +value_t Math::HypTangentDouble(const value_t &a, const value_t &b, const value_t &c) +{ + value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::tanh(a.value(i)); + } + return v; +} + +value_t Math::MinDoubleDouble(const value_t &a, const value_t &b, const value_t &c) +{ + value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::min(a.value(i), b.value(i)); + } + return v; +} + +value_t Math::MaxDoubleDouble(const value_t &a, const value_t &b, const value_t &c) +{ + value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::max(a.value(i), b.value(i)); + } + return v; +} + +value_t Math::ClampDoubleDoubleDouble(const value_t &a, const value_t &b, const value_t &c) +{ + value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::clamp(a.value(i), b.value(i), c.value(i)); } return v; } -value_t Math::AddMatrixMatrix(const value_t &a, const value_t &b) +value_t Math::AddMatrixMatrix(const value_t &a, const value_t &b, const value_t &c) { value_t v(TYPE_MATRIX, std::max(a.size(), b.size())); for (size_t i = 0; i < v.size(); i++) { @@ -85,7 +184,7 @@ value_t Math::AddMatrixMatrix(const value_t &a, const value_t &b) return v; } -value_t Math::SubtractMatrixMatrix(const value_t &a, const value_t &b) +value_t Math::SubtractMatrixMatrix(const value_t &a, const value_t &b, const value_t &c) { value_t v(TYPE_MATRIX, std::max(a.size(), b.size())); for (size_t i = 0; i < v.size(); i++) { @@ -94,7 +193,7 @@ value_t Math::SubtractMatrixMatrix(const value_t &a, const value_t &b) return v; } -value_t Math::MultiplyMatrixMatrix(const value_t &a, const value_t &b) +value_t Math::MultiplyMatrixMatrix(const value_t &a, const value_t &b, const value_t &c) { value_t v(TYPE_MATRIX, std::max(a.size(), b.size())); for (size_t i = 0; i < v.size(); i++) { diff --git a/app/node/math/math/mathfunctions.h b/app/node/math/math/mathfunctions.h index a1117ded5f..969a78bde4 100644 --- a/app/node/math/math/mathfunctions.h +++ b/app/node/math/math/mathfunctions.h @@ -28,20 +28,34 @@ namespace olive { class Math { public: - static value_t AddIntegerInteger(const value_t &a, const value_t &b); + static value_t AddDoubleDouble(const value_t &a, const value_t &b, const value_t &c); + static value_t SubtractDoubleDouble(const value_t &a, const value_t &b, const value_t &c); + static value_t MultiplyDoubleDouble(const value_t &a, const value_t &b, const value_t &c); + static value_t DivideDoubleDouble(const value_t &a, const value_t &b, const value_t &c); + static value_t PowerDoubleDouble(const value_t &a, const value_t &b, const value_t &c); - static value_t AddDoubleDouble(const value_t &a, const value_t &b); - static value_t SubtractDoubleDouble(const value_t &a, const value_t &b); - static value_t MultiplyDoubleDouble(const value_t &a, const value_t &b); - static value_t DivideDoubleDouble(const value_t &a, const value_t &b); - static value_t PowerDoubleDouble(const value_t &a, const value_t &b); + static value_t SineDouble(const value_t &a, const value_t &b, const value_t &c); + static value_t CosineDouble(const value_t &a, const value_t &b, const value_t &c); + static value_t TangentDouble(const value_t &a, const value_t &b, const value_t &c); - static value_t AddMatrixMatrix(const value_t &a, const value_t &b); - static value_t SubtractMatrixMatrix(const value_t &a, const value_t &b); - static value_t MultiplyMatrixMatrix(const value_t &a, const value_t &b); + static value_t ArcSineDouble(const value_t &a, const value_t &b, const value_t &c); + static value_t ArcCosineDouble(const value_t &a, const value_t &b, const value_t &c); + static value_t ArcTangentDouble(const value_t &a, const value_t &b, const value_t &c); - static value_t MultiplySamplesDouble(const value_t &a, const value_t &b); - static value_t MultiplyDoubleSamples(const value_t &a, const value_t &b); + static value_t HypSineDouble(const value_t &a, const value_t &b, const value_t &c); + static value_t HypCosineDouble(const value_t &a, const value_t &b, const value_t &c); + static value_t HypTangentDouble(const value_t &a, const value_t &b, const value_t &c); + + static value_t MinDoubleDouble(const value_t &a, const value_t &b, const value_t &c); + static value_t MaxDoubleDouble(const value_t &a, const value_t &b, const value_t &c); + static value_t ClampDoubleDoubleDouble(const value_t &a, const value_t &b, const value_t &c); + + static value_t AddMatrixMatrix(const value_t &a, const value_t &b, const value_t &c); + static value_t SubtractMatrixMatrix(const value_t &a, const value_t &b, const value_t &c); + static value_t MultiplyMatrixMatrix(const value_t &a, const value_t &b, const value_t &c); + + static value_t MultiplySamplesDouble(const value_t &a, const value_t &b, const value_t &c); + static value_t MultiplyDoubleSamples(const value_t &a, const value_t &b, const value_t &c); }; From f9ed0a1ed0159f16ffd3ccdce5a038ae842d0f70 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 21 May 2023 15:05:51 -0700 Subject: [PATCH 34/71] hide deprecated nodes --- app/node/distort/transform/transformdistortnode.cpp | 3 +++ app/node/generator/matrix/matrix.cpp | 3 +++ app/node/math/trigonometry/trigonometry.cpp | 3 +++ 3 files changed, 9 insertions(+) diff --git a/app/node/distort/transform/transformdistortnode.cpp b/app/node/distort/transform/transformdistortnode.cpp index 3ce0e7610a..fd25100eb7 100644 --- a/app/node/distort/transform/transformdistortnode.cpp +++ b/app/node/distort/transform/transformdistortnode.cpp @@ -66,6 +66,9 @@ TransformDistortNode::TransformDistortNode() SetFlag(kVideoEffect); SetEffectInput(kTextureInput); + + // Undo MatrixGenerator deprecation flag for derivative + SetFlag(kDontShowInCreateMenu, false); } void TransformDistortNode::Retranslate() diff --git a/app/node/generator/matrix/matrix.cpp b/app/node/generator/matrix/matrix.cpp index e31f1630b9..43fd117704 100644 --- a/app/node/generator/matrix/matrix.cpp +++ b/app/node/generator/matrix/matrix.cpp @@ -49,6 +49,9 @@ MatrixGenerator::MatrixGenerator() AddInput(kUniformScaleInput, TYPE_BOOL, true, kInputFlagNotConnectable | kInputFlagNotKeyframable); AddInput(kAnchorInput, TYPE_VEC2, QVector2D(0.0, 0.0)); + + // Deprecated: Use Transform instead, which now outputs a matrix too + SetFlag(kDontShowInCreateMenu); } QString MatrixGenerator::Name() const diff --git a/app/node/math/trigonometry/trigonometry.cpp b/app/node/math/trigonometry/trigonometry.cpp index 0579c1ce66..437eb8d34c 100644 --- a/app/node/math/trigonometry/trigonometry.cpp +++ b/app/node/math/trigonometry/trigonometry.cpp @@ -32,6 +32,9 @@ TrigonometryNode::TrigonometryNode() AddInput(kMethodIn, TYPE_COMBO, kInputFlagNotConnectable | kInputFlagNotKeyframable); AddInput(kXIn, TYPE_DOUBLE, 0.0); + + // Deprecated: Use Math instead, which implements trig functions now too + SetFlag(kDontShowInCreateMenu); } QString TrigonometryNode::Name() const From e6aa010968d9b2bed6100952d1fb9385b82fad2e Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 21 May 2023 15:06:18 -0700 Subject: [PATCH 35/71] use actual input channel count in nodevaluetree --- app/widget/nodevaluetree/nodevaluetree.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/widget/nodevaluetree/nodevaluetree.cpp b/app/widget/nodevaluetree/nodevaluetree.cpp index 6e6ecadd7f..6d5cba64b5 100644 --- a/app/widget/nodevaluetree/nodevaluetree.cpp +++ b/app/widget/nodevaluetree/nodevaluetree.cpp @@ -69,7 +69,7 @@ void NodeValueTree::SetNode(const NodeInput &input) swizzler_items->setFlags(Qt::NoItemFlags); ValueSwizzleWidget *b = new ValueSwizzleWidget(); - b->set_channels(4, 4); + b->set_channels(4, input.GetChannelCount()); b->set(vh.swizzle()); connect(b, &ValueSwizzleWidget::value_changed, this, &NodeValueTree::SwizzleChanged); this->setItemWidget(swizzler_items, 1, b); From f5bd882fd4656048e37e2f9f433810b44dc21550 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 21 May 2023 15:06:39 -0700 Subject: [PATCH 36/71] re-add matrix output to transform --- .../transform/transformdistortnode.cpp | 63 ++++++++++--------- .../distort/transform/transformdistortnode.h | 1 + 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/app/node/distort/transform/transformdistortnode.cpp b/app/node/distort/transform/transformdistortnode.cpp index fd25100eb7..f3fe44358b 100644 --- a/app/node/distort/transform/transformdistortnode.cpp +++ b/app/node/distort/transform/transformdistortnode.cpp @@ -28,6 +28,7 @@ const QString TransformDistortNode::kParentInput = QStringLiteral("parent_in"); const QString TransformDistortNode::kTextureInput = QStringLiteral("tex_in"); const QString TransformDistortNode::kAutoscaleInput = QStringLiteral("autoscale_in"); const QString TransformDistortNode::kInterpolationInput = QStringLiteral("interpolation_in"); +const QString TransformDistortNode::kMatrixOutput = QStringLiteral("matrix_out"); #define super MatrixGenerator @@ -67,6 +68,8 @@ TransformDistortNode::TransformDistortNode() SetFlag(kVideoEffect); SetEffectInput(kTextureInput); + AddOutput(kMatrixOutput); + // Undo MatrixGenerator deprecation flag for derivative SetFlag(kDontShowInCreateMenu, false); } @@ -82,6 +85,8 @@ void TransformDistortNode::Retranslate() SetComboBoxStrings(kAutoscaleInput, {tr("None"), tr("Fit"), tr("Fill"), tr("Stretch")}); SetComboBoxStrings(kInterpolationInput, {tr("Nearest Neighbor"), tr("Bilinear"), tr("Mipmapped Bilinear")}); + + SetOutputName(kMatrixOutput, tr("Matrix")); } ShaderCode TransformDistortNode::GetShaderCode(const QString &id) @@ -95,36 +100,38 @@ value_t TransformDistortNode::Value(const ValueParams &p) const // Generate matrix QMatrix4x4 generated_matrix = GenerateMatrix(p, false, false, false, GetInputValue(p, kParentInput).toMatrix()); - // Pop texture - value_t texture_meta = GetInputValue(p, kTextureInput); - - TexturePtr job_to_push = nullptr; - - // If we have a texture, generate a matrix and make it happen - if (TexturePtr texture = texture_meta.toTexture()) { - // Adjust our matrix by the resolutions involved - QMatrix4x4 real_matrix = GenerateAutoScaledMatrix(generated_matrix, p, texture->params()); - - if (!real_matrix.isIdentity()) { - // The matrix will transform things - ShaderJob job; - job.Insert(QStringLiteral("ove_maintex"), texture_meta); - job.Insert(QStringLiteral("ove_mvpmat"), real_matrix); - job.SetInterpolation(QStringLiteral("ove_maintex"), static_cast(GetInputValue(p, kInterpolationInput).toInt())); - job.set_function(GetShaderCode); - - // Use global resolution rather than texture resolution because this may result in a size change - job_to_push = Texture::Job(p.vparams(), job); + if (p.output() == kMatrixOutput) { + return generated_matrix; + } else { + // Pop texture + value_t texture_meta = GetInputValue(p, kTextureInput); + + TexturePtr job_to_push = nullptr; + + // If we have a texture, generate a matrix and make it happen + if (TexturePtr texture = texture_meta.toTexture()) { + // Adjust our matrix by the resolutions involved + QMatrix4x4 real_matrix = GenerateAutoScaledMatrix(generated_matrix, p, texture->params()); + + if (!real_matrix.isIdentity()) { + // The matrix will transform things + ShaderJob job; + job.Insert(QStringLiteral("ove_maintex"), texture_meta); + job.Insert(QStringLiteral("ove_mvpmat"), real_matrix); + job.SetInterpolation(QStringLiteral("ove_maintex"), static_cast(GetInputValue(p, kInterpolationInput).toInt())); + job.set_function(GetShaderCode); + + // Use global resolution rather than texture resolution because this may result in a size change + job_to_push = Texture::Job(p.vparams(), job); + } } - } - - //table->Push(NodeValue::kMatrix, QVariant::fromValue(generated_matrix), this); - if (!job_to_push) { - // Re-push whatever value we received - return texture_meta; - } else { - return job_to_push; + if (!job_to_push) { + // Re-push whatever value we received + return texture_meta; + } else { + return job_to_push; + } } } diff --git a/app/node/distort/transform/transformdistortnode.h b/app/node/distort/transform/transformdistortnode.h index ebbb6c5a44..11c51e1cec 100644 --- a/app/node/distort/transform/transformdistortnode.h +++ b/app/node/distort/transform/transformdistortnode.h @@ -86,6 +86,7 @@ class TransformDistortNode : public MatrixGenerator static const QString kTextureInput; static const QString kAutoscaleInput; static const QString kInterpolationInput; + static const QString kMatrixOutput; protected slots: virtual void GizmoDragStart(const olive::ValueParams &p, double x, double y, const olive::rational &time) override; From fda9a2f9cf432ddab3a56b99a3a4a536f777599c Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Mon, 22 May 2023 11:44:21 -0700 Subject: [PATCH 37/71] permit multiple inputs on nodes --- app/node/node.cpp | 107 ++++++++++-------- app/node/node.h | 18 +-- app/node/nodeundo.cpp | 34 ++---- app/node/nodeundo.h | 24 ++-- app/node/project.cpp | 4 +- app/node/project/folder/folder.h | 2 +- app/node/project/serializer/serializer.h | 2 +- .../project/serializer/serializer230220.cpp | 2 +- app/render/projectcopier.cpp | 2 +- app/timeline/timelineundosplit.cpp | 2 +- app/widget/nodeparamview/nodeparamview.cpp | 2 +- app/widget/nodeview/nodeview.cpp | 8 +- app/widget/nodeview/nodeviewcontext.cpp | 6 +- .../projectexplorer/projectexplorer.cpp | 4 +- 14 files changed, 106 insertions(+), 111 deletions(-) diff --git a/app/node/node.cpp b/app/node/node.cpp index 99fae24611..9edb512482 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -187,17 +187,29 @@ QBrush Node::brush(qreal top, qreal bottom) const } } +bool Node::ConnectionExists(Node *output, const NodeInput &input) +{ + for (auto it = output->output_connections_.cbegin(); it != output->output_connections_.cend(); it++) { + if (it->second == input) { + return true; + } + } + + return false; +} + void Node::ConnectEdge(Node *output, const NodeInput &input) { // Ensure graph is the same Q_ASSERT(input.node()->parent() == output->parent()); - // Ensure a connection isn't getting overwritten - Q_ASSERT(input.node()->input_connections().find(input) == input.node()->input_connections().end()); + // Ensure connection doesn't already exist + Q_ASSERT(!ConnectionExists(output, input)); // Insert connection on both sides - input.node()->input_connections_[input] = output; - output->output_connections_.push_back(std::pair({output, input})); + auto conn = std::pair({output, input}); + input.node()->input_connections_.push_back(conn); + output->output_connections_.push_back(conn); // Call internal events input.node()->InputConnectedEvent(input.input(), input.element(), output); @@ -219,14 +231,16 @@ void Node::DisconnectEdge(Node *output, const NodeInput &input) Q_ASSERT(input.node()->parent() == output->parent()); // Ensure connection exists - Q_ASSERT(input.node()->input_connections().at(input) == output); + Q_ASSERT(ConnectionExists(output, input)); // Remove connection from both sides - InputConnections& inputs = input.node()->input_connections_; - inputs.erase(inputs.find(input)); + auto conn = std::pair({output, input}); - OutputConnections& outputs = output->output_connections_; - outputs.erase(std::find(outputs.begin(), outputs.end(), std::pair({output, input}))); + Connections& inputs = input.node()->input_connections_; + inputs.erase(std::find(inputs.begin(), inputs.end(), conn)); + + Connections& outputs = output->output_connections_; + outputs.erase(std::find(outputs.begin(), outputs.end(), conn)); // Call internal events input.node()->InputDisconnectedEvent(input.input(), input.element(), output); @@ -312,9 +326,10 @@ bool Node::IsInputConnected(const QString &input, int element) const Node *Node::GetConnectedOutput(const QString &input, int element) const { - for (auto it=input_connections_.cbegin(); it!=input_connections_.cend(); it++) { - if (it->first.input() == input && it->first.element() == element) { - return it->second; + // NOTE: Only returns the first output connected to this input, there may be more than one + for (auto it = input_connections_.cbegin(); it != input_connections_.cend(); it++) { + if (it->second.input() == input && it->second.element() == element) { + return it->first; } } @@ -776,15 +791,15 @@ void Node::InputArrayInsert(const QString &id, int index) ArrayResizeInternal(id, InputArraySize(id) + 1); // Move connections down - InputConnections copied_edges = input_connections(); + Connections copied_edges = input_connections(); for (auto it=copied_edges.crbegin(); it!=copied_edges.crend(); it++) { - if (it->first.input() == id && it->first.element() >= index) { + if (it->second.input() == id && it->second.element() >= index) { // Disconnect this and reconnect it one element down - NodeInput new_edge = it->first; + NodeInput new_edge = it->second; new_edge.set_element(new_edge.element() + 1); - DisconnectEdge(it->second, it->first); - ConnectEdge(it->second, new_edge); + DisconnectEdge(it->first, it->second); + ConnectEdge(it->first, new_edge); } } @@ -814,17 +829,17 @@ void Node::InputArrayRemove(const QString &id, int index) ArrayResizeInternal(id, InputArraySize(id) - 1); // Move connections up - InputConnections copied_edges = input_connections(); + Connections copied_edges = input_connections(); for (auto it=copied_edges.cbegin(); it!=copied_edges.cend(); it++) { - if (it->first.input() == id && it->first.element() >= index) { + if (it->second.input() == id && it->second.element() >= index) { // Disconnect this and reconnect it one element up if it's not the element being removed - DisconnectEdge(it->second, it->first); + DisconnectEdge(it->first, it->second); - if (it->first.element() > index) { - NodeInput new_edge = it->first; + if (it->second.element() > index) { + NodeInput new_edge = it->second; new_edge.set_element(new_edge.element() - 1); - ConnectEdge(it->second, new_edge); + ConnectEdge(it->first, new_edge); } } } @@ -1058,12 +1073,12 @@ void Node::CopyDependencyGraph(const QVector &src, const QVector for (auto it=src_node->input_connections_.cbegin(); it!=src_node->input_connections_.cend(); it++) { // Determine if the connected node is in our src list - int connection_index = src.indexOf(it->second); + int connection_index = src.indexOf(it->first); if (connection_index > -1) { // Find the equivalent node in the dst list Node *copied_output = dst.at(connection_index); - NodeInput copied_input = NodeInput(dst_node, it->first.input(), it->first.element()); + NodeInput copied_input = NodeInput(dst_node, it->second.input(), it->second.element()); if (command) { command->add_child(new NodeEdgeAddCommand(copied_output, copied_input)); @@ -1125,8 +1140,8 @@ Node *Node::CopyNodeAndDependencyGraphMinusItemsInternal(QMap& cre // Go through input connections and copy if non-item and connect if item for (auto it=node->input_connections_.cbegin(); it!=node->input_connections_.cend(); it++) { - NodeInput input = it->first; - Node* connected = it->second; + NodeInput input = it->second; + Node* connected = it->first; Node* connected_copy; if (connected->IsItem()) { @@ -1181,7 +1196,7 @@ Node *Node::CopyNodeInGraph(Node *node, MultiUndoCommand *command) void Node::SendInvalidateCache(const TimeRange &range, const InvalidateCacheOptions &options) { - for (const OutputConnection& conn : output_connections_) { + for (const Connection& conn : output_connections_) { // Send clear cache signal to the Node const NodeInput& in = conn.second; @@ -1409,10 +1424,10 @@ void Node::Save(QXmlStreamWriter *writer) const for (auto it=this->input_connections().cbegin(); it!=this->input_connections().cend(); it++) { writer->writeStartElement(QStringLiteral("connection")); - writer->writeAttribute(QStringLiteral("input"), it->first.input()); - writer->writeAttribute(QStringLiteral("element"), QString::number(it->first.element())); + writer->writeAttribute(QStringLiteral("input"), it->second.input()); + writer->writeAttribute(QStringLiteral("element"), QString::number(it->second.element())); - writer->writeTextElement(QStringLiteral("output"), QString::number(reinterpret_cast(it->second))); + writer->writeTextElement(QStringLiteral("output"), QString::number(reinterpret_cast(it->first))); writer->writeEndElement(); // connection } @@ -1887,10 +1902,10 @@ int Node::GetInternalInputArraySize(const QString &input) void FindWaysNodeArrivesHereRecursively(const Node *output, const Node *input, QVector &v) { for (auto it=input->input_connections().cbegin(); it!=input->input_connections().cend(); it++) { - if (it->second == output) { - v.append(it->first); + if (it->first == output) { + v.append(it->second); } else { - FindWaysNodeArrivesHereRecursively(output, it->second, v); + FindWaysNodeArrivesHereRecursively(output, it->first, v); } } } @@ -2003,12 +2018,12 @@ void Node::CopyInput(const Node *src, Node *dst, const QString &input, bool incl if (include_connections) { // Copy all connections for (auto it=src->input_connections().cbegin(); it!=src->input_connections().cend(); it++) { - if (!traverse_arrays && it->first.element() != -1) { + if (!traverse_arrays && it->second.element() != -1) { continue; } - auto conn_output = it->second; - NodeInput conn_input(dst, input, it->first.element()); + auto conn_output = it->first; + NodeInput conn_input(dst, input, it->second.element()); if (command) { command->add_child(new NodeEdgeAddCommand(conn_output, conn_input)); @@ -2086,7 +2101,7 @@ void Node::CopyValuesOfElement(const Node *src, Node *dst, const QString &input, void GetDependenciesRecursively(QVector& list, const Node* node, bool traverse, bool exclusive_only) { for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) { - Node* connected_node = it->second; + Node* connected_node = it->first; if (!exclusive_only || !connected_node->IsItem()) { if (!list.contains(connected_node)) { @@ -2135,7 +2150,7 @@ QVector Node::GetImmediateDependencies() const bool Node::InputsFrom(Node *n, bool recursively) const { for (auto it=input_connections_.cbegin(); it!=input_connections_.cend(); it++) { - Node *connected = it->second; + Node *connected = it->first; if (connected == n) { return true; @@ -2150,7 +2165,7 @@ bool Node::InputsFrom(Node *n, bool recursively) const bool Node::InputsFrom(const QString &id, bool recursively) const { for (auto it=input_connections_.cbegin(); it!=input_connections_.cend(); it++) { - Node *connected = it->second; + Node *connected = it->first; if (connected->id() == id) { return true; @@ -2165,13 +2180,13 @@ bool Node::InputsFrom(const QString &id, bool recursively) const void Node::DisconnectAll() { // Disconnect inputs (copy map since internal map will change as we disconnect) - InputConnections copy = input_connections_; + Connections copy = input_connections_; for (auto it=copy.cbegin(); it!=copy.cend(); it++) { - DisconnectEdge(it->second, it->first); + DisconnectEdge(it->first, it->second); } while (!output_connections_.empty()) { - OutputConnection conn = output_connections_.back(); + Connection conn = output_connections_.back(); DisconnectEdge(conn.first, conn.second); } } @@ -2533,9 +2548,9 @@ void Node::SetValueAtTime(const NodeInput &input, const rational &time, const va bool FindPathInternal(std::list &vec, Node *from, Node *to, int &path_index) { for (auto it = to->input_connections().cbegin(); it != to->input_connections().cend(); it++) { - vec.push_front(it->first); + vec.push_front(it->second); - if (it->second == from) { + if (it->first == from) { // Found a path! Determine if it's the index we want if (path_index == 0) { // It is! @@ -2546,7 +2561,7 @@ bool FindPathInternal(std::list &vec, Node *from, Node *to, int &path } } - if (FindPathInternal(vec, from, it->second, path_index)) { + if (FindPathInternal(vec, from, it->first, path_index)) { return true; } diff --git a/app/node/node.h b/app/node/node.h index 3041ba0a02..9774031f26 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -337,6 +337,8 @@ class Node : public QObject } } + static bool ConnectionExists(Node *output, const NodeInput& input); + static void ConnectEdge(Node *output, const NodeInput& input); static void DisconnectEdge(Node *output, const NodeInput& input); @@ -595,7 +597,8 @@ class Node : public QObject const NodeKeyframeTrack& GetTrackFromKeyframe(NodeKeyframe* key) const; - using InputConnections = std::map; + using Connection = std::pair; + using Connections = std::vector; /** * @brief Return map of input connections @@ -603,21 +606,18 @@ class Node : public QObject * Inputs can only have one connection, so the key is the input connected and the value is the * output that it's connected to. */ - const InputConnections& input_connections() const + const Connections& input_connections() const { return input_connections_; } - using OutputConnection = std::pair; - using OutputConnections = std::vector; - /** * @brief Return list of output connections * * An output can connect an infinite amount of inputs, so in this map, the key is the output and * the value is a vector of inputs. */ - const OutputConnections& output_connections() const + const Connections& output_connections() const { return output_connections_; } @@ -1160,9 +1160,9 @@ protected slots: QMap > array_immediates_; - InputConnections input_connections_; + Connections input_connections_; - OutputConnections output_connections_; + Connections output_connections_; Folder* folder_; @@ -1246,7 +1246,7 @@ template void Node::FindInputNodeInternal(const Node* n, QVector &list, int maximum) { for (auto it=n->input_connections_.cbegin(); it!=n->input_connections_.cend(); it++) { - FindInputNodesConnectedToInputInternal(it->first, list, maximum); + FindInputNodesConnectedToInputInternal(it->second, list, maximum); } } diff --git a/app/node/nodeundo.cpp b/app/node/nodeundo.cpp index 7900d00e3a..7b7f88a4bd 100644 --- a/app/node/nodeundo.cpp +++ b/app/node/nodeundo.cpp @@ -106,7 +106,7 @@ void NodeSetPositionAndDependenciesRecursivelyCommand::move_recursively(Node *no commands_.append(new NodeSetPositionCommand(node_, context_, pos)); for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) { - Node *output = it->second; + Node *output = it->first; if (context_->ContextContainsNode(output)) { move_recursively(output, diff); } @@ -115,36 +115,18 @@ void NodeSetPositionAndDependenciesRecursivelyCommand::move_recursively(Node *no NodeEdgeAddCommand::NodeEdgeAddCommand(Node *output, const NodeInput &input) : output_(output), - input_(input), - remove_command_(nullptr) -{ -} - -NodeEdgeAddCommand::~NodeEdgeAddCommand() + input_(input) { - delete remove_command_; } void NodeEdgeAddCommand::redo() { - if (input_.IsConnected()) { - if (!remove_command_) { - remove_command_ = new NodeEdgeRemoveCommand(input_.GetConnectedOutput(), input_); - } - - remove_command_->redo_now(); - } - Node::ConnectEdge(output_, input_); } void NodeEdgeAddCommand::undo() { Node::DisconnectEdge(output_, input_); - - if (remove_command_) { - remove_command_->undo_now(); - } } Project *NodeEdgeAddCommand::GetRelevantProject() const @@ -212,10 +194,10 @@ void NodeRemoveAndDisconnectCommand::prepare() // Disconnect everything for (auto it=node_->input_connections().cbegin(); it!=node_->input_connections().cend(); it++) { - command_->add_child(new NodeEdgeRemoveCommand(it->second, it->first)); + command_->add_child(new NodeEdgeRemoveCommand(it->first, it->second)); } - for (const Node::OutputConnection& conn : node_->output_connections()) { + for (const Node::Connection& conn : node_->output_connections()) { command_->add_child(new NodeEdgeRemoveCommand(conn.first, conn.second)); } @@ -284,8 +266,8 @@ void NodeViewDeleteCommand::AddNode(Node *node, Node *context) nodes_.append(p); for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) { - if (context->ContextContainsNode(it->second)) { - AddEdge(it->second, it->first); + if (context->ContextContainsNode(it->first)) { + AddEdge(it->first, it->second); } } @@ -298,7 +280,7 @@ void NodeViewDeleteCommand::AddNode(Node *node, Node *context) void NodeViewDeleteCommand::AddEdge(Node *output, const NodeInput &input) { - foreach (const Node::OutputConnection &edge, edges_) { + foreach (const Node::Connection &edge, edges_) { if (edge.first == output && edge.second == input) { return; } @@ -333,7 +315,7 @@ Project *NodeViewDeleteCommand::GetRelevantProject() const void NodeViewDeleteCommand::redo() { - foreach (const Node::OutputConnection &edge, edges_) { + foreach (const Node::Connection &edge, edges_) { Node::DisconnectEdge(edge.first, edge.second); } diff --git a/app/node/nodeundo.h b/app/node/nodeundo.h index d7ce564e85..460a329e11 100644 --- a/app/node/nodeundo.h +++ b/app/node/nodeundo.h @@ -189,16 +189,14 @@ class NodeArrayResizeCommand : public UndoCommand if (old_size_ > size_) { // Decreasing in size, disconnect any extraneous edges - for (int i=size_; iinput_connections().at(input); - - removed_connections_[input] = output; + for (auto it = node_->input_connections().cbegin(); it != node_->input_connections().cend(); it++) { + Node *output = it->first; + const NodeInput &input = it->second; + if (input.input() == input_ && input.element() >= size_) { + removed_connections_.push_back({output, input}); Node::DisconnectEdge(output, input); - } catch (std::out_of_range&) {} + } } } @@ -208,7 +206,7 @@ class NodeArrayResizeCommand : public UndoCommand virtual void undo() override { for (auto it=removed_connections_.cbegin(); it!=removed_connections_.cend(); it++) { - Node::ConnectEdge(it->second, it->first); + Node::ConnectEdge(it->first, it->second); } removed_connections_.clear(); @@ -221,7 +219,7 @@ class NodeArrayResizeCommand : public UndoCommand int size_; int old_size_; - Node::InputConnections removed_connections_; + Node::Connections removed_connections_; }; @@ -310,8 +308,6 @@ class NodeEdgeAddCommand : public UndoCommand { public: NodeEdgeAddCommand(Node *output, const NodeInput& input); - virtual ~NodeEdgeAddCommand() override; - virtual Project* GetRelevantProject() const override; protected: @@ -322,8 +318,6 @@ class NodeEdgeAddCommand : public UndoCommand { Node *output_; NodeInput input_; - NodeEdgeRemoveCommand* remove_command_; - }; class NodeAddCommand : public UndoCommand { @@ -618,7 +612,7 @@ class NodeViewDeleteCommand : public UndoCommand private: QVector nodes_; - QVector edges_; + QVector edges_; struct RemovedNode { Node *node; diff --git a/app/node/project.cpp b/app/node/project.cpp index 9e24aacd02..a1214d3e0d 100644 --- a/app/node/project.cpp +++ b/app/node/project.cpp @@ -234,8 +234,8 @@ void Project::childEvent(QChildEvent *event) // Emit input connections for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) { - if (nodes().contains(it->second)) { - emit InputConnected(it->second, it->first); + if (nodes().contains(it->first)) { + emit InputConnected(it->first, it->second); } } diff --git a/app/node/project/folder/folder.h b/app/node/project/folder/folder.h index 7ed5040272..b4edd73782 100644 --- a/app/node/project/folder/folder.h +++ b/app/node/project/folder/folder.h @@ -174,7 +174,7 @@ class Folder : public Node template static void ListOutputsOfTypeInternal(const Folder* n, QVector& list, bool recursive) { - foreach (const Node::OutputConnection& c, n->output_connections()) { + foreach (const Node::Connection& c, n->output_connections()) { Node* connected = c.second.node(); T* cast_test = dynamic_cast(connected); diff --git a/app/node/project/serializer/serializer.h b/app/node/project/serializer/serializer.h index 7c5bf9e5b6..7e04f745ef 100644 --- a/app/node/project/serializer/serializer.h +++ b/app/node/project/serializer/serializer.h @@ -82,7 +82,7 @@ class ProjectSerializer QVector nodes; - Node::OutputConnections promised_connections; + Node::Connections promised_connections; }; diff --git a/app/node/project/serializer/serializer230220.cpp b/app/node/project/serializer/serializer230220.cpp index 06e140ad68..eb133817a0 100644 --- a/app/node/project/serializer/serializer230220.cpp +++ b/app/node/project/serializer/serializer230220.cpp @@ -283,7 +283,7 @@ ProjectSerializer230220::LoadData ProjectSerializer230220::Load(Project *project if (Node *si = skipped_items.value(sc.output_node)) { // Convert this to a promised connection - Node::OutputConnection oc = {si, sc.input}; + Node::Connection oc = {si, sc.input}; load_data.promised_connections.push_back(oc); it = project_data.desired_connections.erase(it); } else { diff --git a/app/render/projectcopier.cpp b/app/render/projectcopier.cpp index 9bcbb24918..ba8593a8a9 100644 --- a/app/render/projectcopier.cpp +++ b/app/render/projectcopier.cpp @@ -65,7 +65,7 @@ void ProjectCopier::SetProject(Project *project) // Add all connections foreach (Node* node, original_->nodes()) { for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) { - DoEdgeAdd(it->second, it->first); + DoEdgeAdd(it->first, it->second); } } diff --git a/app/timeline/timelineundosplit.cpp b/app/timeline/timelineundosplit.cpp index a4448e4424..4357e44de9 100644 --- a/app/timeline/timelineundosplit.cpp +++ b/app/timeline/timelineundosplit.cpp @@ -67,7 +67,7 @@ void BlockSplitCommand::redo() TransitionBlock* potential_transition = dynamic_cast(new_block()->next()); if (potential_transition) { - for (const Node::OutputConnection& output : block_->output_connections()) { + for (const Node::Connection& output : block_->output_connections()) { if (output.second.node() == potential_transition) { moved_transition_ = NodeInput(potential_transition, TransitionBlock::kOutBlockInput); Node::DisconnectEdge(block_, moved_transition_); diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index 8008aaad6b..b76fcb9160 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -740,7 +740,7 @@ int GetDistanceBetweenNodes(Node *start, Node *end) } for (auto it=start->input_connections().cbegin(); it!=start->input_connections().cend(); it++) { - int this_node_dist = GetDistanceBetweenNodes(it->second, end); + int this_node_dist = GetDistanceBetweenNodes(it->first, end); if (this_node_dist != -1) { return 1 + this_node_dist; } diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index d765b3d248..29a3a07952 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -1641,7 +1641,11 @@ void NodeView::EndEdgeDrag(bool cancel) creating_input = input_group->GetInputFromID(creating_input.input()); } - if (creating_input.IsConnected()) { + if (Node::ConnectionExists(creating_output, creating_input)) { + cancel = true; + } + + /*if (creating_input.IsConnected()) { Node::OutputConnection existing_edge_to_remove = {creating_input.GetConnectedOutput(), creating_input}; Node *already_connected_output = creating_input.GetConnectedOutput(); @@ -1657,7 +1661,7 @@ void NodeView::EndEdgeDrag(bool cancel) if (!cancel) { command->add_child(new NodeEdgeRemoveCommand(existing_edge_to_remove.first, existing_edge_to_remove.second)); } - } + }*/ if (!cancel) { command->add_child(new NodeEdgeAddCommand(creating_output, creating_input)); diff --git a/app/widget/nodeview/nodeviewcontext.cpp b/app/widget/nodeview/nodeviewcontext.cpp index ebabb6c035..392e94e9c7 100644 --- a/app/widget/nodeview/nodeviewcontext.cpp +++ b/app/widget/nodeview/nodeviewcontext.cpp @@ -319,9 +319,9 @@ void NodeViewContext::AddNodeInternal(Node *node, NodeViewItem *item) } for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) { - if (!it->first.IsHidden()) { - if (NodeViewItem *other_item = item_map_.value(it->second)) { - AddEdgeInternal(it->second, it->first, other_item, item->GetItemForInput(it->first)); + if (!it->second.IsHidden()) { + if (NodeViewItem *other_item = item_map_.value(it->first)) { + AddEdgeInternal(it->first, it->second, other_item, item->GetItemForInput(it->second)); } } } diff --git a/app/widget/projectexplorer/projectexplorer.cpp b/app/widget/projectexplorer/projectexplorer.cpp index 04c0d71c72..1efa7f945e 100644 --- a/app/widget/projectexplorer/projectexplorer.cpp +++ b/app/widget/projectexplorer/projectexplorer.cpp @@ -160,7 +160,7 @@ int ProjectExplorer::ConfirmItemDeletion(Node* item) msgbox.setIcon(QMessageBox::Warning); QStringList connected_nodes_names; - foreach (const Node::OutputConnection& connected, item->output_connections()) { + foreach (const Node::Connection& connected, item->output_connections()) { if (!dynamic_cast(connected.second.node())) { connected_nodes_names.append(GetHumanReadableNodeName(connected.second.node())); } @@ -190,7 +190,7 @@ bool ProjectExplorer::DeleteItemsInternal(const QVector& selected, bool& bool can_delete_item = true; if (check_if_item_is_in_use) { - foreach (const Node::OutputConnection& oc, node->output_connections()) { + foreach (const Node::Connection& oc, node->output_connections()) { Folder* folder_test = dynamic_cast(oc.second.node()); if (!folder_test) { // This sequence outputs to SOMETHING, confirm the user if they want to delete this From ed6edf25455111879df21f16d683875b0f259459 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 23 May 2023 12:09:28 -0700 Subject: [PATCH 38/71] node: allow custom widgets --- app/node/generator/polygon/polygon.cpp | 13 +++- app/node/generator/polygon/polygon.h | 2 + app/node/node.cpp | 9 +-- app/node/node.h | 4 +- .../nodeparamviewwidgetbridge.cpp | 64 ++++++++++--------- 5 files changed, 55 insertions(+), 37 deletions(-) diff --git a/app/node/generator/polygon/polygon.cpp b/app/node/generator/polygon/polygon.cpp index f1a91f6696..eb7d818f21 100644 --- a/app/node/generator/polygon/polygon.cpp +++ b/app/node/generator/polygon/polygon.cpp @@ -23,6 +23,8 @@ #include #include +#include "widget/nodeparamview/paramwidget/bezierparamwidget.h" + namespace olive { const QString PolygonGenerator::kPointsInput = QStringLiteral("points_in"); @@ -32,7 +34,7 @@ const QString PolygonGenerator::kColorInput = QStringLiteral("color_in"); PolygonGenerator::PolygonGenerator() { - AddInput(kPointsInput, TYPE_BEZIER, kInputFlagArray); + AddInput(kPointsInput, TYPE_DOUBLE, size_t(6), value_t(), kInputFlagArray); AddInput(kColorInput, TYPE_COLOR, Color(1.0, 1.0, 1.0)); @@ -230,6 +232,15 @@ void PolygonGenerator::UpdateGizmoPositions(const ValueParams &p) poly_gizmo_->SetPath(GeneratePath(points, points.size()).translated(QPointF(half_res.x, half_res.y))); } +AbstractParamWidget *PolygonGenerator::GetCustomWidget(const QString &input) const +{ + if (input == kPointsInput) { + return new BezierParamWidget(); + } else { + return super::GetCustomWidget(input); + } +} + void PolygonGenerator::GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers) { DraggableGizmo *gizmo = static_cast(sender()); diff --git a/app/node/generator/polygon/polygon.h b/app/node/generator/polygon/polygon.h index b5c3ee9435..7463a825fd 100644 --- a/app/node/generator/polygon/polygon.h +++ b/app/node/generator/polygon/polygon.h @@ -51,6 +51,8 @@ class PolygonGenerator : public GeneratorWithMerge virtual void UpdateGizmoPositions(const ValueParams &p) override; + virtual AbstractParamWidget *GetCustomWidget(const QString &input) const override; + static QPainterPath GeneratePath(const NodeValueArray &points, int size); static const QString kPointsInput; diff --git a/app/node/node.cpp b/app/node/node.cpp index 9edb512482..3580a99b4b 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -952,6 +952,11 @@ void Node::SetValueHintForInput(const QString &input, const ValueHint &hint, int InvalidateAll(input, element); } +AbstractParamWidget *Node::GetCustomWidget(const QString &input) const +{ + return nullptr; +} + const NodeKeyframeTrack &Node::GetTrackFromKeyframe(NodeKeyframe *key) const { return GetImmediate(key->input(), key->element())->keyframe_tracks().at(key->track()); @@ -2342,10 +2347,6 @@ type_t Node::ResolveSpecialType(type_t type, size_t &channel_count, QString &sub } else if (type == TYPE_FILE) { subtype = QStringLiteral("file"); return TYPE_STRING; - } else if (type == TYPE_BEZIER) { - channel_count = 6; - subtype = QStringLiteral("bezier"); - return TYPE_DOUBLE; } return type; diff --git a/app/node/node.h b/app/node/node.h index 9774031f26..0ee3a0c327 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -43,6 +43,7 @@ #include "render/job/generatejob.h" #include "render/job/samplejob.h" #include "render/job/shaderjob.h" +#include "widget/nodeparamview/paramwidget/abstractparamwidget.h" namespace olive { @@ -113,7 +114,6 @@ class Node : public QObject static constexpr type_t TYPE_COMBO = "combo"; static constexpr type_t TYPE_FONT = "font"; static constexpr type_t TYPE_FILE = "file"; - static constexpr type_t TYPE_BEZIER = "bezier"; struct ContextPair { Node *node; @@ -595,6 +595,8 @@ class Node : public QObject void SetValueHintForInput(const QString &input, const ValueHint &hint, int element = -1); + virtual AbstractParamWidget *GetCustomWidget(const QString &input) const; + const NodeKeyframeTrack& GetTrackFromKeyframe(NodeKeyframe* key) const; using Connection = std::pair; diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index d35e5fd451..fddce4e9c6 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -38,7 +38,6 @@ #include "widget/colorbutton/colorbutton.h" #include "widget/filefield/filefield.h" #include "widget/nodeparamview/paramwidget/arrayparamwidget.h" -#include "widget/nodeparamview/paramwidget/bezierparamwidget.h" #include "widget/nodeparamview/paramwidget/boolparamwidget.h" #include "widget/nodeparamview/paramwidget/colorparamwidget.h" #include "widget/nodeparamview/paramwidget/comboparamwidget.h" @@ -77,36 +76,39 @@ void NodeParamViewWidgetBridge::CreateWidgets() } else { - // We assume the first data type is the "primary" type - type_t t = GetDataType(); - QString type = GetOuterInput().GetProperty(QStringLiteral("subtype")).toString(); - - if (t == TYPE_INTEGER) { - if (type == QStringLiteral("bool")) { - widget_ = new BoolParamWidget(this); - } else if (type == QStringLiteral("combo")) { - widget_ = new ComboParamWidget(this); - } else { - widget_ = new IntegerSliderParamWidget(this); - } - } else if (t == TYPE_RATIONAL) { - widget_ = new RationalSliderParamWidget(this); - } else if (t == TYPE_DOUBLE) { - if (type == QStringLiteral("color")) { - widget_ = new ColorParamWidget(GetInnerInput(), this); - } else if (type == QStringLiteral("bezier")) { - widget_ = new BezierParamWidget(this); - } else { - widget_ = new FloatSliderParamWidget(this); - } - } else if (t == TYPE_STRING) { - if (type == QStringLiteral("file")) { - widget_ = new FileParamWidget(this); - } else if (type == QStringLiteral("font")) { - widget_ = new FontParamWidget(this); - } else { - widget_ = new TextParamWidget(this); - connect(static_cast(widget_), &TextParamWidget::RequestEditInViewer, this, &NodeParamViewWidgetBridge::RequestEditTextInViewer); + if (AbstractParamWidget *a = GetInnerInput().node()->GetCustomWidget(GetInnerInput().input())) { + a->setParent(this); + widget_ = a; + } else { + // We assume the first data type is the "primary" type + type_t t = GetDataType(); + QString type = GetOuterInput().GetProperty(QStringLiteral("subtype")).toString(); + + if (t == TYPE_INTEGER) { + if (type == QStringLiteral("bool")) { + widget_ = new BoolParamWidget(this); + } else if (type == QStringLiteral("combo")) { + widget_ = new ComboParamWidget(this); + } else { + widget_ = new IntegerSliderParamWidget(this); + } + } else if (t == TYPE_RATIONAL) { + widget_ = new RationalSliderParamWidget(this); + } else if (t == TYPE_DOUBLE) { + if (type == QStringLiteral("color")) { + widget_ = new ColorParamWidget(GetInnerInput(), this); + } else { + widget_ = new FloatSliderParamWidget(this); + } + } else if (t == TYPE_STRING) { + if (type == QStringLiteral("file")) { + widget_ = new FileParamWidget(this); + } else if (type == QStringLiteral("font")) { + widget_ = new FontParamWidget(this); + } else { + widget_ = new TextParamWidget(this); + connect(static_cast(widget_), &TextParamWidget::RequestEditInViewer, this, &NodeParamViewWidgetBridge::RequestEditTextInViewer); + } } } From 397f308933bd8b5f78d58fe3d431d86f475b7df3 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 23 May 2023 12:09:51 -0700 Subject: [PATCH 39/71] viewer: allow hiding toolbar --- app/widget/viewer/viewer.cpp | 14 ++++++++++++++ app/widget/viewer/viewer.h | 2 ++ 2 files changed, 16 insertions(+) diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 11df290184..7b2691fdf1 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -919,6 +919,13 @@ void ViewerWidget::UpdateTextureFromNode() } } +void ViewerWidget::SetToolBarVisible(bool e) +{ + controls_->setVisible(e); + ruler()->setVisible(e); + scrollbar()->setVisible(e); +} + void ViewerWidget::PlayInternal(int speed, bool in_to_out_only) { Q_ASSERT(speed != 0); @@ -1508,6 +1515,13 @@ void ViewerWidget::ShowContextMenu(const QPoint &pos) connect(show_fps_action, &QAction::triggered, display_widget_, &ViewerDisplayWidget::SetShowFPS); } + { + QAction *show_toolbar = menu.addAction(tr("Show Toolbar")); + show_toolbar->setCheckable(true); + show_toolbar->setChecked(ruler()->isVisible()); + connect(show_toolbar, &QAction::triggered, this, &ViewerWidget::SetToolBarVisible); + } + if (context_menu_widget_ == display_widget_) { auto subtitle_menu = new Menu(tr("Subtitles"), &menu); menu.addMenu(subtitle_menu); diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index b9a6e9c817..c623b8a2b9 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -162,6 +162,8 @@ public slots: display_widget_->RequestStartEditingText(); } + void SetToolBarVisible(bool e); + signals: /** * @brief Wrapper for ViewerGLWidget::CursorColor() From 17260cd2640380d983bae235822f0911140ebbdb Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 23 May 2023 13:14:43 -0700 Subject: [PATCH 40/71] enable swizzling texture channels --- app/node/value.cpp | 69 ++++++++++++++++++++++++++++++++++++++++ app/node/value.h | 7 ++-- app/shaders/swizzle.frag | 38 ++++++++++++++++++++++ 3 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 app/shaders/swizzle.frag diff --git a/app/node/value.cpp b/app/node/value.cpp index 56e275e6c0..019ed7a919 100644 --- a/app/node/value.cpp +++ b/app/node/value.cpp @@ -28,6 +28,7 @@ #include "common/tohex.h" #include "render/job/samplejob.h" +#include "render/job/shaderjob.h" #include "render/subtitleparams.h" #include "render/videoparams.h" @@ -37,11 +38,79 @@ std::map > value_t::converters_; QChar CHANNEL_SPLITTER = ':'; +struct TextureChannel +{ + TexturePtr texture; + size_t channel; +}; + +value_t::value_t(TexturePtr texture) : + value_t(TYPE_TEXTURE) +{ + if (texture) { + data_.resize(texture->channel_count()); + for (int i = 0; i < texture->channel_count(); i++) { + data_[i] = TextureChannel({texture, size_t(i)}); + } + } +} + value_t::value_t(const SampleJob &samples) : value_t(TYPE_SAMPLES, samples) { } +ShaderCode GetSwizzleShaderCode(const QString &id) +{ + return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/swizzle.frag"))); +} + +TexturePtr value_t::toTexture() const +{ + if (type_ != TYPE_TEXTURE || data_.empty()) { + return TexturePtr(); + } + + bool swizzled = false; + + TextureChannel last; + VideoParams vp; + + for (auto it = data_.cbegin(); it != data_.cend(); it++) { + const TextureChannel &c = it->value(); + + if (it != data_.cbegin() && !swizzled) { + if (c.texture != last.texture || c.channel != last.channel + 1) { + swizzled = true; + } + } + + if (!vp.is_valid() && c.texture && c.texture->params().is_valid()) { + vp = c.texture->params(); + } + + last = c; + } + + if (swizzled) { + // Return texture(s) wrapped in a swizzle shader + ShaderJob job; + job.set_function(GetSwizzleShaderCode); + job.Insert(QStringLiteral("red_texture"), this->value(0).texture); + job.Insert(QStringLiteral("green_texture"), this->value(1).texture); + job.Insert(QStringLiteral("blue_texture"), this->value(2).texture); + job.Insert(QStringLiteral("alpha_texture"), this->value(3).texture); + job.Insert(QStringLiteral("red_channel"), this->value(0).channel); + job.Insert(QStringLiteral("green_channel"), this->value(1).channel); + job.Insert(QStringLiteral("blue_channel"), this->value(2).channel); + job.Insert(QStringLiteral("alpha_channel"), this->value(3).channel); + return Texture::Job(vp, job); + } else { + // No swizzling has occurred + return last.texture; + } +} + value_t value_t::fromSerializedString(type_t target_type, const QString &str) { QStringList l = str.split(CHANNEL_SPLITTER); diff --git a/app/node/value.h b/app/node/value.h index 4a476b1411..fd2f0a7016 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -166,10 +166,7 @@ class value_t type_(TYPE_NONE) {} - value_t(TexturePtr texture) : - value_t(TYPE_TEXTURE, texture) - { - } + value_t(TexturePtr texture); value_t(const SampleJob &samples); @@ -314,7 +311,7 @@ class value_t QMatrix4x4 toMatrix() const { return value(); } QByteArray toBinary() const { return value(); } NodeValueArray toArray() const { return value(); } - TexturePtr toTexture() const { return value(); } + TexturePtr toTexture() const; SampleBuffer toSamples() const { return value(); } static value_t fromSerializedString(type_t target_type, const QString &s); diff --git a/app/shaders/swizzle.frag b/app/shaders/swizzle.frag new file mode 100644 index 0000000000..a357cd2876 --- /dev/null +++ b/app/shaders/swizzle.frag @@ -0,0 +1,38 @@ +// Input +uniform sampler2D red_texture; +uniform bool red_texture_enabled; +uniform sampler2D green_texture; +uniform bool green_texture_enabled; +uniform sampler2D blue_texture; +uniform bool blue_texture_enabled; +uniform sampler2D alpha_texture; +uniform bool alpha_texture_enabled; +uniform int red_channel; +uniform int green_channel; +uniform int blue_channel; +uniform int alpha_channel; + +in vec2 ove_texcoord; +out vec4 frag_color; + +void main() { + vec4 color = vec4(0.0, 0.0, 0.0, 1.0); + + if (red_texture_enabled) { + color.r = texture(red_texture, ove_texcoord)[red_channel]; + } + + if (green_texture_enabled) { + color.g = texture(green_texture, ove_texcoord)[green_channel]; + } + + if (blue_texture_enabled) { + color.b = texture(blue_texture, ove_texcoord)[blue_channel]; + } + + if (alpha_texture_enabled) { + color.a = texture(alpha_texture, ove_texcoord)[alpha_channel]; + } + + frag_color = color; +} From adbb228bd924e7a84c5c3e57bb27eb83151f86c6 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 23 May 2023 13:15:31 -0700 Subject: [PATCH 41/71] valueswizzlewidget: enable gradients for slightly nicer appearance --- app/widget/nodevaluetree/valueswizzlewidget.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/widget/nodevaluetree/valueswizzlewidget.cpp b/app/widget/nodevaluetree/valueswizzlewidget.cpp index a4df9725f3..181dfc2bb7 100644 --- a/app/widget/nodevaluetree/valueswizzlewidget.cpp +++ b/app/widget/nodevaluetree/valueswizzlewidget.cpp @@ -24,6 +24,8 @@ #include #include +#include "config/config.h" + namespace olive { #define super QGraphicsView @@ -233,8 +235,19 @@ void ValueSwizzleWidget::draw_channel(QPainter *p, size_t i, int x) QRect r(x, i * channel_height_, channel_width_, channel_height_); + const QColor &main_col = i < kChannelColorCount ? kChannelColors[i] : kDefaultColor; + if (OLIVE_CONFIG("UseGradients").toBool()) { + QLinearGradient lg(r.topLeft(), r.bottomLeft()); + + lg.setColorAt(0.0, main_col.lighter()); + lg.setColorAt(1.0, main_col); + + p->setBrush(lg); + } else { + p->setBrush(main_col); + } + p->setPen(Qt::black); - p->setBrush(i < kChannelColorCount ? kChannelColors[i] : kDefaultColor); p->drawRect(r); p->setPen(Qt::white); From 311d4074be558f18c0a7337608b9a8c36a135d43 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 23 May 2023 13:15:54 -0700 Subject: [PATCH 42/71] nodes: reimplement value caching --- app/common/qtutils.cpp | 5 ++++ app/common/qtutils.h | 1 + app/node/globals.cpp | 38 +++++++++++++++++++++++++++++ app/node/globals.h | 25 +++++++++++++++---- app/node/node.cpp | 9 ++++++- app/render/renderprocessor.cpp | 6 +++-- app/render/videoparams.cpp | 6 +++++ app/render/videoparams.h | 3 +++ app/widget/viewer/viewerdisplay.cpp | 20 ++++++++++----- 9 files changed, 99 insertions(+), 14 deletions(-) diff --git a/app/common/qtutils.cpp b/app/common/qtutils.cpp index 95ca7b7125..12db8ef86c 100644 --- a/app/common/qtutils.cpp +++ b/app/common/qtutils.cpp @@ -207,6 +207,11 @@ uint qHash(const core::TimeRange &r, uint seed) return qHash(r.in(), seed) ^ qHash(r.out(), seed); } +uint qHash(const AudioParams &r, uint seed) +{ + return qHash(r.sample_rate(), seed) ^ qHash(r.channel_layout(), seed) ^ qHash(QString::fromStdString(r.format().to_string()), seed); +} + } diff --git a/app/common/qtutils.h b/app/common/qtutils.h index c4eeef0272..7208360455 100644 --- a/app/common/qtutils.h +++ b/app/common/qtutils.h @@ -99,6 +99,7 @@ namespace core { uint qHash(const core::rational& r, uint seed = 0); uint qHash(const core::TimeRange& r, uint seed = 0); +uint qHash(const core::AudioParams& r, uint seed = 0); } diff --git a/app/node/globals.cpp b/app/node/globals.cpp index f572b92392..25daeb8fee 100644 --- a/app/node/globals.cpp +++ b/app/node/globals.cpp @@ -20,6 +20,8 @@ #include "globals.h" +#include "common/qtutils.h" + namespace olive { ValueParams ValueParams::time_transformed(const TimeRange &time) const @@ -43,4 +45,40 @@ ValueParams ValueParams::loop_mode_edited(const LoopMode &lm) const return g; } +bool ValueParams::get_cached_value(const Node *node, const ValueParams &p, value_t &out) const +{ + if (cache_) { + if (cache_->contains(node)) { + const QHash &map = cache_->value(node); + if (map.contains(p)) { + out = map.value(p); + return true; + } + } + } + + return false; +} + +void ValueParams::insert_cached_value(const Node *node, const ValueParams &p, const value_t &in) const +{ + if (cache_) { + (*cache_)[node][p] = in; + } +} + +bool ValueParams::operator==(const ValueParams &p) const +{ + return this->video_params_ == p.video_params_ + && this->audio_params_ == p.audio_params_ + && this->time_ == p.time_ + && this->loop_mode_ == p.loop_mode_ + && this->output_ == p.output_; +} + +uint qHash(const ValueParams &p, uint seed) +{ + return qHash(p.vparams(), seed) ^ qHash(p.aparams(), seed) ^ qHash(p.time(), seed) ^ qHash(int(p.loop_mode()), seed) ^ qHash(p.output(), seed); +} + } diff --git a/app/node/globals.h b/app/node/globals.h index eb89dd7394..b80b4bd180 100644 --- a/app/node/globals.h +++ b/app/node/globals.h @@ -23,6 +23,7 @@ #include +#include "node/value.h" #include "render/cancelatom.h" #include "render/loopmode.h" #include "render/videoparams.h" @@ -32,22 +33,27 @@ namespace olive { class ValueParams { public: + using Cache = QHash>; + ValueParams() { + cache_ = nullptr; + cancel_atom_ = nullptr; } - ValueParams(const VideoParams &vparam, const AudioParams &aparam, const TimeRange &time, const QString &output, LoopMode loop_mode, CancelAtom *cancel) : + ValueParams(const VideoParams &vparam, const AudioParams &aparam, const TimeRange &time, const QString &output, LoopMode loop_mode, CancelAtom *cancel, Cache *cache) : video_params_(vparam), audio_params_(aparam), time_(time), loop_mode_(loop_mode), + output_(output), cancel_atom_(cancel), - output_(output) + cache_(cache) { } - ValueParams(const VideoParams &vparam, const AudioParams &aparam, const rational &time, const QString &output, LoopMode loop_mode, CancelAtom *cancel) : - ValueParams(vparam, aparam, TimeRange(time, time + vparam.frame_rate_as_time_base()), output, loop_mode, cancel) + ValueParams(const VideoParams &vparam, const AudioParams &aparam, const rational &time, const QString &output, LoopMode loop_mode, CancelAtom *cancel, Cache *cache) : + ValueParams(vparam, aparam, TimeRange(time, time + vparam.frame_rate_as_time_base()), output, loop_mode, cancel, cache) { } @@ -66,16 +72,25 @@ class ValueParams ValueParams output_edited(const QString &output) const; ValueParams loop_mode_edited(const LoopMode &lm) const; + bool get_cached_value(const Node *node, const ValueParams &p, value_t &out) const; + void insert_cached_value(const Node *node, const ValueParams &p, const value_t &in) const; + + bool operator==(const ValueParams &p) const; + private: VideoParams video_params_; AudioParams audio_params_; TimeRange time_; LoopMode loop_mode_; - CancelAtom *cancel_atom_; QString output_; + CancelAtom *cancel_atom_; + Cache *cache_; + }; +uint qHash(const ValueParams &p, uint seed = 0); + } #endif // NODEGLOBALS_H diff --git a/app/node/node.cpp b/app/node/node.cpp index 3580a99b4b..ec5fda0c3d 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -900,7 +900,14 @@ value_t Node::GetFakeConnectedValue(const ValueParams &g, Node *output, const QS while (output) { if (output->is_enabled()) { Node::ValueHint vh = this->GetValueHintForInput(input, element); - value_t v = output->Value(g.time_transformed(adjusted_time).output_edited(vh.tag())); + ValueParams connp = g.time_transformed(adjusted_time).output_edited(vh.tag()); + + // Find cached value with this + value_t v; + if (!g.get_cached_value(output, connp, v)) { + v = output->Value(connp); + g.insert_cached_value(output, connp, v); + } // Perform swizzle if requested const SwizzleMap &swizzle = vh.swizzle(); diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 3ef485a64c..d2aa83307b 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -48,7 +48,8 @@ TexturePtr RenderProcessor::GenerateTexture(const rational &time, const rational value_t tex_val; if (Node* node = QtUtils::ValueToPtr(ticket_->property("node"))) { - ValueParams vp(GetCacheVideoParams(), GetCacheAudioParams(), range, ticket_->property("output").toString(), LoopMode::kLoopModeOff, GetCancelPointer()); + ValueParams::Cache cache; + ValueParams vp(GetCacheVideoParams(), GetCacheAudioParams(), range, ticket_->property("output").toString(), LoopMode::kLoopModeOff, GetCancelPointer(), &cache); tex_val = node->Value(vp); } @@ -219,7 +220,8 @@ void RenderProcessor::Run() value_t sample_val; if (Node* node = QtUtils::ValueToPtr(ticket_->property("node"))) { - ValueParams vp(GetCacheVideoParams(), GetCacheAudioParams(), time, ticket_->property("output").toString(), LoopMode::kLoopModeOff, GetCancelPointer()); + ValueParams::Cache cache; + ValueParams vp(GetCacheVideoParams(), GetCacheAudioParams(), time, ticket_->property("output").toString(), LoopMode::kLoopModeOff, GetCancelPointer(), &cache); sample_val = node->Value(vp); } diff --git a/app/render/videoparams.cpp b/app/render/videoparams.cpp index 742e01227d..f14a6341fc 100644 --- a/app/render/videoparams.cpp +++ b/app/render/videoparams.cpp @@ -27,6 +27,7 @@ extern "C" { #include #include +#include "common/qtutils.h" #include "core.h" namespace olive { @@ -407,4 +408,9 @@ void VideoParams::Save(QXmlStreamWriter *writer) const writer->writeTextElement(QStringLiteral("colorrange"), QString::number(color_range_)); } +uint qHash(const VideoParams &p, uint seed) +{ + return qHash(p.width(), seed) ^ qHash(p.height(), seed) ^ qHash(p.depth(), seed) ^ qHash(p.time_base(), seed) ^ qHash(QString::fromUtf8(p.format().to_string()), seed) ^ qHash(p.channel_count(), seed) ^ qHash(p.pixel_aspect_ratio(), seed) ^ qHash(p.interlacing(), seed) ^ qHash(p.divider(), seed); +} + } diff --git a/app/render/videoparams.h b/app/render/videoparams.h index 84ae67b600..995348a90f 100644 --- a/app/render/videoparams.h +++ b/app/render/videoparams.h @@ -391,6 +391,7 @@ class VideoParams { int effective_depth_; int par_width_; + // Footage values bool enabled_; int stream_index_; Type video_type_; @@ -405,6 +406,8 @@ class VideoParams { }; +uint qHash(const VideoParams &p, uint seed = 0); + } Q_DECLARE_METATYPE(olive::VideoParams) diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index c9a02dd9b2..8e2d8f728b 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -451,7 +451,8 @@ void ViewerDisplayWidget::OnPaint() p.setWorldTransform(gizmo_last_draw_transform_); - gizmos_->UpdateGizmoPositions(ValueParams(gizmo_params_, gizmo_audio_params_, gizmo_draw_time_, QString(), LoopMode::kLoopModeOff, nullptr)); + ValueParams::Cache cache; + gizmos_->UpdateGizmoPositions(ValueParams(gizmo_params_, gizmo_audio_params_, gizmo_draw_time_, QString(), LoopMode::kLoopModeOff, nullptr, &cache)); foreach (NodeGizmo *gizmo, gizmos_->GetGizmos()) { if (gizmo->IsVisible()) { gizmo->Draw(&p); @@ -681,7 +682,8 @@ QTransform ViewerDisplayWidget::GenerateGizmoTransform(const TimeRange &range) for (auto it = list.cbegin(); it != list.cend(); it++) { // Transform matrix down the list - ValueParams vp(gizmo_params_, gizmo_audio_params_, ranges.front(), QString(), LoopMode::kLoopModeOff, nullptr); + ValueParams::Cache cache; + ValueParams vp(gizmo_params_, gizmo_audio_params_, ranges.front(), QString(), LoopMode::kLoopModeOff, nullptr, &cache); Node *n = (*it).node(); if (n->is_enabled()) { QTransform this_transform = n->GizmoTransformation(vp); @@ -738,7 +740,9 @@ NodeGizmo *ViewerDisplayWidget::TryGizmoPress(const QPointF &p) void ViewerDisplayWidget::OpenTextGizmo(TextGizmo *text, QMouseEvent *event) { GenerateGizmoTransforms(); - gizmos_->UpdateGizmoPositions(ValueParams(gizmo_params_, gizmo_audio_params_, gizmo_draw_time_, QString(), LoopMode::kLoopModeOff, nullptr)); + + ValueParams::Cache cache; + gizmos_->UpdateGizmoPositions(ValueParams(gizmo_params_, gizmo_audio_params_, gizmo_draw_time_, QString(), LoopMode::kLoopModeOff, nullptr, &cache)); active_text_gizmo_ = text; connect(active_text_gizmo_, &TextGizmo::RectChanged, this, &ViewerDisplayWidget::UpdateActiveTextGizmoSize); @@ -861,7 +865,9 @@ bool ViewerDisplayWidget::OnMousePress(QMouseEvent *event) // Handle gizmo click gizmo_start_drag_ = event->pos(); gizmo_last_drag_ = gizmo_start_drag_; - current_gizmo_->SetGlobals(ValueParams(gizmo_params_, gizmo_audio_params_, GenerateGizmoTime(), QString(), LoopMode::kLoopModeOff, nullptr)); + + ValueParams::Cache cache; + current_gizmo_->SetGlobals(ValueParams(gizmo_params_, gizmo_audio_params_, GenerateGizmoTime(), QString(), LoopMode::kLoopModeOff, nullptr, &cache)); } else { @@ -907,8 +913,9 @@ bool ViewerDisplayWidget::OnMouseMove(QMouseEvent *event) if (!gizmo_drag_started_) { QPointF start = ScreenToScenePoint(gizmo_start_drag_); + ValueParams::Cache cache;; TimeRange gizmo_time = GetGizmoTimeRange(); - ValueParams p(gizmo_params_, gizmo_audio_params_, gizmo_time, QString(), LoopMode::kLoopModeOff, nullptr); + ValueParams p(gizmo_params_, gizmo_audio_params_, gizmo_time, QString(), LoopMode::kLoopModeOff, nullptr, &cache); draggable->DragStart(p, start.x(), start.y(), gizmo_time.in()); gizmo_drag_started_ = true; @@ -1244,7 +1251,8 @@ void ViewerDisplayWidget::GenerateGizmoTransforms() { gizmo_draw_time_ = GenerateGizmoTime(); - ValueParams p(gizmo_params_, gizmo_audio_params_, gizmo_draw_time_, QString(), LoopMode::kLoopModeOff, nullptr); + ValueParams::Cache cache; + ValueParams p(gizmo_params_, gizmo_audio_params_, gizmo_draw_time_, QString(), LoopMode::kLoopModeOff, nullptr, &cache); gizmo_last_draw_transform_ = GenerateGizmoTransform(gizmo_draw_time_); gizmo_last_draw_transform_inverted_ = gizmo_last_draw_transform_.inverted(); From cc4f8ac4e1e421fdf43c5499d3d077e8ce335401 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 24 May 2023 17:06:22 -0700 Subject: [PATCH 43/71] readded outputs --- app/node/block/clip/clip.cpp | 10 +- app/node/block/clip/clip.h | 4 +- app/node/block/transition/transition.cpp | 8 +- app/node/block/transition/transition.h | 4 +- app/node/input/multicam/multicamnode.cpp | 27 ++-- app/node/input/multicam/multicamnode.h | 6 +- app/node/node.cpp | 139 +++++++++++------- app/node/node.h | 41 +++--- app/node/nodeundo.cpp | 16 +- app/node/nodeundo.h | 12 +- app/node/output/track/track.cpp | 12 +- app/node/output/track/track.h | 2 +- app/node/output/viewer/viewer.cpp | 8 +- app/node/output/viewer/viewer.h | 4 +- app/node/param.cpp | 9 ++ app/node/param.h | 49 ++++++ app/node/project.cpp | 2 +- app/node/project.h | 4 +- app/node/project/folder/folder.cpp | 14 +- app/node/project/folder/folder.h | 4 +- app/node/project/sequence/sequence.cpp | 8 +- app/node/project/sequence/sequence.h | 4 +- .../project/serializer/serializer210528.cpp | 11 +- .../project/serializer/serializer210907.cpp | 5 +- .../project/serializer/serializer211228.cpp | 5 +- .../project/serializer/serializer220403.cpp | 5 +- .../project/serializer/serializer230220.cpp | 4 +- app/node/serializeddata.h | 1 + app/node/swizzlemap.h | 41 ++++++ app/render/projectcopier.cpp | 26 ++-- app/render/projectcopier.h | 10 +- app/task/precache/precachetask.cpp | 5 +- app/task/project/loadotio/loadotio.cpp | 12 +- app/timeline/timelineundogeneral.cpp | 38 ++--- app/timeline/timelineundosplit.cpp | 8 +- app/widget/nodeparamview/nodeparamview.cpp | 4 +- .../nodeparamviewconnectedlabel.cpp | 27 ++-- .../nodeparamviewconnectedlabel.h | 6 +- .../nodeparamview/nodeparamviewcontext.cpp | 4 +- .../nodeparamview/nodeparamviewitem.cpp | 2 +- app/widget/nodeparamview/nodeparamviewitem.h | 2 +- app/widget/nodevaluetree/nodevaluetree.cpp | 25 +++- app/widget/nodeview/nodeview.cpp | 8 +- app/widget/nodeview/nodeviewcontext.cpp | 14 +- app/widget/nodeview/nodeviewcontext.h | 6 +- app/widget/nodeview/nodeviewedge.cpp | 2 +- app/widget/nodeview/nodeviewedge.h | 8 +- .../projectexplorer/projectviewmodel.cpp | 2 +- app/widget/timelinewidget/timelinewidget.cpp | 8 +- app/widget/timelinewidget/tool/add.cpp | 2 +- app/widget/timelinewidget/tool/import.cpp | 15 +- app/widget/timelinewidget/tool/pointer.cpp | 4 +- app/widget/timelinewidget/tool/transition.cpp | 6 +- 53 files changed, 419 insertions(+), 284 deletions(-) diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index 0f4ffb00f1..308fd1c39c 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -406,11 +406,12 @@ void ClipBlock::LinkChangeEvent() } } -void ClipBlock::InputConnectedEvent(const QString &input, int element, Node *output) +void ClipBlock::InputConnectedEvent(const QString &input, int element, const NodeOutput &o) { - super::InputConnectedEvent(input, element, output); + super::InputConnectedEvent(input, element, o); if (input == kBufferIn) { + Node *output = o.node(); connect(output->thumbnail_cache(), &FrameHashCache::Invalidated, this, &Block::PreviewChanged); connect(output->waveform_cache(), &AudioPlaybackCache::Invalidated, this, &Block::PreviewChanged); connect(output->video_frame_cache(), &FrameHashCache::Invalidated, this, &Block::PreviewChanged); @@ -422,11 +423,12 @@ void ClipBlock::InputConnectedEvent(const QString &input, int element, Node *out } } -void ClipBlock::InputDisconnectedEvent(const QString &input, int element, Node *output) +void ClipBlock::InputDisconnectedEvent(const QString &input, int element, const NodeOutput &o) { - super::InputDisconnectedEvent(input, element, output); + super::InputDisconnectedEvent(input, element, o); if (input == kBufferIn) { + Node *output = o.node(); disconnect(output->thumbnail_cache(), &FrameHashCache::Invalidated, this, &Block::PreviewChanged); disconnect(output->waveform_cache(), &AudioPlaybackCache::Invalidated, this, &Block::PreviewChanged); disconnect(output->video_frame_cache(), &FrameHashCache::Invalidated, this, &Block::PreviewChanged); diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index 05a79c69be..945be96c17 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -212,9 +212,9 @@ class ClipBlock : public Block protected: virtual void LinkChangeEvent() override; - virtual void InputConnectedEvent(const QString& input, int element, Node *output) override; + virtual void InputConnectedEvent(const QString& input, int element, const NodeOutput &output) override; - virtual void InputDisconnectedEvent(const QString& input, int element, Node *output) override; + virtual void InputDisconnectedEvent(const QString& input, int element, const NodeOutput &output) override; virtual void InputValueChangedEvent(const QString& input, int element) override; diff --git a/app/node/block/transition/transition.cpp b/app/node/block/transition/transition.cpp index 7ed02ebf19..62eaba82b4 100644 --- a/app/node/block/transition/transition.cpp +++ b/app/node/block/transition/transition.cpp @@ -230,24 +230,24 @@ double TransitionBlock::TransformCurve(double linear) const return linear; } -void TransitionBlock::InputConnectedEvent(const QString &input, int element, Node *output) +void TransitionBlock::InputConnectedEvent(const QString &input, int element, const NodeOutput &output) { Q_UNUSED(element) if (input == kOutBlockInput) { // If node is not a block, this will just be null - if ((connected_out_block_ = dynamic_cast(output))) { + if ((connected_out_block_ = dynamic_cast(output.node()))) { connected_out_block_->set_out_transition(this); } } else if (input == kInBlockInput) { // If node is not a block, this will just be null - if ((connected_in_block_ = dynamic_cast(output))) { + if ((connected_in_block_ = dynamic_cast(output.node()))) { connected_in_block_->set_in_transition(this); } } } -void TransitionBlock::InputDisconnectedEvent(const QString &input, int element, Node *output) +void TransitionBlock::InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) { Q_UNUSED(element) Q_UNUSED(output) diff --git a/app/node/block/transition/transition.h b/app/node/block/transition/transition.h index bc1f1e3aec..3b457a2481 100644 --- a/app/node/block/transition/transition.h +++ b/app/node/block/transition/transition.h @@ -79,9 +79,9 @@ class TransitionBlock : public Block virtual void SampleJobEvent(const ValueParams &p, SampleJob *job) const {} - virtual void InputConnectedEvent(const QString& input, int element, Node *output) override; + virtual void InputConnectedEvent(const QString& input, int element, const NodeOutput &output) override; - virtual void InputDisconnectedEvent(const QString& input, int element, Node *output) override; + virtual void InputDisconnectedEvent(const QString& input, int element, const NodeOutput &output) override; virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time, bool clamp) const override; diff --git a/app/node/input/multicam/multicamnode.cpp b/app/node/input/multicam/multicamnode.cpp index abde41f020..3d911ada3a 100644 --- a/app/node/input/multicam/multicamnode.cpp +++ b/app/node/input/multicam/multicamnode.cpp @@ -134,8 +134,9 @@ value_t MultiCamNode::Value(const ValueParams &p) const int c, r; MultiCamNode::IndexToRowCols(i, rows, cols, &r, &c); - if (Node *n = GetSourceNode(i)) { - value_t v = GetFakeConnectedValue(p, n, kSourcesInput, i); + NodeOutput o = GetSourceNode(i); + if (o.IsValid()) { + value_t v = GetFakeConnectedValue(p, o, kSourcesInput, i); job.Insert(QStringLiteral("tex_%1_%2").arg(QString::number(r), QString::number(c)), v); } } @@ -145,8 +146,9 @@ value_t MultiCamNode::Value(const ValueParams &p) const // Default behavior: output currently selected source int current = GetInputValue(p, kCurrentInput).toInt(); - if (Node *n = GetSourceNode(current)) { - return GetFakeConnectedValue(p, n, kSourcesInput, current); + NodeOutput o = GetSourceNode(current); + if (o.IsValid()) { + return GetFakeConnectedValue(p, o, kSourcesInput, current); } } @@ -161,17 +163,17 @@ void MultiCamNode::IndexToRowCols(int index, int total_rows, int total_cols, int *row = index/total_cols; } -void MultiCamNode::InputConnectedEvent(const QString &input, int element, Node *output) +void MultiCamNode::InputConnectedEvent(const QString &input, int element, const NodeOutput &output) { if (input == kSequenceInput) { - if (Sequence *s = dynamic_cast(output)) { + if (Sequence *s = dynamic_cast(output.node())) { SetInputFlag(kSequenceTypeInput, kInputFlagHidden, false); sequence_ = s; } } } -void MultiCamNode::InputDisconnectedEvent(const QString &input, int element, Node *output) +void MultiCamNode::InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) { if (input == kSequenceInput) { SetInputFlag(kSequenceTypeInput, kInputFlagHidden, true); @@ -179,12 +181,12 @@ void MultiCamNode::InputDisconnectedEvent(const QString &input, int element, Nod } } -Node *MultiCamNode::GetSourceNode(int source) const +NodeOutput MultiCamNode::GetSourceNode(int source) const { if (sequence_) { - return GetTrackList()->GetTrackAt(source); + return NodeOutput(GetTrackList()->GetTrackAt(source)); } else { - return GetConnectedOutput(kSourcesInput, source); + return GetConnectedOutput2(kSourcesInput, source); } } @@ -208,8 +210,9 @@ void MultiCamNode::Retranslate() names.reserve(name_count); for (int i=0; iName(); + NodeOutput o = GetSourceNode(i); + if (o.IsValid()) { + src_name = o.node()->Name(); } names.append(tr("%1: %2").arg(QString::number(i+1), src_name)); } diff --git a/app/node/input/multicam/multicamnode.h b/app/node/input/multicam/multicamnode.h index 592dbe8bf7..bc67558626 100644 --- a/app/node/input/multicam/multicamnode.h +++ b/app/node/input/multicam/multicamnode.h @@ -56,13 +56,13 @@ class MultiCamNode : public Node } protected: - virtual void InputConnectedEvent(const QString &input, int element, Node *output) override; - virtual void InputDisconnectedEvent(const QString &input, int element, Node *output) override; + virtual void InputConnectedEvent(const QString &input, int element, const NodeOutput &output) override; + virtual void InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) override; private: static ShaderCode GetShaderCode(const QString &id); - Node *GetSourceNode(int source) const; + NodeOutput GetSourceNode(int source) const; TrackList *GetTrackList() const; diff --git a/app/node/node.cpp b/app/node/node.cpp index ec5fda0c3d..6ecec75a59 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -187,10 +187,10 @@ QBrush Node::brush(qreal top, qreal bottom) const } } -bool Node::ConnectionExists(Node *output, const NodeInput &input) +bool Node::ConnectionExists(const NodeOutput &output, const NodeInput &input) { - for (auto it = output->output_connections_.cbegin(); it != output->output_connections_.cend(); it++) { - if (it->second == input) { + for (auto it = output.node()->output_connections_.cbegin(); it != output.node()->output_connections_.cend(); it++) { + if (it->first == output && it->second == input) { return true; } } @@ -198,26 +198,26 @@ bool Node::ConnectionExists(Node *output, const NodeInput &input) return false; } -void Node::ConnectEdge(Node *output, const NodeInput &input) +void Node::ConnectEdge(const NodeOutput &output, const NodeInput &input) { // Ensure graph is the same - Q_ASSERT(input.node()->parent() == output->parent()); + Q_ASSERT(input.node()->parent() == output.node()->parent()); // Ensure connection doesn't already exist Q_ASSERT(!ConnectionExists(output, input)); // Insert connection on both sides - auto conn = std::pair({output, input}); + auto conn = std::pair({output, input}); input.node()->input_connections_.push_back(conn); - output->output_connections_.push_back(conn); + output.node()->output_connections_.push_back(conn); // Call internal events input.node()->InputConnectedEvent(input.input(), input.element(), output); - output->OutputConnectedEvent(input); + output.node()->OutputConnectedEvent(input); // Emit signals emit input.node()->InputConnected(output, input); - emit output->OutputConnected(output, input); + emit output.node()->OutputConnected(output, input); // Invalidate all if this node isn't ignoring this input if (!(input.node()->GetInputFlags(input.input()) & kInputFlagIgnoreInvalidations)) { @@ -225,29 +225,29 @@ void Node::ConnectEdge(Node *output, const NodeInput &input) } } -void Node::DisconnectEdge(Node *output, const NodeInput &input) +void Node::DisconnectEdge(const NodeOutput &output, const NodeInput &input) { // Ensure graph is the same - Q_ASSERT(input.node()->parent() == output->parent()); + Q_ASSERT(input.node()->parent() == output.node()->parent()); // Ensure connection exists Q_ASSERT(ConnectionExists(output, input)); // Remove connection from both sides - auto conn = std::pair({output, input}); + auto conn = std::pair({output, input}); Connections& inputs = input.node()->input_connections_; inputs.erase(std::find(inputs.begin(), inputs.end(), conn)); - Connections& outputs = output->output_connections_; + Connections& outputs = output.node()->output_connections_; outputs.erase(std::find(outputs.begin(), outputs.end(), conn)); // Call internal events input.node()->InputDisconnectedEvent(input.input(), input.element(), output); - output->OutputDisconnectedEvent(input); + output.node()->OutputDisconnectedEvent(input); emit input.node()->InputDisconnected(output, input); - emit output->OutputDisconnected(output, input); + emit output.node()->OutputDisconnected(output, input); if (!(input.node()->GetInputFlags(input.input()) & kInputFlagIgnoreInvalidations)) { input.node()->InvalidateAll(input.input(), input.element()); @@ -324,7 +324,7 @@ bool Node::IsInputConnected(const QString &input, int element) const return GetConnectedOutput(input, element); } -Node *Node::GetConnectedOutput(const QString &input, int element) const +NodeOutput Node::GetConnectedOutput2(const QString &input, int element) const { // NOTE: Only returns the first output connected to this input, there may be more than one for (auto it = input_connections_.cbegin(); it != input_connections_.cend(); it++) { @@ -333,7 +333,7 @@ Node *Node::GetConnectedOutput(const QString &input, int element) const } } - return nullptr; + return NodeOutput(); } bool Node::IsUsingStandardValue(const QString &input, int track, int element) const @@ -870,7 +870,8 @@ int Node::InputArraySize(const QString &id) const value_t Node::GetInputValue(const ValueParams &g, const QString &input, int element) const { if (!g.is_cancelled()) { - if (Node *output = GetConnectedOutput(input, element)) { + NodeOutput output = GetConnectedOutput2(input, element); + if (output.IsValid()) { return GetFakeConnectedValue(g, output, input, element); } else { TimeRange adjusted_time = InputTimeAdjustment(input, element, g.time(), true); @@ -892,21 +893,21 @@ value_t Node::GetInputValue(const ValueParams &g, const QString &input, int elem return value_t(); } -value_t Node::GetFakeConnectedValue(const ValueParams &g, Node *output, const QString &input, int element) const +value_t Node::GetFakeConnectedValue(const ValueParams &g, NodeOutput output, const QString &input, int element) const { if (!g.is_cancelled()) { TimeRange adjusted_time = InputTimeAdjustment(input, element, g.time(), true); - while (output) { - if (output->is_enabled()) { + while (output.IsValid()) { + if (output.node()->is_enabled()) { Node::ValueHint vh = this->GetValueHintForInput(input, element); - ValueParams connp = g.time_transformed(adjusted_time).output_edited(vh.tag()); + ValueParams connp = g.time_transformed(adjusted_time).output_edited(output.output()); // Find cached value with this value_t v; - if (!g.get_cached_value(output, connp, v)) { - v = output->Value(connp); - g.insert_cached_value(output, connp, v); + if (!g.get_cached_value(output.node(), connp, v)) { + v = output.node()->Value(connp); + g.insert_cached_value(output.node(), connp, v); } // Perform swizzle if requested @@ -942,7 +943,7 @@ value_t Node::GetFakeConnectedValue(const ValueParams &g, Node *output, const QS return v; } else { - output = output->GetConnectedOutput(output->GetEffectInput()); + output = output.node()->GetConnectedOutput2(output.node()->GetEffectInput()); } } } @@ -1085,11 +1086,11 @@ void Node::CopyDependencyGraph(const QVector &src, const QVector for (auto it=src_node->input_connections_.cbegin(); it!=src_node->input_connections_.cend(); it++) { // Determine if the connected node is in our src list - int connection_index = src.indexOf(it->first); + int connection_index = src.indexOf(it->first.node()); if (connection_index > -1) { // Find the equivalent node in the dst list - Node *copied_output = dst.at(connection_index); + NodeOutput copied_output = NodeOutput(dst.at(connection_index), it->first.output()); NodeInput copied_input = NodeInput(dst_node, it->second.input(), it->second.element()); if (command) { @@ -1153,23 +1154,27 @@ Node *Node::CopyNodeAndDependencyGraphMinusItemsInternal(QMap& cre // Go through input connections and copy if non-item and connect if item for (auto it=node->input_connections_.cbegin(); it!=node->input_connections_.cend(); it++) { NodeInput input = it->second; - Node* connected = it->first; + NodeOutput output = it->first; + //Node* connected = it->first.node(); Node* connected_copy; - if (connected->IsItem()) { + if (output.node()->IsItem()) { // This is an item and we avoid copying those and just connect to them directly - connected_copy = connected; + connected_copy = output.node(); } else { // Non-item, we want to clone this too - connected_copy = created.value(connected, nullptr); + connected_copy = created.value(output.node(), nullptr); if (!connected_copy) { - connected_copy = CopyNodeAndDependencyGraphMinusItemsInternal(created, connected, command); + connected_copy = CopyNodeAndDependencyGraphMinusItemsInternal(created, output.node(), command); } } NodeInput copied_input = input; copied_input.set_node(copy); - command->add_child(new NodeEdgeAddCommand(connected_copy, copied_input)); + + NodeOutput copied_output(connected_copy, output.output()); + + command->add_child(new NodeEdgeAddCommand(copied_output, copied_input)); command->add_child(new NodeSetValueHintCommand(copied_input, node->GetValueHintForInput(input.input(), input.element()))); } @@ -1279,8 +1284,6 @@ bool Node::Load(QXmlStreamReader *reader, SerializedData *data) } } - Q_UNUSED(version) - while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("input")) { LoadInput(reader, data); @@ -1315,17 +1318,29 @@ bool Node::Load(QXmlStreamReader *reader, SerializedData *data) } } - QString output_node_id; + QString output_node_id, output_param; while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("output")) { - output_node_id = reader->readElementText(); + if (version < 2) { + output_node_id = reader->readElementText(); + } else { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("node")) { + output_node_id = reader->readElementText(); + } else if (reader->name() == QStringLiteral("param")) { + output_param = reader->readElementText(); + } else { + reader->skipCurrentElement(); + } + } + } } else { reader->skipCurrentElement(); } } - data->desired_connections.append({NodeInput(this, param_id, ele), output_node_id.toULongLong()}); + data->desired_connections.append({NodeInput(this, param_id, ele), output_node_id.toULongLong(), output_param}); } else { reader->skipCurrentElement(); } @@ -1403,7 +1418,7 @@ bool Node::Load(QXmlStreamReader *reader, SerializedData *data) void Node::Save(QXmlStreamWriter *writer) const { - writer->writeAttribute(QStringLiteral("version"), QString::number(1)); + writer->writeAttribute(QStringLiteral("version"), QString::number(2)); writer->writeAttribute(QStringLiteral("id"), this->id()); writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast(this))); @@ -1439,7 +1454,10 @@ void Node::Save(QXmlStreamWriter *writer) const writer->writeAttribute(QStringLiteral("input"), it->second.input()); writer->writeAttribute(QStringLiteral("element"), QString::number(it->second.element())); - writer->writeTextElement(QStringLiteral("output"), QString::number(reinterpret_cast(it->first))); + writer->writeStartElement(QStringLiteral("output")); + writer->writeTextElement(QStringLiteral("node"), QString::number(reinterpret_cast(it->first.node()))); + writer->writeTextElement(QStringLiteral("param"), it->first.output()); + writer->writeEndElement(); // output writer->writeEndElement(); // connection } @@ -1896,14 +1914,14 @@ void Node::ArrayResizeInternal(const QString &id, size_t size) } } -QString Node::GetConnectCommandString(Node *output, const NodeInput &input) +QString Node::GetConnectCommandString(const NodeOutput &output, const NodeInput &input) { - return tr("Connected %1 to %2 - %3").arg(output->GetLabelAndName(), input.node()->GetLabelAndName(), input.GetInputName()); + return tr("Connected %1 to %2 - %3").arg(output.node()->GetLabelAndName(), input.node()->GetLabelAndName(), input.GetInputName()); } -QString Node::GetDisconnectCommandString(Node *output, const NodeInput &input) +QString Node::GetDisconnectCommandString(const NodeOutput &output, const NodeInput &input) { - return tr("Disconnected %1 from %2 - %3").arg(output->GetLabelAndName(), input.node()->GetLabelAndName(), input.GetInputName()); + return tr("Disconnected %1 from %2 - %3").arg(output.node()->GetLabelAndName(), input.node()->GetLabelAndName(), input.GetInputName()); } int Node::GetInternalInputArraySize(const QString &input) @@ -1914,10 +1932,10 @@ int Node::GetInternalInputArraySize(const QString &input) void FindWaysNodeArrivesHereRecursively(const Node *output, const Node *input, QVector &v) { for (auto it=input->input_connections().cbegin(); it!=input->input_connections().cend(); it++) { - if (it->first == output) { + if (it->first.node() == output) { v.append(it->second); } else { - FindWaysNodeArrivesHereRecursively(output, it->first, v); + FindWaysNodeArrivesHereRecursively(output, it->first.node(), v); } } } @@ -2113,7 +2131,7 @@ void Node::CopyValuesOfElement(const Node *src, Node *dst, const QString &input, void GetDependenciesRecursively(QVector& list, const Node* node, bool traverse, bool exclusive_only) { for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) { - Node* connected_node = it->first; + Node* connected_node = it->first.node(); if (!exclusive_only || !connected_node->IsItem()) { if (!list.contains(connected_node)) { @@ -2162,7 +2180,7 @@ QVector Node::GetImmediateDependencies() const bool Node::InputsFrom(Node *n, bool recursively) const { for (auto it=input_connections_.cbegin(); it!=input_connections_.cend(); it++) { - Node *connected = it->first; + Node *connected = it->first.node(); if (connected == n) { return true; @@ -2177,7 +2195,7 @@ bool Node::InputsFrom(Node *n, bool recursively) const bool Node::InputsFrom(const QString &id, bool recursively) const { for (auto it=input_connections_.cbegin(); it!=input_connections_.cend(); it++) { - Node *connected = it->first; + Node *connected = it->first.node(); if (connected->id() == id) { return true; @@ -2365,14 +2383,14 @@ void Node::InputValueChangedEvent(const QString &input, int element) Q_UNUSED(element) } -void Node::InputConnectedEvent(const QString &input, int element, Node *output) +void Node::InputConnectedEvent(const QString &input, int element, const NodeOutput &output) { Q_UNUSED(input) Q_UNUSED(element) Q_UNUSED(output) } -void Node::InputDisconnectedEvent(const QString &input, int element, Node *output) +void Node::InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) { Q_UNUSED(input) Q_UNUSED(element) @@ -2558,7 +2576,7 @@ bool FindPathInternal(std::list &vec, Node *from, Node *to, int &path for (auto it = to->input_connections().cbegin(); it != to->input_connections().cend(); it++) { vec.push_front(it->second); - if (it->first == from) { + if (it->first.node() == from) { // Found a path! Determine if it's the index we want if (path_index == 0) { // It is! @@ -2569,7 +2587,7 @@ bool FindPathInternal(std::list &vec, Node *from, Node *to, int &path } } - if (FindPathInternal(vec, from, it->first, path_index)) { + if (FindPathInternal(vec, from, it->first.node(), path_index)) { return true; } @@ -2599,7 +2617,10 @@ bool Node::ValueHint::load(QXmlStreamReader *reader) while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("tag")) { - this->set_tag(reader->readElementText()); + QString output = reader->readElementText(); + qDebug() << "FIXME: need to propagate output" << output; + } else if (reader->name() == QStringLiteral("swizzle")) { + this->swizzle_.load(reader); } else { reader->skipCurrentElement(); } @@ -2612,7 +2633,13 @@ void Node::ValueHint::save(QXmlStreamWriter *writer) const { writer->writeAttribute(QStringLiteral("version"), QString::number(2)); - writer->writeTextElement(QStringLiteral("tag"), this->tag()); + if (!this->swizzle_.empty()) { + writer->writeStartElement(QStringLiteral("swizzle")); + + this->swizzle_.save(writer); + + writer->writeEndElement(); // swizzle + } } bool Node::Position::load(QXmlStreamReader *reader) diff --git a/app/node/node.h b/app/node/node.h index 0ee3a0c327..a2a07194ee 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -337,11 +337,11 @@ class Node : public QObject } } - static bool ConnectionExists(Node *output, const NodeInput& input); + static bool ConnectionExists(const NodeOutput &output, const NodeInput& input); - static void ConnectEdge(Node *output, const NodeInput& input); + static void ConnectEdge(const NodeOutput &output, const NodeInput& input); - static void DisconnectEdge(Node *output, const NodeInput& input); + static void DisconnectEdge(const NodeOutput &output, const NodeInput& input); void CopyCacheUuidsFrom(Node *n); @@ -385,7 +385,16 @@ class Node : public QObject return IsInputStatic(input.input(), input.element()); } - Node *GetConnectedOutput(const QString& input, int element = -1) const; + NodeOutput GetConnectedOutput2(const QString& input, int element = -1) const; + NodeOutput GetConnectedOutput2(const NodeInput& input) const + { + return GetConnectedOutput2(input.input(), input.element()); + } + + Node *GetConnectedOutput(const QString& input, int element = -1) const + { + return GetConnectedOutput2(input, element).node(); + } Node *GetConnectedOutput(const NodeInput& input) const { @@ -549,7 +558,7 @@ class Node : public QObject int InputArraySize(const QString& id) const; value_t GetInputValue(const ValueParams &g, const QString &input, int element = -1) const; - value_t GetFakeConnectedValue(const ValueParams &g, Node *node, const QString &input, int element = -1) const; + value_t GetFakeConnectedValue(const ValueParams &g, NodeOutput output, const QString &input, int element = -1) const; NodeInputImmediate* GetImmediate(const QString& input, int element) const; @@ -568,9 +577,6 @@ class Node : public QObject public: ValueHint() = default; - const QString& tag() const { return tag_; } - void set_tag(const QString &tag) { tag_ = tag; } - const SwizzleMap &swizzle() const { return swizzle_; } void set_swizzle(const SwizzleMap &m) { swizzle_ = m; } @@ -578,7 +584,6 @@ class Node : public QObject void save(QXmlStreamWriter *writer) const; private: - QString tag_; SwizzleMap swizzle_; }; @@ -599,7 +604,7 @@ class Node : public QObject const NodeKeyframeTrack& GetTrackFromKeyframe(NodeKeyframe* key) const; - using Connection = std::pair; + using Connection = std::pair; using Connections = std::vector; /** @@ -835,8 +840,8 @@ class Node : public QObject virtual void AddedToGraphEvent(Project *p){} virtual void RemovedFromGraphEvent(Project *p){} - static QString GetConnectCommandString(Node *output, const NodeInput &input); - static QString GetDisconnectCommandString(Node *output, const NodeInput &input); + static QString GetConnectCommandString(const NodeOutput &output, const NodeInput &input); + static QString GetDisconnectCommandString(const NodeOutput &output, const NodeInput &input); static const QString kEnabledInput; @@ -916,9 +921,9 @@ class Node : public QObject virtual void InputValueChangedEvent(const QString& input, int element); - virtual void InputConnectedEvent(const QString& input, int element, Node *output); + virtual void InputConnectedEvent(const QString& input, int element, const NodeOutput &output); - virtual void InputDisconnectedEvent(const QString& input, int element, Node *output); + virtual void InputDisconnectedEvent(const QString& input, int element, const NodeOutput &output); virtual void OutputConnectedEvent(const NodeInput& input); @@ -997,13 +1002,13 @@ protected slots: void ValueChanged(const NodeInput& input, const TimeRange& range); - void InputConnected(Node *output, const NodeInput& input); + void InputConnected(const NodeOutput &output, const NodeInput& input); - void InputDisconnected(Node *output, const NodeInput& input); + void InputDisconnected(const NodeOutput &output, const NodeInput& input); - void OutputConnected(Node *output, const NodeInput& input); + void OutputConnected(const NodeOutput &output, const NodeInput& input); - void OutputDisconnected(Node *output, const NodeInput& input); + void OutputDisconnected(const NodeOutput &output, const NodeInput& input); void InputValueHintChanged(const NodeInput& input); diff --git a/app/node/nodeundo.cpp b/app/node/nodeundo.cpp index 7b7f88a4bd..ce0d60cd2e 100644 --- a/app/node/nodeundo.cpp +++ b/app/node/nodeundo.cpp @@ -106,14 +106,14 @@ void NodeSetPositionAndDependenciesRecursivelyCommand::move_recursively(Node *no commands_.append(new NodeSetPositionCommand(node_, context_, pos)); for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) { - Node *output = it->first; + Node *output = it->first.node(); if (context_->ContextContainsNode(output)) { move_recursively(output, diff); } } } -NodeEdgeAddCommand::NodeEdgeAddCommand(Node *output, const NodeInput &input) : +NodeEdgeAddCommand::NodeEdgeAddCommand(const NodeOutput &output, const NodeInput &input) : output_(output), input_(input) { @@ -131,10 +131,10 @@ void NodeEdgeAddCommand::undo() Project *NodeEdgeAddCommand::GetRelevantProject() const { - return output_->project(); + return output_.node()->project(); } -NodeEdgeRemoveCommand::NodeEdgeRemoveCommand(Node *output, const NodeInput &input) : +NodeEdgeRemoveCommand::NodeEdgeRemoveCommand(const NodeOutput &output, const NodeInput &input) : output_(output), input_(input) { @@ -152,7 +152,7 @@ void NodeEdgeRemoveCommand::undo() Project *NodeEdgeRemoveCommand::GetRelevantProject() const { - return output_->project(); + return output_.node()->project(); } NodeAddCommand::NodeAddCommand(Project *graph, Node *node) : @@ -266,7 +266,7 @@ void NodeViewDeleteCommand::AddNode(Node *node, Node *context) nodes_.append(p); for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) { - if (context->ContextContainsNode(it->first)) { + if (context->ContextContainsNode(it->first.node())) { AddEdge(it->first, it->second); } } @@ -278,7 +278,7 @@ void NodeViewDeleteCommand::AddNode(Node *node, Node *context) } } -void NodeViewDeleteCommand::AddEdge(Node *output, const NodeInput &input) +void NodeViewDeleteCommand::AddEdge(const NodeOutput &output, const NodeInput &input) { foreach (const Node::Connection &edge, edges_) { if (edge.first == output && edge.second == input) { @@ -307,7 +307,7 @@ Project *NodeViewDeleteCommand::GetRelevantProject() const } if (!edges_.isEmpty()) { - return edges_.first().first->project(); + return edges_.first().first.node()->project(); } return nullptr; diff --git a/app/node/nodeundo.h b/app/node/nodeundo.h index 460a329e11..f09f42a404 100644 --- a/app/node/nodeundo.h +++ b/app/node/nodeundo.h @@ -190,7 +190,7 @@ class NodeArrayResizeCommand : public UndoCommand if (old_size_ > size_) { // Decreasing in size, disconnect any extraneous edges for (auto it = node_->input_connections().cbegin(); it != node_->input_connections().cend(); it++) { - Node *output = it->first; + const NodeOutput &output = it->first; const NodeInput &input = it->second; if (input.input() == input_ && input.element() >= size_) { @@ -285,7 +285,7 @@ class NodeArrayRemoveCommand : public UndoCommand */ class NodeEdgeRemoveCommand : public UndoCommand { public: - NodeEdgeRemoveCommand(Node *output, const NodeInput& input); + NodeEdgeRemoveCommand(const NodeOutput &output, const NodeInput& input); virtual Project* GetRelevantProject() const override; @@ -294,7 +294,7 @@ class NodeEdgeRemoveCommand : public UndoCommand { virtual void undo() override; private: - Node *output_; + NodeOutput output_; NodeInput input_; }; @@ -306,7 +306,7 @@ class NodeEdgeRemoveCommand : public UndoCommand { */ class NodeEdgeAddCommand : public UndoCommand { public: - NodeEdgeAddCommand(Node *output, const NodeInput& input); + NodeEdgeAddCommand(const NodeOutput &output, const NodeInput& input); virtual Project* GetRelevantProject() const override; @@ -315,7 +315,7 @@ class NodeEdgeAddCommand : public UndoCommand { virtual void undo() override; private: - Node *output_; + NodeOutput output_; NodeInput input_; }; @@ -598,7 +598,7 @@ class NodeViewDeleteCommand : public UndoCommand void AddNode(Node *node, Node *context); - void AddEdge(Node *output, const NodeInput &input); + void AddEdge(const NodeOutput &output, const NodeInput &input); bool ContainsNode(Node *node, Node *context); diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index da2b4ed2e7..67031c7437 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -541,7 +541,7 @@ void Track::RippleRemoveBlock(Block *block) blocks_.removeAt(index); block_array_indexes_.removeAt(index); - Node::DisconnectEdge(block, NodeInput(this, kBlockInput, array_index)); + Node::DisconnectEdge(NodeOutput(block), NodeInput(this, kBlockInput, array_index)); empty_inputs_.push_back(array_index); disconnect(block, &Block::LengthChanged, this, &Track::BlockLengthChanged); @@ -577,8 +577,8 @@ void Track::ReplaceBlock(Block *old, Block *replace) int cache_index = blocks_.indexOf(old); int index_of_old_block = GetArrayIndexFromCacheIndex(cache_index); - DisconnectEdge(old, NodeInput(this, kBlockInput, index_of_old_block)); - ConnectEdge(replace, NodeInput(this, kBlockInput, index_of_old_block)); + DisconnectEdge(NodeOutput(old), NodeInput(this, kBlockInput, index_of_old_block)); + ConnectEdge(NodeOutput(replace), NodeInput(this, kBlockInput, index_of_old_block)); blocks_.replace(cache_index, replace); disconnect(old, &Block::LengthChanged, this, &Track::BlockLengthChanged); connect(replace, &Block::LengthChanged, this, &Track::BlockLengthChanged); @@ -641,7 +641,7 @@ void Track::SetLocked(bool e) locked_ = e; } -void Track::InputConnectedEvent(const QString &input, int element, Node *node) +void Track::InputConnectedEvent(const QString &input, int element, const NodeOutput &node) { if (arraymap_invalid_ && input == kBlockInput && element >= 0) { RefreshBlockCacheFromArrayMap(); @@ -716,13 +716,13 @@ int Track::ConnectBlock(Block *b) int index = empty_inputs_.front(); empty_inputs_.pop_front(); - Node::ConnectEdge(b, NodeInput(this, kBlockInput, index)); + Node::ConnectEdge(NodeOutput(b), NodeInput(this, kBlockInput, index)); return index; } else { int old_sz = InputArraySize(kBlockInput); InputArrayAppend(kBlockInput); - Node::ConnectEdge(b, NodeInput(this, kBlockInput, old_sz)); + Node::ConnectEdge(NodeOutput(b), NodeInput(this, kBlockInput, old_sz)); return old_sz; } } diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index 2e325c5269..ff8b920b71 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -447,7 +447,7 @@ public slots: void BlocksRefreshed(); protected: - virtual void InputConnectedEvent(const QString& input, int element, Node *node) override; + virtual void InputConnectedEvent(const QString& input, int element, const NodeOutput &node) override; virtual void InputValueChangedEvent(const QString& input, int element) override; private: diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index 695f4a262e..dfc6cfdcfa 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -344,23 +344,23 @@ void ViewerOutput::SetPlayhead(const rational &t) emit PlayheadChanged(t); } -void ViewerOutput::InputConnectedEvent(const QString &input, int element, Node *output) +void ViewerOutput::InputConnectedEvent(const QString &input, int element, const NodeOutput &output) { if (input == kTextureInput) { emit TextureInputChanged(); } else if (input == kSamplesInput) { - connect(output->waveform_cache(), &AudioWaveformCache::Validated, this, &ViewerOutput::ConnectedWaveformChanged); + connect(output.node()->waveform_cache(), &AudioWaveformCache::Validated, this, &ViewerOutput::ConnectedWaveformChanged); } super::InputConnectedEvent(input, element, output); } -void ViewerOutput::InputDisconnectedEvent(const QString &input, int element, Node *output) +void ViewerOutput::InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) { if (input == kTextureInput) { emit TextureInputChanged(); } else if (input == kSamplesInput) { - disconnect(output->waveform_cache(), &AudioWaveformCache::Validated, this, &ViewerOutput::ConnectedWaveformChanged); + disconnect(output.node()->waveform_cache(), &AudioWaveformCache::Validated, this, &ViewerOutput::ConnectedWaveformChanged); } super::InputDisconnectedEvent(input, element, output); diff --git a/app/node/output/viewer/viewer.h b/app/node/output/viewer/viewer.h index 160c93d091..4bb61e69da 100644 --- a/app/node/output/viewer/viewer.h +++ b/app/node/output/viewer/viewer.h @@ -235,9 +235,9 @@ public slots: void SetPlayhead(const rational &t); protected: - virtual void InputConnectedEvent(const QString &input, int element, Node *output) override; + virtual void InputConnectedEvent(const QString &input, int element, const NodeOutput &output) override; - virtual void InputDisconnectedEvent(const QString &input, int element, Node *output) override; + virtual void InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) override; virtual rational VerifyLengthInternal(Track::Type type) const; diff --git a/app/node/param.cpp b/app/node/param.cpp index 1d5b5a8b89..e6d8f4c133 100644 --- a/app/node/param.cpp +++ b/app/node/param.cpp @@ -96,6 +96,15 @@ Node *NodeInput::GetConnectedOutput() const } } +NodeOutput NodeInput::GetConnectedOutput2() const +{ + if (IsValid()) { + return node_->GetConnectedOutput2(*this); + } else { + return NodeOutput(); + } +} + type_t NodeInput::GetDataType() const { if (IsValid()) { diff --git a/app/node/param.h b/app/node/param.h index 882d469dc5..ffb01003f2 100644 --- a/app/node/param.h +++ b/app/node/param.h @@ -96,6 +96,54 @@ struct NodeInputPair QString input; }; +class NodeOutput +{ +public: + NodeOutput() + { + node_ = nullptr; + } + + explicit NodeOutput(Node *node, const QString &output = QString()) + { + node_ = node; + output_ = output; + } + + bool operator==(const NodeOutput &rhs) const + { + return node_ == rhs.node_ && output_ == rhs.output_; + } + + bool operator!=(const NodeOutput& rhs) const + { + return !(*this == rhs); + } + + bool operator<(const NodeOutput& rhs) const + { + if (node_ != rhs.node_) { + return node_ < rhs.node_; + } + + return output_ < rhs.output_; + } + + Node *node() const { return node_; } + const QString &output() const { return output_; } + void set_node(Node *n) { node_ = n; } + void set_output(const QString &o) { output_ = o; } + + void Reset() { *this = NodeOutput(); } + + bool IsValid() const { return node_; } + +private: + Node *node_; + QString output_; + +}; + /** * @brief Defines a Node input */ @@ -193,6 +241,7 @@ class NodeInput QString GetInputName() const; Node *GetConnectedOutput() const; + NodeOutput GetConnectedOutput2() const; type_t GetDataType() const; diff --git a/app/node/project.cpp b/app/node/project.cpp index a1214d3e0d..409ad450f4 100644 --- a/app/node/project.cpp +++ b/app/node/project.cpp @@ -234,7 +234,7 @@ void Project::childEvent(QChildEvent *event) // Emit input connections for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) { - if (nodes().contains(it->first)) { + if (nodes().contains(it->first.node())) { emit InputConnected(it->first, it->second); } } diff --git a/app/node/project.h b/app/node/project.h index c2ef72f1fb..a18696a17a 100644 --- a/app/node/project.h +++ b/app/node/project.h @@ -176,9 +176,9 @@ class Project : public QObject */ void NodeRemoved(Node* node); - void InputConnected(Node *output, const NodeInput& input); + void InputConnected(const NodeOutput &output, const NodeInput& input); - void InputDisconnected(Node *output, const NodeInput& input); + void InputDisconnected(const NodeOutput &output, const NodeInput& input); void ValueChanged(const NodeInput& input); diff --git a/app/node/project/folder/folder.cpp b/app/node/project/folder/folder.cpp index b056f8a91b..a3168c51aa 100644 --- a/app/node/project/folder/folder.cpp +++ b/app/node/project/folder/folder.cpp @@ -103,10 +103,10 @@ int Folder::index_of_child_in_array(Node *item) const return item_element_index_.at(index_of_item); } -void Folder::InputConnectedEvent(const QString &input, int element, Node *output) +void Folder::InputConnectedEvent(const QString &input, int element, const NodeOutput &output) { if (input == kChildInput && element != -1) { - Node* item = output; + Node* item = output.node(); // The insert index is always our "count" because we only support appending in our internal // model. For sorting/organizing, a QSortFilterProxyModel is used instead. @@ -118,10 +118,10 @@ void Folder::InputConnectedEvent(const QString &input, int element, Node *output } } -void Folder::InputDisconnectedEvent(const QString &input, int element, Node *output) +void Folder::InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) { if (input == kChildInput && element != -1) { - Node* item = output; + Node* item = output.node(); int child_index = item_children_.indexOf(item); emit BeginRemoveItem(item, child_index); @@ -147,12 +147,12 @@ void FolderAddChild::redo() { int array_index = folder_->InputArraySize(Folder::kChildInput); folder_->InputArrayAppend(Folder::kChildInput); - Node::ConnectEdge(child_, NodeInput(folder_, Folder::kChildInput, array_index)); + Node::ConnectEdge(NodeOutput(child_), NodeInput(folder_, Folder::kChildInput, array_index)); } void FolderAddChild::undo() { - Node::DisconnectEdge(child_, NodeInput(folder_, Folder::kChildInput, folder_->InputArraySize(Folder::kChildInput)-1)); + Node::DisconnectEdge(NodeOutput(child_), NodeInput(folder_, Folder::kChildInput, folder_->InputArraySize(Folder::kChildInput)-1)); folder_->InputArrayRemoveLast(Folder::kChildInput); } @@ -163,7 +163,7 @@ void Folder::RemoveElementCommand::redo() if (remove_index_ != -1) { NodeInput connected_input(folder_, Folder::kChildInput, remove_index_); subcommand_ = new MultiUndoCommand(); - subcommand_->add_child(new NodeEdgeRemoveCommand(folder_->GetConnectedOutput(connected_input), connected_input)); + subcommand_->add_child(new NodeEdgeRemoveCommand(NodeOutput(folder_->GetConnectedOutput(connected_input)), connected_input)); subcommand_->add_child(new NodeArrayRemoveCommand(folder_, Folder::kChildInput, remove_index_)); } } diff --git a/app/node/project/folder/folder.h b/app/node/project/folder/folder.h index b4edd73782..681026cd49 100644 --- a/app/node/project/folder/folder.h +++ b/app/node/project/folder/folder.h @@ -166,9 +166,9 @@ class Folder : public Node void EndRemoveItem(); protected: - virtual void InputConnectedEvent(const QString& input, int element, Node *output) override; + virtual void InputConnectedEvent(const QString& input, int element, const NodeOutput &output) override; - virtual void InputDisconnectedEvent(const QString& input, int element, Node *output) override; + virtual void InputDisconnectedEvent(const QString& input, int element, const NodeOutput &output) override; private: template diff --git a/app/node/project/sequence/sequence.cpp b/app/node/project/sequence/sequence.cpp index 3bdbd3963d..ef96ef21cc 100644 --- a/app/node/project/sequence/sequence.cpp +++ b/app/node/project/sequence/sequence.cpp @@ -150,12 +150,12 @@ rational Sequence::VerifyLengthInternal(Track::Type type) const return 0; } -void Sequence::InputConnectedEvent(const QString &input, int element, Node *output) +void Sequence::InputConnectedEvent(const QString &input, int element, const NodeOutput &output) { foreach (TrackList* list, track_lists_) { if (list->track_input() == input) { // Return because we found our input - list->TrackConnected(output, element); + list->TrackConnected(output.node(), element); return; } } @@ -163,12 +163,12 @@ void Sequence::InputConnectedEvent(const QString &input, int element, Node *outp super::InputConnectedEvent(input, element, output); } -void Sequence::InputDisconnectedEvent(const QString &input, int element, Node *output) +void Sequence::InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) { foreach (TrackList* list, track_lists_) { if (list->track_input() == input) { // Return because we found our input - list->TrackDisconnected(output, element); + list->TrackDisconnected(output.node(), element); return; } } diff --git a/app/node/project/sequence/sequence.h b/app/node/project/sequence/sequence.h index 42b10702a4..2faed58aeb 100644 --- a/app/node/project/sequence/sequence.h +++ b/app/node/project/sequence/sequence.h @@ -88,9 +88,9 @@ class Sequence : public ViewerOutput static const QString kTrackInputFormat; protected: - virtual void InputConnectedEvent(const QString &input, int element, Node *output) override; + virtual void InputConnectedEvent(const QString &input, int element, const NodeOutput &output) override; - virtual void InputDisconnectedEvent(const QString &input, int element, Node *output) override; + virtual void InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) override; virtual rational VerifyLengthInternal(Track::Type type) const override; diff --git a/app/node/project/serializer/serializer210528.cpp b/app/node/project/serializer/serializer210528.cpp index 768b08b97a..bd5efa7664 100644 --- a/app/node/project/serializer/serializer210528.cpp +++ b/app/node/project/serializer/serializer210528.cpp @@ -597,13 +597,7 @@ void ProjectSerializer210528::PostConnect(const XMLNodeData &xml_node_data) cons foreach (const XMLNodeData::SerializedConnection& con, xml_node_data.desired_connections) { if (Node *out = xml_node_data.node_ptrs.value(con.output_node)) { // Use output param as hint tag since we grandfathered those in - Node::ValueHint hint; - - hint.set_tag(con.output_param); - - Node::ConnectEdge(out, con.input); - - con.input.node()->SetValueHintForInput(con.input.input(), hint, con.input.element()); + Node::ConnectEdge(NodeOutput(out, con.output_param), con.input); } } @@ -760,7 +754,8 @@ void ProjectSerializer210528::LoadValueHint(Node::ValueHint *hint, QXmlStreamRea { while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("tag")) { - hint->set_tag(reader->readElementText()); + QString output = reader->readElementText(); + qDebug() << "FIXME: need to propagate output" << output; } else { reader->skipCurrentElement(); } diff --git a/app/node/project/serializer/serializer210907.cpp b/app/node/project/serializer/serializer210907.cpp index 178cd81c34..758efa6803 100644 --- a/app/node/project/serializer/serializer210907.cpp +++ b/app/node/project/serializer/serializer210907.cpp @@ -593,7 +593,7 @@ void ProjectSerializer210907::PostConnect(const XMLNodeData &xml_node_data) cons { foreach (const XMLNodeData::SerializedConnection& con, xml_node_data.desired_connections) { if (Node *out = xml_node_data.node_ptrs.value(con.output_node)) { - Node::ConnectEdge(out, con.input); + Node::ConnectEdge(NodeOutput(out), con.input); } } @@ -750,7 +750,8 @@ void ProjectSerializer210907::LoadValueHint(Node::ValueHint *hint, QXmlStreamRea { while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("tag")) { - hint->set_tag(reader->readElementText()); + QString output = reader->readElementText(); + qDebug() << "FIXME: need to propagate output" << output; } else { reader->skipCurrentElement(); } diff --git a/app/node/project/serializer/serializer211228.cpp b/app/node/project/serializer/serializer211228.cpp index 032470f083..a278dbc886 100644 --- a/app/node/project/serializer/serializer211228.cpp +++ b/app/node/project/serializer/serializer211228.cpp @@ -643,7 +643,7 @@ void ProjectSerializer211228::PostConnect(const XMLNodeData &xml_node_data) cons { foreach (const XMLNodeData::SerializedConnection& con, xml_node_data.desired_connections) { if (Node *out = xml_node_data.node_ptrs.value(con.output_node)) { - Node::ConnectEdge(out, con.input); + Node::ConnectEdge(NodeOutput(out), con.input); } } @@ -800,7 +800,8 @@ void ProjectSerializer211228::LoadValueHint(Node::ValueHint *hint, QXmlStreamRea { while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("tag")) { - hint->set_tag(reader->readElementText()); + QString output = reader->readElementText(); + qDebug() << "FIXME: need to propagate output" << output; } else { reader->skipCurrentElement(); } diff --git a/app/node/project/serializer/serializer220403.cpp b/app/node/project/serializer/serializer220403.cpp index 1841744fbf..4afc6b20a1 100644 --- a/app/node/project/serializer/serializer220403.cpp +++ b/app/node/project/serializer/serializer220403.cpp @@ -815,7 +815,7 @@ void ProjectSerializer220403::PostConnect(const XMLNodeData &xml_node_data) cons { foreach (const XMLNodeData::SerializedConnection& con, xml_node_data.desired_connections) { if (Node *out = xml_node_data.node_ptrs.value(con.output_node)) { - Node::ConnectEdge(out, con.input); + Node::ConnectEdge(NodeOutput(out), con.input); } } @@ -1029,7 +1029,8 @@ void ProjectSerializer220403::LoadValueHint(Node::ValueHint *hint, QXmlStreamRea { while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("tag")) { - hint->set_tag(reader->readElementText()); + QString output = reader->readElementText(); + qDebug() << "FIXME: need to propagate output" << output; } else { reader->skipCurrentElement(); } diff --git a/app/node/project/serializer/serializer230220.cpp b/app/node/project/serializer/serializer230220.cpp index eb133817a0..86374e9723 100644 --- a/app/node/project/serializer/serializer230220.cpp +++ b/app/node/project/serializer/serializer230220.cpp @@ -283,7 +283,7 @@ ProjectSerializer230220::LoadData ProjectSerializer230220::Load(Project *project if (Node *si = skipped_items.value(sc.output_node)) { // Convert this to a promised connection - Node::Connection oc = {si, sc.input}; + Node::Connection oc = {NodeOutput(si, sc.output_param), sc.input}; load_data.promised_connections.push_back(oc); it = project_data.desired_connections.erase(it); } else { @@ -459,7 +459,7 @@ void ProjectSerializer230220::PostConnect(const QVector &nodes, Serializ { foreach (const SerializedData::SerializedConnection& con, project_data->desired_connections) { if (Node *out = project_data->node_ptrs.value(con.output_node)) { - Node::ConnectEdge(out, con.input); + Node::ConnectEdge(NodeOutput(out, con.output_param), con.input); } } diff --git a/app/node/serializeddata.h b/app/node/serializeddata.h index fcdc02b7a1..30f1a63ab5 100644 --- a/app/node/serializeddata.h +++ b/app/node/serializeddata.h @@ -34,6 +34,7 @@ struct SerializedData { struct SerializedConnection { NodeInput input; quintptr output_node; + QString output_param; }; struct BlockLink { diff --git a/app/node/swizzlemap.h b/app/node/swizzlemap.h index 9f22ab479f..0a57c0f2a5 100644 --- a/app/node/swizzlemap.h +++ b/app/node/swizzlemap.h @@ -23,6 +23,10 @@ #include #include +#include +#include + +#include "common/xmlutils.h" namespace olive { @@ -45,6 +49,43 @@ class SwizzleMap map_.erase(to); } + void load(QXmlStreamReader *reader) + { + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("entry")) { + bool got_from = false, got_to = false; + size_t from, to; + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("from")) { + from = reader->readElementText().toULongLong(); + got_from = true; + } else if (reader->name() == QStringLiteral("to")) { + to = reader->readElementText().toULongLong(); + got_to = true; + } else { + reader->skipCurrentElement(); + } + } + + if (got_from && got_to) { + insert(to, from); + } + } else { + reader->skipCurrentElement(); + } + } + } + + void save(QXmlStreamWriter *writer) const + { + for (auto it = map_.cbegin(); it != map_.cend(); it++) { + writer->writeStartElement(QStringLiteral("entry")); + writer->writeTextElement(QStringLiteral("to"), QString::number(it->first)); + writer->writeTextElement(QStringLiteral("from"), QString::number(it->second)); + writer->writeEndElement(); // entry + } + } + bool operator==(const SwizzleMap &m) const { return map_ == m.map_; } bool operator!=(const SwizzleMap &m) const { return map_ != m.map_; } diff --git a/app/render/projectcopier.cpp b/app/render/projectcopier.cpp index ba8593a8a9..ff41566e50 100644 --- a/app/render/projectcopier.cpp +++ b/app/render/projectcopier.cpp @@ -165,22 +165,22 @@ void ProjectCopier::DoNodeRemove(Node *node) delete copy; } -void ProjectCopier::DoEdgeAdd(Node *output, const NodeInput &input) +void ProjectCopier::DoEdgeAdd(const NodeOutput &output, const NodeInput &input) { // Create same connection with our copied graph - Node* our_output = copy_map_.value(output); + Node* our_output = copy_map_.value(output.node()); Node* our_input = copy_map_.value(input.node()); - Node::ConnectEdge(our_output, NodeInput(our_input, input.input(), input.element())); + Node::ConnectEdge(NodeOutput(our_output, output.output()), NodeInput(our_input, input.input(), input.element())); } -void ProjectCopier::DoEdgeRemove(Node *output, const NodeInput &input) +void ProjectCopier::DoEdgeRemove(const NodeOutput &output, const NodeInput &input) { // Remove same connection with our copied graph - Node* our_output = copy_map_.value(output); + Node* our_output = copy_map_.value(output.node()); Node* our_input = copy_map_.value(input.node()); - Node::DisconnectEdge(our_output, NodeInput(our_input, input.input(), input.element())); + Node::DisconnectEdge(NodeOutput(our_output, output.output()), NodeInput(our_input, input.input(), input.element())); } void ProjectCopier::DoValueChange(const NodeInput &input) @@ -227,23 +227,23 @@ void ProjectCopier::InsertIntoCopyMap(Node *node, Node *copy) void ProjectCopier::QueueNodeAdd(Node *node) { - graph_update_queue_.push_back({QueuedJob::kNodeAdded, node, NodeInput(), nullptr, QString(), QString()}); + graph_update_queue_.push_back({QueuedJob::kNodeAdded, node, NodeInput(), NodeOutput(), QString(), QString()}); UpdateGraphChangeValue(); } void ProjectCopier::QueueNodeRemove(Node *node) { - graph_update_queue_.push_back({QueuedJob::kNodeRemoved, node, NodeInput(), nullptr, QString(), QString()}); + graph_update_queue_.push_back({QueuedJob::kNodeRemoved, node, NodeInput(), NodeOutput(), QString(), QString()}); UpdateGraphChangeValue(); } -void ProjectCopier::QueueEdgeAdd(Node *output, const NodeInput &input) +void ProjectCopier::QueueEdgeAdd(const NodeOutput &output, const NodeInput &input) { graph_update_queue_.push_back({QueuedJob::kEdgeAdded, nullptr, input, output, QString(), QString()}); UpdateGraphChangeValue(); } -void ProjectCopier::QueueEdgeRemove(Node *output, const NodeInput &input) +void ProjectCopier::QueueEdgeRemove(const NodeOutput &output, const NodeInput &input) { graph_update_queue_.push_back({QueuedJob::kEdgeRemoved, nullptr, input, output, QString(), QString()}); UpdateGraphChangeValue(); @@ -259,19 +259,19 @@ void ProjectCopier::QueueValueChange(const NodeInput &input) } }*/ - graph_update_queue_.push_back({QueuedJob::kValueChanged, nullptr, input, nullptr, QString(), QString()}); + graph_update_queue_.push_back({QueuedJob::kValueChanged, nullptr, input, NodeOutput(), QString(), QString()}); UpdateGraphChangeValue(); } void ProjectCopier::QueueValueHintChange(const NodeInput &input) { - graph_update_queue_.push_back({QueuedJob::kValueHintChanged, nullptr, input, nullptr, QString(), QString()}); + graph_update_queue_.push_back({QueuedJob::kValueHintChanged, nullptr, input, NodeOutput(), QString(), QString()}); UpdateGraphChangeValue(); } void ProjectCopier::QueueProjectSettingChange(const QString &key, const QString &value) { - graph_update_queue_.push_back({QueuedJob::kProjectSettingChanged, nullptr, NodeInput(), nullptr, key, value}); + graph_update_queue_.push_back({QueuedJob::kProjectSettingChanged, nullptr, NodeInput(), NodeOutput(), key, value}); UpdateGraphChangeValue(); } diff --git a/app/render/projectcopier.h b/app/render/projectcopier.h index 727c62515b..88a55bb1f7 100644 --- a/app/render/projectcopier.h +++ b/app/render/projectcopier.h @@ -69,8 +69,8 @@ class ProjectCopier : public QObject private: void DoNodeAdd(Node* node); void DoNodeRemove(Node* node); - void DoEdgeAdd(Node *output, const NodeInput& input); - void DoEdgeRemove(Node *output, const NodeInput& input); + void DoEdgeAdd(const NodeOutput &output, const NodeInput& input); + void DoEdgeRemove(const NodeOutput &output, const NodeInput& input); void DoValueChange(const NodeInput& input); void DoValueHintChange(const NodeInput &input); void DoProjectSettingChange(const QString &key, const QString &value); @@ -98,7 +98,7 @@ class ProjectCopier : public QObject Type type; Node* node; NodeInput input; - Node *output; + NodeOutput output; QString key; QString value; @@ -117,9 +117,9 @@ private slots: void QueueNodeRemove(Node* node); - void QueueEdgeAdd(Node *output, const NodeInput& input); + void QueueEdgeAdd(const NodeOutput &output, const NodeInput& input); - void QueueEdgeRemove(Node *output, const NodeInput& input); + void QueueEdgeRemove(const NodeOutput &output, const NodeInput& input); void QueueValueChange(const NodeInput& input); diff --git a/app/task/precache/precachetask.cpp b/app/task/precache/precachetask.cpp index 5caa62c07d..3910736a5d 100644 --- a/app/task/precache/precachetask.cpp +++ b/app/task/precache/precachetask.cpp @@ -47,10 +47,7 @@ PreCacheTask::PreCacheTask(Footage *footage, int index, Sequence* sequence) footage_->setParent(project_); Node::CopyInputs(footage, footage_, false); - Node::ConnectEdge(footage_, NodeInput(viewer(), ViewerOutput::kTextureInput)); - Node::ValueHint hint; - hint.set_tag(Track::Reference(Track::kVideo, index).ToString()); - viewer()->SetValueHintForInput(ViewerOutput::kTextureInput, hint); + Node::ConnectEdge(NodeOutput(footage_, Track::Reference(Track::kVideo, index).ToString()), NodeInput(viewer(), ViewerOutput::kTextureInput)); SetTitle(tr("Pre-caching %1:%2").arg(footage_->filename(), QString::number(index))); } diff --git a/app/task/project/loadotio/loadotio.cpp b/app/task/project/loadotio/loadotio.cpp index 6904575c0f..61b32eddfd 100644 --- a/app/task/project/loadotio/loadotio.cpp +++ b/app/task/project/loadotio/loadotio.cpp @@ -232,7 +232,7 @@ bool LoadOTIOTask::Run() // If the previous block was a transition, connect the current block to it if (prev_block_transition) { TransitionBlock* previous_transition_block = static_cast(previous_block); - Node::ConnectEdge(block, NodeInput(previous_transition_block, TransitionBlock::kInBlockInput)); + Node::ConnectEdge(NodeOutput(block), NodeInput(previous_transition_block, TransitionBlock::kInBlockInput)); prev_block_transition = false; } @@ -244,7 +244,7 @@ bool LoadOTIOTask::Run() transition_block->set_offsets_and_length(rational::fromRationalTime(otio_block_transition->in_offset()), rational::fromRationalTime(otio_block_transition->out_offset())); if (previous_block) { - Node::ConnectEdge(previous_block, NodeInput(transition_block, TransitionBlock::kOutBlockInput)); + Node::ConnectEdge(NodeOutput(previous_block), NodeInput(transition_block, TransitionBlock::kOutBlockInput)); } prev_block_transition = true; @@ -306,15 +306,15 @@ bool LoadOTIOTask::Run() TransformDistortNode* transform = new TransformDistortNode(); transform->setParent(sequence->parent()); - Node::ConnectEdge(probed_item, NodeInput(transform, TransformDistortNode::kTextureInput)); - Node::ConnectEdge(transform, NodeInput(block, ClipBlock::kBufferIn)); + Node::ConnectEdge(NodeOutput(probed_item), NodeInput(transform, TransformDistortNode::kTextureInput)); + Node::ConnectEdge(NodeOutput(transform), NodeInput(block, ClipBlock::kBufferIn)); block->SetNodePositionInContext(transform, QPointF(-1, 0)); } else { VolumeNode* volume_node = new VolumeNode(); volume_node->setParent(sequence->parent()); - Node::ConnectEdge(probed_item, NodeInput(volume_node, VolumeNode::kSamplesInput)); - Node::ConnectEdge(volume_node, NodeInput(block, ClipBlock::kBufferIn)); + Node::ConnectEdge(NodeOutput(probed_item), NodeInput(volume_node, VolumeNode::kSamplesInput)); + Node::ConnectEdge(NodeOutput(volume_node), NodeInput(block, ClipBlock::kBufferIn)); block->SetNodePositionInContext(volume_node, QPointF(-1, 0)); } } diff --git a/app/timeline/timelineundogeneral.cpp b/app/timeline/timelineundogeneral.cpp index 9c18e9cdbb..475a1367e6 100644 --- a/app/timeline/timelineundogeneral.cpp +++ b/app/timeline/timelineundogeneral.cpp @@ -130,7 +130,7 @@ void TimelineAddTrackCommand::redo() track_->SetTrackHeight(timeline_->GetTrackAt(timeline_->GetTrackCount()-1)->GetTrackHeight()); } timeline_->ArrayAppend(); - Node::ConnectEdge(track_, timeline_->track_input(timeline_->ArraySize() - 1)); + Node::ConnectEdge(NodeOutput(track_), timeline_->track_input(timeline_->ArraySize() - 1)); qreal position_factor = 0.5; if (timeline_->type() == Track::kVideo) { @@ -150,10 +150,10 @@ void TimelineAddTrackCommand::redo() merge_->setParent(timeline_->GetParentGraph()); // Connect merge between what used to be here - Node::DisconnectEdge(previous_connection, direct_); - Node::ConnectEdge(merge_, direct_); - Node::ConnectEdge(previous_connection, base_); - Node::ConnectEdge(track_, blend_); + Node::DisconnectEdge(NodeOutput(previous_connection), direct_); + Node::ConnectEdge(NodeOutput(merge_), direct_); + Node::ConnectEdge(NodeOutput(previous_connection), base_); + Node::ConnectEdge(NodeOutput(track_), blend_); if (create_pos_command) { position_command_->add_child(new NodeSetPositionCommand(track_, sequence, sequence->GetNodePositionInContext(sequence) + QPointF(-1, -position_factor))); @@ -162,7 +162,7 @@ void TimelineAddTrackCommand::redo() } } else if (direct_.IsValid() && !direct_.IsConnected()) { // If no merge, we have a direct connection, and nothing else is connected, connect this - Node::ConnectEdge(track_, direct_); + Node::ConnectEdge(NodeOutput(track_), direct_); if (create_pos_command) { // Just position directly next to the context node @@ -186,18 +186,18 @@ void TimelineAddTrackCommand::undo() if (merge_) { Node *previous_connection = base_.GetConnectedOutput(); - Node::DisconnectEdge(track_, blend_); - Node::DisconnectEdge(previous_connection, base_); - Node::DisconnectEdge(merge_, direct_); - Node::ConnectEdge(previous_connection, direct_); + Node::DisconnectEdge(NodeOutput(track_), blend_); + Node::DisconnectEdge(NodeOutput(previous_connection), base_); + Node::DisconnectEdge(NodeOutput(merge_), direct_); + Node::ConnectEdge(NodeOutput(previous_connection), direct_); merge_->setParent(&memory_manager_); } else if (direct_.IsValid() && direct_.GetConnectedOutput() == track_) { - Node::DisconnectEdge(track_, direct_); + Node::DisconnectEdge(NodeOutput(track_), direct_); } // Remove track - Node::DisconnectEdge(track_, timeline_->track_input(timeline_->ArraySize() - 1)); + Node::DisconnectEdge(NodeOutput(track_), timeline_->track_input(timeline_->ArraySize() - 1)); timeline_->ArrayRemoveLast(); track_->setParent(&memory_manager_); } @@ -224,11 +224,11 @@ void TransitionRemoveCommand::redo() } if (in_block_) { - Node::DisconnectEdge(in_block_, NodeInput(block_, TransitionBlock::kInBlockInput)); + Node::DisconnectEdge(NodeOutput(in_block_), NodeInput(block_, TransitionBlock::kInBlockInput)); } if (out_block_) { - Node::DisconnectEdge(out_block_, NodeInput(block_, TransitionBlock::kOutBlockInput)); + Node::DisconnectEdge(NodeOutput(out_block_), NodeInput(block_, TransitionBlock::kOutBlockInput)); } track_->RippleRemoveBlock(block_); @@ -255,11 +255,11 @@ void TransitionRemoveCommand::undo() } if (in_block_) { - Node::ConnectEdge(in_block_, NodeInput(block_, TransitionBlock::kInBlockInput)); + Node::ConnectEdge(NodeOutput(in_block_), NodeInput(block_, TransitionBlock::kInBlockInput)); } if (out_block_) { - Node::ConnectEdge(out_block_, NodeInput(block_, TransitionBlock::kOutBlockInput)); + Node::ConnectEdge(NodeOutput(out_block_), NodeInput(block_, TransitionBlock::kOutBlockInput)); } // These if statements must be separated because in_offset and out_offset report different things @@ -629,13 +629,13 @@ void TimelineAddDefaultTransitionCommand::AddTransition(ClipBlock *c, CreateTran // Connect switch (mode) { case kIn: - commands_.append(new NodeEdgeAddCommand(c, NodeInput(transition, TransitionBlock::kInBlockInput))); + commands_.append(new NodeEdgeAddCommand(NodeOutput(c), NodeInput(transition, TransitionBlock::kInBlockInput))); break; case kOutDual: - commands_.append(new NodeEdgeAddCommand(c->next(), NodeInput(transition, TransitionBlock::kInBlockInput))); + commands_.append(new NodeEdgeAddCommand(NodeOutput(c->next()), NodeInput(transition, TransitionBlock::kInBlockInput))); /* fall through */ case kOut: - commands_.append(new NodeEdgeAddCommand(c, NodeInput(transition, TransitionBlock::kOutBlockInput))); + commands_.append(new NodeEdgeAddCommand(NodeOutput(c), NodeInput(transition, TransitionBlock::kOutBlockInput))); break; } } diff --git a/app/timeline/timelineundosplit.cpp b/app/timeline/timelineundosplit.cpp index 4357e44de9..9bbcdac580 100644 --- a/app/timeline/timelineundosplit.cpp +++ b/app/timeline/timelineundosplit.cpp @@ -70,8 +70,8 @@ void BlockSplitCommand::redo() for (const Node::Connection& output : block_->output_connections()) { if (output.second.node() == potential_transition) { moved_transition_ = NodeInput(potential_transition, TransitionBlock::kOutBlockInput); - Node::DisconnectEdge(block_, moved_transition_); - Node::ConnectEdge(new_block(), moved_transition_); + Node::DisconnectEdge(NodeOutput(block_), moved_transition_); + Node::ConnectEdge(NodeOutput(new_block()), moved_transition_); break; } } @@ -83,8 +83,8 @@ void BlockSplitCommand::undo() Track* track = block_->track(); if (moved_transition_.IsValid()) { - Node::DisconnectEdge(new_block(), moved_transition_); - Node::ConnectEdge(block_, moved_transition_); + Node::DisconnectEdge(NodeOutput(new_block()), moved_transition_); + Node::ConnectEdge(NodeOutput(block_), moved_transition_); } block_->set_length_and_media_out(old_length_); diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index b76fcb9160..06fc28c104 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -372,7 +372,7 @@ void ReconnectOutputsIfNotDeletingNode(MultiUndoCommand *c, NodeViewDeleteComman // Uh-oh we're deleting this node too, instead connect to its outputs ReconnectOutputsIfNotDeletingNode(c, dc, output, proposed_reconnect.node(), context); } else { - c->add_child(new NodeEdgeAddCommand(output, it->second)); + c->add_child(new NodeEdgeAddCommand(it->first, it->second)); } } } @@ -740,7 +740,7 @@ int GetDistanceBetweenNodes(Node *start, Node *end) } for (auto it=start->input_connections().cbegin(); it!=start->input_connections().cend(); it++) { - int this_node_dist = GetDistanceBetweenNodes(it->first, end); + int this_node_dist = GetDistanceBetweenNodes(it->first.node(), end); if (this_node_dist != -1) { return 1 + this_node_dist; } diff --git a/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp b/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp index 2f2bb569b0..6424a411c5 100644 --- a/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp +++ b/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp @@ -33,7 +33,6 @@ namespace olive { NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(const NodeInput &input, QWidget *parent) : QWidget(parent), input_(input), - connected_node_(nullptr), viewer_(nullptr) { QVBoxLayout *layout = new QVBoxLayout(this); @@ -73,9 +72,9 @@ NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(const NodeInput &input, connected_to_lbl_->setFont(link_font); if (input_.IsConnected()) { - InputConnected(input_.GetConnectedOutput(), input_); + InputConnected(input_.GetConnectedOutput2(), input_); } else { - InputDisconnected(nullptr, input_); + InputDisconnected(NodeOutput(), input_); } connect(input_.node(), &Node::InputConnected, this, &NodeParamViewConnectedLabel::InputConnected); @@ -116,18 +115,18 @@ void NodeParamViewConnectedLabel::CreateTree() layout()->addWidget(value_tree_); } -void NodeParamViewConnectedLabel::InputConnected(Node *output, const NodeInput& input) +void NodeParamViewConnectedLabel::InputConnected(const NodeOutput &output, const NodeInput& input) { if (input_ != input) { return; } - connected_node_ = output; + output_ = output; UpdateLabel(); } -void NodeParamViewConnectedLabel::InputDisconnected(Node *output, const NodeInput &input) +void NodeParamViewConnectedLabel::InputDisconnected(const NodeOutput &output, const NodeInput &input) { if (input_ != input) { return; @@ -135,7 +134,7 @@ void NodeParamViewConnectedLabel::InputDisconnected(Node *output, const NodeInpu Q_UNUSED(output) - connected_node_ = nullptr; + output_.Reset(); UpdateLabel(); } @@ -146,7 +145,7 @@ void NodeParamViewConnectedLabel::ShowLabelContextMenu() QAction* disconnect_action = m.addAction(tr("Disconnect")); connect(disconnect_action, &QAction::triggered, this, [this](){ - Core::instance()->undo_stack()->push(new NodeEdgeRemoveCommand(connected_node_, input_), Node::GetDisconnectCommandString(connected_node_, input_)); + Core::instance()->undo_stack()->push(new NodeEdgeRemoveCommand(output_, input_), Node::GetDisconnectCommandString(output_, input_)); }); m.exec(QCursor::pos()); @@ -154,19 +153,19 @@ void NodeParamViewConnectedLabel::ShowLabelContextMenu() void NodeParamViewConnectedLabel::ConnectionClicked() { - if (connected_node_) { - emit RequestSelectNode(connected_node_); + if (output_.IsValid()) { + emit RequestSelectNode(output_.node()); } } void NodeParamViewConnectedLabel::UpdateLabel() { - collapse_btn_->setVisible(connected_node_); - connected_to_lbl_->setVisible(connected_node_); + collapse_btn_->setVisible(output_.IsValid()); + connected_to_lbl_->setVisible(output_.IsValid()); - if (connected_node_) { + if (output_.IsValid()) { prefix_lbl_->setText(tr("Connected to")); - connected_to_lbl_->setText(connected_node_->GetLabelAndName()); + connected_to_lbl_->setText(output_.node()->GetLabelAndName()); prefix_lbl_->setForegroundRole(QPalette::Text); } else { prefix_lbl_->setText(tr("(Not Connected)")); diff --git a/app/widget/nodeparamview/nodeparamviewconnectedlabel.h b/app/widget/nodeparamview/nodeparamviewconnectedlabel.h index 5b2b885e14..a9ce593fd9 100644 --- a/app/widget/nodeparamview/nodeparamviewconnectedlabel.h +++ b/app/widget/nodeparamview/nodeparamviewconnectedlabel.h @@ -41,9 +41,9 @@ class NodeParamViewConnectedLabel : public QWidget { void RequestSelectNode(Node *n); private slots: - void InputConnected(Node *output, const NodeInput &input); + void InputConnected(const NodeOutput &output, const NodeInput &input); - void InputDisconnected(Node *output, const NodeInput &input); + void InputDisconnected(const NodeOutput &output, const NodeInput &input); void ShowLabelContextMenu(); @@ -64,7 +64,7 @@ private slots: NodeInput input_; - Node *connected_node_; + NodeOutput output_; NodeValueTree *value_tree_; diff --git a/app/widget/nodeparamview/nodeparamviewcontext.cpp b/app/widget/nodeparamview/nodeparamviewcontext.cpp index 477fc7765e..26d81f7032 100644 --- a/app/widget/nodeparamview/nodeparamviewcontext.cpp +++ b/app/widget/nodeparamview/nodeparamviewcontext.cpp @@ -171,13 +171,13 @@ void NodeParamViewContext::AddEffectMenuItemTriggered(QAction *a) command->add_child(new NodeSetPositionCommand(ctx, ctx, ctx->GetNodePositionInContext(ctx) + QPointF(1, 0))); if (ctx_input.IsConnected()) { - Node *prev_output = ctx_input.GetConnectedOutput(); + NodeOutput prev_output = ctx_input.GetConnectedOutput2(); command->add_child(new NodeEdgeRemoveCommand(prev_output, ctx_input)); command->add_child(new NodeEdgeAddCommand(prev_output, new_node_input)); } - command->add_child(new NodeEdgeAddCommand(n, ctx_input)); + command->add_child(new NodeEdgeAddCommand(NodeOutput(n), ctx_input)); } Core::instance()->undo_stack()->push(command, tr("Added %1 to Node Chain").arg(n->Name())); diff --git a/app/widget/nodeparamview/nodeparamviewitem.cpp b/app/widget/nodeparamview/nodeparamviewitem.cpp index 6f84c45fb8..dd45445554 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.cpp +++ b/app/widget/nodeparamview/nodeparamviewitem.cpp @@ -350,7 +350,7 @@ int NodeParamViewItemBody::GetElementY(NodeInput c) const return lbl_center.y(); } -void NodeParamViewItemBody::EdgeChanged(Node *output, const NodeInput& input) +void NodeParamViewItemBody::EdgeChanged(const NodeOutput &output, const NodeInput& input) { Q_UNUSED(output) diff --git a/app/widget/nodeparamview/nodeparamviewitem.h b/app/widget/nodeparamview/nodeparamviewitem.h index 0b6e3a2f4f..d4eda780bb 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.h +++ b/app/widget/nodeparamview/nodeparamviewitem.h @@ -144,7 +144,7 @@ class NodeParamViewItemBody : public QWidget { static const int kMaxWidgetColumn; private slots: - void EdgeChanged(Node *output, const NodeInput &input); + void EdgeChanged(const NodeOutput &output, const NodeInput &input); void ArrayCollapseBtnPressed(bool checked); diff --git a/app/widget/nodevaluetree/nodevaluetree.cpp b/app/widget/nodevaluetree/nodevaluetree.cpp index 6d5cba64b5..8a0cb3c6b1 100644 --- a/app/widget/nodevaluetree/nodevaluetree.cpp +++ b/app/widget/nodevaluetree/nodevaluetree.cpp @@ -40,7 +40,9 @@ void NodeValueTree::SetNode(const NodeInput &input) input_ = input; - if (Node *connected_node = input.GetConnectedOutput()) { + NodeOutput output = input.GetConnectedOutput2(); + if (output.IsValid()) { + Node *connected_node = output.node(); connected_node->Retranslate(); connect(connected_node, &Node::InputValueHintChanged, this, &NodeValueTree::ValueHintChanged); @@ -59,7 +61,7 @@ void NodeValueTree::SetNode(const NodeInput &input) radio->setProperty("output", o.id); connect(radio, &QRadioButton::clicked, this, &NodeValueTree::RadioButtonChecked); - if (vh.tag() == o.id) { + if (output.output() == o.id) { radio->setChecked(true); } @@ -69,7 +71,8 @@ void NodeValueTree::SetNode(const NodeInput &input) swizzler_items->setFlags(Qt::NoItemFlags); ValueSwizzleWidget *b = new ValueSwizzleWidget(); - b->set_channels(4, input.GetChannelCount()); + //b->set_channels(4, input.GetChannelCount()); + b->set_channels(4, 4); b->set(vh.swizzle()); connect(b, &ValueSwizzleWidget::value_changed, this, &NodeValueTree::SwizzleChanged); this->setItemWidget(swizzler_items, 1, b); @@ -115,9 +118,14 @@ void NodeValueTree::RadioButtonChecked(bool e) QString tag = btn->property("output").toString(); NodeInput input = btn->property("input").value(); - Node::ValueHint hint = input.node()->GetValueHintForInput(input.input(), input.element()); - hint.set_tag(tag); - Core::instance()->undo_stack()->push(new NodeSetValueHintCommand(input, hint), tr("Switched Connected Output Parameter")); + NodeOutput old_output = input.GetConnectedOutput2(); + MultiUndoCommand *command = new MultiUndoCommand(); + command->add_child(new NodeEdgeAddCommand(old_output, input)); + + old_output.set_output(tag); + command->add_child(new NodeEdgeAddCommand(old_output, input)); + + Core::instance()->undo_stack()->push(command, tr("Switched Connected Output Parameter")); } } @@ -139,11 +147,14 @@ void NodeValueTree::ValueHintChanged(const NodeInput &input) { if (input == input_) { Node::ValueHint vh = input.node()->GetValueHintForInput(input.input(), input.element()); + + NodeOutput output = input.GetConnectedOutput2(); + for (int i = 0; i < this->topLevelItemCount(); i++) { QTreeWidgetItem *item = this->topLevelItem(i); QRadioButton *rb = static_cast(this->itemWidget(item, 0)); - if (rb->property("output").toString() == vh.tag()) { + if (rb->property("output").toString() == output.output()) { rb->setChecked(true); ValueSwizzleWidget *b = GetSwizzleWidgetFromTopLevelItem(i); diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index 29a3a07952..711c5fbe17 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -1133,7 +1133,7 @@ QVector NodeView::ProcessDroppingAttachedNodes(MultiUndoCommand *command, // Place new edges drop_edge_command->add_child(new NodeEdgeAddCommand(drop_edge_->output(), drop_input_)); - drop_edge_command->add_child(new NodeEdgeAddCommand(dropping_node, drop_edge_->input())); + drop_edge_command->add_child(new NodeEdgeAddCommand(NodeOutput(dropping_node), drop_edge_->input())); } drop_edge_ = nullptr; @@ -1641,7 +1641,7 @@ void NodeView::EndEdgeDrag(bool cancel) creating_input = input_group->GetInputFromID(creating_input.input()); } - if (Node::ConnectionExists(creating_output, creating_input)) { + if (Node::ConnectionExists(NodeOutput(creating_output), creating_input)) { cancel = true; } @@ -1664,9 +1664,9 @@ void NodeView::EndEdgeDrag(bool cancel) }*/ if (!cancel) { - command->add_child(new NodeEdgeAddCommand(creating_output, creating_input)); + command->add_child(new NodeEdgeAddCommand(NodeOutput(creating_output), creating_input)); - command_name = Node::GetConnectCommandString(creating_output, creating_input); + command_name = Node::GetConnectCommandString(NodeOutput(creating_output), creating_input); // If the output is not in the input's context, add it now. We check the item rather than // the node itself, because sometimes a node may not be in the context but another node diff --git a/app/widget/nodeview/nodeviewcontext.cpp b/app/widget/nodeview/nodeviewcontext.cpp index 392e94e9c7..5a5637c352 100644 --- a/app/widget/nodeview/nodeviewcontext.cpp +++ b/app/widget/nodeview/nodeviewcontext.cpp @@ -105,7 +105,7 @@ void NodeViewContext::RemoveChild(Node *node) // be changed...) QVector edges_to_remove = item->GetAllEdgesRecursively(); foreach (NodeViewEdge *edge, edges_to_remove) { - if (node == item->GetNode() || edge->output() == node || edge->input().node() == node) { + if (node == item->GetNode() || edge->output().node() == node || edge->input().node() == node) { ChildInputDisconnected(edge->output(), edge->input()); } } @@ -129,17 +129,17 @@ void NodeViewContext::RemoveChild(Node *node) UpdateRect(); } -void NodeViewContext::ChildInputConnected(Node *output, const NodeInput &input) +void NodeViewContext::ChildInputConnected(const NodeOutput &output, const NodeInput &input) { // Add edge if (!input.IsHidden()) { - if (NodeViewItem* output_item = item_map_.value(output)) { + if (NodeViewItem* output_item = item_map_.value(output.node())) { AddEdgeInternal(output, input, output_item, item_map_.value(input.node())->GetItemForInput(input)); } } } -bool NodeViewContext::ChildInputDisconnected(Node *output, const NodeInput &input) +bool NodeViewContext::ChildInputDisconnected(const NodeOutput &output, const NodeInput &input) { // Remove edge for (int i=0; ioutput_connections().cbegin(); it!=node->output_connections().cend(); it++) { if (!it->second.IsHidden()) { if (NodeViewItem *other_item = item_map_.value(it->second.node())) { - AddEdgeInternal(node, it->second, item, other_item->GetItemForInput(it->second)); + AddEdgeInternal(it->first, it->second, item, other_item->GetItemForInput(it->second)); } } } for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) { if (!it->second.IsHidden()) { - if (NodeViewItem *other_item = item_map_.value(it->first)) { + if (NodeViewItem *other_item = item_map_.value(it->first.node())) { AddEdgeInternal(it->first, it->second, other_item, item->GetItemForInput(it->second)); } } } } -void NodeViewContext::AddEdgeInternal(Node *output, const NodeInput& input, NodeViewItem *from, NodeViewItem *to) +void NodeViewContext::AddEdgeInternal(const NodeOutput &output, const NodeInput& input, NodeViewItem *from, NodeViewItem *to) { if (from == to) { return; diff --git a/app/widget/nodeview/nodeviewcontext.h b/app/widget/nodeview/nodeviewcontext.h index f88f6869a3..81fd486336 100644 --- a/app/widget/nodeview/nodeviewcontext.h +++ b/app/widget/nodeview/nodeviewcontext.h @@ -52,9 +52,9 @@ public slots: void RemoveChild(Node *node); - void ChildInputConnected(Node *output, const NodeInput& input); + void ChildInputConnected(const NodeOutput &output, const NodeInput& input); - bool ChildInputDisconnected(Node *output, const NodeInput& input); + bool ChildInputDisconnected(const NodeOutput &output, const NodeInput& input); signals: void ItemAboutToBeDeleted(NodeViewItem *item); @@ -67,7 +67,7 @@ public slots: private: void AddNodeInternal(Node *node, NodeViewItem *item); - void AddEdgeInternal(Node *output, const NodeInput& input, NodeViewItem *from, NodeViewItem *to); + void AddEdgeInternal(const NodeOutput &output, const NodeInput& input, NodeViewItem *from, NodeViewItem *to); Node *context_; diff --git a/app/widget/nodeview/nodeviewedge.cpp b/app/widget/nodeview/nodeviewedge.cpp index b5a63b400c..838ea163e4 100644 --- a/app/widget/nodeview/nodeviewedge.cpp +++ b/app/widget/nodeview/nodeviewedge.cpp @@ -38,7 +38,7 @@ NodeViewEdge::NodeViewEdge(QGraphicsItem *parent) : { } -NodeViewEdge::NodeViewEdge(Node *output, const NodeInput &input, +NodeViewEdge::NodeViewEdge(const NodeOutput &output, const NodeInput &input, NodeViewItem* from_item, NodeViewItem* to_item, QGraphicsItem* parent) : NodeViewEdge(parent) diff --git a/app/widget/nodeview/nodeviewedge.h b/app/widget/nodeview/nodeviewedge.h index 8abad088bd..83008197a8 100644 --- a/app/widget/nodeview/nodeviewedge.h +++ b/app/widget/nodeview/nodeviewedge.h @@ -39,7 +39,7 @@ class NodeViewItem; class NodeViewEdge : public CurvedConnectorItem { public: - NodeViewEdge(Node *output, const NodeInput& input, + NodeViewEdge(const NodeOutput &output, const NodeInput& input, NodeViewItem* from_item, NodeViewItem* to_item, QGraphicsItem* parent = nullptr); @@ -47,8 +47,8 @@ class NodeViewEdge : public CurvedConnectorItem virtual ~NodeViewEdge() override; - Node *output() const { return output_; } - const NodeInput& input() const { return input_; } + const NodeOutput &output() const { return output_; } + const NodeInput &input() const { return input_; } int element() const { return element_; } NodeViewItem* from_item() const { return from_item_; } NodeViewItem* to_item() const { return to_item_; } @@ -63,7 +63,7 @@ class NodeViewEdge : public CurvedConnectorItem virtual NodeViewCommon::FlowDirection GetToDirection() const override; private: - Node *output_; + NodeOutput output_; NodeInput input_; diff --git a/app/widget/projectexplorer/projectviewmodel.cpp b/app/widget/projectexplorer/projectviewmodel.cpp index d97f5b0c12..890105ee8a 100644 --- a/app/widget/projectexplorer/projectviewmodel.cpp +++ b/app/widget/projectexplorer/projectviewmodel.cpp @@ -369,7 +369,7 @@ bool ProjectViewModel::dropMimeData(const QMimeData *data, Qt::DropAction action if (item != drop_location && item->folder() != drop_location && (!dynamic_cast(item) || !ItemIsParentOfChild(static_cast(item), drop_location))) { - move_command->add_child(new NodeEdgeRemoveCommand(item, NodeInput(item->folder(), Folder::kChildInput, item->folder()->index_of_child_in_array(item)))); + move_command->add_child(new NodeEdgeRemoveCommand(NodeOutput(item), NodeInput(item->folder(), Folder::kChildInput, item->folder()->index_of_child_in_array(item)))); move_command->add_child(new FolderAddChild(drop_location, item)); count++; } diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 36cfab5afe..4f30f5e5ef 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -1542,11 +1542,11 @@ void TimelineWidget::MulticamEnabledTriggered(bool e) // connect to the multicam instead QVector inputs = c->FindWaysNodeArrivesHere(s); for (const NodeInput &i : inputs) { - command->add_child(new NodeEdgeRemoveCommand(s, i)); - command->add_child(new NodeEdgeAddCommand(n, i)); + command->add_child(new NodeEdgeRemoveCommand(NodeOutput(s), i)); + command->add_child(new NodeEdgeAddCommand(NodeOutput(n), i)); } - command->add_child(new NodeEdgeAddCommand(s, NodeInput(n, n->kSequenceInput))); + command->add_child(new NodeEdgeAddCommand(NodeOutput(s), NodeInput(n, n->kSequenceInput))); // Move sequence node one unit back, and place multicam in sequence's spot QPointF sequence_pos = c->GetNodePositionInContext(s); @@ -1562,7 +1562,7 @@ void TimelineWidget::MulticamEnabledTriggered(bool e) if (MultiCamNode *mcn = dynamic_cast(i.node())) { for (auto it=mcn->output_connections().cbegin(); it!=mcn->output_connections().cend(); it++) { command->add_child(new NodeEdgeRemoveCommand(it->first, it->second)); - command->add_child(new NodeEdgeAddCommand(s, it->second)); + command->add_child(new NodeEdgeAddCommand(NodeOutput(s), it->second)); } command->add_child(new NodeRemoveAndDisconnectCommand(mcn)); diff --git a/app/widget/timelinewidget/tool/add.cpp b/app/widget/timelinewidget/tool/add.cpp index 0571ad8824..aa1282744f 100644 --- a/app/widget/timelinewidget/tool/add.cpp +++ b/app/widget/timelinewidget/tool/add.cpp @@ -174,7 +174,7 @@ Node *AddTool::CreateAddableClip(MultiUndoCommand *command, Sequence *sequence, if (node_to_add) { QPointF extra_node_offset(kDefaultDistanceFromOutput, 0); command->add_child(new NodeAddCommand(graph, node_to_add)); - command->add_child(new NodeEdgeAddCommand(node_to_add, NodeInput(clip, ClipBlock::kBufferIn))); + command->add_child(new NodeEdgeAddCommand(NodeOutput(node_to_add), NodeInput(clip, ClipBlock::kBufferIn))); command->add_child(new NodeSetPositionCommand(node_to_add, clip, extra_node_offset)); if (!rect.isNull()) { diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index 7d77ab5778..0490388885 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -448,19 +448,14 @@ void ImportTool::DropGhosts(bool insert, MultiUndoCommand *parent_command) dep_pos++; - Node::ValueHint hint; - hint.set_tag(footage_stream.output); - switch (Track::Reference::TypeFromString(footage_stream.output)) { case Track::kVideo: { TransformDistortNode* transform = new TransformDistortNode(); command->add_child(new NodeAddCommand(dst_graph, transform)); - command->add_child(new NodeSetValueHintCommand(transform, TransformDistortNode::kTextureInput, -1, hint)); - - command->add_child(new NodeEdgeAddCommand(footage_stream.footage, NodeInput(transform, TransformDistortNode::kTextureInput))); - command->add_child(new NodeEdgeAddCommand(transform, NodeInput(clip, ClipBlock::kBufferIn))); + command->add_child(new NodeEdgeAddCommand(NodeOutput(footage_stream.footage, footage_stream.output), NodeInput(transform, TransformDistortNode::kTextureInput))); + command->add_child(new NodeEdgeAddCommand(NodeOutput(transform), NodeInput(clip, ClipBlock::kBufferIn))); command->add_child(new NodeSetPositionCommand(transform, clip, QPointF(dep_pos, 0))); break; } @@ -469,10 +464,8 @@ void ImportTool::DropGhosts(bool insert, MultiUndoCommand *parent_command) VolumeNode* volume_node = new VolumeNode(); command->add_child(new NodeAddCommand(dst_graph, volume_node)); - command->add_child(new NodeSetValueHintCommand(volume_node, VolumeNode::kSamplesInput, -1, hint)); - - command->add_child(new NodeEdgeAddCommand(footage_stream.footage, NodeInput(volume_node, VolumeNode::kSamplesInput))); - command->add_child(new NodeEdgeAddCommand(volume_node, NodeInput(clip, ClipBlock::kBufferIn))); + command->add_child(new NodeEdgeAddCommand(NodeOutput(footage_stream.footage, footage_stream.output), NodeInput(volume_node, VolumeNode::kSamplesInput))); + command->add_child(new NodeEdgeAddCommand(NodeOutput(volume_node), NodeInput(clip, ClipBlock::kBufferIn))); command->add_child(new NodeSetPositionCommand(volume_node, clip, QPointF(dep_pos, 0))); break; } diff --git a/app/widget/timelinewidget/tool/pointer.cpp b/app/widget/timelinewidget/tool/pointer.cpp index 452e8dfe94..20111afc66 100644 --- a/app/widget/timelinewidget/tool/pointer.cpp +++ b/app/widget/timelinewidget/tool/pointer.cpp @@ -736,12 +736,12 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event) if (og_in_transition && relinks.contains(og_in_transition)) { TransitionBlock *cp_in_transition = static_cast(relinks.value(og_in_transition)); - command->add_child(new NodeEdgeAddCommand(cp_clip, NodeInput(cp_in_transition, TransitionBlock::kInBlockInput))); + command->add_child(new NodeEdgeAddCommand(NodeOutput(cp_clip), NodeInput(cp_in_transition, TransitionBlock::kInBlockInput))); } if (og_out_transition && relinks.contains(og_out_transition)) { TransitionBlock *cp_out_transition = static_cast(relinks.value(og_out_transition)); - command->add_child(new NodeEdgeAddCommand(cp_clip, NodeInput(cp_out_transition, TransitionBlock::kOutBlockInput))); + command->add_child(new NodeEdgeAddCommand(NodeOutput(cp_clip), NodeInput(cp_out_transition, TransitionBlock::kOutBlockInput))); } } } diff --git a/app/widget/timelinewidget/tool/transition.cpp b/app/widget/timelinewidget/tool/transition.cpp index a67496775e..16073b440d 100644 --- a/app/widget/timelinewidget/tool/transition.cpp +++ b/app/widget/timelinewidget/tool/transition.cpp @@ -132,10 +132,10 @@ void TransitionTool::MouseRelease(TimelineViewMouseEvent *event) Block* in_block = (ghost_->GetMode() == Timeline::kTrimIn) ? active_block : friend_block; // Connect block to transition - command->add_child(new NodeEdgeAddCommand(out_block, + command->add_child(new NodeEdgeAddCommand(NodeOutput(out_block), NodeInput(transition, TransitionBlock::kOutBlockInput))); - command->add_child(new NodeEdgeAddCommand(in_block, + command->add_child(new NodeEdgeAddCommand(NodeOutput(in_block), NodeInput(transition, TransitionBlock::kInBlockInput))); command->add_child(new NodeSetPositionCommand(out_block, transition, QPointF(-1, -0.5))); @@ -151,7 +151,7 @@ void TransitionTool::MouseRelease(TimelineViewMouseEvent *event) } // Connect block to transition - command->add_child(new NodeEdgeAddCommand(block_to_transition, + command->add_child(new NodeEdgeAddCommand(NodeOutput(block_to_transition), NodeInput(transition, transition_input_to_connect))); command->add_child(new NodeSetPositionCommand(block_to_transition, transition, QPointF(-1, 0))); From 95aab6a8a1b8e5fd205bd1a5def06bc0251785dc Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 24 May 2023 17:06:39 -0700 Subject: [PATCH 44/71] nodevaluetree: fix issue where swizzles didn't auto update --- app/widget/nodevaluetree/nodevaluetree.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/widget/nodevaluetree/nodevaluetree.cpp b/app/widget/nodevaluetree/nodevaluetree.cpp index 8a0cb3c6b1..38a7b2578c 100644 --- a/app/widget/nodevaluetree/nodevaluetree.cpp +++ b/app/widget/nodevaluetree/nodevaluetree.cpp @@ -44,7 +44,8 @@ void NodeValueTree::SetNode(const NodeInput &input) if (output.IsValid()) { Node *connected_node = output.node(); connected_node->Retranslate(); - connect(connected_node, &Node::InputValueHintChanged, this, &NodeValueTree::ValueHintChanged); + + connect(input_.node(), &Node::InputValueHintChanged, this, &NodeValueTree::ValueHintChanged); const QVector &outputs = connected_node->outputs(); From 4149ab473694423d11d6cc2d6946822934020cfe Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Thu, 25 May 2023 01:47:22 -0700 Subject: [PATCH 45/71] implement swizzling audio --- app/node/audio/pan/pan.cpp | 5 +- app/node/math/math/math.cpp | 8 +++ app/node/project/footage/footage.cpp | 2 +- app/node/value.cpp | 101 +++++++++++++++++++++++++-- app/node/value.h | 15 +++- app/render/job/CMakeLists.txt | 2 + app/render/job/audiojob.cpp | 45 ++++++++++++ app/render/job/audiojob.h | 75 ++++++++++++++++++++ app/render/renderprocessor.cpp | 45 +++++++----- app/render/renderprocessor.h | 1 + 10 files changed, 272 insertions(+), 27 deletions(-) create mode 100644 app/render/job/audiojob.cpp create mode 100644 app/render/job/audiojob.h diff --git a/app/node/audio/pan/pan.cpp b/app/node/audio/pan/pan.cpp index d38fdd9ac9..b2d7947e85 100644 --- a/app/node/audio/pan/pan.cpp +++ b/app/node/audio/pan/pan.cpp @@ -67,7 +67,10 @@ void PanNode::ProcessSamples(const void *context, const SampleJob &job, SampleBu const PanNode *n = static_cast(context); const ValueParams &p = job.value_params(); - SampleBuffer input = job.Get(PanNode::kSamplesInput).toSamples(); + const SampleBuffer input = job.Get(PanNode::kSamplesInput).toSamples(); + if (!input.is_allocated()) { + return; + } // This node is only compatible with stereo audio if (job.audio_params().channel_count() == 2) { diff --git a/app/node/math/math/math.cpp b/app/node/math/math/math.cpp index de9ccb2c99..4fa689e9b8 100644 --- a/app/node/math/math/math.cpp +++ b/app/node/math/math/math.cpp @@ -133,6 +133,10 @@ void MathNode::ProcessSamplesSamples(const void *context, const SampleJob &job, const SampleBuffer samples_b = job.Get(QStringLiteral("b")).toSamples(); const MathNode::Operation operation = static_cast(job.Get(QStringLiteral("operation")).toInt()); + if (!samples_a.is_allocated() || !samples_b.is_allocated()) { + return; + } + size_t max_samples = qMax(samples_a.sample_count(), samples_b.sample_count()); size_t min_samples = qMin(samples_a.sample_count(), samples_b.sample_count()); @@ -398,6 +402,10 @@ void MathNode::ProcessSamplesDouble(const void *context, const SampleJob &job, S const QString number_in = job.Get(QStringLiteral("number")).toString(); const Operation operation = static_cast(job.Get(QStringLiteral("operation")).toInt()); + if (!input.is_allocated()) { + return; + } + if (n->IsInputStatic(number_in)) { auto f = n->GetStandardValue(number_in).toDouble(); diff --git a/app/node/project/footage/footage.cpp b/app/node/project/footage/footage.cpp index b0b00566d0..c151d65741 100644 --- a/app/node/project/footage/footage.cpp +++ b/app/node/project/footage/footage.cpp @@ -271,7 +271,7 @@ value_t Footage::Value(const ValueParams &p) const job.set_audio_params(ap); job.set_cache_path(project()->cache_path()); - return value_t(TYPE_SAMPLES, job); + return AudioJob::Create(ap, job); } break; case Track::kSubtitle: diff --git a/app/node/value.cpp b/app/node/value.cpp index 019ed7a919..bfe23d093f 100644 --- a/app/node/value.cpp +++ b/app/node/value.cpp @@ -1,7 +1,7 @@ /*** Olive - Non-Linear Video Editor - Copyright (C) 2022 Olive Team + Copyright (C) 2023 Olive Studios LLC 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 @@ -44,19 +44,38 @@ struct TextureChannel size_t channel; }; +struct SampleJobChannel +{ + AudioJobPtr job; + size_t channel; +}; + value_t::value_t(TexturePtr texture) : value_t(TYPE_TEXTURE) { if (texture) { - data_.resize(texture->channel_count()); - for (int i = 0; i < texture->channel_count(); i++) { - data_[i] = TextureChannel({texture, size_t(i)}); + size_t sz = texture->channel_count(); + data_.resize(sz); + for (size_t i = 0; i < sz; i++) { + data_[i] = TextureChannel({texture, i}); + } + } +} + +value_t::value_t(AudioJobPtr job) +{ + if (job) { + size_t sz = job->params().channel_count(); + data_.resize(sz); + for (size_t i = 0; i < sz; i++) { + data_[i] = SampleJobChannel({job, i}); } + type_ = TYPE_SAMPLES; } } -value_t::value_t(const SampleJob &samples) : - value_t(TYPE_SAMPLES, samples) +value_t::value_t(const SampleJob &job) : + value_t(AudioJob::Create(job.audio_params(), job)) { } @@ -65,6 +84,17 @@ ShaderCode GetSwizzleShaderCode(const QString &id) return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/swizzle.frag"))); } +void SwizzleAudio(const void *context, const SampleJob &job, SampleBuffer &output) +{ + for (int i = 0; i < output.channel_count(); i++) { + SampleBuffer b = job.Get(QStringLiteral("b.%1").arg(i)).toSamples(); + if (b.is_allocated()) { + size_t c = job.Get(QStringLiteral("c.%1").arg(i)).toInt(); + output.fast_set(b, i, c); + } + } +} + TexturePtr value_t::toTexture() const { if (type_ != TYPE_TEXTURE || data_.empty()) { @@ -111,6 +141,65 @@ TexturePtr value_t::toTexture() const } } +AudioJobPtr value_t::toAudioJob() const +{ + if (type_ != TYPE_SAMPLES || data_.empty()) { + return AudioJobPtr(); + } + + bool swizzled = false; + + SampleJobChannel last; + AudioParams ap; + TimeRange time; + + for (auto it = data_.cbegin(); it != data_.cend(); it++) { + const SampleJobChannel &c = it->value(); + + if (it != data_.cbegin() && !swizzled) { + if (c.job != last.job || c.channel != last.channel + 1) { + swizzled = true; + } + } + + if (c.job) { + if (!ap.is_valid() && c.job->params().is_valid()) { + ap = c.job->params(); + } + + if (time.length().isNull()) { + time = c.job->time(); + } + } + + last = c; + } + + if (swizzled) { + if (ap.is_valid()) { + // Return texture(s) wrapped in a swizzle shader + ap.set_channel_layout(av_get_default_channel_layout(data_.size())); + + SampleJob swizzle(ValueParams(VideoParams(), ap, time, QString(), LoopMode(), nullptr, nullptr)); + + for (size_t i = 0; i < data_.size(); i++) { + const SampleJobChannel &c = data_.at(i).value(); + swizzle.Insert(QStringLiteral("b.%1").arg(i), c.job); + swizzle.Insert(QStringLiteral("c.%1").arg(i), c.channel); + } + + swizzle.set_function(SwizzleAudio, nullptr); + + return AudioJob::Create(ap, swizzle); + } else { + return nullptr; + } + } else { + // No swizzling has occurred + return last.job; + } +} + value_t value_t::fromSerializedString(type_t target_type, const QString &str) { QStringList l = str.split(CHANNEL_SPLITTER); diff --git a/app/node/value.h b/app/node/value.h index fd2f0a7016..54597d17dd 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -1,7 +1,7 @@ /*** Olive - Non-Linear Video Editor - Copyright (C) 2022 Olive Team + Copyright (C) 2023 Olive Studios LLC 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 @@ -24,6 +24,7 @@ #include #include +#include "render/job/audiojob.h" #include "render/texture.h" namespace olive { @@ -168,7 +169,14 @@ class value_t value_t(TexturePtr texture); - value_t(const SampleJob &samples); + value_t(AudioJobPtr job); + + value_t(const SampleJob &job); + + value_t(const SampleBuffer &samples) : + value_t(TYPE_SAMPLES, samples) + { + } value_t(const QVector2D &vec) : value_t(TYPE_DOUBLE, size_t(2)) @@ -297,7 +305,7 @@ class value_t std::vector &data() { return data_; } const std::vector &data() const { return data_; } - bool isValid() const { return type_ != TYPE_NONE; } + bool isValid() const { return type_ != TYPE_NONE && !data_.empty(); } bool toBool() const { return toInt(); } double toDouble() const { return value(); } @@ -313,6 +321,7 @@ class value_t NodeValueArray toArray() const { return value(); } TexturePtr toTexture() const; SampleBuffer toSamples() const { return value(); } + AudioJobPtr toAudioJob() const; static value_t fromSerializedString(type_t target_type, const QString &s); QString toSerializedString() const; diff --git a/app/render/job/CMakeLists.txt b/app/render/job/CMakeLists.txt index 6ad233bbe0..932cb73a5f 100644 --- a/app/render/job/CMakeLists.txt +++ b/app/render/job/CMakeLists.txt @@ -18,6 +18,8 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} render/job/acceleratedjob.cpp render/job/acceleratedjob.h + render/job/audiojob.cpp + render/job/audiojob.h render/job/footagejob.h render/job/generatejob.h render/job/samplejob.h diff --git a/app/render/job/audiojob.cpp b/app/render/job/audiojob.cpp new file mode 100644 index 0000000000..8aa86f5bc2 --- /dev/null +++ b/app/render/job/audiojob.cpp @@ -0,0 +1,45 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "audiojob.h" + +#include "acceleratedjob.h" +#include "footagejob.h" +#include "samplejob.h" + +namespace olive { + +AudioJob::~AudioJob() +{ + delete job_; +} + +TimeRange AudioJob::time() const +{ + if (FootageJob *f = dynamic_cast(job_)) { + return f->time(); + } else if (SampleJob *s = dynamic_cast(job_)) { + return s->value_params().time(); + } + + return TimeRange(); +} + +} diff --git a/app/render/job/audiojob.h b/app/render/job/audiojob.h new file mode 100644 index 0000000000..43604c0170 --- /dev/null +++ b/app/render/job/audiojob.h @@ -0,0 +1,75 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef AUDIOJOB_H +#define AUDIOJOB_H + +#include +#include + +#include "common/define.h" + +namespace olive { + +class AcceleratedJob; +using namespace core; + +class AudioJob; +using AudioJobPtr = std::shared_ptr; + +class AudioJob +{ +public: + AudioJob() + { + job_ = nullptr; + } + + template + AudioJob(const AudioParams ¶ms, const T &job) + { + job_ = new T(job); + params_ = params; + } + + template + static AudioJobPtr Create(const AudioParams ¶ms, const T &job) + { + return std::make_shared(params, job); + } + + ~AudioJob(); + + DISABLE_COPY_MOVE(AudioJob) + + AcceleratedJob *job() const { return job_; } + const AudioParams ¶ms() const { return params_; } + + TimeRange time() const; + +private: + AudioParams params_; + AcceleratedJob *job_; + +}; + +} + +#endif // AUDIOJOB_H diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index d2aa83307b..7e8d5df6ae 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -636,23 +636,36 @@ void RenderProcessor::ResolveJobs(value_t &val) } else if (val.type() == TYPE_SAMPLES) { - SampleJob sjob; - FootageJob fjob; - - if (val.get(&sjob)) { - for (auto it=sjob.GetValues().begin(); it!=sjob.GetValues().end(); it++) { - // Jobs will almost always be submitted with one of these types - value_t &subval = it.value(); - ResolveJobs(subval); - } + if (AudioJobPtr job = val.toAudioJob()) { + if (resolved_sample_cache_.contains(job.get())) { + + val = resolved_sample_cache_.value(job.get()); + + } else { + + if (SampleJob *sjob = dynamic_cast(job->job())) { - SampleBuffer output_buffer = CreateSampleBuffer(sjob.audio_params(), sjob.sample_count()); - ProcessSamples(output_buffer, sjob); - val = value_t(TYPE_SAMPLES, output_buffer); - } else if (val.get(&fjob)) { - SampleBuffer buffer = CreateSampleBuffer(GetCacheAudioParams(), fjob.time().length()); - ProcessAudioFootage(buffer, &fjob, fjob.time()); - val = value_t(TYPE_SAMPLES, buffer); + for (auto it=sjob->GetValues().begin(); it!=sjob->GetValues().end(); it++) { + // Jobs will almost always be submitted with one of these types + value_t &subval = it.value(); + ResolveJobs(subval); + } + + SampleBuffer output_buffer = CreateSampleBuffer(sjob->audio_params(), sjob->sample_count()); + ProcessSamples(output_buffer, *sjob); + val = output_buffer; + + } else if (FootageJob *fjob = dynamic_cast(job->job())) { + + SampleBuffer buffer = CreateSampleBuffer(GetCacheAudioParams(), fjob->time().length()); + ProcessAudioFootage(buffer, fjob, fjob->time()); + val = buffer; + + } + + resolved_sample_cache_.insert(job.get(), val); + + } } } } diff --git a/app/render/renderprocessor.h b/app/render/renderprocessor.h index 28435b9898..6e89d87d36 100644 --- a/app/render/renderprocessor.h +++ b/app/render/renderprocessor.h @@ -117,6 +117,7 @@ class RenderProcessor CancelAtom *cancel_atom_; QHash resolved_texture_cache_; + QHash resolved_sample_cache_; }; From 955414843dd82ca4c9557d722b8d18ffb334e53c Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Thu, 25 May 2023 01:47:38 -0700 Subject: [PATCH 46/71] floatsliderparamwidget: fixed max not being set correctly --- app/widget/nodeparamview/paramwidget/floatsliderparamwidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/widget/nodeparamview/paramwidget/floatsliderparamwidget.cpp b/app/widget/nodeparamview/paramwidget/floatsliderparamwidget.cpp index 0b6e73c8ca..fd19c81f61 100644 --- a/app/widget/nodeparamview/paramwidget/floatsliderparamwidget.cpp +++ b/app/widget/nodeparamview/paramwidget/floatsliderparamwidget.cpp @@ -58,7 +58,7 @@ void FloatSliderParamWidget::SetProperty(const QString &key, const value_t &val) } } else if (key == QStringLiteral("max")) { for (size_t i = 0; i < val.size() && i < GetWidgets().size(); i++) { - static_cast(GetWidgets().at(i))->SetValue(val.value(i)); + static_cast(GetWidgets().at(i))->SetMaximum(val.value(i)); } } else if (key == QStringLiteral("view")) { FloatSlider::DisplayType display_type = static_cast(val.toInt()); From 0c312e64d44fb29c429e125308884987d916a3f0 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Thu, 25 May 2023 12:49:16 -0700 Subject: [PATCH 47/71] check if track is within bounds --- app/node/inputimmediate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/node/inputimmediate.h b/app/node/inputimmediate.h index c41a53bdae..02225072ab 100644 --- a/app/node/inputimmediate.h +++ b/app/node/inputimmediate.h @@ -146,7 +146,7 @@ class NodeInputImmediate bool is_using_standard_value(int track) const { - return (!is_keyframing() || keyframe_tracks_.at(track).isEmpty()); + return (!is_keyframing() || track >= keyframe_tracks_.size() || keyframe_tracks_.at(track).isEmpty()); } private: From b8d7fb8b7bfc7d8147c3b5649923a9568323c051 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Thu, 25 May 2023 12:49:27 -0700 Subject: [PATCH 48/71] valueparams: allow editing value cache --- app/node/globals.cpp | 7 +++++++ app/node/globals.h | 1 + 2 files changed, 8 insertions(+) diff --git a/app/node/globals.cpp b/app/node/globals.cpp index 25daeb8fee..e35251178f 100644 --- a/app/node/globals.cpp +++ b/app/node/globals.cpp @@ -45,6 +45,13 @@ ValueParams ValueParams::loop_mode_edited(const LoopMode &lm) const return g; } +ValueParams ValueParams::with_cache(Cache *cache) const +{ + ValueParams g = *this; + g.cache_ = cache; + return g; +} + bool ValueParams::get_cached_value(const Node *node, const ValueParams &p, value_t &out) const { if (cache_) { diff --git a/app/node/globals.h b/app/node/globals.h index b80b4bd180..dd74cadf47 100644 --- a/app/node/globals.h +++ b/app/node/globals.h @@ -71,6 +71,7 @@ class ValueParams ValueParams time_transformed(const TimeRange &time) const; ValueParams output_edited(const QString &output) const; ValueParams loop_mode_edited(const LoopMode &lm) const; + ValueParams with_cache(Cache *cache) const; bool get_cached_value(const Node *node, const ValueParams &p, value_t &out) const; void insert_cached_value(const Node *node, const ValueParams &p, const value_t &in) const; From f85755043e05c488f5afe2a5d5034e2862048bab Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Thu, 25 May 2023 13:46:30 -0700 Subject: [PATCH 49/71] implement tonegenerator --- app/node/factory.cpp | 3 + app/node/factory.h | 1 + app/node/generator/CMakeLists.txt | 1 + app/node/generator/tone/CMakeLists.txt | 22 ++++++ app/node/generator/tone/tonegenerator.cpp | 93 +++++++++++++++++++++++ app/node/generator/tone/tonegenerator.h | 50 ++++++++++++ app/widget/timelinewidget/tool/add.cpp | 5 +- 7 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 app/node/generator/tone/CMakeLists.txt create mode 100644 app/node/generator/tone/tonegenerator.cpp create mode 100644 app/node/generator/tone/tonegenerator.h diff --git a/app/node/factory.cpp b/app/node/factory.cpp index 1839d8f080..e8a6f83d1c 100644 --- a/app/node/factory.cpp +++ b/app/node/factory.cpp @@ -53,6 +53,7 @@ #include "generator/text/textv1.h" #include "generator/text/textv2.h" #include "generator/text/textv3.h" +#include "generator/tone/tonegenerator.h" #include "input/multicam/multicamnode.h" #include "input/time/timeinput.h" #include "input/value/valuenode.h" @@ -309,6 +310,8 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id) return new RippleDistortNode(); case kMulticamNode: return new MultiCamNode(); + case kToneGenerator: + return new ToneGenerator(); case kInternalNodeCount: break; diff --git a/app/node/factory.h b/app/node/factory.h index fbf3535514..2cf99ade6d 100644 --- a/app/node/factory.h +++ b/app/node/factory.h @@ -81,6 +81,7 @@ class NodeFactory kTileDistort, kSwirlDistort, kMulticamNode, + kToneGenerator, // Count value kInternalNodeCount diff --git a/app/node/generator/CMakeLists.txt b/app/node/generator/CMakeLists.txt index 424c3a0d4e..304a2c253f 100644 --- a/app/node/generator/CMakeLists.txt +++ b/app/node/generator/CMakeLists.txt @@ -20,6 +20,7 @@ add_subdirectory(polygon) add_subdirectory(shape) add_subdirectory(solid) add_subdirectory(text) +add_subdirectory(tone) set(OLIVE_SOURCES ${OLIVE_SOURCES} diff --git a/app/node/generator/tone/CMakeLists.txt b/app/node/generator/tone/CMakeLists.txt new file mode 100644 index 0000000000..d20fe5032b --- /dev/null +++ b/app/node/generator/tone/CMakeLists.txt @@ -0,0 +1,22 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2022 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + node/generator/tone/tonegenerator.cpp + node/generator/tone/tonegenerator.h + PARENT_SCOPE +) diff --git a/app/node/generator/tone/tonegenerator.cpp b/app/node/generator/tone/tonegenerator.cpp new file mode 100644 index 0000000000..bfd242f2a3 --- /dev/null +++ b/app/node/generator/tone/tonegenerator.cpp @@ -0,0 +1,93 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "tonegenerator.h" + +#include + +#include "widget/slider/floatslider.h" + +namespace olive { + +const QString ToneGenerator::kFrequencyInput = QStringLiteral("freq_in"); + +#define super Node + +ToneGenerator::ToneGenerator() +{ + AddInput(kFrequencyInput, TYPE_DOUBLE, 1000.0, kInputFlagStatic); +} + +QString ToneGenerator::Name() const +{ + return tr("Tone"); +} + +QString ToneGenerator::id() const +{ + return QStringLiteral("org.olivevideoeditor.Olive.tonegenerator"); +} + +QVector ToneGenerator::Category() const +{ + return {kCategoryGenerator}; +} + +QString ToneGenerator::Description() const +{ + return tr("Generates an audio tone."); +} + +void ToneGenerator::Retranslate() +{ + super::Retranslate(); + + SetInputName(kFrequencyInput, tr("Frequency")); +} + +void ProcessSamples(const void *context, const SampleJob &job, SampleBuffer &output) +{ + const double freq = job.Get(ToneGenerator::kFrequencyInput).toDouble(); + + const ValueParams &vp = job.value_params(); + const double sample_interval = vp.aparams().sample_rate_as_time_base().toDouble(); + double time = vp.time().in().toDouble(); + + for (size_t i = 0; i < output.sample_count(); i++) { + static const double PI = 3.14159265358979323846264; + double val = std::sin(time * 2 * PI * freq); + + for (int j = 0; j < output.channel_count(); j++) { + output.data(j)[i] = val; + } + + time += sample_interval; + } +} + +value_t ToneGenerator::Value(const ValueParams &p) const +{ + SampleJob job(p); + job.Insert(kFrequencyInput, GetStandardValue(kFrequencyInput)); + job.set_function(ProcessSamples, this); + return job; +} + +} diff --git a/app/node/generator/tone/tonegenerator.h b/app/node/generator/tone/tonegenerator.h new file mode 100644 index 0000000000..36f32b6e12 --- /dev/null +++ b/app/node/generator/tone/tonegenerator.h @@ -0,0 +1,50 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef TONEGENERATOR_H +#define TONEGENERATOR_H + +#include "node/node.h" + +namespace olive { + +class ToneGenerator : public Node +{ +public: + ToneGenerator(); + + NODE_DEFAULT_FUNCTIONS(ToneGenerator) + + virtual QString Name() const override; + virtual QString id() const override; + virtual QVector Category() const override; + virtual QString Description() const override; + + virtual void Retranslate() override; + + virtual value_t Value(const ValueParams &p) const override; + + static const QString kFrequencyInput; + +}; + +} + +#endif // TONEGENERATOR_H diff --git a/app/widget/timelinewidget/tool/add.cpp b/app/widget/timelinewidget/tool/add.cpp index aa1282744f..0c3eceecf0 100644 --- a/app/widget/timelinewidget/tool/add.cpp +++ b/app/widget/timelinewidget/tool/add.cpp @@ -25,6 +25,7 @@ #include "node/generator/shape/shapenode.h" #include "node/generator/solid/solid.h" #include "node/generator/text/textv3.h" +#include "node/generator/tone/tonegenerator.h" #include "node/nodeundo.h" #include "timeline/timelineundopointer.h" #include "widget/timelinewidget/timelinewidget.h" @@ -158,8 +159,10 @@ Node *AddTool::CreateAddableClip(MultiUndoCommand *command, Sequence *sequence, case Tool::kAddableTitle: node_to_add = new TextGeneratorV3(); break; - case Tool::kAddableBars: case Tool::kAddableTone: + node_to_add = new ToneGenerator(); + break; + case Tool::kAddableBars: // Not implemented yet qWarning() << "Unimplemented add object:" << Core::instance()->GetSelectedAddableObject(); break; From 55f2aeab0df61b5f720941de70b68d6261754f37 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Mon, 29 May 2023 11:06:13 -0700 Subject: [PATCH 50/71] implement some different appearance for channels --- app/widget/nodevaluetree/nodevaluetree.cpp | 1 + .../nodevaluetree/valueswizzlewidget.cpp | 29 ++++++++++++++++--- app/widget/nodevaluetree/valueswizzlewidget.h | 2 ++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/app/widget/nodevaluetree/nodevaluetree.cpp b/app/widget/nodevaluetree/nodevaluetree.cpp index 38a7b2578c..522370c1c7 100644 --- a/app/widget/nodevaluetree/nodevaluetree.cpp +++ b/app/widget/nodevaluetree/nodevaluetree.cpp @@ -74,6 +74,7 @@ void NodeValueTree::SetNode(const NodeInput &input) ValueSwizzleWidget *b = new ValueSwizzleWidget(); //b->set_channels(4, input.GetChannelCount()); b->set_channels(4, 4); + b->set_type(input_.GetDataType()); b->set(vh.swizzle()); connect(b, &ValueSwizzleWidget::value_changed, this, &NodeValueTree::SwizzleChanged); this->setItemWidget(swizzler_items, 1, b); diff --git a/app/widget/nodevaluetree/valueswizzlewidget.cpp b/app/widget/nodevaluetree/valueswizzlewidget.cpp index 181dfc2bb7..4cf6a5bf41 100644 --- a/app/widget/nodevaluetree/valueswizzlewidget.cpp +++ b/app/widget/nodevaluetree/valueswizzlewidget.cpp @@ -44,8 +44,7 @@ ValueSwizzleWidget::ValueSwizzleWidget(QWidget *parent) : setScene(scene_); new_item_ = nullptr; - //labels_ = kRGBALabels; - labels_ = kXYZWLabels; + labels_ = kNumberLabels; channel_height_ = this->fontMetrics().height() * 3 / 2; channel_width_ = channel_height_ * 2; @@ -106,6 +105,19 @@ void ValueSwizzleWidget::set_channels(size_t from, size_t to) set(cached_map_); } +void ValueSwizzleWidget::set_type(type_t t) +{ + if (t == TYPE_TEXTURE) { + labels_ = kRGBALabels; + } else if (t == TYPE_DOUBLE) { + labels_ = kXYZWLabels; + } else { + labels_ = kNumberLabels; + } + + viewport()->update(); +} + void ValueSwizzleWidget::drawBackground(QPainter *p, const QRectF &r) { for (size_t i = 0; i < from_count_; i++) { @@ -225,16 +237,25 @@ void ValueSwizzleWidget::resizeEvent(QResizeEvent *e) void ValueSwizzleWidget::draw_channel(QPainter *p, size_t i, int x) { static const size_t kChannelColorCount = 4; - static const QColor kDefaultColor = QColor(32, 32, 32); - const QColor kChannelColors[kChannelColorCount] = { + static const QColor kDefaultColor = QColor(16, 16, 16); + static const QColor kRGBAChannelColors[kChannelColorCount] = { QColor(160, 32, 32), QColor(32, 160, 32), QColor(32, 32, 160), QColor(64, 64, 64) }; + static const QColor kGrayChannelColors[kChannelColorCount] = { + QColor(104, 104, 104), + QColor(80, 80, 80), + QColor(56, 56, 56), + QColor(32, 32, 32), + }; + QRect r(x, i * channel_height_, channel_width_, channel_height_); + const QColor *kChannelColors = labels_ == kRGBALabels ? kRGBAChannelColors : kGrayChannelColors; + const QColor &main_col = i < kChannelColorCount ? kChannelColors[i] : kDefaultColor; if (OLIVE_CONFIG("UseGradients").toBool()) { QLinearGradient lg(r.topLeft(), r.bottomLeft()); diff --git a/app/widget/nodevaluetree/valueswizzlewidget.h b/app/widget/nodevaluetree/valueswizzlewidget.h index f1bce8ae07..eb9ed46226 100644 --- a/app/widget/nodevaluetree/valueswizzlewidget.h +++ b/app/widget/nodevaluetree/valueswizzlewidget.h @@ -67,6 +67,8 @@ class ValueSwizzleWidget : public QGraphicsView void set_label_type(Labels l) { labels_ = l; } + void set_type(type_t t); + protected: virtual void drawBackground(QPainter *p, const QRectF &r) override; From 0b952e84c9b03f93b348873bb74a8bea21af627d Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Mon, 29 May 2023 11:07:16 -0700 Subject: [PATCH 51/71] autoconvert double to samples --- app/node/node.cpp | 49 ++++++++++++++++++++++++++++++-------- app/node/node.h | 4 ++-- app/render/job/samplejob.h | 2 +- 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/app/node/node.cpp b/app/node/node.cpp index 6ecec75a59..6846eede5a 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -867,12 +867,12 @@ int Node::InputArraySize(const QString &id) const } } -value_t Node::GetInputValue(const ValueParams &g, const QString &input, int element) const +value_t Node::GetInputValue(const ValueParams &g, const QString &input, int element, bool autoconversion) const { if (!g.is_cancelled()) { NodeOutput output = GetConnectedOutput2(input, element); if (output.IsValid()) { - return GetFakeConnectedValue(g, output, input, element); + return GetFakeConnectedValue(g, output, input, element, autoconversion); } else { TimeRange adjusted_time = InputTimeAdjustment(input, element, g.time(), true); @@ -893,7 +893,24 @@ value_t Node::GetInputValue(const ValueParams &g, const QString &input, int elem return value_t(); } -value_t Node::GetFakeConnectedValue(const ValueParams &g, NodeOutput output, const QString &input, int element) const +void ConvertDoubleToSamples(const void *context, const SampleJob &input, SampleBuffer &output) +{ + const Node *n = static_cast(context); + + const QString param = input.Get(QStringLiteral("input")).toString(); + const int element = input.Get(QStringLiteral("element")).toInt(); + const TimeRange &time = input.value_params().time(); + + for (size_t i = 0; i < output.sample_count(); i++) { + TimeRange this_sample_time = time + output.audio_params().sample_rate_as_time_base() * i; + value_t v = n->GetInputValue(input.value_params().time_transformed(this_sample_time), param, element, false); + for (int j = 0; j < output.channel_count(); j++) { + output.data(j)[i] = v.toDouble(); + } + } +} + +value_t Node::GetFakeConnectedValue(const ValueParams &g, NodeOutput output, const QString &input, int element, bool autoconversion) const { if (!g.is_cancelled()) { TimeRange adjusted_time = InputTimeAdjustment(input, element, g.time(), true); @@ -931,13 +948,25 @@ value_t Node::GetFakeConnectedValue(const ValueParams &g, NodeOutput output, con // Perform conversion if necessary type_t expected_type = this->GetInputDataType(input); - if (expected_type != TYPE_NONE && expected_type != v.type() && !(this->GetInputFlags(input) & kInputFlagDontAutoConvert)) { - bool ok; - v = v.converted(expected_type, &ok); - if (!ok) { - // Return null value instead of converted value because node is probably not set up to - // handle this type (unless kInputFlagDontAutoConvert is specified of course) - v = value_t(); + if (autoconversion && expected_type != TYPE_NONE && expected_type != v.type() && !(this->GetInputFlags(input) & kInputFlagDontAutoConvert)) { + if (v.type() == TYPE_DOUBLE && expected_type == TYPE_SAMPLES) { + // Create a job to generate audio from numbers + SampleJob job(g); + + job.Insert(QStringLiteral("input"), input); + job.Insert(QStringLiteral("element"), element); + + job.set_function(ConvertDoubleToSamples, this); + + v = job; + } else { + bool ok; + v = v.converted(expected_type, &ok); + if (!ok) { + // Return null value instead of converted value because node is probably not set up to + // handle this type (unless kInputFlagDontAutoConvert is specified of course) + v = value_t(); + } } } diff --git a/app/node/node.h b/app/node/node.h index a2a07194ee..984f76af08 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -557,8 +557,8 @@ class Node : public QObject int InputArraySize(const QString& id) const; - value_t GetInputValue(const ValueParams &g, const QString &input, int element = -1) const; - value_t GetFakeConnectedValue(const ValueParams &g, NodeOutput output, const QString &input, int element = -1) const; + value_t GetInputValue(const ValueParams &g, const QString &input, int element = -1, bool autoconversion = true) const; + value_t GetFakeConnectedValue(const ValueParams &g, NodeOutput output, const QString &input, int element = -1, bool autoconversion = true) const; NodeInputImmediate* GetImmediate(const QString& input, int element) const; diff --git a/app/render/job/samplejob.h b/app/render/job/samplejob.h index b39a155bc4..92a6d83811 100644 --- a/app/render/job/samplejob.h +++ b/app/render/job/samplejob.h @@ -40,7 +40,7 @@ class SampleJob : public AcceleratedJob SampleJob(const ValueParams &p, size_t sample_count) { - value_params_ = p; + value_params_ = p.with_cache(nullptr); // Remove cache because it actually slows per-sample ops sample_count_ = sample_count; function_ = nullptr; function_context_ = nullptr; From 2921eb63d6d768a277d2f997cbfb03c1f1da9473 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Mon, 29 May 2023 11:32:00 -0700 Subject: [PATCH 52/71] fixed viewer on wayland --- app/render/job/colortransformjob.h | 6 ++++++ app/render/renderer.cpp | 1 + app/render/renderer.h | 2 +- app/shaders/colormanage.frag | 5 +++++ app/widget/manageddisplay/manageddisplay.cpp | 1 + app/widget/viewer/viewerdisplay.cpp | 1 + 6 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/render/job/colortransformjob.h b/app/render/job/colortransformjob.h index 003a7fdb18..3519692729 100644 --- a/app/render/job/colortransformjob.h +++ b/app/render/job/colortransformjob.h @@ -43,6 +43,7 @@ class ColorTransformJob : public AcceleratedJob custom_shader_ = nullptr; input_alpha_association_ = kAlphaNone; clear_destination_ = true; + force_opaque_ = false; } ColorTransformJob(const NodeValueRow &row) : @@ -91,6 +92,9 @@ class ColorTransformJob : public AcceleratedJob const QString &GetFunctionName() const { return function_name_; } void SetFunctionName(const QString &function_name = QString()) { function_name_ = function_name; }; + bool GetForceOpaque() const { return force_opaque_; } + void SetForceOpaque(bool e) { force_opaque_ = e; } + private: ColorProcessorPtr processor_; QString id_; @@ -109,6 +113,8 @@ class ColorTransformJob : public AcceleratedJob QString function_name_; + bool force_opaque_; + }; } diff --git a/app/render/renderer.cpp b/app/render/renderer.cpp index 0ca0cadc12..18701262bf 100644 --- a/app/render/renderer.cpp +++ b/app/render/renderer.cpp @@ -296,6 +296,7 @@ void Renderer::BlitColorManaged(const ColorTransformJob &color_job, Texture *des job.Insert(QStringLiteral("ove_mvpmat"), color_job.GetTransformMatrix()); job.Insert(QStringLiteral("ove_cropmatrix"), color_job.GetCropMatrix().inverted()); job.Insert(QStringLiteral("ove_maintex_alpha"), color_job.GetInputAlphaAssociation()); + job.Insert(QStringLiteral("ove_force_opaque"), color_job.GetForceOpaque()); job.Insert(color_job.GetValues()); foreach (const ColorContext::LUT& l, color_ctx.lut3d_textures) { diff --git a/app/render/renderer.h b/app/render/renderer.h index 24bbfa3f14..08f1665467 100644 --- a/app/render/renderer.h +++ b/app/render/renderer.h @@ -84,7 +84,7 @@ class Renderer : public QObject virtual void PostInit() = 0; - virtual void ClearDestination(olive::Texture *texture = nullptr, double r = 0.0, double g = 0.0, double b = 0.0, double a = 0.0) = 0; + virtual void ClearDestination(olive::Texture *texture = nullptr, double r = 0.0, double g = 0.0, double b = 0.0, double a = 1.0) = 0; virtual QVariant CreateNativeShader(olive::ShaderCode code) = 0; diff --git a/app/shaders/colormanage.frag b/app/shaders/colormanage.frag index 25efe5b50d..074af83fbe 100644 --- a/app/shaders/colormanage.frag +++ b/app/shaders/colormanage.frag @@ -2,6 +2,7 @@ uniform sampler2D ove_maintex; uniform int ove_maintex_alpha; uniform mat4 ove_cropmatrix; +uniform bool ove_force_opaque; // Macros defining `ove_maintex_alpha` state // Matches `AlphaAssociated` C++ enum @@ -53,5 +54,9 @@ void main() { col = assoc(col); } + if (ove_force_opaque) { + col.a = 1.0; + } + frag_color = col; } diff --git a/app/widget/manageddisplay/manageddisplay.cpp b/app/widget/manageddisplay/manageddisplay.cpp index d90a9d5fbd..abe053db01 100644 --- a/app/widget/manageddisplay/manageddisplay.cpp +++ b/app/widget/manageddisplay/manageddisplay.cpp @@ -43,6 +43,7 @@ ManagedDisplayWidget::ManagedDisplayWidget(QWidget *parent) : if (RenderManager::instance()->backend() == RenderManager::kOpenGL) { // Create OpenGL widget inner_widget_ = new ManagedDisplayWidgetOpenGL(); + inner_widget_->setAttribute(Qt::WA_TranslucentBackground, false); connect(static_cast(inner_widget_), &ManagedDisplayWidgetOpenGL::OnInit, this, &ManagedDisplayWidget::OnInit, Qt::DirectConnection); diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index 8e2d8f728b..083bbfb9d5 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -437,6 +437,7 @@ void ViewerDisplayWidget::OnPaint() ctj.SetClearDestinationEnabled(false); ctj.SetTransformMatrix(combined_matrix_flipped_); ctj.SetCropMatrix(crop_matrix_); + ctj.SetForceOpaque(true); renderer()->BlitColorManaged(ctj, device_params); } From f062b068a19497b76b21b1e8d06f6399ac80fd6d Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Mon, 29 May 2023 12:14:48 -0700 Subject: [PATCH 53/71] fix slider ladder on wayland --- app/widget/slider/base/numericsliderbase.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/widget/slider/base/numericsliderbase.cpp b/app/widget/slider/base/numericsliderbase.cpp index 88fb6142dd..f29b920d12 100644 --- a/app/widget/slider/base/numericsliderbase.cpp +++ b/app/widget/slider/base/numericsliderbase.cpp @@ -52,17 +52,16 @@ void NumericSliderBase::SetDragMultiplier(const double &d) void NumericSliderBase::LabelPressed() { - // Generate width hint drag_ladder_ = new SliderLadder(drag_multiplier_, ladder_element_count_, GetFormattedValueToString(99999999)); connect(drag_ladder_, &SliderLadder::DraggedByValue, this, &NumericSliderBase::LadderDragged); connect(drag_ladder_, &SliderLadder::Released, this, &NumericSliderBase::LadderReleased); drag_ladder_->SetValue(GetFormattedValueToString()); + drag_ladder_->resize(drag_ladder_->sizeHint()); + RepositionLadder(); drag_ladder_->show(); drag_start_value_ = GetValueInternal(); - - QMetaObject::invokeMethod(this, "RepositionLadder", Qt::QueuedConnection); } void NumericSliderBase::LadderDragged(int value, double multiplier) From 59f2284a414c6a8eefab1178dbdcad68878bf24d Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Mon, 29 May 2023 12:25:30 -0700 Subject: [PATCH 54/71] fix actionsearch --- app/dialog/actionsearch/actionsearch.cpp | 38 ++++++++++++++++-------- app/dialog/actionsearch/actionsearch.h | 3 +- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/app/dialog/actionsearch/actionsearch.cpp b/app/dialog/actionsearch/actionsearch.cpp index ac632f4151..e2732460bb 100644 --- a/app/dialog/actionsearch/actionsearch.cpp +++ b/app/dialog/actionsearch/actionsearch.cpp @@ -20,10 +20,10 @@ #include "actionsearch.h" -#include #include -#include #include +#include +#include namespace olive { @@ -228,21 +228,35 @@ void ActionSearch::move_selection_down() { ActionSearchEntry::ActionSearchEntry(QWidget *parent) : QLineEdit(parent) {} -void ActionSearchEntry::keyPressEvent(QKeyEvent * event) { - - // Listen for up/down, otherwise pass the key event to the base class. - - switch (event->key()) { - case Qt::Key_Up: - emit moveSelectionUp(); +bool ActionSearchEntry::event(QEvent *e) +{ + switch (e->type()) { + case QEvent::ShortcutOverride: + switch (static_cast(e)->key()) { + case Qt::Key_Up: + case Qt::Key_Down: + e->accept(); + return true; + } break; - case Qt::Key_Down: - emit moveSelectionDown(); + case QEvent::KeyPress: + // Listen for up/down, otherwise pass the key event to the base class. + switch (static_cast(e)->key()) { + case Qt::Key_Up: + e->accept(); + emit moveSelectionUp(); + return true; + case Qt::Key_Down: + e->accept(); + emit moveSelectionDown(); + return true; + } break; default: - QLineEdit::keyPressEvent(event); + break; } + return QLineEdit::event(e); } ActionSearchList::ActionSearchList(QWidget *parent) : QListWidget(parent) {} diff --git a/app/dialog/actionsearch/actionsearch.h b/app/dialog/actionsearch/actionsearch.h index ecb4469551..96829e5799 100644 --- a/app/dialog/actionsearch/actionsearch.h +++ b/app/dialog/actionsearch/actionsearch.h @@ -169,7 +169,8 @@ class ActionSearchEntry : public QLineEdit { * @brief Override of QLineEdit's key press event that listens for up/down key presses. * @param event */ - void keyPressEvent(QKeyEvent * event); + virtual bool event(QEvent *e) override; + signals: /** * @brief Emitted when the user presses the up arrow key. From f6bcebd8792de4f157703df6056ac2ee6afe81aa Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 31 May 2023 16:59:06 -0700 Subject: [PATCH 55/71] auto-add keyframe track where necessary --- app/node/node.cpp | 7 +++++- app/node/node.h | 2 ++ app/widget/nodeparamview/nodeparamview.cpp | 24 +++++++++++++++++++ app/widget/nodeparamview/nodeparamview.h | 2 ++ .../nodeparamview/nodeparamviewitem.cpp | 1 + app/widget/nodeparamview/nodeparamviewitem.h | 2 ++ 6 files changed, 37 insertions(+), 1 deletion(-) diff --git a/app/node/node.cpp b/app/node/node.cpp index 6846eede5a..6fdd662036 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -2444,7 +2444,12 @@ void Node::childEvent(QChildEvent *event) NodeInput i(this, key->input(), key->element()); if (event->type() == QEvent::ChildAdded) { - GetImmediate(key->input(), key->element())->insert_keyframe(key); + NodeInputImmediate *imm = GetImmediate(key->input(), key->element()); + int old_sz = imm->keyframe_tracks().size(); + imm->insert_keyframe(key); + for (int j = old_sz; j < imm->keyframe_tracks().size(); j++) { + emit KeyframeTrackAdded(key->input(), key->element(), j); + } connect(key, &NodeKeyframe::TimeChanged, this, &Node::InvalidateFromKeyframeTimeChange); connect(key, &NodeKeyframe::ValueChanged, this, &Node::InvalidateFromKeyframeValueChange); diff --git a/app/node/node.h b/app/node/node.h index 984f76af08..6f3e50019a 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -1030,6 +1030,8 @@ protected slots: void KeyframeEnableChanged(const NodeInput& input, bool enabled); + void KeyframeTrackAdded(const QString &input, int element, int track); + void InputAdded(const QString& id); void InputRemoved(const QString& id); diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index 06fc28c104..8d703ded24 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -728,6 +728,7 @@ void NodeParamView::AddNode(Node *n, Node *ctx, NodeParamViewContext *context) connect(item, &NodeParamViewItem::ExpandedChanged, this, &NodeParamView::QueueKeyframePositionUpdate); connect(item, &NodeParamViewItem::Moved, this, &NodeParamView::QueueKeyframePositionUpdate); connect(item, &NodeParamViewItem::InputArraySizeChanged, this, &NodeParamView::InputArraySizeChanged); + connect(item, &NodeParamViewItem::ElementKeyframeTrackAdded, this, &NodeParamView::ElementKeyframeTrackAdded); item->SetKeyframeConnections(keyframe_view_->AddKeyframesOfNode(n)); } @@ -1032,4 +1033,27 @@ void NodeParamView::InputArraySizeChanged(const QString &input, int, int new_siz QueueKeyframePositionUpdate(); } +void NodeParamView::ElementKeyframeTrackAdded(const QString &input, int element, int track) +{ + NodeParamViewItem *sender = static_cast(this->sender()); + + KeyframeView::NodeConnections &connections = sender->GetKeyframeConnections(); + KeyframeView::InputConnections &inputs = connections[input]; + KeyframeView::ElementConnections &elements = inputs[element + 1]; + + auto conn = keyframe_view_->AddKeyframesOfTrack(NodeKeyframeTrackReference(NodeInput(sender->GetNode(), input, element), track)); + + if (track >= elements.size()) { + int old = elements.size(); + elements.resize(track + 1); + for (int i = old; i < elements.size(); i++) { + elements[i] = nullptr; + } + } + + elements[track] = conn; + + QueueKeyframePositionUpdate(); +} + } diff --git a/app/widget/nodeparamview/nodeparamview.h b/app/widget/nodeparamview/nodeparamview.h index b610ff092d..6a0e37d2f2 100644 --- a/app/widget/nodeparamview/nodeparamview.h +++ b/app/widget/nodeparamview/nodeparamview.h @@ -191,6 +191,8 @@ private slots: void InputArraySizeChanged(const QString &input, int old_size, int new_size); + void ElementKeyframeTrackAdded(const QString &input, int element, int track); + }; } diff --git a/app/widget/nodeparamview/nodeparamviewitem.cpp b/app/widget/nodeparamview/nodeparamviewitem.cpp index dd45445554..198e47647d 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.cpp +++ b/app/widget/nodeparamview/nodeparamviewitem.cpp @@ -60,6 +60,7 @@ NodeParamViewItem::NodeParamViewItem(Node *node, NodeParamViewCheckBoxBehavior c connect(node_, &Node::LabelChanged, this, &NodeParamViewItem::Retranslate); connect(node_, &Node::InputArraySizeChanged, this, &NodeParamViewItem::InputArraySizeChanged); + connect(node_, &Node::KeyframeTrackAdded, this, &NodeParamViewItem::ElementKeyframeTrackAdded); // FIXME: Implemented to pick up when an input is set to hidden or not - DEFINITELY not a fast // way of doing this, but "fine" for now. diff --git a/app/widget/nodeparamview/nodeparamviewitem.h b/app/widget/nodeparamview/nodeparamviewitem.h index d4eda780bb..f761372cdc 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.h +++ b/app/widget/nodeparamview/nodeparamviewitem.h @@ -228,6 +228,8 @@ class NodeParamViewItem : public NodeParamViewItemBase void InputArraySizeChanged(const QString &input, int old_size, int new_size); + void ElementKeyframeTrackAdded(const QString &input, int element, int track); + protected slots: virtual void Retranslate() override; From abd37779c89b5614e07c46a140c5f6ca5ab16245 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 11 Jun 2023 19:39:01 -0700 Subject: [PATCH 56/71] node: ensure widgets are correctly recreated on subtype change --- app/node/input/value/valuenode.cpp | 39 +++++++------------ app/node/input/value/valuenode.h | 1 + .../nodeparamviewwidgetbridge.cpp | 26 ++++++++----- .../nodeparamview/nodeparamviewwidgetbridge.h | 2 + 4 files changed, 35 insertions(+), 33 deletions(-) diff --git a/app/node/input/value/valuenode.cpp b/app/node/input/value/valuenode.cpp index cd4c4fcdd0..8b4d35514f 100644 --- a/app/node/input/value/valuenode.cpp +++ b/app/node/input/value/valuenode.cpp @@ -41,17 +41,17 @@ constexpr type_t OUR_BOOL = "bool"; const QVector ValueNode::kSupportedTypes = { - {OUR_DOUBLE, TYPE_DOUBLE, 1}, - {OUR_INTEGER, TYPE_INTEGER, 1}, - {OUR_RATIONAL, TYPE_RATIONAL, 1}, - {OUR_VEC2, TYPE_DOUBLE, 2}, - {OUR_VEC3, TYPE_DOUBLE, 3}, - {OUR_VEC4, TYPE_DOUBLE, 4}, - {OUR_COLOR, TYPE_DOUBLE, 4}, - {OUR_STRING, TYPE_STRING, 1}, - {OUR_MATRIX, TYPE_MATRIX, 1}, - {OUR_FONT, TYPE_STRING, 1}, - {OUR_BOOL, TYPE_DOUBLE, 1}, + {OUR_DOUBLE, TYPE_DOUBLE, QString(), 1}, + {OUR_INTEGER, TYPE_INTEGER, QString(), 1}, + {OUR_RATIONAL, TYPE_RATIONAL, QString(), 1}, + {OUR_VEC2, TYPE_DOUBLE, QString(), 2}, + {OUR_VEC3, TYPE_DOUBLE, QString(), 3}, + {OUR_VEC4, TYPE_DOUBLE, QString(), 4}, + {OUR_COLOR, TYPE_DOUBLE, QStringLiteral("color"), 4}, + {OUR_STRING, TYPE_STRING, QString(), 1}, + {OUR_MATRIX, TYPE_MATRIX, QString(), 1}, + {OUR_FONT, TYPE_STRING, QStringLiteral("font"), 1}, + {OUR_BOOL, TYPE_INTEGER, QStringLiteral("bool"), 1}, }; ValueNode::ValueNode() @@ -74,18 +74,6 @@ void ValueNode::Retranslate() type_names.append(GetPrettyTypeName(type.our_type)); } - type_names.append(tr("Double")); - type_names.append(tr("Integer")); - type_names.append(tr("Rational")); - type_names.append(tr("Vector 2D")); - type_names.append(tr("Vector 3D")); - type_names.append(tr("Vector 4D")); - type_names.append(tr("Color")); - type_names.append(tr("Text")); - type_names.append(tr("Matrix")); - type_names.append(tr("Font")); - type_names.append(tr("Boolean")); - SetComboBoxStrings(kTypeInput, type_names); } @@ -102,7 +90,10 @@ void ValueNode::InputValueChangedEvent(const QString &input, int element) if (input == kTypeInput) { int64_t k = GetStandardValue(kTypeInput).toInt(); - SetInputDataType(kValueInput, kSupportedTypes.at(k).base_type, kSupportedTypes.at(k).channel_count); + const Type &t = kSupportedTypes.at(k); + + SetInputProperty(kValueInput, QStringLiteral("subtype"), t.subtype); + SetInputDataType(kValueInput, t.base_type, t.channel_count); } super::InputValueChangedEvent(input, element); diff --git a/app/node/input/value/valuenode.h b/app/node/input/value/valuenode.h index 9bb2a6f680..57d4f71648 100644 --- a/app/node/input/value/valuenode.h +++ b/app/node/input/value/valuenode.h @@ -70,6 +70,7 @@ class ValueNode : public Node { type_t our_type; type_t base_type; + QString subtype; size_t channel_count; }; diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index fddce4e9c6..dce34b1552 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -248,7 +248,9 @@ void NodeParamViewWidgetBridge::InputValueChanged(const NodeInput &input, const void NodeParamViewWidgetBridge::SetProperty(const QString &key, const value_t &value) { - if (widget_) { + if (key == QStringLiteral("subtype")) { + //RecreateWidgets(); + } else if (widget_) { widget_->SetProperty(key, value); } } @@ -256,14 +258,7 @@ void NodeParamViewWidgetBridge::SetProperty(const QString &key, const value_t &v void NodeParamViewWidgetBridge::InputDataTypeChanged(const QString &input, type_t type) { if (sender() == GetOuterInput().node() && input == GetOuterInput().input()) { - // Delete all widgets - delete widget_; - - // Create new widgets - CreateWidgets(); - - // Signal that widgets are new - emit WidgetsRecreated(GetOuterInput()); + RecreateWidgets(); } } @@ -294,6 +289,19 @@ void NodeParamViewWidgetBridge::UpdateProperties() } } +void NodeParamViewWidgetBridge::RecreateWidgets() +{ + // Delete all widgets + delete widget_; + widget_ = nullptr; + + // Create new widgets + CreateWidgets(); + + // Signal that widgets are new + emit WidgetsRecreated(GetOuterInput()); +} + bool NodeParamViewScrollBlocker::eventFilter(QObject *watched, QEvent *event) { Q_UNUSED(watched) diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h index 1ad48a4796..a0cb2dd318 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h @@ -100,6 +100,8 @@ class NodeParamViewWidgetBridge : public QObject, public TimeTargetObject void UpdateProperties(); + void RecreateWidgets(); + QVector input_hierarchy_; AbstractParamWidget *widget_; From f2c1327694b735cc19d4e94b24ea1b9fd439e48b Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 11 Jun 2023 19:39:17 -0700 Subject: [PATCH 57/71] math: added floor/ceil/round/abs ops --- app/node/math/math/math.cpp | 97 +++++++++++++++++++++------- app/node/math/math/math.h | 7 +- app/node/math/math/mathfunctions.cpp | 36 +++++++++++ app/node/math/math/mathfunctions.h | 5 ++ 4 files changed, 120 insertions(+), 25 deletions(-) diff --git a/app/node/math/math/math.cpp b/app/node/math/math/math.cpp index 4fa689e9b8..f0364a7de2 100644 --- a/app/node/math/math/math.cpp +++ b/app/node/math/math/math.cpp @@ -122,6 +122,11 @@ void MathNode::Retranslate() GetOperationName(kOpMin), GetOperationName(kOpMax), GetOperationName(kOpClamp), + QString(), + GetOperationName(kOpFloor), + GetOperationName(kOpCeil), + GetOperationName(kOpRound), + GetOperationName(kOpAbs), }; SetComboBoxStrings(kMethodIn, operations); @@ -254,6 +259,10 @@ void MathNode::OperateSampleNumber(Operation operation, const float *input, floa case kOpHypCosine: case kOpHypTangent: case kOpClamp: + case kOpFloor: + case kOpCeil: + case kOpRound: + case kOpAbs: break; } } @@ -348,6 +357,10 @@ void MathNode::OperateSampleSample(Operation operation, const float *input, floa case kOpHypCosine: case kOpHypTangent: case kOpClamp: + case kOpFloor: + case kOpCeil: + case kOpRound: + case kOpAbs: break; } } @@ -355,23 +368,30 @@ void MathNode::OperateSampleSample(Operation operation, const float *input, floa int MathNode::GetNumberOfOperands(Operation o) { switch (o) { - case kOpAdd: return 2; - case kOpSubtract: return 2; - case kOpMultiply: return 2; - case kOpDivide: return 2; - case kOpPower: return 2; - case kOpSine: return 1; - case kOpCosine: return 1; - case kOpTangent: return 1; - case kOpArcSine: return 1; - case kOpArcCosine: return 1; - case kOpArcTangent: return 1; - case kOpHypSine: return 1; - case kOpHypCosine: return 1; - case kOpHypTangent: return 1; - case kOpMin: return 2; - case kOpMax: return 2; - case kOpClamp: return 3; + case kOpSine: + case kOpCosine: + case kOpTangent: + case kOpArcSine: + case kOpArcCosine: + case kOpArcTangent: + case kOpHypSine: + case kOpHypCosine: + case kOpHypTangent: + case kOpFloor: + case kOpCeil: + case kOpRound: + case kOpAbs: + return 1; + case kOpAdd: + case kOpSubtract: + case kOpMultiply: + case kOpDivide: + case kOpPower: + case kOpMin: + case kOpMax: + return 2; + case kOpClamp: + return 3; } return 0; @@ -465,6 +485,7 @@ ShaderCode MathNode::GetShaderCode(const QString &id) Operation op = static_cast(j.at(0).toInt()); type_t atype = type_t::fromString(j.at(1)); type_t btype = type_t::fromString(j.at(2)); + type_t ctype = type_t::fromString(j.at(3)); QString line; @@ -502,34 +523,48 @@ ShaderCode MathNode::GetShaderCode(const QString &id) case kOpHypSine: line = QStringLiteral("sinh(%1)"); break; case kOpHypCosine: line = QStringLiteral("cosh(%1)"); break; case kOpHypTangent: line = QStringLiteral("tanh(%1)"); break; - case kOpClamp: - qWarning() << "texture clamping is currently a stub!"; + case kOpFloor: line = QStringLiteral("floor(%1)"); break; + case kOpCeil: line = QStringLiteral("ceil(%1)"); break; + case kOpRound: line = QStringLiteral("round(%1)"); break; + case kOpAbs: line = QStringLiteral("abs(%1)"); break; + case kOpClamp: line = QStringLiteral("clamp(%1, %2, %3)"); break; break; } line = line.arg(GetShaderVariableCall(kParamAIn, atype), - GetShaderVariableCall(kParamBIn, btype)); + GetShaderVariableCall(kParamBIn, btype), + GetShaderVariableCall(kParamCIn, ctype)); static const QString stub = - QStringLiteral("uniform %1 %3;\n" - "uniform %2 %4;\n" + QStringLiteral("uniform %1 %4;\n" + "uniform %2 %5;\n" + "uniform %3 %6;\n" "\n" "in vec2 ove_texcoord;\n" "out vec4 frag_color;\n" "\n" "void main(void) {\n" - " vec4 c = %5;\n" + " vec4 c = %7;\n" " c.a = clamp(c.a, 0.0, 1.0);\n" // Ensure alpha is between 0.0 and 1.0 " frag_color = c;\n" "}\n"); return stub.arg(GetShaderUniformType(atype), GetShaderUniformType(btype), + GetShaderUniformType(ctype), kParamAIn, kParamBIn, + kParamCIn, line); } +void NormalizeNumber(value_t &in) +{ + if (in.type() == TYPE_RATIONAL || in.type() == TYPE_INTEGER) { + in = in.converted(TYPE_DOUBLE); + } +} + value_t MathNode::Value(const ValueParams &p) const { // Auto-detect what values to operate with @@ -545,6 +580,11 @@ value_t MathNode::Value(const ValueParams &p) const if (count < 2) { bval = value_t(); } if (count < 3) { cval = value_t(); } + // Treat integers and rationals as doubles + NormalizeNumber(aval); + NormalizeNumber(bval); + NormalizeNumber(cval); + if (aval.type() == TYPE_SAMPLES || bval.type() == TYPE_SAMPLES) { if (aval.type() == TYPE_SAMPLES && bval.type() == TYPE_SAMPLES) { SampleJob job(p); @@ -570,7 +610,7 @@ value_t MathNode::Value(const ValueParams &p) const } else if (aval.type() == TYPE_TEXTURE || bval.type() == TYPE_TEXTURE) { // Produce shader job ShaderJob job = CreateShaderJob(p, GetShaderCode); - job.SetShaderID(QStringLiteral("%1:%2:%3").arg(QString::number(operation), aval.type().toString(), bval.type().toString())); + job.SetShaderID(QStringLiteral("%1:%2:%3:%4").arg(QString::number(operation), aval.type().toString(), bval.type().toString(), cval.type().toString())); return Texture::Job(aval.type() == TYPE_TEXTURE ? aval.toTexture()->params() : bval.toTexture()->params(), job); } else { // Operation can be done entirely here @@ -603,6 +643,10 @@ QString MathNode::GetOperationName(Operation o) case kOpMin: return tr("Minimum"); case kOpMax: return tr("Maximum"); case kOpClamp: return tr("Clamp"); + case kOpFloor: return tr("Floor"); + case kOpCeil: return tr("Ceil"); + case kOpRound: return tr("Round"); + case kOpAbs: return tr("Absolute"); } return QString(); @@ -632,6 +676,11 @@ void MathNode::PopulateOperations() operations_[kOpMax][TYPE_DOUBLE][TYPE_DOUBLE][TYPE_NONE] = Math::MaxDoubleDouble; operations_[kOpClamp][TYPE_DOUBLE][TYPE_DOUBLE][TYPE_DOUBLE] = Math::ClampDoubleDoubleDouble; + operations_[kOpFloor][TYPE_DOUBLE][TYPE_NONE][TYPE_NONE] = Math::FloorDouble; + operations_[kOpCeil][TYPE_DOUBLE][TYPE_NONE][TYPE_NONE] = Math::CeilDouble; + operations_[kOpRound][TYPE_DOUBLE][TYPE_NONE][TYPE_NONE] = Math::RoundDouble; + operations_[kOpAbs][TYPE_DOUBLE][TYPE_NONE][TYPE_NONE] = Math::AbsDouble; + operations_[kOpAdd][TYPE_MATRIX][TYPE_MATRIX][TYPE_NONE] = Math::AddMatrixMatrix; operations_[kOpSubtract][TYPE_MATRIX][TYPE_MATRIX][TYPE_NONE] = Math::SubtractMatrixMatrix; operations_[kOpMultiply][TYPE_MATRIX][TYPE_MATRIX][TYPE_NONE] = Math::MultiplyMatrixMatrix; diff --git a/app/node/math/math/math.h b/app/node/math/math/math.h index 33456f5441..7f4f4b1c17 100644 --- a/app/node/math/math/math.h +++ b/app/node/math/math/math.h @@ -53,7 +53,12 @@ class MathNode : public Node kOpMin, kOpMax, - kOpClamp + kOpClamp, + + kOpFloor, + kOpCeil, + kOpRound, + kOpAbs }; NODE_DEFAULT_FUNCTIONS(MathNode) diff --git a/app/node/math/math/mathfunctions.cpp b/app/node/math/math/mathfunctions.cpp index ef1f08134e..149f0cc54b 100644 --- a/app/node/math/math/mathfunctions.cpp +++ b/app/node/math/math/mathfunctions.cpp @@ -175,6 +175,42 @@ value_t Math::ClampDoubleDoubleDouble(const value_t &a, const value_t &b, const return v; } +value_t Math::FloorDouble(const value_t &a, const value_t &b, const value_t &c) +{ + value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::floor(a.value(i)); + } + return v; +} + +value_t Math::CeilDouble(const value_t &a, const value_t &b, const value_t &c) +{ + value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::ceil(a.value(i)); + } + return v; +} + +value_t Math::RoundDouble(const value_t &a, const value_t &b, const value_t &c) +{ + value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::round(a.value(i)); + } + return v; +} + +value_t Math::AbsDouble(const value_t &a, const value_t &b, const value_t &c) +{ + value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::abs(a.value(i)); + } + return v; +} + value_t Math::AddMatrixMatrix(const value_t &a, const value_t &b, const value_t &c) { value_t v(TYPE_MATRIX, std::max(a.size(), b.size())); diff --git a/app/node/math/math/mathfunctions.h b/app/node/math/math/mathfunctions.h index 969a78bde4..99d619cb0f 100644 --- a/app/node/math/math/mathfunctions.h +++ b/app/node/math/math/mathfunctions.h @@ -50,6 +50,11 @@ class Math static value_t MaxDoubleDouble(const value_t &a, const value_t &b, const value_t &c); static value_t ClampDoubleDoubleDouble(const value_t &a, const value_t &b, const value_t &c); + static value_t FloorDouble(const value_t &a, const value_t &b, const value_t &c); + static value_t CeilDouble(const value_t &a, const value_t &b, const value_t &c); + static value_t RoundDouble(const value_t &a, const value_t &b, const value_t &c); + static value_t AbsDouble(const value_t &a, const value_t &b, const value_t &c); + static value_t AddMatrixMatrix(const value_t &a, const value_t &b, const value_t &c); static value_t SubtractMatrixMatrix(const value_t &a, const value_t &b, const value_t &c); static value_t MultiplyMatrixMatrix(const value_t &a, const value_t &b, const value_t &c); From d870da8168b04d9c21a1c8c8b616d9031a30690d Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 11 Jun 2023 23:03:34 -0700 Subject: [PATCH 58/71] update core library --- ext/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/core b/ext/core index 60b9d4590f..08eeee5fa9 160000 --- a/ext/core +++ b/ext/core @@ -1 +1 @@ -Subproject commit 60b9d4590f385e1f1f3feaad8f6f4ddb4746c4d3 +Subproject commit 08eeee5fa90bed55442f2be9e324482f0be0a73c From a6a1e7408b3c432c1a40c263258b54478161b14d Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 11 Jun 2023 23:23:24 -0700 Subject: [PATCH 59/71] fixed CI compiler issues --- app/node/value.h | 2 +- tests/timeline/timeline-tests.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/node/value.h b/app/node/value.h index 54597d17dd..e52bcde825 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -104,7 +104,7 @@ class value_t try { *out = std::any_cast(data_); return true; - } catch (std::bad_any_cast &e) {} + } catch (std::bad_any_cast &) {} } return false; diff --git a/tests/timeline/timeline-tests.cpp b/tests/timeline/timeline-tests.cpp index 6cb72c387a..8c278ce878 100644 --- a/tests/timeline/timeline-tests.cpp +++ b/tests/timeline/timeline-tests.cpp @@ -459,10 +459,10 @@ OLIVE_ADD_TEST(ReplaceBlockWithGap_ClipsAndTransitions) b_out->setParent(&project); track->AppendBlock(b_out); - Node::ConnectEdge(a, NodeInput(a_in, UsingTransition::kInBlockInput)); - Node::ConnectEdge(a, NodeInput(a_to_b, UsingTransition::kOutBlockInput)); - Node::ConnectEdge(b, NodeInput(a_to_b, UsingTransition::kInBlockInput)); - Node::ConnectEdge(b, NodeInput(b_out, UsingTransition::kOutBlockInput)); + Node::ConnectEdge(NodeOutput(a), NodeInput(a_in, UsingTransition::kInBlockInput)); + Node::ConnectEdge(NodeOutput(a), NodeInput(a_to_b, UsingTransition::kOutBlockInput)); + Node::ConnectEdge(NodeOutput(b), NodeInput(a_to_b, UsingTransition::kInBlockInput)); + Node::ConnectEdge(NodeOutput(b), NodeInput(b_out, UsingTransition::kOutBlockInput)); { // Replace A with gap From 7440ffcca59e6e1a16599868163c861757187dfa Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 11 Jun 2023 23:40:26 -0700 Subject: [PATCH 60/71] fix value ambiguity --- app/node/value.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/node/value.cpp b/app/node/value.cpp index bfe23d093f..dd64126746 100644 --- a/app/node/value.cpp +++ b/app/node/value.cpp @@ -130,10 +130,10 @@ TexturePtr value_t::toTexture() const job.Insert(QStringLiteral("green_texture"), this->value(1).texture); job.Insert(QStringLiteral("blue_texture"), this->value(2).texture); job.Insert(QStringLiteral("alpha_texture"), this->value(3).texture); - job.Insert(QStringLiteral("red_channel"), this->value(0).channel); - job.Insert(QStringLiteral("green_channel"), this->value(1).channel); - job.Insert(QStringLiteral("blue_channel"), this->value(2).channel); - job.Insert(QStringLiteral("alpha_channel"), this->value(3).channel); + job.Insert(QStringLiteral("red_channel"), int(this->value(0).channel)); + job.Insert(QStringLiteral("green_channel"), int(this->value(1).channel)); + job.Insert(QStringLiteral("blue_channel"), int(this->value(2).channel)); + job.Insert(QStringLiteral("alpha_channel"), int(this->value(3).channel)); return Texture::Job(vp, job); } else { // No swizzling has occurred From 12b5d38be7a6446b42e398ce0f72ef6de357a5a7 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Thu, 15 Jun 2023 00:01:17 -0700 Subject: [PATCH 61/71] mathnode: fix regression in texture ops --- app/node/math/math/math.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/node/math/math/math.cpp b/app/node/math/math/math.cpp index f0364a7de2..d392f4d527 100644 --- a/app/node/math/math/math.cpp +++ b/app/node/math/math/math.cpp @@ -478,7 +478,7 @@ QString GetShaderVariableCall(const QString &input_id, const type_t &type, const ShaderCode MathNode::GetShaderCode(const QString &id) { QStringList j = id.split(':'); - if (j.size() != 3) { + if (j.size() != 4) { return ShaderCode(); } From 3c947df4844c99a120a4e589ce591da2e967617c Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Thu, 15 Jun 2023 00:01:35 -0700 Subject: [PATCH 62/71] nodevaluetree: fix obvious mistake --- app/widget/nodevaluetree/nodevaluetree.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/widget/nodevaluetree/nodevaluetree.cpp b/app/widget/nodevaluetree/nodevaluetree.cpp index 522370c1c7..0df1f79601 100644 --- a/app/widget/nodevaluetree/nodevaluetree.cpp +++ b/app/widget/nodevaluetree/nodevaluetree.cpp @@ -122,7 +122,7 @@ void NodeValueTree::RadioButtonChecked(bool e) NodeOutput old_output = input.GetConnectedOutput2(); MultiUndoCommand *command = new MultiUndoCommand(); - command->add_child(new NodeEdgeAddCommand(old_output, input)); + command->add_child(new NodeEdgeRemoveCommand(old_output, input)); old_output.set_output(tag); command->add_child(new NodeEdgeAddCommand(old_output, input)); From dae9f870ff6eb3cf7e09c2c01a383f56fb507c58 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Thu, 15 Jun 2023 00:23:58 -0700 Subject: [PATCH 63/71] value: fixed macos/clang issue --- app/node/value.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/node/value.cpp b/app/node/value.cpp index dd64126746..7fc3af09d3 100644 --- a/app/node/value.cpp +++ b/app/node/value.cpp @@ -185,7 +185,7 @@ AudioJobPtr value_t::toAudioJob() const for (size_t i = 0; i < data_.size(); i++) { const SampleJobChannel &c = data_.at(i).value(); swizzle.Insert(QStringLiteral("b.%1").arg(i), c.job); - swizzle.Insert(QStringLiteral("c.%1").arg(i), c.channel); + swizzle.Insert(QStringLiteral("c.%1").arg(i), int(c.channel)); } swizzle.set_function(SwizzleAudio, nullptr); From b25acc0116c4ec174283b04987ab3ba88840bed1 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 27 Jun 2023 00:29:02 -0700 Subject: [PATCH 64/71] unsubmodule core library In retrospect, this was premature and poorly done. It's been reverted with potential for library building later on. --- .gitmodules | 3 - CMakeLists.txt | 4 +- app/audio/audioprocessor.h | 4 +- app/audio/audiovisualwaveform.cpp | 1 + app/audio/audiovisualwaveform.h | 5 +- app/codec/encoder.cpp | 20 +- app/codec/encoder.h | 2 +- app/codec/exportformat.h | 1 + app/codec/ffmpeg/ffmpegencoder.h | 2 +- app/codec/frame.h | 2 +- app/codec/oiio/oiioencoder.h | 2 +- app/codec/planarfiledevice.h | 5 +- app/common/ffmpegutils.cpp | 2 + app/common/ffmpegutils.h | 7 +- app/common/html.cpp | 2 +- app/common/qtutils.cpp | 11 +- app/common/qtutils.h | 29 +- app/config/config.cpp | 4 +- app/core.cpp | 6 +- app/core.h | 2 +- .../preferences/tabs/preferencesaudiotab.cpp | 8 +- app/dialog/sequence/sequencepreset.h | 9 +- app/node/generator/text/textv2.cpp | 3 +- app/node/gizmo/draggable.h | 4 +- app/node/globals.h | 2 + app/node/keyframe.cpp | 4 +- app/node/math/math/mathbase.cpp | 1 + app/node/node.cpp | 6 +- app/node/node.h | 6 +- app/node/output/viewer/viewer.cpp | 2 +- app/node/output/viewer/viewer.h | 1 + app/node/project/footage/footage.h | 2 +- .../project/footage/footagedescription.cpp | 6 +- app/node/project/serializer/CMakeLists.txt | 5 - app/node/project/serializer/serializer.h | 1 - .../project/serializer/serializer210528.cpp | 13 +- .../project/serializer/serializer210907.cpp | 13 +- .../project/serializer/serializer211228.cpp | 13 +- .../project/serializer/serializer220403.cpp | 13 +- .../project/serializer/typeserializer.cpp | 63 - app/node/traverser.h | 1 + app/node/value.cpp | 4 +- app/node/value.h | 4 +- app/render/framehashcache.h | 1 + app/render/managedcolor.h | 3 +- app/render/playbackcache.h | 4 +- app/render/renderjobtracker.h | 5 +- app/render/subtitleparams.cpp | 8 +- app/render/subtitleparams.h | 3 +- app/render/videoparams.cpp | 12 +- app/render/videoparams.h | 6 +- app/task/export/export.cpp | 1 + app/timeline/timelinecommon.h | 4 - app/timeline/timelinemarker.cpp | 8 +- app/timeline/timelinemarker.h | 4 +- app/timeline/timelineworkarea.cpp | 8 +- app/timeline/timelineworkarea.h | 6 +- app/ui/colorcoding.h | 5 +- app/ui/humanstrings.cpp | 2 + app/ui/humanstrings.h | 5 +- app/widget/bezier/bezierwidget.h | 4 +- app/widget/menu/menushared.h | 4 +- app/widget/nodeview/nodeviewcontext.cpp | 4 +- .../playbackcontrols/playbackcontrols.cpp | 6 +- app/widget/slider/rationalslider.cpp | 8 +- app/widget/slider/rationalslider.h | 4 +- .../standardcombos/channellayoutcombobox.h | 4 +- app/widget/standardcombos/frameratecombobox.h | 2 +- .../standardcombos/sampleformatcombobox.h | 3 - .../standardcombos/sampleratecombobox.h | 4 +- app/widget/taskview/elapsedcounterwidget.cpp | 9 +- .../timebased/timebasedviewselectionmanager.h | 5 +- app/widget/timebased/timescaledobject.h | 1 - app/widget/timelinewidget/timelinewidget.cpp | 6 +- app/widget/timelinewidget/tool/import.cpp | 6 +- app/widget/timelinewidget/tool/pointer.cpp | 8 +- app/widget/timelinewidget/tool/slip.cpp | 8 +- .../timelinewidget/view/timelineview.cpp | 6 +- app/widget/timeruler/timeruler.cpp | 2 +- app/widget/viewer/viewer.cpp | 4 +- app/widget/viewer/viewersizer.h | 5 +- cmake/FindOlive.cmake | 59 - ext/CMakeLists.txt | 2 - ext/core | 1 - lib/CMakeLists.txt | 17 + lib/olive/CMakeLists.txt | 33 + lib/olive/include/render/audioparams.h | 192 + lib/olive/include/render/pixelformat.h | 108 + lib/olive/include/render/samplebuffer.h | 123 + lib/olive/include/render/sampleformat.h | 264 + lib/olive/include/util/bezier.h | 98 + lib/olive/include/util/color.h | 150 + lib/olive/include/util/cpuoptimize.h | 32 + lib/olive/include/util/log.h | 52 + .../olive/include/util/math.h | 26 +- lib/olive/include/util/rational.h | 148 + lib/olive/include/util/sse2neon.h | 8757 +++++++++++++++++ lib/olive/include/util/tests.h | 62 + lib/olive/include/util/timecodefunctions.h | 78 + lib/olive/include/util/timerange.h | 310 + lib/olive/src/CMakeLists.txt | 24 + lib/olive/src/render/CMakeLists.txt | 23 + lib/olive/src/render/audioparams.cpp | 199 + lib/olive/src/render/samplebuffer.cpp | 301 + lib/olive/src/util/CMakeLists.txt | 27 + lib/olive/src/util/bezier.cpp | 108 + lib/olive/src/util/color.cpp | 309 + lib/olive/src/util/rational.cpp | 294 + lib/olive/src/util/tests.cpp | 61 + lib/olive/src/util/timecodefunctions.cpp | 333 + lib/olive/src/util/timerange.cpp | 381 + tests/general/CMakeLists.txt | 4 + tests/general/rational-test.cpp | 102 + tests/general/stringutils-test.cpp | 46 + tests/general/timecode-test.cpp | 60 + tests/general/timerange-test.cpp | 95 + 116 files changed, 12991 insertions(+), 374 deletions(-) delete mode 100644 app/node/project/serializer/typeserializer.cpp delete mode 100644 cmake/FindOlive.cmake delete mode 160000 ext/core create mode 100644 lib/CMakeLists.txt create mode 100644 lib/olive/CMakeLists.txt create mode 100644 lib/olive/include/render/audioparams.h create mode 100644 lib/olive/include/render/pixelformat.h create mode 100644 lib/olive/include/render/samplebuffer.h create mode 100644 lib/olive/include/render/sampleformat.h create mode 100644 lib/olive/include/util/bezier.h create mode 100644 lib/olive/include/util/color.h create mode 100644 lib/olive/include/util/cpuoptimize.h create mode 100644 lib/olive/include/util/log.h rename app/node/project/serializer/typeserializer.h => lib/olive/include/util/math.h (60%) create mode 100644 lib/olive/include/util/rational.h create mode 100644 lib/olive/include/util/sse2neon.h create mode 100644 lib/olive/include/util/tests.h create mode 100644 lib/olive/include/util/timecodefunctions.h create mode 100644 lib/olive/include/util/timerange.h create mode 100644 lib/olive/src/CMakeLists.txt create mode 100644 lib/olive/src/render/CMakeLists.txt create mode 100644 lib/olive/src/render/audioparams.cpp create mode 100644 lib/olive/src/render/samplebuffer.cpp create mode 100644 lib/olive/src/util/CMakeLists.txt create mode 100644 lib/olive/src/util/bezier.cpp create mode 100644 lib/olive/src/util/color.cpp create mode 100644 lib/olive/src/util/rational.cpp create mode 100644 lib/olive/src/util/tests.cpp create mode 100644 lib/olive/src/util/timecodefunctions.cpp create mode 100644 lib/olive/src/util/timerange.cpp create mode 100644 tests/general/rational-test.cpp create mode 100644 tests/general/stringutils-test.cpp create mode 100644 tests/general/timecode-test.cpp create mode 100644 tests/general/timerange-test.cpp diff --git a/.gitmodules b/.gitmodules index 46b51ee955..2bbb3fe858 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "ext/core"] - path = ext/core - url = https://github.com/olive-editor/core [submodule "ext/KDDockWidgets"] path = ext/KDDockWidgets url = https://github.com/olive-editor/KDDockWidgets.git diff --git a/CMakeLists.txt b/CMakeLists.txt index db83ef6be2..9be21e6b20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,6 +64,7 @@ else() -Wextra -Wno-unused-parameter -Wshadow + -Wno-deprecated-declarations ) if (USE_WERROR) list(APPEND OLIVE_COMPILE_OPTIONS "-Werror") @@ -101,7 +102,7 @@ list(APPEND OLIVE_INCLUDE_DIRS ${OPENEXR_INCLUDES}) # Link Olive list(APPEND OLIVE_LIBRARIES olivecore) -list(APPEND OLIVE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/ext/core/include) +list(APPEND OLIVE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/lib/olive/include) # Link Qt set(QT_LIBRARIES @@ -257,6 +258,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) list(APPEND OLIVE_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/ext) add_subdirectory(ext) +add_subdirectory(lib) add_subdirectory(app) if (BUILD_TESTS) diff --git a/app/audio/audioprocessor.h b/app/audio/audioprocessor.h index a92e7627f1..36e6f587fe 100644 --- a/app/audio/audioprocessor.h +++ b/app/audio/audioprocessor.h @@ -22,7 +22,6 @@ #define AUDIOPROCESSOR_H #include -#include #include extern "C" { @@ -30,11 +29,10 @@ extern "C" { } #include "common/define.h" +#include "core.h" namespace olive { -using namespace core; - class AudioProcessor { public: diff --git a/app/audio/audiovisualwaveform.cpp b/app/audio/audiovisualwaveform.cpp index 0558eb600c..fef2001f62 100644 --- a/app/audio/audiovisualwaveform.cpp +++ b/app/audio/audiovisualwaveform.cpp @@ -24,6 +24,7 @@ #include #include "config/config.h" +#include "util/cpuoptimize.h" namespace olive { diff --git a/app/audio/audiovisualwaveform.h b/app/audio/audiovisualwaveform.h index a75e46a2dd..1240df5b11 100644 --- a/app/audio/audiovisualwaveform.h +++ b/app/audio/audiovisualwaveform.h @@ -21,13 +21,12 @@ #ifndef SUMSAMPLES_H #define SUMSAMPLES_H -#include #include #include -namespace olive { +#include "render/samplebuffer.h" -using namespace core; +namespace olive { /** * @brief A buffer of data used to store a visual representation of audio diff --git a/app/codec/encoder.cpp b/app/codec/encoder.cpp index 810cdcd1bb..eb36a31325 100644 --- a/app/codec/encoder.cpp +++ b/app/codec/encoder.cpp @@ -201,8 +201,8 @@ void EncodingParams::Save(QXmlStreamWriter *writer) const writer->writeTextElement(QStringLiteral("format"), QString::number(format_)); writer->writeTextElement(QStringLiteral("range"), QString::number(has_custom_range_)); - writer->writeTextElement(QStringLiteral("customrangein"), QString::fromStdString(custom_range_.in().toString())); - writer->writeTextElement(QStringLiteral("customrangeout"), QString::fromStdString(custom_range_.out().toString())); + writer->writeTextElement(QStringLiteral("customrangein"), custom_range_.in().toString()); + writer->writeTextElement(QStringLiteral("customrangeout"), custom_range_.out().toString()); writer->writeStartElement(QStringLiteral("video")); @@ -213,8 +213,8 @@ void EncodingParams::Save(QXmlStreamWriter *writer) const writer->writeTextElement(QStringLiteral("width"), QString::number(video_params_.width())); writer->writeTextElement(QStringLiteral("height"), QString::number(video_params_.height())); writer->writeTextElement(QStringLiteral("format"), QString::number(video_params_.format())); - writer->writeTextElement(QStringLiteral("pixelaspect"), QString::fromStdString(video_params_.pixel_aspect_ratio().toString())); - writer->writeTextElement(QStringLiteral("timebase"), QString::fromStdString(video_params_.time_base().toString())); + writer->writeTextElement(QStringLiteral("pixelaspect"), video_params_.pixel_aspect_ratio().toString()); + writer->writeTextElement(QStringLiteral("timebase"), video_params_.time_base().toString()); writer->writeTextElement(QStringLiteral("divider"), QString::number(video_params_.divider())); writer->writeTextElement(QStringLiteral("bitrate"), QString::number(video_bit_rate_)); writer->writeTextElement(QStringLiteral("minbitrate"), QString::number(video_min_bit_rate_)); @@ -257,7 +257,7 @@ void EncodingParams::Save(QXmlStreamWriter *writer) const writer->writeTextElement(QStringLiteral("codec"), QString::number(audio_codec_)); writer->writeTextElement(QStringLiteral("samplerate"), QString::number(audio_params_.sample_rate())); writer->writeTextElement(QStringLiteral("channellayout"), QString::number(audio_params_.channel_layout())); - writer->writeTextElement(QStringLiteral("format"), QString::fromStdString(audio_params_.format().to_string())); + writer->writeTextElement(QStringLiteral("format"), audio_params_.format().to_string()); writer->writeTextElement(QStringLiteral("bitrate"), QString::number(audio_bit_rate_)); } @@ -380,9 +380,9 @@ bool EncodingParams::LoadV1(QXmlStreamReader *reader) } else if (reader->name() == QStringLiteral("range")) { has_custom_range_ = reader->readElementText().toInt(); } else if (reader->name() == QStringLiteral("customrangein")) { - custom_range_in = rational::fromString(reader->readElementText().toStdString()); + custom_range_in = rational::fromString(reader->readElementText()); } else if (reader->name() == QStringLiteral("customrangeout")) { - custom_range_out = rational::fromString(reader->readElementText().toStdString()); + custom_range_out = rational::fromString(reader->readElementText()); } else if (reader->name() == QStringLiteral("video")) { XMLAttributeLoop(reader, attr) { if (attr.name() == QStringLiteral("enabled")) { @@ -400,9 +400,9 @@ bool EncodingParams::LoadV1(QXmlStreamReader *reader) } else if (reader->name() == QStringLiteral("format")) { video_params_.set_format(static_cast(reader->readElementText().toInt())); } else if (reader->name() == QStringLiteral("pixelaspect")) { - video_params_.set_pixel_aspect_ratio(rational::fromString(reader->readElementText().toStdString())); + video_params_.set_pixel_aspect_ratio(rational::fromString(reader->readElementText())); } else if (reader->name() == QStringLiteral("timebase")) { - video_params_.set_time_base(rational::fromString(reader->readElementText().toStdString())); + video_params_.set_time_base(rational::fromString(reader->readElementText())); } else if (reader->name() == QStringLiteral("divider")) { video_params_.set_divider(reader->readElementText().toInt()); } else if (reader->name() == QStringLiteral("bitrate")) { @@ -471,7 +471,7 @@ bool EncodingParams::LoadV1(QXmlStreamReader *reader) } else if (reader->name() == QStringLiteral("channellayout")) { audio_params_.set_channel_layout(reader->readElementText().toULongLong()); } else if (reader->name() == QStringLiteral("format")) { - audio_params_.set_format(SampleFormat::from_string(reader->readElementText().toStdString())); + audio_params_.set_format(SampleFormat::from_string(reader->readElementText())); } else if (reader->name() == QStringLiteral("bitrate")) { audio_bit_rate_ = reader->readElementText().toLongLong(); } else { diff --git a/app/codec/encoder.h b/app/codec/encoder.h index 65702a36f2..2f6757d6b5 100644 --- a/app/codec/encoder.h +++ b/app/codec/encoder.h @@ -229,7 +229,7 @@ class Encoder : public QObject public slots: virtual bool Open() = 0; - virtual bool WriteFrame(olive::FramePtr frame, olive::core::rational time) = 0; + virtual bool WriteFrame(olive::FramePtr frame, rational time) = 0; virtual bool WriteAudio(const olive::SampleBuffer &audio) = 0; virtual bool WriteSubtitle(const SubtitleBlock *sub_block) = 0; diff --git a/app/codec/exportformat.h b/app/codec/exportformat.h index 1ede4ab499..b397fa31bc 100644 --- a/app/codec/exportformat.h +++ b/app/codec/exportformat.h @@ -26,6 +26,7 @@ #include "common/define.h" #include "exportcodec.h" +#include "render/sampleformat.h" namespace olive { diff --git a/app/codec/ffmpeg/ffmpegencoder.h b/app/codec/ffmpeg/ffmpegencoder.h index 3c1ec49ed1..c095cc5560 100644 --- a/app/codec/ffmpeg/ffmpegencoder.h +++ b/app/codec/ffmpeg/ffmpegencoder.h @@ -45,7 +45,7 @@ class FFmpegEncoder : public Encoder virtual bool Open() override; - virtual bool WriteFrame(olive::FramePtr frame, olive::core::rational time) override; + virtual bool WriteFrame(olive::FramePtr frame, rational time) override; virtual bool WriteAudio(const olive::SampleBuffer &audio) override; diff --git a/app/codec/frame.h b/app/codec/frame.h index 30a6ebe86c..221060644f 100644 --- a/app/codec/frame.h +++ b/app/codec/frame.h @@ -22,11 +22,11 @@ #define FRAME_H #include -#include #include #include "common/define.h" #include "render/videoparams.h" +#include "util/color.h" namespace olive { diff --git a/app/codec/oiio/oiioencoder.h b/app/codec/oiio/oiioencoder.h index 778a914747..7a6268c5da 100644 --- a/app/codec/oiio/oiioencoder.h +++ b/app/codec/oiio/oiioencoder.h @@ -34,7 +34,7 @@ class OIIOEncoder : public Encoder public slots: virtual bool Open() override; - virtual bool WriteFrame(olive::FramePtr frame, olive::core::rational time) override; + virtual bool WriteFrame(olive::FramePtr frame, rational time) override; virtual bool WriteAudio(const SampleBuffer &audio) override; virtual bool WriteSubtitle(const SubtitleBlock *sub_block) override; diff --git a/app/codec/planarfiledevice.h b/app/codec/planarfiledevice.h index feaf2c6475..d0327ab4fd 100644 --- a/app/codec/planarfiledevice.h +++ b/app/codec/planarfiledevice.h @@ -21,13 +21,12 @@ #ifndef PLANARFILEDEVICE_H #define PLANARFILEDEVICE_H -#include #include #include -namespace olive { +#include "core.h" -using namespace core; +namespace olive { class PlanarFileDevice : public QObject { diff --git a/app/common/ffmpegutils.cpp b/app/common/ffmpegutils.cpp index 81ae18b4f3..06800019fb 100644 --- a/app/common/ffmpegutils.cpp +++ b/app/common/ffmpegutils.cpp @@ -20,6 +20,8 @@ #include "common/ffmpegutils.h" +#include "render/videoparams.h" + namespace olive { AVPixelFormat FFmpegUtils::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt, PixelFormat maximum) diff --git a/app/common/ffmpegutils.h b/app/common/ffmpegutils.h index 89252fd53d..81e85e239e 100644 --- a/app/common/ffmpegutils.h +++ b/app/common/ffmpegutils.h @@ -27,14 +27,13 @@ extern "C" { #include } -#include +#include -#include "render/videoparams.h" +#include "render/pixelformat.h" +#include "render/sampleformat.h" namespace olive { -using namespace core; - class FFmpegUtils { public: /** diff --git a/app/common/html.cpp b/app/common/html.cpp index 74cc805f91..bcc802aa3a 100644 --- a/app/common/html.cpp +++ b/app/common/html.cpp @@ -248,7 +248,7 @@ void Html::WriteCharFormat(QString *style, const QTextCharFormat &fmt) } if (fmt.foreground().style() != Qt::NoBrush) { - const QColor &color = fmt.foreground().color(); + QColor color = fmt.foreground().color(); QString cs; if (color.alpha() == 255) { diff --git a/app/common/qtutils.cpp b/app/common/qtutils.cpp index 95ca7b7125..30fc87a0a8 100644 --- a/app/common/qtutils.cpp +++ b/app/common/qtutils.cpp @@ -182,7 +182,7 @@ void QtUtils::SetComboBoxData(QComboBox *cb, const QString &data) } } -QColor QtUtils::toQColor(const core::Color &i) +QColor QtUtils::toQColor(const Color &i) { QColor c; @@ -195,19 +195,14 @@ QColor QtUtils::toQColor(const core::Color &i) return c; } -namespace core { - -uint qHash(const core::rational &r, uint seed) +uint qHash(const rational &r, uint seed) { return ::qHash(r.toDouble(), seed); } -uint qHash(const core::TimeRange &r, uint seed) +uint qHash(const TimeRange &r, uint seed) { return qHash(r.in(), seed) ^ qHash(r.out(), seed); } - -} - } diff --git a/app/common/qtutils.h b/app/common/qtutils.h index c4eeef0272..c996b48f38 100644 --- a/app/common/qtutils.h +++ b/app/common/qtutils.h @@ -21,7 +21,6 @@ #ifndef QTVERSIONABSTRACTION_H #define QTVERSIONABSTRACTION_H -#include #include #include #include @@ -29,6 +28,12 @@ #include #include +#include "render/samplebuffer.h" +#include "util/bezier.h" +#include "util/color.h" +#include "util/rational.h" +#include "util/timerange.h" + namespace olive { class QtUtils { @@ -74,7 +79,7 @@ class QtUtils { return nullptr; } - static QColor toQColor(const core::Color &c); + static QColor toQColor(const Color &c); /** * @brief Convert a pointer to a value that can be sent between NodeParams @@ -95,20 +100,16 @@ class QtUtils { }; -namespace core { - -uint qHash(const core::rational& r, uint seed = 0); -uint qHash(const core::TimeRange& r, uint seed = 0); - -} +uint qHash(const rational &r, uint seed = 0); +uint qHash(const TimeRange &r, uint seed = 0); } -Q_DECLARE_METATYPE(olive::core::rational); -Q_DECLARE_METATYPE(olive::core::Color); -Q_DECLARE_METATYPE(olive::core::TimeRange); -Q_DECLARE_METATYPE(olive::core::Bezier); -Q_DECLARE_METATYPE(olive::core::AudioParams); -Q_DECLARE_METATYPE(olive::core::SampleBuffer); +Q_DECLARE_METATYPE(olive::rational); +Q_DECLARE_METATYPE(olive::Color); +Q_DECLARE_METATYPE(olive::TimeRange); +Q_DECLARE_METATYPE(olive::Bezier); +Q_DECLARE_METATYPE(olive::AudioParams); +Q_DECLARE_METATYPE(olive::SampleBuffer); #endif // QTVERSIONABSTRACTION_H diff --git a/app/config/config.cpp b/app/config/config.cpp index 336b1eeb49..36964994f7 100644 --- a/app/config/config.cpp +++ b/app/config/config.cpp @@ -138,13 +138,13 @@ void Config::SetDefaults() SetEntryInternal(QStringLiteral("AudioOutputSampleRate"), NodeValue::kInt, 48000); SetEntryInternal(QStringLiteral("AudioOutputChannelLayout"), NodeValue::kInt, AV_CH_LAYOUT_STEREO); - SetEntryInternal(QStringLiteral("AudioOutputSampleFormat"), NodeValue::kText, QString::fromStdString(SampleFormat(SampleFormat::S16).to_string())); + SetEntryInternal(QStringLiteral("AudioOutputSampleFormat"), NodeValue::kText, SampleFormat(SampleFormat::S16).to_string()); SetEntryInternal(QStringLiteral("AudioRecordingFormat"), NodeValue::kInt, ExportFormat::kFormatWAV); SetEntryInternal(QStringLiteral("AudioRecordingCodec"), NodeValue::kInt, ExportCodec::kCodecPCM); SetEntryInternal(QStringLiteral("AudioRecordingSampleRate"), NodeValue::kInt, 48000); SetEntryInternal(QStringLiteral("AudioRecordingChannelLayout"), NodeValue::kInt, AV_CH_LAYOUT_STEREO); - SetEntryInternal(QStringLiteral("AudioRecordingSampleFormat"), NodeValue::kText, QString::fromStdString(SampleFormat(SampleFormat::S16).to_string())); + SetEntryInternal(QStringLiteral("AudioRecordingSampleFormat"), NodeValue::kText, SampleFormat(SampleFormat::S16).to_string()); SetEntryInternal(QStringLiteral("AudioRecordingBitRate"), NodeValue::kInt, 320); SetEntryInternal(QStringLiteral("DiskCacheBehind"), NodeValue::kRational, QVariant::fromValue(rational(0))); diff --git a/app/core.cpp b/app/core.cpp index bdd794e73d..21d5fa8db5 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -107,7 +107,7 @@ Core *Core::instance() void Core::DeclareTypesForQt() { - qRegisterMetaType(); + qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); @@ -116,8 +116,8 @@ void Core::DeclareTypesForQt() qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); diff --git a/app/core.h b/app/core.h index 10018e7bae..f9ecf00675 100644 --- a/app/core.h +++ b/app/core.h @@ -21,12 +21,12 @@ #ifndef CORE_H #define CORE_H -#include #include #include #include #include +#include "core.h" #include "node/project/footage/footage.h" #include "node/project.h" #include "node/project/sequence/sequence.h" diff --git a/app/dialog/preferences/tabs/preferencesaudiotab.cpp b/app/dialog/preferences/tabs/preferencesaudiotab.cpp index 211f070cb7..bb870d3ab0 100644 --- a/app/dialog/preferences/tabs/preferencesaudiotab.cpp +++ b/app/dialog/preferences/tabs/preferencesaudiotab.cpp @@ -100,7 +100,7 @@ PreferencesAudioTab::PreferencesAudioTab() output_fmt_combo_ = new SampleFormatComboBox(); output_fmt_combo_->SetPackedFormats(); - output_fmt_combo_->SetSampleFormat(SampleFormat::from_string(OLIVE_CONFIG("AudioOutputSampleFormat").toString().toStdString())); + output_fmt_combo_->SetSampleFormat(SampleFormat::from_string(OLIVE_CONFIG("AudioOutputSampleFormat").toString())); output_param_layout->addWidget(output_fmt_combo_, output_row, 1); } } @@ -142,7 +142,7 @@ PreferencesAudioTab::PreferencesAudioTab() record_options_->sample_rate_combobox()->SetSampleRate(OLIVE_CONFIG("AudioRecordingSampleRate").toInt()); record_options_->channel_layout_combobox()->SetChannelLayout(OLIVE_CONFIG("AudioRecordingChannelLayout").toULongLong()); record_options_->bit_rate_slider()->SetValue(OLIVE_CONFIG("AudioRecordingBitRate").toInt()); - record_options_->sample_format_combobox()->SetSampleFormat(SampleFormat::from_string(OLIVE_CONFIG("AudioRecordingSampleFormat").toString().toStdString())); + record_options_->sample_format_combobox()->SetSampleFormat(SampleFormat::from_string(OLIVE_CONFIG("AudioRecordingSampleFormat").toString())); recording_layout->addWidget(record_options_); connect(record_format_combo_, &ExportFormatComboBox::FormatChanged, record_options_, &ExportAudioTab::SetFormat); @@ -182,14 +182,14 @@ void PreferencesAudioTab::Accept(MultiUndoCommand *command) OLIVE_CONFIG("AudioOutputSampleRate") = output_rate_combo_->GetSampleRate(); OLIVE_CONFIG("AudioOutputChannelLayout") = QVariant::fromValue(output_ch_layout_combo_->GetChannelLayout()); - OLIVE_CONFIG("AudioOutputSampleFormat") = QString::fromStdString(output_fmt_combo_->GetSampleFormat().to_string()); + OLIVE_CONFIG("AudioOutputSampleFormat") = output_fmt_combo_->GetSampleFormat().to_string(); 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") = QString::fromStdString(record_options_->sample_format_combobox()->GetSampleFormat().to_string()); + OLIVE_CONFIG("AudioRecordingSampleFormat") = record_options_->sample_format_combobox()->GetSampleFormat().to_string(); emit AudioManager::instance()->OutputParamsChanged(); } diff --git a/app/dialog/sequence/sequencepreset.h b/app/dialog/sequence/sequencepreset.h index 2a2f5699b8..a172ca6d1e 100644 --- a/app/dialog/sequence/sequencepreset.h +++ b/app/dialog/sequence/sequencepreset.h @@ -21,7 +21,6 @@ #ifndef SEQUENCEPARAM_H #define SEQUENCEPARAM_H -#include #include #include "common/xmlutils.h" @@ -69,9 +68,9 @@ class SequencePreset : public Preset { } else if (reader->name() == QStringLiteral("height")) { height_ = reader->readElementText().toInt(); } else if (reader->name() == QStringLiteral("framerate")) { - frame_rate_ = rational::fromString(reader->readElementText().toStdString()); + frame_rate_ = rational::fromString(reader->readElementText()); } else if (reader->name() == QStringLiteral("pixelaspect")) { - pixel_aspect_ = rational::fromString(reader->readElementText().toStdString()); + pixel_aspect_ = rational::fromString(reader->readElementText()); } else if (reader->name() == QStringLiteral("interlacing")) { interlacing_ = static_cast(reader->readElementText().toInt()); } else if (reader->name() == QStringLiteral("samplerate")) { @@ -95,8 +94,8 @@ class SequencePreset : public Preset { writer->writeTextElement(QStringLiteral("name"), GetName()); writer->writeTextElement(QStringLiteral("width"), QString::number(width_)); writer->writeTextElement(QStringLiteral("height"), QString::number(height_)); - writer->writeTextElement(QStringLiteral("framerate"), QString::fromStdString(frame_rate_.toString())); - writer->writeTextElement(QStringLiteral("pixelaspect"), QString::fromStdString(pixel_aspect_.toString())); + writer->writeTextElement(QStringLiteral("framerate"), frame_rate_.toString()); + writer->writeTextElement(QStringLiteral("pixelaspect"), pixel_aspect_.toString()); writer->writeTextElement(QStringLiteral("interlacing_"), QString::number(interlacing_)); writer->writeTextElement(QStringLiteral("samplerate"), QString::number(sample_rate_)); writer->writeTextElement(QStringLiteral("chlayout"), QString::number(channel_layout_)); diff --git a/app/node/generator/text/textv2.cpp b/app/node/generator/text/textv2.cpp index 3472bb6015..e925bba58e 100644 --- a/app/node/generator/text/textv2.cpp +++ b/app/node/generator/text/textv2.cpp @@ -20,11 +20,12 @@ #include "textv2.h" -#include #include #include #include +#include "util/cpuoptimize.h" + namespace olive { #define super ShapeNodeBase diff --git a/app/node/gizmo/draggable.h b/app/node/gizmo/draggable.h index 560f1a50b2..9532951dc5 100644 --- a/app/node/gizmo/draggable.h +++ b/app/node/gizmo/draggable.h @@ -45,7 +45,7 @@ class DraggableGizmo : public NodeGizmo explicit DraggableGizmo(QObject *parent = nullptr); - void DragStart(const NodeValueRow &row, double abs_x, double abs_y, const olive::core::rational &time); + void DragStart(const NodeValueRow &row, double abs_x, double abs_y, const rational &time); void DragMove(double x, double y, const Qt::KeyboardModifiers &modifiers); @@ -66,7 +66,7 @@ class DraggableGizmo : public NodeGizmo void SetDragValueBehavior(DragValueBehavior d) { drag_value_behavior_ = d; } signals: - void HandleStart(const olive::NodeValueRow &row, double x, double y, const olive::core::rational &time); + void HandleStart(const olive::NodeValueRow &row, double x, double y, const rational &time); void HandleMovement(double x, double y, const Qt::KeyboardModifiers &modifiers); diff --git a/app/node/globals.h b/app/node/globals.h index 85540e5043..7fe8aef0c2 100644 --- a/app/node/globals.h +++ b/app/node/globals.h @@ -24,7 +24,9 @@ #include #include "render/loopmode.h" +#include "render/audioparams.h" #include "render/videoparams.h" +#include "util/timerange.h" namespace olive { diff --git a/app/node/keyframe.cpp b/app/node/keyframe.cpp index d329add422..6bad5420f9 100644 --- a/app/node/keyframe.cpp +++ b/app/node/keyframe.cpp @@ -220,7 +220,7 @@ bool NodeKeyframe::load(QXmlStreamReader *reader, NodeValue::Type data_type) if (attr.name() == QStringLiteral("input")) { key_input = attr.value().toString(); } else if (attr.name() == QStringLiteral("time")) { - this->set_time(rational::fromString(attr.value().toString().toStdString())); + this->set_time(rational::fromString(attr.value().toString())); } else if (attr.name() == QStringLiteral("type")) { this->set_type_no_bezier_adj(static_cast(attr.value().toInt())); } else if (attr.name() == QStringLiteral("inhandlex")) { @@ -245,7 +245,7 @@ bool NodeKeyframe::load(QXmlStreamReader *reader, NodeValue::Type data_type) void NodeKeyframe::save(QXmlStreamWriter *writer, NodeValue::Type data_type) const { writer->writeAttribute(QStringLiteral("input"), this->input()); - writer->writeAttribute(QStringLiteral("time"), QString::fromStdString(this->time().toString())); + writer->writeAttribute(QStringLiteral("time"), this->time().toString()); writer->writeAttribute(QStringLiteral("type"), QString::number(this->type())); writer->writeAttribute(QStringLiteral("inhandlex"), QString::number(this->bezier_control_in().x())); writer->writeAttribute(QStringLiteral("inhandley"), QString::number(this->bezier_control_in().y())); diff --git a/app/node/math/math/mathbase.cpp b/app/node/math/math/mathbase.cpp index af8ded1e37..e4d76d8c3f 100644 --- a/app/node/math/math/mathbase.cpp +++ b/app/node/math/math/mathbase.cpp @@ -25,6 +25,7 @@ #include "common/tohex.h" #include "node/distort/transform/transformdistortnode.h" +#include "util/cpuoptimize.h" namespace olive { diff --git a/app/node/node.cpp b/app/node/node.cpp index c392f783af..19842f7658 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -29,7 +29,6 @@ #include "core.h" #include "config/config.h" #include "node/group/group.h" -#include "node/project/serializer/typeserializer.h" #include "nodeundo.h" #include "project.h" #include "serializeddata.h" @@ -1548,7 +1547,8 @@ bool Node::LoadImmediate(QXmlStreamReader *reader, const QString &input, int ele vp.Load(reader); value_on_track = QVariant::fromValue(vp); } else if (data_type == NodeValue::kAudioParams) { - AudioParams ap = TypeSerializer::LoadAudioParams(reader); + AudioParams ap; + ap.load(reader); value_on_track = QVariant::fromValue(ap); } else { QString value_text = reader->readElementText(); @@ -1629,7 +1629,7 @@ void Node::SaveImmediate(QXmlStreamWriter *writer, const QString &input, int ele if (data_type == NodeValue::kVideoParams) { v.value().Save(writer); } else if (data_type == NodeValue::kAudioParams) { - TypeSerializer::SaveAudioParams(writer, v.value()); + v.value().save(writer); } else { writer->writeCharacters(NodeValue::ValueToString(data_type, v, true)); } diff --git a/app/node/node.h b/app/node/node.h index 6058122dee..590c5eb249 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -1081,7 +1081,7 @@ class Node : public QObject } protected slots: - virtual void GizmoDragStart(const olive::NodeValueRow &row, double x, double y, const olive::core::rational &time){} + virtual void GizmoDragStart(const olive::NodeValueRow &row, double x, double y, const rational &time){} virtual void GizmoDragMove(double x, double y, const Qt::KeyboardModifiers &modifiers){} @@ -1202,8 +1202,8 @@ protected slots: QVector GetDependenciesInternal(bool traverse, bool exclusive_only) const; - void ParameterValueChanged(const QString &input, int element, const olive::core::TimeRange &range); - void ParameterValueChanged(const NodeInput& input, const olive::core::TimeRange &range) + void ParameterValueChanged(const QString &input, int element, const TimeRange &range); + void ParameterValueChanged(const NodeInput& input, const TimeRange &range) { ParameterValueChanged(input.input(), input.element(), range); } diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index adada2aa50..46d9ca7f97 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -118,7 +118,7 @@ QVariant ViewerOutput::data(const DataType &d) const if (!using_timebase.isNull()) { // Return time transformed to timecode - return QString::fromStdString(Timecode::time_to_timecode(GetLength(), using_timebase, using_display)); + return Timecode::time_to_timecode(GetLength(), using_timebase, using_display); } break; } diff --git a/app/node/output/viewer/viewer.h b/app/node/output/viewer/viewer.h index 0ad696614d..d55ff1594d 100644 --- a/app/node/output/viewer/viewer.h +++ b/app/node/output/viewer/viewer.h @@ -26,6 +26,7 @@ #include "node/output/track/track.h" #include "render/audioplaybackcache.h" #include "render/framehashcache.h" +#include "render/sampleformat.h" #include "render/subtitleparams.h" #include "render/videoparams.h" #include "timeline/timelinemarker.h" diff --git a/app/node/project/footage/footage.h b/app/node/project/footage/footage.h index 39d5c1f586..5576e62479 100644 --- a/app/node/project/footage/footage.h +++ b/app/node/project/footage/footage.h @@ -21,7 +21,6 @@ #ifndef FOOTAGE_H #define FOOTAGE_H -#include #include #include @@ -30,6 +29,7 @@ #include "node/output/viewer/viewer.h" #include "render/cancelatom.h" #include "render/videoparams.h" +#include "util/rational.h" namespace olive { diff --git a/app/node/project/footage/footagedescription.cpp b/app/node/project/footage/footagedescription.cpp index cc878d1c92..537747fb2e 100644 --- a/app/node/project/footage/footagedescription.cpp +++ b/app/node/project/footage/footagedescription.cpp @@ -25,7 +25,6 @@ #include #include "common/xmlutils.h" -#include "node/project/serializer/typeserializer.h" namespace olive { @@ -75,7 +74,8 @@ bool FootageDescription::Load(const QString &filename) vp.Load(&reader); AddVideoStream(vp); } else if (reader.name() == QStringLiteral("audio")) { - AudioParams ap = TypeSerializer::LoadAudioParams(&reader); + AudioParams ap; + ap.load(&reader); AddAudioStream(ap); } else if (reader.name() == QStringLiteral("subtitle")) { SubtitleParams sp; @@ -136,7 +136,7 @@ bool FootageDescription::Save(const QString &filename) const foreach (const AudioParams& ap, audio_streams_) { writer.writeStartElement(QStringLiteral("audio")); - TypeSerializer::SaveAudioParams(&writer, ap); + ap.save(&writer); writer.writeEndElement(); // audio } diff --git a/app/node/project/serializer/CMakeLists.txt b/app/node/project/serializer/CMakeLists.txt index df529037c2..e67b8b3d7f 100644 --- a/app/node/project/serializer/CMakeLists.txt +++ b/app/node/project/serializer/CMakeLists.txt @@ -31,10 +31,5 @@ set(OLIVE_SOURCES node/project/serializer/serializer220403.h node/project/serializer/serializer230220.cpp node/project/serializer/serializer230220.h - - - node/project/serializer/typeserializer.cpp - node/project/serializer/typeserializer.h - PARENT_SCOPE ) diff --git a/app/node/project/serializer/serializer.h b/app/node/project/serializer/serializer.h index 7c5bf9e5b6..8b8b1cfe87 100644 --- a/app/node/project/serializer/serializer.h +++ b/app/node/project/serializer/serializer.h @@ -25,7 +25,6 @@ #include "common/define.h" #include "node/project.h" -#include "typeserializer.h" namespace olive { diff --git a/app/node/project/serializer/serializer210528.cpp b/app/node/project/serializer/serializer210528.cpp index a4a3854503..e16a27df29 100644 --- a/app/node/project/serializer/serializer210528.cpp +++ b/app/node/project/serializer/serializer210528.cpp @@ -469,7 +469,8 @@ void ProjectSerializer210528::LoadImmediate(QXmlStreamReader *reader, Node *node vp.Load(reader); value_on_track = QVariant::fromValue(vp); } else if (data_type == NodeValue::kAudioParams) { - AudioParams ap = TypeSerializer::LoadAudioParams(reader); + AudioParams ap; + ap.load(reader); value_on_track = QVariant::fromValue(ap); } else { QString value_text = reader->readElementText(); @@ -518,7 +519,7 @@ void ProjectSerializer210528::LoadImmediate(QXmlStreamReader *reader, Node *node if (attr.name() == QStringLiteral("input")) { key_input = attr.value().toString(); } else if (attr.name() == QStringLiteral("time")) { - key_time = rational::fromString(attr.value().toString().toStdString()); + key_time = rational::fromString(attr.value().toString()); } else if (attr.name() == QStringLiteral("type")) { key_type = static_cast(attr.value().toInt()); } else if (attr.name() == QStringLiteral("inhandlex")) { @@ -715,9 +716,9 @@ void ProjectSerializer210528::LoadWorkArea(QXmlStreamReader *reader, TimelineWor if (attr.name() == QStringLiteral("enabled")) { workarea->set_enabled(attr.value() != QStringLiteral("0")); } else if (attr.name() == QStringLiteral("in")) { - range_in = rational::fromString(attr.value().toString().toStdString()); + range_in = rational::fromString(attr.value().toString()); } else if (attr.name() == QStringLiteral("out")) { - range_out = rational::fromString(attr.value().toString().toStdString()); + range_out = rational::fromString(attr.value().toString()); } } @@ -741,9 +742,9 @@ void ProjectSerializer210528::LoadMarkerList(QXmlStreamReader *reader, TimelineM if (attr.name() == QStringLiteral("name")) { name = attr.value().toString(); } else if (attr.name() == QStringLiteral("in")) { - in = rational::fromString(attr.value().toString().toStdString()); + in = rational::fromString(attr.value().toString()); } else if (attr.name() == QStringLiteral("out")) { - out = rational::fromString(attr.value().toString().toStdString()); + out = rational::fromString(attr.value().toString()); } } diff --git a/app/node/project/serializer/serializer210907.cpp b/app/node/project/serializer/serializer210907.cpp index 197f97b769..a27c924d83 100644 --- a/app/node/project/serializer/serializer210907.cpp +++ b/app/node/project/serializer/serializer210907.cpp @@ -466,7 +466,8 @@ void ProjectSerializer210907::LoadImmediate(QXmlStreamReader *reader, Node *node vp.Load(reader); value_on_track = QVariant::fromValue(vp); } else if (data_type == NodeValue::kAudioParams) { - AudioParams ap = TypeSerializer::LoadAudioParams(reader); + AudioParams ap; + ap.load(reader); value_on_track = QVariant::fromValue(ap); } else { QString value_text = reader->readElementText(); @@ -515,7 +516,7 @@ void ProjectSerializer210907::LoadImmediate(QXmlStreamReader *reader, Node *node if (attr.name() == QStringLiteral("input")) { key_input = attr.value().toString(); } else if (attr.name() == QStringLiteral("time")) { - key_time = rational::fromString(attr.value().toString().toStdString()); + key_time = rational::fromString(attr.value().toString()); } else if (attr.name() == QStringLiteral("type")) { key_type = static_cast(attr.value().toInt()); } else if (attr.name() == QStringLiteral("inhandlex")) { @@ -707,9 +708,9 @@ void ProjectSerializer210907::LoadWorkArea(QXmlStreamReader *reader, TimelineWor if (attr.name() == QStringLiteral("enabled")) { workarea->set_enabled(attr.value() != QStringLiteral("0")); } else if (attr.name() == QStringLiteral("in")) { - range_in = rational::fromString(attr.value().toString().toStdString()); + range_in = rational::fromString(attr.value().toString()); } else if (attr.name() == QStringLiteral("out")) { - range_out = rational::fromString(attr.value().toString().toStdString()); + range_out = rational::fromString(attr.value().toString()); } } @@ -733,9 +734,9 @@ void ProjectSerializer210907::LoadMarkerList(QXmlStreamReader *reader, TimelineM if (attr.name() == QStringLiteral("name")) { name = attr.value().toString(); } else if (attr.name() == QStringLiteral("in")) { - in = rational::fromString(attr.value().toString().toStdString()); + in = rational::fromString(attr.value().toString()); } else if (attr.name() == QStringLiteral("out")) { - out = rational::fromString(attr.value().toString().toStdString()); + out = rational::fromString(attr.value().toString()); } } diff --git a/app/node/project/serializer/serializer211228.cpp b/app/node/project/serializer/serializer211228.cpp index a9ccf3d0a0..0c8094530a 100644 --- a/app/node/project/serializer/serializer211228.cpp +++ b/app/node/project/serializer/serializer211228.cpp @@ -516,7 +516,8 @@ void ProjectSerializer211228::LoadImmediate(QXmlStreamReader *reader, Node *node vp.Load(reader); value_on_track = QVariant::fromValue(vp); } else if (data_type == NodeValue::kAudioParams) { - AudioParams ap = TypeSerializer::LoadAudioParams(reader); + AudioParams ap; + ap.load(reader); value_on_track = QVariant::fromValue(ap); } else { QString value_text = reader->readElementText(); @@ -565,7 +566,7 @@ void ProjectSerializer211228::LoadImmediate(QXmlStreamReader *reader, Node *node if (attr.name() == QStringLiteral("input")) { key_input = attr.value().toString(); } else if (attr.name() == QStringLiteral("time")) { - key_time = rational::fromString(attr.value().toString().toStdString()); + key_time = rational::fromString(attr.value().toString()); } else if (attr.name() == QStringLiteral("type")) { key_type = static_cast(attr.value().toInt()); } else if (attr.name() == QStringLiteral("inhandlex")) { @@ -757,9 +758,9 @@ void ProjectSerializer211228::LoadWorkArea(QXmlStreamReader *reader, TimelineWor if (attr.name() == QStringLiteral("enabled")) { workarea->set_enabled(attr.value() != QStringLiteral("0")); } else if (attr.name() == QStringLiteral("in")) { - range_in = rational::fromString(attr.value().toString().toStdString()); + range_in = rational::fromString(attr.value().toString()); } else if (attr.name() == QStringLiteral("out")) { - range_out = rational::fromString(attr.value().toString().toStdString()); + range_out = rational::fromString(attr.value().toString()); } } @@ -783,9 +784,9 @@ void ProjectSerializer211228::LoadMarkerList(QXmlStreamReader *reader, TimelineM if (attr.name() == QStringLiteral("name")) { name = attr.value().toString(); } else if (attr.name() == QStringLiteral("in")) { - in = rational::fromString(attr.value().toString().toStdString()); + in = rational::fromString(attr.value().toString()); } else if (attr.name() == QStringLiteral("out")) { - out = rational::fromString(attr.value().toString().toStdString()); + out = rational::fromString(attr.value().toString()); } } diff --git a/app/node/project/serializer/serializer220403.cpp b/app/node/project/serializer/serializer220403.cpp index 0ee37bc434..bd082e75b4 100644 --- a/app/node/project/serializer/serializer220403.cpp +++ b/app/node/project/serializer/serializer220403.cpp @@ -681,7 +681,8 @@ void ProjectSerializer220403::LoadImmediate(QXmlStreamReader *reader, Node *node vp.Load(reader); value_on_track = QVariant::fromValue(vp); } else if (data_type == NodeValue::kAudioParams) { - AudioParams ap = TypeSerializer::LoadAudioParams(reader); + AudioParams ap; + ap.load(reader); value_on_track = QVariant::fromValue(ap); } else { QString value_text = reader->readElementText(); @@ -760,7 +761,7 @@ void ProjectSerializer220403::LoadKeyframe(QXmlStreamReader *reader, NodeKeyfram if (attr.name() == QStringLiteral("input")) { key_input = attr.value().toString(); } else if (attr.name() == QStringLiteral("time")) { - key->set_time(rational::fromString(attr.value().toString().toStdString())); + key->set_time(rational::fromString(attr.value().toString())); } else if (attr.name() == QStringLiteral("type")) { key->set_type_no_bezier_adj(static_cast(attr.value().toInt())); } else if (attr.name() == QStringLiteral("inhandlex")) { @@ -975,9 +976,9 @@ void ProjectSerializer220403::LoadMarker(QXmlStreamReader *reader, TimelineMarke if (attr.name() == QStringLiteral("name")) { marker->set_name(attr.value().toString()); } else if (attr.name() == QStringLiteral("in")) { - in = rational::fromString(attr.value().toString().toStdString()); + in = rational::fromString(attr.value().toString()); } else if (attr.name() == QStringLiteral("out")) { - out = rational::fromString(attr.value().toString().toStdString()); + out = rational::fromString(attr.value().toString()); } else if (attr.name() == QStringLiteral("color")) { marker->set_color(attr.value().toInt()); } @@ -998,9 +999,9 @@ void ProjectSerializer220403::LoadWorkArea(QXmlStreamReader *reader, TimelineWor if (attr.name() == QStringLiteral("enabled")) { workarea->set_enabled(attr.value() != QStringLiteral("0")); } else if (attr.name() == QStringLiteral("in")) { - range_in = rational::fromString(attr.value().toString().toStdString()); + range_in = rational::fromString(attr.value().toString()); } else if (attr.name() == QStringLiteral("out")) { - range_out = rational::fromString(attr.value().toString().toStdString()); + range_out = rational::fromString(attr.value().toString()); } } diff --git a/app/node/project/serializer/typeserializer.cpp b/app/node/project/serializer/typeserializer.cpp deleted file mode 100644 index 003613597f..0000000000 --- a/app/node/project/serializer/typeserializer.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2023 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 . - -***/ - -#include "typeserializer.h" - -namespace olive { - -AudioParams TypeSerializer::LoadAudioParams(QXmlStreamReader *reader) -{ - AudioParams a; - - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("samplerate")) { - a.set_sample_rate(reader->readElementText().toInt()); - } else if (reader->name() == QStringLiteral("channellayout")) { - a.set_channel_layout(reader->readElementText().toULongLong()); - } else if (reader->name() == QStringLiteral("format")) { - a.set_format(SampleFormat::from_string(reader->readElementText().toStdString())); - } else if (reader->name() == QStringLiteral("enabled")) { - a.set_enabled(reader->readElementText().toInt()); - } else if (reader->name() == QStringLiteral("streamindex")) { - a.set_stream_index(reader->readElementText().toInt()); - } else if (reader->name() == QStringLiteral("duration")) { - a.set_duration(reader->readElementText().toLongLong()); - } else if (reader->name() == QStringLiteral("timebase")) { - a.set_time_base(rational::fromString(reader->readElementText().toStdString())); - } else { - reader->skipCurrentElement(); - } - } - - return a; -} - -void TypeSerializer::SaveAudioParams(QXmlStreamWriter *writer, const AudioParams &a) -{ - writer->writeTextElement(QStringLiteral("samplerate"), QString::number(a.sample_rate())); - writer->writeTextElement(QStringLiteral("channellayout"), QString::number(a.channel_layout())); - writer->writeTextElement(QStringLiteral("format"), QString::fromStdString(a.format().to_string())); - writer->writeTextElement(QStringLiteral("enabled"), QString::number(a.enabled())); - writer->writeTextElement(QStringLiteral("streamindex"), QString::number(a.stream_index())); - writer->writeTextElement(QStringLiteral("duration"), QString::number(a.duration())); - writer->writeTextElement(QStringLiteral("timebase"), QString::fromStdString(a.time_base().toString())); -} - -} diff --git a/app/node/traverser.h b/app/node/traverser.h index 84284a0d80..83f8f494b6 100644 --- a/app/node/traverser.h +++ b/app/node/traverser.h @@ -25,6 +25,7 @@ #include "codec/decoder.h" #include "common/cancelableobject.h" +#include "common/qtutils.h" #include "node/output/track/track.h" #include "render/job/cachejob.h" #include "render/cancelatom.h" diff --git a/app/node/value.cpp b/app/node/value.cpp index dab17809ed..599aacd990 100644 --- a/app/node/value.cpp +++ b/app/node/value.cpp @@ -69,7 +69,7 @@ QString NodeValue::ValueToString(Type data_type, const QVariant &value, bool val QString::number(b.cp2_x()), QString::number(b.cp2_y())); } else if (data_type == kRational) { - return QString::fromStdString(value.value().toString()); + return value.value().toString(); } else if (data_type == kTexture || data_type == kSamples || data_type == kNone) { @@ -244,7 +244,7 @@ QVariant NodeValue::StringToValue(Type data_type, const QString &string, bool va } else if (data_type == kInt) { return QVariant::fromValue(string.toLongLong()); } else if (data_type == kRational) { - return QVariant::fromValue(rational::fromString(string.toStdString())); + return QVariant::fromValue(rational::fromString(string)); } else if (data_type == kBinary) { return QByteArray::fromBase64(string.toLatin1()); } else { diff --git a/app/node/value.h b/app/node/value.h index d42ba9a598..33c3b2bf3d 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -341,9 +341,9 @@ class NodeValue bool toBool() const { return value(); } double toDouble() const { return value(); } int64_t toInt() const { return value(); } - rational toRational() const { return value(); } + rational toRational() const { return value(); } QString toString() const { return value(); } - Color toColor() const { return value(); } + Color toColor() const { return value(); } QMatrix4x4 toMatrix() const { return value(); } VideoParams toVideoParams() const { return value(); } AudioParams toAudioParams() const { return value(); } diff --git a/app/render/framehashcache.h b/app/render/framehashcache.h index cb7ddcd3b9..49fee2d30d 100644 --- a/app/render/framehashcache.h +++ b/app/render/framehashcache.h @@ -24,6 +24,7 @@ #include "codec/frame.h" #include "render/playbackcache.h" #include "render/videoparams.h" +#include "util/timecodefunctions.h" namespace olive { diff --git a/app/render/managedcolor.h b/app/render/managedcolor.h index 93411425ae..9b56c38778 100644 --- a/app/render/managedcolor.h +++ b/app/render/managedcolor.h @@ -21,9 +21,8 @@ #ifndef MANAGEDCOLOR_H #define MANAGEDCOLOR_H -#include - #include "colortransform.h" +#include "util/color.h" namespace olive { diff --git a/app/render/playbackcache.h b/app/render/playbackcache.h index d4de7fe32b..94b7abebdd 100644 --- a/app/render/playbackcache.h +++ b/app/render/playbackcache.h @@ -21,7 +21,6 @@ #ifndef PLAYBACKCACHE_H #define PLAYBACKCACHE_H -#include #include #include #include @@ -29,8 +28,7 @@ #include #include "common/jobtime.h" - -using namespace olive::core; +#include "util/timerange.h" namespace olive { diff --git a/app/render/renderjobtracker.h b/app/render/renderjobtracker.h index d475818a73..e17216ee37 100644 --- a/app/render/renderjobtracker.h +++ b/app/render/renderjobtracker.h @@ -21,14 +21,11 @@ #ifndef RENDERJOBTRACKER_H #define RENDERJOBTRACKER_H -#include - #include "common/jobtime.h" +#include "util/timerange.h" namespace olive { -using namespace core; - class RenderJobTracker { public: diff --git a/app/render/subtitleparams.cpp b/app/render/subtitleparams.cpp index ecf985a8ca..442fa39945 100644 --- a/app/render/subtitleparams.cpp +++ b/app/render/subtitleparams.cpp @@ -125,9 +125,9 @@ void SubtitleParams::Load(QXmlStreamReader *reader) XMLAttributeLoop(reader, attr) { if (attr.name() == QStringLiteral("in")) { - in = rational::fromString(attr.value().toString().toStdString()); + in = rational::fromString(attr.value().toString()); } else if (attr.name() == QStringLiteral("out")) { - out = rational::fromString(attr.value().toString().toStdString()); + out = rational::fromString(attr.value().toString()); } } @@ -152,8 +152,8 @@ void SubtitleParams::Save(QXmlStreamWriter *writer) const writer->writeStartElement(QStringLiteral("subtitles")); for (auto it=this->cbegin(); it!=this->cend(); it++) { writer->writeStartElement(QStringLiteral("subtitle")); - writer->writeAttribute(QStringLiteral("in"), QString::fromStdString(it->time().in().toString())); - writer->writeAttribute(QStringLiteral("out"), QString::fromStdString(it->time().out().toString())); + writer->writeAttribute(QStringLiteral("in"), it->time().in().toString()); + writer->writeAttribute(QStringLiteral("out"), it->time().out().toString()); writer->writeCharacters(it->text()); writer->writeEndElement(); // subtitle } diff --git a/app/render/subtitleparams.h b/app/render/subtitleparams.h index 7d82273e72..33a8b2686c 100644 --- a/app/render/subtitleparams.h +++ b/app/render/subtitleparams.h @@ -21,13 +21,12 @@ #ifndef SUBTITLEPARAMS_H #define SUBTITLEPARAMS_H -#include #include #include #include #include -using namespace olive::core; +#include "util/timerange.h" namespace olive { diff --git a/app/render/videoparams.cpp b/app/render/videoparams.cpp index 742e01227d..9ad5a6151f 100644 --- a/app/render/videoparams.cpp +++ b/app/render/videoparams.cpp @@ -344,13 +344,13 @@ void VideoParams::Load(QXmlStreamReader *reader) } else if (reader->name() == QStringLiteral("depth")) { set_depth(reader->readElementText().toInt()); } else if (reader->name() == QStringLiteral("timebase")) { - set_time_base(rational::fromString(reader->readElementText().toStdString())); + set_time_base(rational::fromString(reader->readElementText())); } else if (reader->name() == QStringLiteral("format")) { set_format(static_cast(reader->readElementText().toInt())); } else if (reader->name() == QStringLiteral("channelcount")) { set_channel_count(reader->readElementText().toInt()); } else if (reader->name() == QStringLiteral("pixelaspectratio")) { - set_pixel_aspect_ratio(rational::fromString(reader->readElementText().toStdString())); + set_pixel_aspect_ratio(rational::fromString(reader->readElementText())); } else if (reader->name() == QStringLiteral("interlacing")) { set_interlacing(static_cast(reader->readElementText().toInt())); } else if (reader->name() == QStringLiteral("divider")) { @@ -366,7 +366,7 @@ void VideoParams::Load(QXmlStreamReader *reader) } else if (reader->name() == QStringLiteral("videotype")) { set_video_type(static_cast(reader->readElementText().toInt())); } else if (reader->name() == QStringLiteral("framerate")) { - set_frame_rate(rational::fromString(reader->readElementText().toStdString())); + set_frame_rate(rational::fromString(reader->readElementText())); } else if (reader->name() == QStringLiteral("starttime")) { set_start_time(reader->readElementText().toLongLong()); } else if (reader->name() == QStringLiteral("duration")) { @@ -388,10 +388,10 @@ void VideoParams::Save(QXmlStreamWriter *writer) const writer->writeTextElement(QStringLiteral("width"), QString::number(width_)); writer->writeTextElement(QStringLiteral("height"), QString::number(height_)); writer->writeTextElement(QStringLiteral("depth"), QString::number(depth_)); - writer->writeTextElement(QStringLiteral("timebase"), QString::fromStdString(time_base_.toString())); + writer->writeTextElement(QStringLiteral("timebase"), time_base_.toString()); writer->writeTextElement(QStringLiteral("format"), QString::number(format_)); writer->writeTextElement(QStringLiteral("channelcount"), QString::number(channel_count_)); - writer->writeTextElement(QStringLiteral("pixelaspectratio"), QString::fromStdString(pixel_aspect_ratio_.toString())); + writer->writeTextElement(QStringLiteral("pixelaspectratio"), pixel_aspect_ratio_.toString()); writer->writeTextElement(QStringLiteral("interlacing"), QString::number(interlacing_)); writer->writeTextElement(QStringLiteral("divider"), QString::number(divider_)); writer->writeTextElement(QStringLiteral("enabled"), QString::number(enabled_)); @@ -399,7 +399,7 @@ void VideoParams::Save(QXmlStreamWriter *writer) const writer->writeTextElement(QStringLiteral("y"), QString::number(y_)); writer->writeTextElement(QStringLiteral("streamindex"), QString::number(stream_index_)); writer->writeTextElement(QStringLiteral("videotype"), QString::number(video_type_)); - writer->writeTextElement(QStringLiteral("framerate"), QString::fromStdString(frame_rate_.toString())); + writer->writeTextElement(QStringLiteral("framerate"), frame_rate_.toString()); writer->writeTextElement(QStringLiteral("starttime"), QString::number(start_time_)); writer->writeTextElement(QStringLiteral("duration"), QString::number(duration_)); writer->writeTextElement(QStringLiteral("premultipliedalpha"), QString::number(premultiplied_alpha_)); diff --git a/app/render/videoparams.h b/app/render/videoparams.h index 84ae67b600..908725156d 100644 --- a/app/render/videoparams.h +++ b/app/render/videoparams.h @@ -21,14 +21,14 @@ #ifndef VIDEOPARAMS_H #define VIDEOPARAMS_H -#include #include #include #include -namespace olive { +#include "render/pixelformat.h" +#include "util/rational.h" -using namespace core; +namespace olive { class VideoParams { public: diff --git a/app/task/export/export.cpp b/app/task/export/export.cpp index a171df1ce0..78c2807d38 100644 --- a/app/task/export/export.cpp +++ b/app/task/export/export.cpp @@ -20,6 +20,7 @@ #include "export.h" +#include "common/qtutils.h" #include "node/color/colormanager/colormanager.h" namespace olive { diff --git a/app/timeline/timelinecommon.h b/app/timeline/timelinecommon.h index 0bafa466e0..40b7f8d94a 100644 --- a/app/timeline/timelinecommon.h +++ b/app/timeline/timelinecommon.h @@ -21,12 +21,8 @@ #ifndef TIMELINECOMMON_H #define TIMELINECOMMON_H -#include - #include "common/define.h" -using namespace olive::core; - namespace olive { class Block; diff --git a/app/timeline/timelinemarker.cpp b/app/timeline/timelinemarker.cpp index e4ad0f5821..47a2122f1c 100644 --- a/app/timeline/timelinemarker.cpp +++ b/app/timeline/timelinemarker.cpp @@ -152,9 +152,9 @@ bool TimelineMarker::load(QXmlStreamReader *reader) if (attr.name() == QStringLiteral("name")) { this->set_name(attr.value().toString()); } else if (attr.name() == QStringLiteral("in")) { - in = rational::fromString(attr.value().toString().toStdString()); + in = rational::fromString(attr.value().toString()); } else if (attr.name() == QStringLiteral("out")) { - out = rational::fromString(attr.value().toString().toStdString()); + out = rational::fromString(attr.value().toString()); } else if (attr.name() == QStringLiteral("color")) { this->set_color(attr.value().toInt()); } @@ -171,8 +171,8 @@ bool TimelineMarker::load(QXmlStreamReader *reader) void TimelineMarker::save(QXmlStreamWriter *writer) const { writer->writeAttribute(QStringLiteral("name"), this->name()); - writer->writeAttribute(QStringLiteral("in"), QString::fromStdString(this->time().in().toString())); - writer->writeAttribute(QStringLiteral("out"), QString::fromStdString(this->time().out().toString())); + writer->writeAttribute(QStringLiteral("in"), this->time().in().toString()); + writer->writeAttribute(QStringLiteral("out"), this->time().out().toString()); writer->writeAttribute(QStringLiteral("color"), QString::number(this->color())); } diff --git a/app/timeline/timelinemarker.h b/app/timeline/timelinemarker.h index f1b6a5f3c0..d27814d029 100644 --- a/app/timeline/timelinemarker.h +++ b/app/timeline/timelinemarker.h @@ -21,16 +21,14 @@ #ifndef TIMELINEMARKER_H #define TIMELINEMARKER_H -#include #include #include #include #include +#include "util/timerange.h" #include "undo/undocommand.h" -using namespace olive::core; - namespace olive { class TimelineMarker : public QObject diff --git a/app/timeline/timelineworkarea.cpp b/app/timeline/timelineworkarea.cpp index ba2c09e79b..077f91c774 100644 --- a/app/timeline/timelineworkarea.cpp +++ b/app/timeline/timelineworkarea.cpp @@ -72,9 +72,9 @@ bool TimelineWorkArea::load(QXmlStreamReader *reader) if (reader->name() == QStringLiteral("enabled")) { this->set_enabled(reader->readElementText() != QStringLiteral("0")); } else if (reader->name() == QStringLiteral("in")) { - range_in = rational::fromString(reader->readElementText().toStdString()); + range_in = rational::fromString(reader->readElementText()); } else if (reader->name() == QStringLiteral("out")) { - range_out = rational::fromString(reader->readElementText().toStdString()); + range_out = rational::fromString(reader->readElementText()); } else { reader->skipCurrentElement(); } @@ -94,8 +94,8 @@ void TimelineWorkArea::save(QXmlStreamWriter *writer) const writer->writeAttribute(QStringLiteral("version"), QString::number(1)); writer->writeTextElement(QStringLiteral("enabled"), QString::number(this->enabled())); - writer->writeTextElement(QStringLiteral("in"), QString::fromStdString(this->in().toString())); - writer->writeTextElement(QStringLiteral("out"), QString::fromStdString(this->out().toString())); + writer->writeTextElement(QStringLiteral("in"), this->in().toString()); + writer->writeTextElement(QStringLiteral("out"), this->out().toString()); } const rational &TimelineWorkArea::in() const diff --git a/app/timeline/timelineworkarea.h b/app/timeline/timelineworkarea.h index e48e748e23..97230fb1d7 100644 --- a/app/timeline/timelineworkarea.h +++ b/app/timeline/timelineworkarea.h @@ -21,14 +21,14 @@ #ifndef TIMELINEWORKAREA_H #define TIMELINEWORKAREA_H -#include #include #include #include -namespace olive { +#include "util/rational.h" +#include "util/timerange.h" -using namespace core; +namespace olive { class TimelineWorkArea : public QObject { diff --git a/app/ui/colorcoding.h b/app/ui/colorcoding.h index 5cc16d58c0..6835766c3f 100644 --- a/app/ui/colorcoding.h +++ b/app/ui/colorcoding.h @@ -21,12 +21,11 @@ #ifndef COLORCODING_H #define COLORCODING_H -#include #include -namespace olive { +#include "util/color.h" -using namespace core; +namespace olive { class ColorCoding : public QObject { diff --git a/app/ui/humanstrings.cpp b/app/ui/humanstrings.cpp index 6a52a9ccb1..eb0d057215 100644 --- a/app/ui/humanstrings.cpp +++ b/app/ui/humanstrings.cpp @@ -2,6 +2,8 @@ #include +#include "common/ffmpegutils.h" + namespace olive { QString HumanStrings::SampleRateToString(const int &sample_rate) diff --git a/app/ui/humanstrings.h b/app/ui/humanstrings.h index 6c94fb2921..08079480e9 100644 --- a/app/ui/humanstrings.h +++ b/app/ui/humanstrings.h @@ -1,12 +1,11 @@ #ifndef HUMANSTRINGS_H #define HUMANSTRINGS_H -#include #include -namespace olive { +#include "render/sampleformat.h" -using namespace core; +namespace olive { class HumanStrings : public QObject { diff --git a/app/widget/bezier/bezierwidget.h b/app/widget/bezier/bezierwidget.h index e16d0601de..0e59c8d070 100644 --- a/app/widget/bezier/bezierwidget.h +++ b/app/widget/bezier/bezierwidget.h @@ -21,16 +21,14 @@ #ifndef BEZIERWIDGET_H #define BEZIERWIDGET_H -#include #include #include +#include "util/bezier.h" #include "widget/slider/floatslider.h" namespace olive { -using namespace core; - class BezierWidget : public QWidget { Q_OBJECT diff --git a/app/widget/menu/menushared.h b/app/widget/menu/menushared.h index b3d2cba8aa..bcddbc938f 100644 --- a/app/widget/menu/menushared.h +++ b/app/widget/menu/menushared.h @@ -21,14 +21,12 @@ #ifndef MENUSHARED_H #define MENUSHARED_H -#include +#include "util/rational.h" #include "widget/colorlabelmenu/colorlabelmenu.h" #include "widget/menu/menu.h" namespace olive { -using namespace core; - /** * @brief A static object that provides various "stock" menus for use throughout the application */ diff --git a/app/widget/nodeview/nodeviewcontext.cpp b/app/widget/nodeview/nodeviewcontext.cpp index 661c53bb89..9540c1682a 100644 --- a/app/widget/nodeview/nodeviewcontext.cpp +++ b/app/widget/nodeview/nodeviewcontext.cpp @@ -30,8 +30,8 @@ NodeViewContext::NodeViewContext(Node *context, QGraphicsItem *item) : lbl_ = QCoreApplication::translate("NodeViewContext", "%1 [%2] :: %3 - %4").arg(block->GetLabelAndName(), Track::Reference::TypeToTranslatedString(block->track()->type()), - QString::fromStdString(Timecode::time_to_timecode(block->in(), timebase, Core::instance()->GetTimecodeDisplay())), - QString::fromStdString(Timecode::time_to_timecode(block->out(), timebase, Core::instance()->GetTimecodeDisplay()))); + Timecode::time_to_timecode(block->in(), timebase, Core::instance()->GetTimecodeDisplay()), + Timecode::time_to_timecode(block->out(), timebase, Core::instance()->GetTimecodeDisplay())); } else { lbl_ = context_->GetLabelAndName(); } diff --git a/app/widget/playbackcontrols/playbackcontrols.cpp b/app/widget/playbackcontrols/playbackcontrols.cpp index e11d5fe225..4d5e64a114 100644 --- a/app/widget/playbackcontrols/playbackcontrols.cpp +++ b/app/widget/playbackcontrols/playbackcontrols.cpp @@ -198,9 +198,9 @@ void PlaybackControls::SetEndTime(const rational &r) end_time_ = r; - end_tc_lbl_->setText(QString::fromStdString(Timecode::time_to_timecode(end_time_, - time_base_, - Core::instance()->GetTimecodeDisplay()))); + end_tc_lbl_->setText(Timecode::time_to_timecode(end_time_, + time_base_, + Core::instance()->GetTimecodeDisplay())); } void PlaybackControls::ShowPauseButton() diff --git a/app/widget/slider/rationalslider.cpp b/app/widget/slider/rationalslider.cpp index bcad1f4932..7b3b6a5e14 100644 --- a/app/widget/slider/rationalslider.cpp +++ b/app/widget/slider/rationalslider.cpp @@ -106,11 +106,11 @@ QString RationalSlider::ValueToString(const QVariant &v) const switch (display_type_) { case kTime: - return QString::fromStdString(Timecode::time_to_timecode(r, timebase_, Core::instance()->GetTimecodeDisplay())); + return Timecode::time_to_timecode(r, timebase_, Core::instance()->GetTimecodeDisplay()); case kFloat: return FloatToString(val, GetDecimalPlaces(), GetAutoTrimDecimalPlaces()); case kRational: - return QString::fromStdString(v.value().toString()); + return v.value().toString(); } return v.toString(); @@ -125,7 +125,7 @@ QVariant RationalSlider::StringToValue(const QString &s, bool *ok) const switch (display_type_) { case kTime: { - r = Timecode::timecode_to_time(s.toStdString(), timebase_, Core::instance()->GetTimecodeDisplay(), ok); + r = Timecode::timecode_to_time(s, timebase_, Core::instance()->GetTimecodeDisplay(), ok); break; } case kFloat: @@ -141,7 +141,7 @@ QVariant RationalSlider::StringToValue(const QString &s, bool *ok) const break; } case kRational: - r = rational::fromString(s.toStdString(), ok); + r = rational::fromString(s, ok); break; } diff --git a/app/widget/slider/rationalslider.h b/app/widget/slider/rationalslider.h index 4da25b96ac..ac24a5cdef 100644 --- a/app/widget/slider/rationalslider.h +++ b/app/widget/slider/rationalslider.h @@ -21,15 +21,13 @@ #ifndef RATIONALSLIDER_H #define RATIONALSLIDER_H -#include #include #include "base/decimalsliderbase.h" +#include "util/rational.h" namespace olive { -using namespace core; - /** * @brief A olive::rational based slider * diff --git a/app/widget/standardcombos/channellayoutcombobox.h b/app/widget/standardcombos/channellayoutcombobox.h index 0cdf7a503a..272664c9c4 100644 --- a/app/widget/standardcombos/channellayoutcombobox.h +++ b/app/widget/standardcombos/channellayoutcombobox.h @@ -21,15 +21,13 @@ #ifndef CHANNELLAYOUTCOMBOBOX_H #define CHANNELLAYOUTCOMBOBOX_H -#include #include +#include "render/audioparams.h" #include "ui/humanstrings.h" namespace olive { -using namespace core; - class ChannelLayoutComboBox : public QComboBox { Q_OBJECT diff --git a/app/widget/standardcombos/frameratecombobox.h b/app/widget/standardcombos/frameratecombobox.h index c0d63d24df..8072d7e2d7 100644 --- a/app/widget/standardcombos/frameratecombobox.h +++ b/app/widget/standardcombos/frameratecombobox.h @@ -120,7 +120,7 @@ private slots: r = rational::fromDouble(d, &ok); } else { // Try converting to rational in case someone formatted that way - r = rational::fromString(s.toStdString(), &ok); + r = rational::fromString(s, &ok); } if (ok) { diff --git a/app/widget/standardcombos/sampleformatcombobox.h b/app/widget/standardcombos/sampleformatcombobox.h index 2c991bc340..595a707ebe 100644 --- a/app/widget/standardcombos/sampleformatcombobox.h +++ b/app/widget/standardcombos/sampleformatcombobox.h @@ -21,15 +21,12 @@ #ifndef SAMPLEFORMATCOMBOBOX_H #define SAMPLEFORMATCOMBOBOX_H -#include #include #include "ui/humanstrings.h" namespace olive { -using namespace core; - class SampleFormatComboBox : public QComboBox { Q_OBJECT diff --git a/app/widget/standardcombos/sampleratecombobox.h b/app/widget/standardcombos/sampleratecombobox.h index dabb86f388..e090700aaa 100644 --- a/app/widget/standardcombos/sampleratecombobox.h +++ b/app/widget/standardcombos/sampleratecombobox.h @@ -21,15 +21,13 @@ #ifndef SAMPLERATECOMBOBOX_H #define SAMPLERATECOMBOBOX_H -#include #include +#include "render/audioparams.h" #include "ui/humanstrings.h" namespace olive { -using namespace core; - class SampleRateComboBox : public QComboBox { Q_OBJECT diff --git a/app/widget/taskview/elapsedcounterwidget.cpp b/app/widget/taskview/elapsedcounterwidget.cpp index 4c7a4788ca..4f1b2efb62 100644 --- a/app/widget/taskview/elapsedcounterwidget.cpp +++ b/app/widget/taskview/elapsedcounterwidget.cpp @@ -20,14 +20,13 @@ #include "elapsedcounterwidget.h" -#include #include #include #include -namespace olive { +#include "util/timecodefunctions.h" -using namespace core; +namespace olive { ElapsedCounterWidget::ElapsedCounterWidget(QWidget* parent) : QWidget(parent), @@ -88,8 +87,8 @@ void ElapsedCounterWidget::UpdateTimers() remaining_ms = 0; } - elapsed_lbl_->setText(tr("Elapsed: %1").arg(QString::fromStdString(Timecode::time_to_string(elapsed_ms)))); - remaining_lbl_->setText(tr("Remaining: %1").arg(QString::fromStdString(Timecode::time_to_string(remaining_ms)))); + elapsed_lbl_->setText(tr("Elapsed: %1").arg(Timecode::time_to_string(elapsed_ms))); + remaining_lbl_->setText(tr("Remaining: %1").arg(Timecode::time_to_string(remaining_ms))); } } diff --git a/app/widget/timebased/timebasedviewselectionmanager.h b/app/widget/timebased/timebasedviewselectionmanager.h index 7390fb1b46..b33cc3867e 100644 --- a/app/widget/timebased/timebasedviewselectionmanager.h +++ b/app/widget/timebased/timebasedviewselectionmanager.h @@ -309,9 +309,8 @@ class TimeBasedViewSelectionManager display_time = initial_drag_item_->time(); } - QString tip = QString::fromStdString(Timecode::time_to_timecode( - display_time, timebase_, - Core::instance()->GetTimecodeDisplay(), false)); + QString tip = Timecode::time_to_timecode(display_time, timebase_, + Core::instance()->GetTimecodeDisplay(), false); last_used_tip_format_ = tip_format; if (!tip_format.isEmpty()) { diff --git a/app/widget/timebased/timescaledobject.h b/app/widget/timebased/timescaledobject.h index ca4a597657..1c4f319b37 100644 --- a/app/widget/timebased/timescaledobject.h +++ b/app/widget/timebased/timescaledobject.h @@ -21,7 +21,6 @@ #ifndef TIMELINESCALEDOBJECT_H #define TIMELINESCALEDOBJECT_H -#include #include #include "node/block/block.h" diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 8d6101655c..88355ceb60 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -671,7 +671,7 @@ bool TimelineWidget::CopySelected(bool cut) } foreach (Block* block, selected_blocks_) { - properties[block][QStringLiteral("in")] = QString::fromStdString((block->in() - earliest_in).toString()); + properties[block][QStringLiteral("in")] = (block->in() - earliest_in).toString(); properties[block][QStringLiteral("track")] = block->track()->ToReference().ToString(); } @@ -1965,7 +1965,7 @@ bool TimelineWidget::PasteInternal(bool insert) for (auto it=res.GetLoadData().properties.cbegin(); it!=res.GetLoadData().properties.cend(); it++) { rational length = static_cast(it.key())->length(); - rational in = rational::fromString(it.value()[QStringLiteral("in")].toStdString()); + rational in = rational::fromString(it.value()[QStringLiteral("in")]); paste_end = qMax(paste_end, paste_start + in + length); } @@ -1977,7 +1977,7 @@ bool TimelineWidget::PasteInternal(bool insert) for (auto it=res.GetLoadData().properties.cbegin(); it!=res.GetLoadData().properties.cend(); it++) { Block *block = static_cast(it.key()); - rational in = rational::fromString(it.value()[QStringLiteral("in")].toStdString()); + rational in = rational::fromString(it.value()[QStringLiteral("in")]); Track::Reference track = Track::Reference::FromString(it.value()[QStringLiteral("track")]); command->add_child(new TrackPlaceBlockCommand(sequence()->track_list(track.type()), diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index 3e1b454fd7..8b1a3df89f 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -149,9 +149,9 @@ void ImportTool::DragMove(TimelineViewMouseEvent *event) // Generate tooltip (showing earliest in point of imported clip) rational tooltip_timebase = parent()->GetTimebaseForTrackType(event->GetTrack().type()); - QString tooltip_text = QString::fromStdString(Timecode::time_to_timecode(earliest_ghost, - tooltip_timebase, - Core::instance()->GetTimecodeDisplay())); + QString tooltip_text = Timecode::time_to_timecode(earliest_ghost, + tooltip_timebase, + Core::instance()->GetTimecodeDisplay()); // Force tooltip to update (otherwise the tooltip won't move as written in the documentation, and could get in the way // of the cursor) diff --git a/app/widget/timelinewidget/tool/pointer.cpp b/app/widget/timelinewidget/tool/pointer.cpp index 452e8dfe94..42136a8b3d 100644 --- a/app/widget/timelinewidget/tool/pointer.cpp +++ b/app/widget/timelinewidget/tool/pointer.cpp @@ -594,10 +594,10 @@ void PointerTool::ProcessDrag(const TimelineCoordinate &mouse_pos) rational tooltip_timebase = parent()->GetTimebaseForTrackType(drag_start_.GetTrack().type()); QToolTip::hideText(); QToolTip::showText(QCursor::pos(), - QString::fromStdString(Timecode::time_to_timecode(time_movement, - tooltip_timebase, - Core::instance()->GetTimecodeDisplay(), - true)), + Timecode::time_to_timecode(time_movement, + tooltip_timebase, + Core::instance()->GetTimecodeDisplay(), + true), parent()); } diff --git a/app/widget/timelinewidget/tool/slip.cpp b/app/widget/timelinewidget/tool/slip.cpp index aa7447df3e..9abae4ece6 100644 --- a/app/widget/timelinewidget/tool/slip.cpp +++ b/app/widget/timelinewidget/tool/slip.cpp @@ -57,10 +57,10 @@ void SlipTool::ProcessDrag(const TimelineCoordinate &mouse_pos) rational tooltip_timebase = parent()->GetTimebaseForTrackType(drag_start_.GetTrack().type()); QToolTip::hideText(); QToolTip::showText(QCursor::pos(), - QString::fromStdString(Timecode::time_to_timecode(time_movement, - tooltip_timebase, - Core::instance()->GetTimecodeDisplay(), - true)), + Timecode::time_to_timecode(time_movement, + tooltip_timebase, + Core::instance()->GetTimecodeDisplay(), + true), parent()); } diff --git a/app/widget/timelinewidget/view/timelineview.cpp b/app/widget/timelinewidget/view/timelineview.cpp index 306a82f21a..b1a6a040ee 100644 --- a/app/widget/timelinewidget/view/timelineview.cpp +++ b/app/widget/timelinewidget/view/timelineview.cpp @@ -105,9 +105,9 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) Block* b = GetItemAtScenePos(timeline_event.GetFrame(), timeline_event.GetTrack().index()); if (b) { setToolTip(tr("In: %1\nOut: %2\nDuration: %3").arg( - QString::fromStdString(Timecode::time_to_timecode(b->in(), timebase(), Core::instance()->GetTimecodeDisplay())), - QString::fromStdString(Timecode::time_to_timecode(b->out(), timebase(), Core::instance()->GetTimecodeDisplay())), - QString::fromStdString(Timecode::time_to_timecode(b->length(), timebase(), Core::instance()->GetTimecodeDisplay())) + Timecode::time_to_timecode(b->in(), timebase(), Core::instance()->GetTimecodeDisplay()), + Timecode::time_to_timecode(b->out(), timebase(), Core::instance()->GetTimecodeDisplay()), + Timecode::time_to_timecode(b->length(), timebase(), Core::instance()->GetTimecodeDisplay()) )); } else { setToolTip(QString()); diff --git a/app/widget/timeruler/timeruler.cpp b/app/widget/timeruler/timeruler.cpp index 87cc879bb1..7b087af46b 100644 --- a/app/widget/timeruler/timeruler.cpp +++ b/app/widget/timeruler/timeruler.cpp @@ -205,7 +205,7 @@ void TimeRuler::drawForeground(QPainter *p, const QRectF &rect) if (text_visible_) { QRect text_rect; Qt::Alignment text_align; - QString timecode_str = QString::fromStdString(Timecode::time_to_timecode(SceneToTime(i), timebase(), Core::instance()->GetTimecodeDisplay())); + QString timecode_str = Timecode::time_to_timecode(SceneToTime(i), timebase(), Core::instance()->GetTimecodeDisplay()); int timecode_width = QtUtils::QFontMetricsWidth(fm, timecode_str); int timecode_left; diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 61d4c30285..2f8e0ceb76 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -514,7 +514,7 @@ void ViewerWidget::UpdateAudioProcessor() AudioParams packed(OLIVE_CONFIG("AudioOutputSampleRate").toInt(), OLIVE_CONFIG("AudioOutputChannelLayout").toULongLong(), - SampleFormat::from_string(OLIVE_CONFIG("AudioOutputSampleFormat").toString().toStdString())); + SampleFormat::from_string(OLIVE_CONFIG("AudioOutputSampleFormat").toString())); audio_processor_.Open(ap, packed, (playback_speed_ == 0) ? 1 : std::abs(playback_speed_)); } @@ -1561,7 +1561,7 @@ void ViewerWidget::Play(bool in_to_out_only) AudioParams ap(OLIVE_CONFIG("AudioRecordingSampleRate").toInt(), OLIVE_CONFIG("AudioRecordingChannelLayout").toULongLong(), - SampleFormat::from_string(OLIVE_CONFIG("AudioRecordingSampleFormat").toString().toStdString())); + SampleFormat::from_string(OLIVE_CONFIG("AudioRecordingSampleFormat").toString())); EncodingParams encode_param; encode_param.EnableAudio(ap, static_cast(OLIVE_CONFIG("AudioRecordingCodec").toInt())); diff --git a/app/widget/viewer/viewersizer.h b/app/widget/viewer/viewersizer.h index 189e99a277..2b748706fc 100644 --- a/app/widget/viewer/viewersizer.h +++ b/app/widget/viewer/viewersizer.h @@ -21,13 +21,12 @@ #ifndef VIEWERSIZER_H #define VIEWERSIZER_H -#include #include #include -namespace olive { +#include "util/rational.h" -using namespace core; +namespace olive { /** * @brief A container widget that enforces the aspect ratio of a child widget diff --git a/cmake/FindOlive.cmake b/cmake/FindOlive.cmake deleted file mode 100644 index 01b57204ae..0000000000 --- a/cmake/FindOlive.cmake +++ /dev/null @@ -1,59 +0,0 @@ -# Olive - Non-Linear Video Editor -# Copyright (C) 2023 Olive Studios LLC -# -# 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 . - -set(LIBOLIVE_COMPONENTS - Core - #Codec -) - -foreach (COMPONENT ${LIBOLIVE_COMPONENTS}) - string(TOLOWER ${COMPONENT} LOWER_COMPONENT) - string(TOUPPER ${COMPONENT} UPPER_COMPONENT) - - # Find include directory for this component - find_path(LIBOLIVE_${UPPER_COMPONENT}_INCLUDEDIR - olive/${LOWER_COMPONENT}/${LOWER_COMPONENT}.h - HINTS - "${LIBOLIVE_LOCATION}" - "$ENV{LIBOLIVE_LOCATION}" - "${LIBOLIVE_ROOT}" - "$ENV{LIBOLIVE_ROOT}" - PATH_SUFFIXES - include/ - ) - - find_library(LIBOLIVE_${UPPER_COMPONENT}_LIBRARY - olive${LOWER_COMPONENT} - HINTS - "${LIBOLIVE_LOCATION}" - "$ENV{LIBOLIVE_LOCATION}" - "${LIBOLIVE_ROOT}" - "$ENV{LIBOLIVE_ROOT}" - PATH_SUFFIXES - lib/ - ) - - list(APPEND LIBOLIVE_LIBRARIES ${LIBOLIVE_${UPPER_COMPONENT}_LIBRARY}) - list(APPEND LIBOLIVE_INCLUDE_DIRS ${LIBOLIVE_${UPPER_COMPONENT}_INCLUDEDIR}) -endforeach() - -include(FindPackageHandleStandardArgs) - -find_package_handle_standard_args(Olive - REQUIRED_VARS - LIBOLIVE_LIBRARIES - LIBOLIVE_INCLUDE_DIRS -) diff --git a/ext/CMakeLists.txt b/ext/CMakeLists.txt index 135d557c33..a70c9570d3 100644 --- a/ext/CMakeLists.txt +++ b/ext/CMakeLists.txt @@ -14,8 +14,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -add_subdirectory(core EXCLUDE_FROM_ALL) - set(KDDockWidgets_STATIC ON CACHE INTERNAL "Force KDDockWidgets to build statically") set(KDDockWidgets_QT6 ${BUILD_QT6} CACHE INTERNAL "Conform KDDockWidgets' Qt 6 setting to ours") add_subdirectory(KDDockWidgets EXCLUDE_FROM_ALL) diff --git a/ext/core b/ext/core deleted file mode 160000 index 2777928248..0000000000 --- a/ext/core +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 277792824801495e868580ca86f6e7a1b53e4779 diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt new file mode 100644 index 0000000000..f4448fa503 --- /dev/null +++ b/lib/CMakeLists.txt @@ -0,0 +1,17 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2023 Olive Studios LLC +# +# 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 . + +add_subdirectory(olive) diff --git a/lib/olive/CMakeLists.txt b/lib/olive/CMakeLists.txt new file mode 100644 index 0000000000..3f84a6d0e3 --- /dev/null +++ b/lib/olive/CMakeLists.txt @@ -0,0 +1,33 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2023 Olive Studios LLC +# +# 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 . + +add_subdirectory(src) + +add_library(olivecore + ${OLIVECORE_SOURCES} +) + +target_include_directories(olivecore PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/include" + "${CMAKE_SOURCE_DIR}/app" +) +target_link_libraries(olivecore PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets) + +if (OpenTimelineIO_FOUND) + target_compile_definitions(olivecore PRIVATE USE_OTIO) + target_include_directories(olivecore PRIVATE ${OTIO_INCLUDE_DIRS}) + target_link_libraries(olivecore PRIVATE ${OTIO_LIBRARIES}) +endif() diff --git a/lib/olive/include/render/audioparams.h b/lib/olive/include/render/audioparams.h new file mode 100644 index 0000000000..4a5ef4cd66 --- /dev/null +++ b/lib/olive/include/render/audioparams.h @@ -0,0 +1,192 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef LIBOLIVECORE_AUDIOPARAMS_H +#define LIBOLIVECORE_AUDIOPARAMS_H + +extern "C" { +#include +} + +#include +#include +#include + +#include "sampleformat.h" +#include "util/rational.h" + +namespace olive { + +class AudioParams { +public: + AudioParams() : + sample_rate_(0), + channel_layout_(0), + format_(SampleFormat::INVALID) + { + set_default_footage_parameters(); + + // Cache channel count + calculate_channel_count(); + } + + AudioParams(const int& sample_rate, const uint64_t& channel_layout, const SampleFormat& format) : + sample_rate_(sample_rate), + channel_layout_(channel_layout), + format_(format) + { + set_default_footage_parameters(); + timebase_ = sample_rate_as_time_base(); + + // Cache channel count + calculate_channel_count(); + } + + int sample_rate() const + { + return sample_rate_; + } + + void set_sample_rate(int sample_rate) + { + sample_rate_ = sample_rate; + } + + uint64_t channel_layout() const + { + return channel_layout_; + } + + void set_channel_layout(uint64_t channel_layout) + { + channel_layout_ = channel_layout; + calculate_channel_count(); + } + + rational time_base() const + { + return timebase_; + } + + void set_time_base(const rational& timebase) + { + timebase_ = timebase; + } + + rational sample_rate_as_time_base() const + { + return rational(1, sample_rate()); + } + + SampleFormat format() const + { + return format_; + } + + void set_format(SampleFormat format) + { + format_ = format; + } + + bool enabled() const + { + return enabled_; + } + + void set_enabled(bool e) + { + enabled_ = e; + } + + int stream_index() const + { + return stream_index_; + } + + void set_stream_index(int s) + { + stream_index_ = s; + } + + int64_t duration() const + { + return duration_; + } + + void set_duration(int64_t duration) + { + duration_ = duration; + } + + int64_t time_to_bytes(const double& time) const; + int64_t time_to_bytes(const rational& time) const; + int64_t time_to_bytes_per_channel(const double& time) const; + int64_t time_to_bytes_per_channel(const rational& time) const; + int64_t time_to_samples(const double& time) const; + int64_t time_to_samples(const rational& time) const; + int64_t samples_to_bytes(const int64_t& samples) const; + int64_t samples_to_bytes_per_channel(const int64_t& samples) const; + rational samples_to_time(const int64_t& samples) const; + int64_t bytes_to_samples(const int64_t &bytes) const; + rational bytes_to_time(const int64_t &bytes) const; + rational bytes_per_channel_to_time(const int64_t &bytes) const; + int channel_count() const; + int bytes_per_sample_per_channel() const; + int bits_per_sample() const; + bool is_valid() const; + + void load(QXmlStreamReader *reader); + void save(QXmlStreamWriter *writer) const; + + bool operator==(const AudioParams& other) const; + bool operator!=(const AudioParams& other) const; + + static const std::vector kSupportedChannelLayouts; + static const std::vector kSupportedSampleRates; + +private: + void set_default_footage_parameters() + { + enabled_ = true; + stream_index_ = 0; + duration_ = 0; + } + + void calculate_channel_count(); + + int sample_rate_; + + uint64_t channel_layout_; + + int channel_count_; + + SampleFormat format_; + + // Footage-specific + int enabled_; // Switching this to int fixes GCC 11 stringop-overflow issue, I guess a byte-alignment issue? + int stream_index_; + int64_t duration_; + rational timebase_; + +}; + +} + +#endif // LIBOLIVECORE_AUDIOPARAMS_H diff --git a/lib/olive/include/render/pixelformat.h b/lib/olive/include/render/pixelformat.h new file mode 100644 index 0000000000..6bdae212e1 --- /dev/null +++ b/lib/olive/include/render/pixelformat.h @@ -0,0 +1,108 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef LIBOLIVECORE_PIXELFORMAT_H +#define LIBOLIVECORE_PIXELFORMAT_H + +namespace olive { + +class PixelFormat +{ +public: + enum Format { + INVALID = -1, + U8, + U16, + F16, + F32, + COUNT + }; + + PixelFormat(Format f = INVALID) { f_ = f; } + + operator Format() const { return f_; } + + static int byte_count(Format f) + { + switch (f) { + case INVALID: + case COUNT: + break; + case U8: + return 1; + case U16: + case F16: + return 2; + case F32: + return 4; + } + + return 0; + } + + const char *to_string() const + { + switch (f_) { + case U8: return "u8"; + case U16: return "u16"; + case F16: return "f16"; + case F32: return "f32"; + case INVALID: + case COUNT: + break; + } + + return ""; + } + + int byte_count() const + { + return byte_count(f_); + } + + static bool is_float(Format f) + { + switch (f) { + case INVALID: + case COUNT: + case U8: + case U16: + break; + case F16: + case F32: + return true; + } + + return false; + } + + bool is_float() const + { + return is_float(f_); + } + +private: + Format f_; + +}; + +} + +#endif // LIBOLIVECORE_PIXELFORMAT_H diff --git a/lib/olive/include/render/samplebuffer.h b/lib/olive/include/render/samplebuffer.h new file mode 100644 index 0000000000..53b9bbade9 --- /dev/null +++ b/lib/olive/include/render/samplebuffer.h @@ -0,0 +1,123 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef LIBOLIVECORE_SAMPLEBUFFER_H +#define LIBOLIVECORE_SAMPLEBUFFER_H + +#include +#include + +#include "audioparams.h" +#include "../util/rational.h" + +namespace olive { + +/** + * @brief A buffer of audio samples + * + * Audio samples in this structure are always stored in PLANAR (separated by channel). This is done to simplify audio + * rendering code. This replaces the old system of using QByteArrays (containing packed audio) and while SampleBuffer + * replaces many of those in the rendering/processing side of things, QByteArrays are currently still in use for + * playback, including reading to and from the cache. + */ +class SampleBuffer +{ +public: + SampleBuffer(); + SampleBuffer(const AudioParams& audio_params, const rational& length); + SampleBuffer(const AudioParams& audio_params, size_t samples_per_channel); + + SampleBuffer rip_channel(int channel) const; + std::vector rip_channel_vector(int channel) const; + + const AudioParams& audio_params() const; + void set_audio_params(const AudioParams& params); + + const size_t &sample_count() const { return sample_count_per_channel_; } + void set_sample_count(const size_t &sample_count); + void set_sample_count(const rational &length) + { + set_sample_count(audio_params_.time_to_samples(length)); + } + + float* data(int channel) + { + return data_[channel].data(); + } + + const float* data(int channel) const + { + return data_.at(channel).data(); + } + + std::vector to_raw_ptrs() + { + std::vector r(data_.size()); + for (size_t i=0; i > data_; + +}; + +} + +#endif // LIBOLIVECORE_SAMPLEBUFFER_H diff --git a/lib/olive/include/render/sampleformat.h b/lib/olive/include/render/sampleformat.h new file mode 100644 index 0000000000..1d9abda49b --- /dev/null +++ b/lib/olive/include/render/sampleformat.h @@ -0,0 +1,264 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef LIBOLIVECORE_SAMPLEFORMAT_H +#define LIBOLIVECORE_SAMPLEFORMAT_H + +#include +#include + +namespace olive { + +class SampleFormat +{ +public: + enum Format { + INVALID = -1, + + U8P, + S16P, + S32P, + S64P, + F32P, + F64P, + + U8, + S16, + S32, + S64, + F32, + F64, + + COUNT, + + PLANAR_START = U8P, + PACKED_START = U8, + PLANAR_END = PACKED_START, + PACKED_END = COUNT, + }; + + SampleFormat(Format f = INVALID) { f_ = f; } + + operator Format() const { return f_; } + + static int byte_count(Format f) + { + switch (f) { + case U8: + case U8P: + return 1; + case S16: + case S16P: + return 2; + case S32: + case F32: + case S32P: + case F32P: + return 4; + case S64: + case F64: + case S64P: + case F64P: + return 8; + case INVALID: + case COUNT: + break; + } + + return 0; + } + + int byte_count() const + { + return byte_count(f_); + } + + static QString to_string(Format f) + { + switch (f) { + case INVALID: + case COUNT: + break; + case U8: return "u8"; + case S16: return "s16"; + case S32: return "s32"; + case S64: return "s64"; + case F32: return "f32"; + case F64: return "f64"; + case U8P: return "u8p"; + case S16P: return "s16p"; + case S32P: return "s32p"; + case S64P: return "s64p"; + case F32P: return "f32p"; + case F64P: return "f64p"; + } + + return ""; + } + + QString to_string() const + { + return to_string(f_); + } + + static SampleFormat from_string(const QString &s) + { + if (s.isEmpty()) { + return INVALID; + } else if (s == "u8") { + return U8; + } else if (s == "s16") { + return S16; + } else if (s == "s32") { + return S32; + } else if (s == "s64") { + return S64; + } else if (s == "f32") { + return F32; + } else if (s == "f64") { + return F64; + } else if (s == "u8p") { + return U8P; + } else if (s == "s16p") { + return S16P; + } else if (s == "s32p") { + return S32P; + } else if (s == "s64p") { + return S64P; + } else if (s == "f32p") { + return F32P; + } else if (s == "f64p") { + return F64P; + } else { + // Deprecated: sample formats used to be serialized as an integer. Handle that here, but we'll + // probably remove that eventually. + bool ok; + int i = s.toInt(&ok); + if (ok && i > INVALID && i < COUNT) { + return static_cast(i); + } else { + // Failed to deserialize from string + return INVALID; + } + } + } + + static bool is_packed(Format f) + { + return f >= PACKED_START && f < PACKED_END; + } + + bool is_packed() const { return is_packed(f_); } + + static bool is_planar(Format f) + { + return f >= PLANAR_START && f < PLANAR_END; + } + + bool is_planar() const { return is_planar(f_); } + + static SampleFormat to_packed_equivalent(SampleFormat fmt) + { + switch (fmt) { + + // For packed input, just return input + case U8: + case S16: + case S32: + case S64: + case F32: + case F64: + return fmt; + + // Convert to packed + case U8P: + return U8; + case S16P: + return S16; + case S32P: + return S32; + case S64P: + return S64; + case F32P: + return F32; + case F64P: + return F64; + + case INVALID: + case COUNT: + break; + } + + return INVALID; + } + + SampleFormat to_packed_equivalent() const + { + return to_packed_equivalent(f_); + } + + static SampleFormat to_planar_equivalent(SampleFormat fmt) + { + switch (fmt) { + + // Convert to planar + case U8: + return U8P; + case S16: + return S16P; + case S32: + return S32P; + case S64: + return S64P; + case F32: + return F32P; + case F64: + return F64P; + + // For planar input, just return input + case U8P: + case S16P: + case S32P: + case S64P: + case F32P: + case F64P: + return fmt; + + case INVALID: + case COUNT: + break; + } + + return INVALID; + } + + SampleFormat to_planar_equivalent() const + { + return to_planar_equivalent(f_); + } + +private: + Format f_; + +}; + +} + +#endif // LIBOLIVECORE_SAMPLEFORMAT_H diff --git a/lib/olive/include/util/bezier.h b/lib/olive/include/util/bezier.h new file mode 100644 index 0000000000..1d7cc4faf3 --- /dev/null +++ b/lib/olive/include/util/bezier.h @@ -0,0 +1,98 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef LIBOLIVECORE_BEZIER_H +#define LIBOLIVECORE_BEZIER_H + +#include + +namespace olive { + +class Bezier +{ +public: + Bezier(); + Bezier(double x, double y); + Bezier(double x, double y, double cp1_x, double cp1_y, double cp2_x, double cp2_y); + + const double &x() const {return x_; } + const double &y() const {return y_; } + const double &cp1_x() const { return cp1_x_; } + const double &cp1_y() const { return cp1_y_; } + const double &cp2_x() const { return cp2_x_; } + const double &cp2_y() const { return cp2_y_; } + + Imath::V2d to_vec() const + { + return Imath::V2d(x_, y_); + } + + Imath::V2d control_point_1_to_vec() const + { + return Imath::V2d(cp1_x_, cp1_y_); + } + + Imath::V2d control_point_2_to_vec() const + { + return Imath::V2d(cp2_x_, cp2_y_); + } + + void set_x(const double &x) { x_ = x; } + void set_y(const double &y) { y_ = y; } + void set_cp1_x(const double &cp1_x) { cp1_x_ = cp1_x; } + void set_cp1_y(const double &cp1_y) { cp1_y_ = cp1_y; } + void set_cp2_x(const double &cp2_x) { cp2_x_ = cp2_x; } + void set_cp2_y(const double &cp2_y) { cp2_y_ = cp2_y; } + + static double QuadraticXtoT(double x, double a, double b, double c); + + static double QuadraticTtoY(double a, double b, double c, double t); + + static double QuadraticXtoY(double x, const Imath::V2d &a, const Imath::V2d &b, const Imath::V2d &c) + { + return QuadraticTtoY(a.y, b.y, c.y, QuadraticXtoT(x, a.x, b.x, c.x)); + } + + static double CubicXtoT(double x, double a, double b, double c, double d); + + static double CubicTtoY(double a, double b, double c, double d, double t); + + static double CubicXtoY(double x, const Imath::V2d &a, const Imath::V2d &b, const Imath::V2d &c, const Imath::V2d &d) + { + return CubicTtoY(a.y, b.y, c.y, d.y, CubicXtoT(x, a.x, b.x, c.x, d.x)); + } + +private: + static double CalculateTFromX(bool cubic, double x, double a, double b, double c, double d); + + double x_; + double y_; + + double cp1_x_; + double cp1_y_; + + double cp2_x_; + double cp2_y_; + +}; + +} + +#endif // LIBOLIVECORE_BEZIER_H diff --git a/lib/olive/include/util/color.h b/lib/olive/include/util/color.h new file mode 100644 index 0000000000..921bc44bff --- /dev/null +++ b/lib/olive/include/util/color.h @@ -0,0 +1,150 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef LIBOLIVECORE_COLOR_H +#define LIBOLIVECORE_COLOR_H + +#include "../render/pixelformat.h" + +namespace olive { + +/** + * @brief High precision 32-bit DataType based RGBA color value + */ +class Color +{ +public: + using DataType = float; + static constexpr unsigned int RGBA = 4; + + Color() + { + for (unsigned int i=0;i. + +***/ + +#ifndef LIBOLIVECORE_CPUOPTIMIZE_H +#define LIBOLIVECORE_CPUOPTIMIZE_H + +#if defined(__x86_64__) || defined(__i386__) +#define OLIVE_PROCESSOR_X86 +#include +#elif defined(__aarch64__) +#define OLIVE_PROCESSOR_ARM +#include "sse2neon.h" +#endif + +#endif // LIBOLIVECORE_CPUOPTIMIZE_H diff --git a/lib/olive/include/util/log.h b/lib/olive/include/util/log.h new file mode 100644 index 0000000000..3323093258 --- /dev/null +++ b/lib/olive/include/util/log.h @@ -0,0 +1,52 @@ +#ifndef LOG_H +#define LOG_H + +#include + +namespace olive { + +class Log +{ +public: + Log(const char *type) + { + std::cerr << "[" << type << "]"; + } + + ~Log() + { + std::cerr << std::endl; + } + + template + Log &operator<<(const T &t) + { + std::cerr << " " << t; + return *this; + } + + static Log Debug() + { + return Log("DEBUG"); + } + + static Log Info() + { + return Log("INFO"); + } + + static Log Warning() + { + return Log("WARNING"); + } + + static Log Error() + { + return Log("ERROR"); + } + +}; + +} + +#endif // LOG_H diff --git a/app/node/project/serializer/typeserializer.h b/lib/olive/include/util/math.h similarity index 60% rename from app/node/project/serializer/typeserializer.h rename to lib/olive/include/util/math.h index c1b2bd5058..95afc389f2 100644 --- a/app/node/project/serializer/typeserializer.h +++ b/lib/olive/include/util/math.h @@ -1,7 +1,7 @@ /*** Olive - Non-Linear Video Editor - Copyright (C) 2023 Olive Team + Copyright (C) 2023 Olive Studios LLC 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 @@ -18,29 +18,11 @@ ***/ -#ifndef TYPESERIALIZER_H -#define TYPESERIALIZER_H - -#include -#include -#include - -#include "common/xmlutils.h" +#ifndef LIBOLIVECORE_MATH_H +#define LIBOLIVECORE_MATH_H namespace olive { -using namespace core; - -class TypeSerializer -{ -public: - TypeSerializer() = default; - - static AudioParams LoadAudioParams(QXmlStreamReader *reader); - static void SaveAudioParams(QXmlStreamWriter *writer, const AudioParams &a); - -}; - } -#endif // TYPESERIALIZER_H +#endif // LIBOLIVECORE_MATH_H diff --git a/lib/olive/include/util/rational.h b/lib/olive/include/util/rational.h new file mode 100644 index 0000000000..1e613565c3 --- /dev/null +++ b/lib/olive/include/util/rational.h @@ -0,0 +1,148 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef LIBOLIVECORE_RATIONAL_H +#define LIBOLIVECORE_RATIONAL_H + +extern "C" { +#include +} + +#include +#include + +#ifdef USE_OTIO +#include +#endif + +namespace olive { + +class rational +{ +public: + rational(const int &numerator = 0) + { + r_.num = numerator; + r_.den = 1; + } + + rational(const int &numerator, const int &denominator) + { + r_.num = numerator; + r_.den = denominator; + + fix_signs(); + reduce(); + } + + rational(const rational &rhs) = default; + + rational(const AVRational& r) + { + r_ = r; + + fix_signs(); + } + + static rational fromDouble(const double& flt, bool *ok = nullptr); + static rational fromString(const QString& str, bool* ok = nullptr); + + static const rational NaN; + + //Assignment Operators + const rational& operator=(const rational &rhs); + const rational& operator+=(const rational &rhs); + const rational& operator-=(const rational &rhs); + const rational& operator/=(const rational &rhs); + const rational& operator*=(const rational &rhs); + + //Binary math operators + rational operator+(const rational &rhs) const; + rational operator-(const rational &rhs) const; + rational operator/(const rational &rhs) const; + rational operator*(const rational &rhs) const; + + //Relational and equality operators + bool operator<(const rational &rhs) const; + bool operator<=(const rational &rhs) const; + bool operator>(const rational &rhs) const; + bool operator>=(const rational &rhs) const; + bool operator==(const rational &rhs) const; + bool operator!=(const rational &rhs) const; + + //Unary operators + const rational& operator+() const { return *this; } + rational operator-() const { return rational(r_.num, -r_.den); } + bool operator!() const { return !r_.num; } + + //Function: convert to double + double toDouble() const; + + AVRational toAVRational() const; + +#ifdef USE_OTIO + static rational fromRationalTime(const opentime::RationalTime &t) + { + // Is this the best way to do this? + return fromDouble(t.to_seconds()); + } + + // Convert Olive rationals to opentime rationals with the given framerate (defaults to 24) + opentime::RationalTime toRationalTime(double framerate = 24) const; +#endif + + // Produce "flipped" version + rational flipped() const; + void flip(); + + // Returns whether the rational is valid but equal to zero or not + // + // A NaN is always a null, but a null is not always a NaN + bool isNull() const { return r_.num == 0; } + + // Returns whether this rational is not a valid number (denominator == 0) + bool isNaN() const { return r_.den == 0; } + + const int& numerator() const { return r_.num; } + const int& denominator() const { return r_.den; } + + QString toString() const; + + friend std::ostream& operator<<(std::ostream &out, const rational &value) + { + out << value.r_.num << '/' << value.r_.den; + + return out; + } + +private: + void fix_signs(); + void reduce(); + + AVRational r_; + +}; + +#define RATIONAL_MIN rational(INT_MIN) +#define RATIONAL_MAX rational(INT_MAX) + +} + +#endif // LIBOLIVECORE_RATIONAL_H diff --git a/lib/olive/include/util/sse2neon.h b/lib/olive/include/util/sse2neon.h new file mode 100644 index 0000000000..c3ce3e3189 --- /dev/null +++ b/lib/olive/include/util/sse2neon.h @@ -0,0 +1,8757 @@ +#ifndef SSE2NEON_H +#define SSE2NEON_H + +// This header file provides a simple API translation layer +// between SSE intrinsics to their corresponding Arm/Aarch64 NEON versions +// +// This header file does not yet translate all of the SSE intrinsics. +// +// Contributors to this work are: +// John W. Ratcliff +// Brandon Rowlett +// Ken Fast +// Eric van Beurden +// Alexander Potylitsin +// Hasindu Gamaarachchi +// Jim Huang +// Mark Cheng +// Malcolm James MacLeod +// Devin Hussey (easyaspi314) +// Sebastian Pop +// Developer Ecosystem Engineering +// Danila Kutenin +// François Turban (JishinMaster) +// Pei-Hsuan Hung +// Yang-Hao Yuan +// Syoyo Fujita +// Brecht Van Lommel + +/* + * sse2neon is freely redistributable under the MIT License. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Tunable configurations */ + +/* Enable precise implementation of math operations + * This would slow down the computation a bit, but gives consistent result with + * x86 SSE. (e.g. would solve a hole or NaN pixel in the rendering result) + */ +/* _mm_min|max_ps|ss|pd|sd */ +#ifndef SSE2NEON_PRECISE_MINMAX +#define SSE2NEON_PRECISE_MINMAX (0) +#endif +/* _mm_rcp_ps and _mm_div_ps */ +#ifndef SSE2NEON_PRECISE_DIV +#define SSE2NEON_PRECISE_DIV (0) +#endif +/* _mm_sqrt_ps and _mm_rsqrt_ps */ +#ifndef SSE2NEON_PRECISE_SQRT +#define SSE2NEON_PRECISE_SQRT (0) +#endif +/* _mm_dp_pd */ +#ifndef SSE2NEON_PRECISE_DP +#define SSE2NEON_PRECISE_DP (0) +#endif + +/* compiler specific definitions */ +#if defined(__GNUC__) || defined(__clang__) +#pragma push_macro("FORCE_INLINE") +#pragma push_macro("ALIGN_STRUCT") +#define FORCE_INLINE static inline __attribute__((always_inline)) +#define ALIGN_STRUCT(x) __attribute__((aligned(x))) +#define _sse2neon_likely(x) __builtin_expect(!!(x), 1) +#define _sse2neon_unlikely(x) __builtin_expect(!!(x), 0) +#else /* non-GNU / non-clang compilers */ +#warning "Macro name collisions may happen with unsupported compiler." +#ifndef FORCE_INLINE +#define FORCE_INLINE static inline +#endif +#ifndef ALIGN_STRUCT +#define ALIGN_STRUCT(x) __declspec(align(x)) +#endif +#define _sse2neon_likely(x) (x) +#define _sse2neon_unlikely(x) (x) +#endif + +#include +#include + +/* Architecture-specific build options */ +/* FIXME: #pragma GCC push_options is only available on GCC */ +#if defined(__GNUC__) +#if defined(__arm__) && __ARM_ARCH == 7 +/* According to ARM C Language Extensions Architecture specification, + * __ARM_NEON is defined to a value indicating the Advanced SIMD (NEON) + * architecture supported. + */ +#if !defined(__ARM_NEON) || !defined(__ARM_NEON__) +#error "You must enable NEON instructions (e.g. -mfpu=neon) to use SSE2NEON." +#endif +#if !defined(__clang__) +#pragma GCC push_options +#pragma GCC target("fpu=neon") +#endif +#elif defined(__aarch64__) +#if !defined(__clang__) +#pragma GCC push_options +#pragma GCC target("+simd") +#endif +#else +#error "Unsupported target. Must be either ARMv7-A+NEON or ARMv8-A." +#endif +#endif + +#include + +/* Rounding functions require either Aarch64 instructions or libm failback */ +#if !defined(__aarch64__) +#include +#endif + +/* "__has_builtin" can be used to query support for built-in functions + * provided by gcc/clang and other compilers that support it. + */ +#ifndef __has_builtin /* GCC prior to 10 or non-clang compilers */ +/* Compatibility with gcc <= 9 */ +#if __GNUC__ <= 9 +#define __has_builtin(x) HAS##x +#define HAS__builtin_popcount 1 +#define HAS__builtin_popcountll 1 +#else +#define __has_builtin(x) 0 +#endif +#endif + +/** + * MACRO for shuffle parameter for _mm_shuffle_ps(). + * Argument fp3 is a digit[0123] that represents the fp from argument "b" + * of mm_shuffle_ps that will be placed in fp3 of result. fp2 is the same + * for fp2 in result. fp1 is a digit[0123] that represents the fp from + * argument "a" of mm_shuffle_ps that will be places in fp1 of result. + * fp0 is the same for fp0 of result. + */ +#define _MM_SHUFFLE(fp3, fp2, fp1, fp0) \ + (((fp3) << 6) | ((fp2) << 4) | ((fp1) << 2) | ((fp0))) + +/* Rounding mode macros. */ +#define _MM_FROUND_TO_NEAREST_INT 0x00 +#define _MM_FROUND_TO_NEG_INF 0x01 +#define _MM_FROUND_TO_POS_INF 0x02 +#define _MM_FROUND_TO_ZERO 0x03 +#define _MM_FROUND_CUR_DIRECTION 0x04 +#define _MM_FROUND_NO_EXC 0x08 +#define _MM_FROUND_RAISE_EXC 0x00 +#define _MM_FROUND_NINT (_MM_FROUND_TO_NEAREST_INT | _MM_FROUND_RAISE_EXC) +#define _MM_FROUND_FLOOR (_MM_FROUND_TO_NEG_INF | _MM_FROUND_RAISE_EXC) +#define _MM_FROUND_CEIL (_MM_FROUND_TO_POS_INF | _MM_FROUND_RAISE_EXC) +#define _MM_FROUND_TRUNC (_MM_FROUND_TO_ZERO | _MM_FROUND_RAISE_EXC) +#define _MM_FROUND_RINT (_MM_FROUND_CUR_DIRECTION | _MM_FROUND_RAISE_EXC) +#define _MM_FROUND_NEARBYINT (_MM_FROUND_CUR_DIRECTION | _MM_FROUND_NO_EXC) +#define _MM_ROUND_NEAREST 0x0000 +#define _MM_ROUND_DOWN 0x2000 +#define _MM_ROUND_UP 0x4000 +#define _MM_ROUND_TOWARD_ZERO 0x6000 +/* Flush zero mode macros. */ +#define _MM_FLUSH_ZERO_MASK 0x8000 +#define _MM_FLUSH_ZERO_ON 0x8000 +#define _MM_FLUSH_ZERO_OFF 0x0000 +/* Denormals are zeros mode macros. */ +#define _MM_DENORMALS_ZERO_MASK 0x0040 +#define _MM_DENORMALS_ZERO_ON 0x0040 +#define _MM_DENORMALS_ZERO_OFF 0x0000 + +/* indicate immediate constant argument in a given range */ +#define __constrange(a, b) const + +/* A few intrinsics accept traditional data types like ints or floats, but + * most operate on data types that are specific to SSE. + * If a vector type ends in d, it contains doubles, and if it does not have + * a suffix, it contains floats. An integer vector type can contain any type + * of integer, from chars to shorts to unsigned long longs. + */ +typedef int64x1_t __m64; +typedef float32x4_t __m128; /* 128-bit vector containing 4 floats */ +// On ARM 32-bit architecture, the float64x2_t is not supported. +// The data type __m128d should be represented in a different way for related +// intrinsic conversion. +#if defined(__aarch64__) +typedef float64x2_t __m128d; /* 128-bit vector containing 2 doubles */ +#else +typedef float32x4_t __m128d; +#endif +typedef int64x2_t __m128i; /* 128-bit vector containing integers */ + +/* type-safe casting between types */ + +#define vreinterpretq_m128_f16(x) vreinterpretq_f32_f16(x) +#define vreinterpretq_m128_f32(x) (x) +#define vreinterpretq_m128_f64(x) vreinterpretq_f32_f64(x) + +#define vreinterpretq_m128_u8(x) vreinterpretq_f32_u8(x) +#define vreinterpretq_m128_u16(x) vreinterpretq_f32_u16(x) +#define vreinterpretq_m128_u32(x) vreinterpretq_f32_u32(x) +#define vreinterpretq_m128_u64(x) vreinterpretq_f32_u64(x) + +#define vreinterpretq_m128_s8(x) vreinterpretq_f32_s8(x) +#define vreinterpretq_m128_s16(x) vreinterpretq_f32_s16(x) +#define vreinterpretq_m128_s32(x) vreinterpretq_f32_s32(x) +#define vreinterpretq_m128_s64(x) vreinterpretq_f32_s64(x) + +#define vreinterpretq_f16_m128(x) vreinterpretq_f16_f32(x) +#define vreinterpretq_f32_m128(x) (x) +#define vreinterpretq_f64_m128(x) vreinterpretq_f64_f32(x) + +#define vreinterpretq_u8_m128(x) vreinterpretq_u8_f32(x) +#define vreinterpretq_u16_m128(x) vreinterpretq_u16_f32(x) +#define vreinterpretq_u32_m128(x) vreinterpretq_u32_f32(x) +#define vreinterpretq_u64_m128(x) vreinterpretq_u64_f32(x) + +#define vreinterpretq_s8_m128(x) vreinterpretq_s8_f32(x) +#define vreinterpretq_s16_m128(x) vreinterpretq_s16_f32(x) +#define vreinterpretq_s32_m128(x) vreinterpretq_s32_f32(x) +#define vreinterpretq_s64_m128(x) vreinterpretq_s64_f32(x) + +#define vreinterpretq_m128i_s8(x) vreinterpretq_s64_s8(x) +#define vreinterpretq_m128i_s16(x) vreinterpretq_s64_s16(x) +#define vreinterpretq_m128i_s32(x) vreinterpretq_s64_s32(x) +#define vreinterpretq_m128i_s64(x) (x) + +#define vreinterpretq_m128i_u8(x) vreinterpretq_s64_u8(x) +#define vreinterpretq_m128i_u16(x) vreinterpretq_s64_u16(x) +#define vreinterpretq_m128i_u32(x) vreinterpretq_s64_u32(x) +#define vreinterpretq_m128i_u64(x) vreinterpretq_s64_u64(x) + +#define vreinterpretq_f32_m128i(x) vreinterpretq_f32_s64(x) +#define vreinterpretq_f64_m128i(x) vreinterpretq_f64_s64(x) + +#define vreinterpretq_s8_m128i(x) vreinterpretq_s8_s64(x) +#define vreinterpretq_s16_m128i(x) vreinterpretq_s16_s64(x) +#define vreinterpretq_s32_m128i(x) vreinterpretq_s32_s64(x) +#define vreinterpretq_s64_m128i(x) (x) + +#define vreinterpretq_u8_m128i(x) vreinterpretq_u8_s64(x) +#define vreinterpretq_u16_m128i(x) vreinterpretq_u16_s64(x) +#define vreinterpretq_u32_m128i(x) vreinterpretq_u32_s64(x) +#define vreinterpretq_u64_m128i(x) vreinterpretq_u64_s64(x) + +#define vreinterpret_m64_s8(x) vreinterpret_s64_s8(x) +#define vreinterpret_m64_s16(x) vreinterpret_s64_s16(x) +#define vreinterpret_m64_s32(x) vreinterpret_s64_s32(x) +#define vreinterpret_m64_s64(x) (x) + +#define vreinterpret_m64_u8(x) vreinterpret_s64_u8(x) +#define vreinterpret_m64_u16(x) vreinterpret_s64_u16(x) +#define vreinterpret_m64_u32(x) vreinterpret_s64_u32(x) +#define vreinterpret_m64_u64(x) vreinterpret_s64_u64(x) + +#define vreinterpret_m64_f16(x) vreinterpret_s64_f16(x) +#define vreinterpret_m64_f32(x) vreinterpret_s64_f32(x) +#define vreinterpret_m64_f64(x) vreinterpret_s64_f64(x) + +#define vreinterpret_u8_m64(x) vreinterpret_u8_s64(x) +#define vreinterpret_u16_m64(x) vreinterpret_u16_s64(x) +#define vreinterpret_u32_m64(x) vreinterpret_u32_s64(x) +#define vreinterpret_u64_m64(x) vreinterpret_u64_s64(x) + +#define vreinterpret_s8_m64(x) vreinterpret_s8_s64(x) +#define vreinterpret_s16_m64(x) vreinterpret_s16_s64(x) +#define vreinterpret_s32_m64(x) vreinterpret_s32_s64(x) +#define vreinterpret_s64_m64(x) (x) + +#define vreinterpret_f32_m64(x) vreinterpret_f32_s64(x) + +#if defined(__aarch64__) +#define vreinterpretq_m128d_s32(x) vreinterpretq_f64_s32(x) +#define vreinterpretq_m128d_s64(x) vreinterpretq_f64_s64(x) + +#define vreinterpretq_m128d_u64(x) vreinterpretq_f64_u64(x) + +#define vreinterpretq_m128d_f32(x) vreinterpretq_f64_f32(x) +#define vreinterpretq_m128d_f64(x) (x) + +#define vreinterpretq_s64_m128d(x) vreinterpretq_s64_f64(x) + +#define vreinterpretq_u32_m128d(x) vreinterpretq_u32_f64(x) +#define vreinterpretq_u64_m128d(x) vreinterpretq_u64_f64(x) + +#define vreinterpretq_f64_m128d(x) (x) +#define vreinterpretq_f32_m128d(x) vreinterpretq_f32_f64(x) +#else +#define vreinterpretq_m128d_s32(x) vreinterpretq_f32_s32(x) +#define vreinterpretq_m128d_s64(x) vreinterpretq_f32_s64(x) + +#define vreinterpretq_m128d_u32(x) vreinterpretq_f32_u32(x) +#define vreinterpretq_m128d_u64(x) vreinterpretq_f32_u64(x) + +#define vreinterpretq_m128d_f32(x) (x) + +#define vreinterpretq_s64_m128d(x) vreinterpretq_s64_f32(x) + +#define vreinterpretq_u32_m128d(x) vreinterpretq_u32_f32(x) +#define vreinterpretq_u64_m128d(x) vreinterpretq_u64_f32(x) + +#define vreinterpretq_f32_m128d(x) (x) +#endif + +// A struct is defined in this header file called 'SIMDVec' which can be used +// by applications which attempt to access the contents of an __m128 struct +// directly. It is important to note that accessing the __m128 struct directly +// is bad coding practice by Microsoft: @see: +// https://docs.microsoft.com/en-us/cpp/cpp/m128 +// +// However, some legacy source code may try to access the contents of an __m128 +// struct directly so the developer can use the SIMDVec as an alias for it. Any +// casting must be done manually by the developer, as you cannot cast or +// otherwise alias the base NEON data type for intrinsic operations. +// +// union intended to allow direct access to an __m128 variable using the names +// that the MSVC compiler provides. This union should really only be used when +// trying to access the members of the vector as integer values. GCC/clang +// allow native access to the float members through a simple array access +// operator (in C since 4.6, in C++ since 4.8). +// +// Ideally direct accesses to SIMD vectors should not be used since it can cause +// a performance hit. If it really is needed however, the original __m128 +// variable can be aliased with a pointer to this union and used to access +// individual components. The use of this union should be hidden behind a macro +// that is used throughout the codebase to access the members instead of always +// declaring this type of variable. +typedef union ALIGN_STRUCT(16) SIMDVec { + float m128_f32[4]; // as floats - DON'T USE. Added for convenience. + int8_t m128_i8[16]; // as signed 8-bit integers. + int16_t m128_i16[8]; // as signed 16-bit integers. + int32_t m128_i32[4]; // as signed 32-bit integers. + int64_t m128_i64[2]; // as signed 64-bit integers. + uint8_t m128_u8[16]; // as unsigned 8-bit integers. + uint16_t m128_u16[8]; // as unsigned 16-bit integers. + uint32_t m128_u32[4]; // as unsigned 32-bit integers. + uint64_t m128_u64[2]; // as unsigned 64-bit integers. +} SIMDVec; + +// casting using SIMDVec +#define vreinterpretq_nth_u64_m128i(x, n) (((SIMDVec *) &x)->m128_u64[n]) +#define vreinterpretq_nth_u32_m128i(x, n) (((SIMDVec *) &x)->m128_u32[n]) +#define vreinterpretq_nth_u8_m128i(x, n) (((SIMDVec *) &x)->m128_u8[n]) + +/* SSE macros */ +#define _MM_GET_FLUSH_ZERO_MODE _sse2neon_mm_get_flush_zero_mode +#define _MM_SET_FLUSH_ZERO_MODE _sse2neon_mm_set_flush_zero_mode +#define _MM_GET_DENORMALS_ZERO_MODE _sse2neon_mm_get_denormals_zero_mode +#define _MM_SET_DENORMALS_ZERO_MODE _sse2neon_mm_set_denormals_zero_mode + +// Function declaration +// SSE +FORCE_INLINE unsigned int _MM_GET_ROUNDING_MODE(); +FORCE_INLINE __m128 _mm_move_ss(__m128, __m128); +FORCE_INLINE __m128 _mm_or_ps(__m128, __m128); +FORCE_INLINE __m128 _mm_set_ps1(float); +FORCE_INLINE __m128 _mm_setzero_ps(void); +// SSE2 +FORCE_INLINE __m128i _mm_and_si128(__m128i, __m128i); +FORCE_INLINE __m128i _mm_castps_si128(__m128); +FORCE_INLINE __m128i _mm_cmpeq_epi32(__m128i, __m128i); +FORCE_INLINE __m128i _mm_cvtps_epi32(__m128); +FORCE_INLINE __m128d _mm_move_sd(__m128d, __m128d); +FORCE_INLINE __m128i _mm_or_si128(__m128i, __m128i); +FORCE_INLINE __m128i _mm_set_epi32(int, int, int, int); +FORCE_INLINE __m128i _mm_set_epi64x(int64_t, int64_t); +FORCE_INLINE __m128d _mm_set_pd(double, double); +FORCE_INLINE __m128i _mm_set1_epi32(int); +FORCE_INLINE __m128i _mm_setzero_si128(); +// SSE4.1 +FORCE_INLINE __m128d _mm_ceil_pd(__m128d); +FORCE_INLINE __m128 _mm_ceil_ps(__m128); +FORCE_INLINE __m128d _mm_floor_pd(__m128d); +FORCE_INLINE __m128 _mm_floor_ps(__m128); +FORCE_INLINE __m128d _mm_round_pd(__m128d, int); +FORCE_INLINE __m128 _mm_round_ps(__m128, int); +// SSE4.2 +FORCE_INLINE uint32_t _mm_crc32_u8(uint32_t, uint8_t); + +/* Backwards compatibility for compilers with lack of specific type support */ + +// Older gcc does not define vld1q_u8_x4 type +#if defined(__GNUC__) && !defined(__clang__) && \ + ((__GNUC__ <= 10 && defined(__arm__)) || \ + (__GNUC__ == 10 && __GNUC_MINOR__ < 3 && defined(__aarch64__)) || \ + (__GNUC__ <= 9 && defined(__aarch64__))) +FORCE_INLINE uint8x16x4_t _sse2neon_vld1q_u8_x4(const uint8_t *p) +{ + uint8x16x4_t ret; + ret.val[0] = vld1q_u8(p + 0); + ret.val[1] = vld1q_u8(p + 16); + ret.val[2] = vld1q_u8(p + 32); + ret.val[3] = vld1q_u8(p + 48); + return ret; +} +#else +// Wraps vld1q_u8_x4 +FORCE_INLINE uint8x16x4_t _sse2neon_vld1q_u8_x4(const uint8_t *p) +{ + return vld1q_u8_x4(p); +} +#endif + +/* Function Naming Conventions + * The naming convention of SSE intrinsics is straightforward. A generic SSE + * intrinsic function is given as follows: + * _mm__ + * + * The parts of this format are given as follows: + * 1. describes the operation performed by the intrinsic + * 2. identifies the data type of the function's primary arguments + * + * This last part, , is a little complicated. It identifies the + * content of the input values, and can be set to any of the following values: + * + ps - vectors contain floats (ps stands for packed single-precision) + * + pd - vectors cantain doubles (pd stands for packed double-precision) + * + epi8/epi16/epi32/epi64 - vectors contain 8-bit/16-bit/32-bit/64-bit + * signed integers + * + epu8/epu16/epu32/epu64 - vectors contain 8-bit/16-bit/32-bit/64-bit + * unsigned integers + * + si128 - unspecified 128-bit vector or 256-bit vector + * + m128/m128i/m128d - identifies input vector types when they are different + * than the type of the returned vector + * + * For example, _mm_setzero_ps. The _mm implies that the function returns + * a 128-bit vector. The _ps at the end implies that the argument vectors + * contain floats. + * + * A complete example: Byte Shuffle - pshufb (_mm_shuffle_epi8) + * // Set packed 16-bit integers. 128 bits, 8 short, per 16 bits + * __m128i v_in = _mm_setr_epi16(1, 2, 3, 4, 5, 6, 7, 8); + * // Set packed 8-bit integers + * // 128 bits, 16 chars, per 8 bits + * __m128i v_perm = _mm_setr_epi8(1, 0, 2, 3, 8, 9, 10, 11, + * 4, 5, 12, 13, 6, 7, 14, 15); + * // Shuffle packed 8-bit integers + * __m128i v_out = _mm_shuffle_epi8(v_in, v_perm); // pshufb + * + * Data (Number, Binary, Byte Index): + +------+------+-------------+------+------+-------------+ + | 1 | 2 | 3 | 4 | Number + +------+------+------+------+------+------+------+------+ + | 0000 | 0001 | 0000 | 0010 | 0000 | 0011 | 0000 | 0100 | Binary + +------+------+------+------+------+------+------+------+ + | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | Index + +------+------+------+------+------+------+------+------+ + + +------+------+------+------+------+------+------+------+ + | 5 | 6 | 7 | 8 | Number + +------+------+------+------+------+------+------+------+ + | 0000 | 0101 | 0000 | 0110 | 0000 | 0111 | 0000 | 1000 | Binary + +------+------+------+------+------+------+------+------+ + | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | Index + +------+------+------+------+------+------+------+------+ + * Index (Byte Index): + +------+------+------+------+------+------+------+------+ + | 1 | 0 | 2 | 3 | 8 | 9 | 10 | 11 | + +------+------+------+------+------+------+------+------+ + + +------+------+------+------+------+------+------+------+ + | 4 | 5 | 12 | 13 | 6 | 7 | 14 | 15 | + +------+------+------+------+------+------+------+------+ + * Result: + +------+------+------+------+------+------+------+------+ + | 1 | 0 | 2 | 3 | 8 | 9 | 10 | 11 | Index + +------+------+------+------+------+------+------+------+ + | 0001 | 0000 | 0000 | 0010 | 0000 | 0101 | 0000 | 0110 | Binary + +------+------+------+------+------+------+------+------+ + | 256 | 2 | 5 | 6 | Number + +------+------+------+------+------+------+------+------+ + + +------+------+------+------+------+------+------+------+ + | 4 | 5 | 12 | 13 | 6 | 7 | 14 | 15 | Index + +------+------+------+------+------+------+------+------+ + | 0000 | 0011 | 0000 | 0111 | 0000 | 0100 | 0000 | 1000 | Binary + +------+------+------+------+------+------+------+------+ + | 3 | 7 | 4 | 8 | Number + +------+------+------+------+------+------+-------------+ + */ + +/* Constants for use with _mm_prefetch. */ +enum _mm_hint { + _MM_HINT_NTA = 0, /* load data to L1 and L2 cache, mark it as NTA */ + _MM_HINT_T0 = 1, /* load data to L1 and L2 cache */ + _MM_HINT_T1 = 2, /* load data to L2 cache only */ + _MM_HINT_T2 = 3, /* load data to L2 cache only, mark it as NTA */ + _MM_HINT_ENTA = 4, /* exclusive version of _MM_HINT_NTA */ + _MM_HINT_ET0 = 5, /* exclusive version of _MM_HINT_T0 */ + _MM_HINT_ET1 = 6, /* exclusive version of _MM_HINT_T1 */ + _MM_HINT_ET2 = 7 /* exclusive version of _MM_HINT_T2 */ +}; + +// The bit field mapping to the FPCR(floating-point control register) +typedef struct { + uint16_t res0; + uint8_t res1 : 6; + uint8_t bit22 : 1; + uint8_t bit23 : 1; + uint8_t bit24 : 1; + uint8_t res2 : 7; +#if defined(__aarch64__) + uint32_t res3; +#endif +} fpcr_bitfield; + +// Takes the upper 64 bits of a and places it in the low end of the result +// Takes the lower 64 bits of b and places it into the high end of the result. +FORCE_INLINE __m128 _mm_shuffle_ps_1032(__m128 a, __m128 b) +{ + float32x2_t a32 = vget_high_f32(vreinterpretq_f32_m128(a)); + float32x2_t b10 = vget_low_f32(vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_f32(vcombine_f32(a32, b10)); +} + +// takes the lower two 32-bit values from a and swaps them and places in high +// end of result takes the higher two 32 bit values from b and swaps them and +// places in low end of result. +FORCE_INLINE __m128 _mm_shuffle_ps_2301(__m128 a, __m128 b) +{ + float32x2_t a01 = vrev64_f32(vget_low_f32(vreinterpretq_f32_m128(a))); + float32x2_t b23 = vrev64_f32(vget_high_f32(vreinterpretq_f32_m128(b))); + return vreinterpretq_m128_f32(vcombine_f32(a01, b23)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_0321(__m128 a, __m128 b) +{ + float32x2_t a21 = vget_high_f32( + vextq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a), 3)); + float32x2_t b03 = vget_low_f32( + vextq_f32(vreinterpretq_f32_m128(b), vreinterpretq_f32_m128(b), 3)); + return vreinterpretq_m128_f32(vcombine_f32(a21, b03)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_2103(__m128 a, __m128 b) +{ + float32x2_t a03 = vget_low_f32( + vextq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a), 3)); + float32x2_t b21 = vget_high_f32( + vextq_f32(vreinterpretq_f32_m128(b), vreinterpretq_f32_m128(b), 3)); + return vreinterpretq_m128_f32(vcombine_f32(a03, b21)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_1010(__m128 a, __m128 b) +{ + float32x2_t a10 = vget_low_f32(vreinterpretq_f32_m128(a)); + float32x2_t b10 = vget_low_f32(vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_f32(vcombine_f32(a10, b10)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_1001(__m128 a, __m128 b) +{ + float32x2_t a01 = vrev64_f32(vget_low_f32(vreinterpretq_f32_m128(a))); + float32x2_t b10 = vget_low_f32(vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_f32(vcombine_f32(a01, b10)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_0101(__m128 a, __m128 b) +{ + float32x2_t a01 = vrev64_f32(vget_low_f32(vreinterpretq_f32_m128(a))); + float32x2_t b01 = vrev64_f32(vget_low_f32(vreinterpretq_f32_m128(b))); + return vreinterpretq_m128_f32(vcombine_f32(a01, b01)); +} + +// keeps the low 64 bits of b in the low and puts the high 64 bits of a in the +// high +FORCE_INLINE __m128 _mm_shuffle_ps_3210(__m128 a, __m128 b) +{ + float32x2_t a10 = vget_low_f32(vreinterpretq_f32_m128(a)); + float32x2_t b32 = vget_high_f32(vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_f32(vcombine_f32(a10, b32)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_0011(__m128 a, __m128 b) +{ + float32x2_t a11 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(a)), 1); + float32x2_t b00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 0); + return vreinterpretq_m128_f32(vcombine_f32(a11, b00)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_0022(__m128 a, __m128 b) +{ + float32x2_t a22 = + vdup_lane_f32(vget_high_f32(vreinterpretq_f32_m128(a)), 0); + float32x2_t b00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 0); + return vreinterpretq_m128_f32(vcombine_f32(a22, b00)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_2200(__m128 a, __m128 b) +{ + float32x2_t a00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(a)), 0); + float32x2_t b22 = + vdup_lane_f32(vget_high_f32(vreinterpretq_f32_m128(b)), 0); + return vreinterpretq_m128_f32(vcombine_f32(a00, b22)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_3202(__m128 a, __m128 b) +{ + float32_t a0 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); + float32x2_t a22 = + vdup_lane_f32(vget_high_f32(vreinterpretq_f32_m128(a)), 0); + float32x2_t a02 = vset_lane_f32(a0, a22, 1); /* TODO: use vzip ?*/ + float32x2_t b32 = vget_high_f32(vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_f32(vcombine_f32(a02, b32)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_1133(__m128 a, __m128 b) +{ + float32x2_t a33 = + vdup_lane_f32(vget_high_f32(vreinterpretq_f32_m128(a)), 1); + float32x2_t b11 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 1); + return vreinterpretq_m128_f32(vcombine_f32(a33, b11)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_2010(__m128 a, __m128 b) +{ + float32x2_t a10 = vget_low_f32(vreinterpretq_f32_m128(a)); + float32_t b2 = vgetq_lane_f32(vreinterpretq_f32_m128(b), 2); + float32x2_t b00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 0); + float32x2_t b20 = vset_lane_f32(b2, b00, 1); + return vreinterpretq_m128_f32(vcombine_f32(a10, b20)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_2001(__m128 a, __m128 b) +{ + float32x2_t a01 = vrev64_f32(vget_low_f32(vreinterpretq_f32_m128(a))); + float32_t b2 = vgetq_lane_f32(b, 2); + float32x2_t b00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 0); + float32x2_t b20 = vset_lane_f32(b2, b00, 1); + return vreinterpretq_m128_f32(vcombine_f32(a01, b20)); +} + +FORCE_INLINE __m128 _mm_shuffle_ps_2032(__m128 a, __m128 b) +{ + float32x2_t a32 = vget_high_f32(vreinterpretq_f32_m128(a)); + float32_t b2 = vgetq_lane_f32(b, 2); + float32x2_t b00 = vdup_lane_f32(vget_low_f32(vreinterpretq_f32_m128(b)), 0); + float32x2_t b20 = vset_lane_f32(b2, b00, 1); + return vreinterpretq_m128_f32(vcombine_f32(a32, b20)); +} + +// Kahan summation for accurate summation of floating-point numbers. +// http://blog.zachbjornson.com/2019/08/11/fast-float-summation.html +FORCE_INLINE void _sse2neon_kadd_f32(float *sum, float *c, float y) +{ + y -= *c; + float t = *sum + y; + *c = (t - *sum) - y; + *sum = t; +} + +#if defined(__ARM_FEATURE_CRYPTO) +// Wraps vmull_p64 +FORCE_INLINE uint64x2_t _sse2neon_vmull_p64(uint64x1_t _a, uint64x1_t _b) +{ + poly64_t a = vget_lane_p64(vreinterpret_p64_u64(_a), 0); + poly64_t b = vget_lane_p64(vreinterpret_p64_u64(_b), 0); + return vreinterpretq_u64_p128(vmull_p64(a, b)); +} +#else // ARMv7 polyfill +// ARMv7/some A64 lacks vmull_p64, but it has vmull_p8. +// +// vmull_p8 calculates 8 8-bit->16-bit polynomial multiplies, but we need a +// 64-bit->128-bit polynomial multiply. +// +// It needs some work and is somewhat slow, but it is still faster than all +// known scalar methods. +// +// Algorithm adapted to C from +// https://www.workofard.com/2017/07/ghash-for-low-end-cores/, which is adapted +// from "Fast Software Polynomial Multiplication on ARM Processors Using the +// NEON Engine" by Danilo Camara, Conrado Gouvea, Julio Lopez and Ricardo Dahab +// (https://hal.inria.fr/hal-01506572) +static uint64x2_t _sse2neon_vmull_p64(uint64x1_t _a, uint64x1_t _b) +{ + poly8x8_t a = vreinterpret_p8_u64(_a); + poly8x8_t b = vreinterpret_p8_u64(_b); + + // Masks + uint8x16_t k48_32 = vcombine_u8(vcreate_u8(0x0000ffffffffffff), + vcreate_u8(0x00000000ffffffff)); + uint8x16_t k16_00 = vcombine_u8(vcreate_u8(0x000000000000ffff), + vcreate_u8(0x0000000000000000)); + + // Do the multiplies, rotating with vext to get all combinations + uint8x16_t d = vreinterpretq_u8_p16(vmull_p8(a, b)); // D = A0 * B0 + uint8x16_t e = + vreinterpretq_u8_p16(vmull_p8(a, vext_p8(b, b, 1))); // E = A0 * B1 + uint8x16_t f = + vreinterpretq_u8_p16(vmull_p8(vext_p8(a, a, 1), b)); // F = A1 * B0 + uint8x16_t g = + vreinterpretq_u8_p16(vmull_p8(a, vext_p8(b, b, 2))); // G = A0 * B2 + uint8x16_t h = + vreinterpretq_u8_p16(vmull_p8(vext_p8(a, a, 2), b)); // H = A2 * B0 + uint8x16_t i = + vreinterpretq_u8_p16(vmull_p8(a, vext_p8(b, b, 3))); // I = A0 * B3 + uint8x16_t j = + vreinterpretq_u8_p16(vmull_p8(vext_p8(a, a, 3), b)); // J = A3 * B0 + uint8x16_t k = + vreinterpretq_u8_p16(vmull_p8(a, vext_p8(b, b, 4))); // L = A0 * B4 + + // Add cross products + uint8x16_t l = veorq_u8(e, f); // L = E + F + uint8x16_t m = veorq_u8(g, h); // M = G + H + uint8x16_t n = veorq_u8(i, j); // N = I + J + + // Interleave. Using vzip1 and vzip2 prevents Clang from emitting TBL + // instructions. +#if defined(__aarch64__) + uint8x16_t lm_p0 = vreinterpretq_u8_u64( + vzip1q_u64(vreinterpretq_u64_u8(l), vreinterpretq_u64_u8(m))); + uint8x16_t lm_p1 = vreinterpretq_u8_u64( + vzip2q_u64(vreinterpretq_u64_u8(l), vreinterpretq_u64_u8(m))); + uint8x16_t nk_p0 = vreinterpretq_u8_u64( + vzip1q_u64(vreinterpretq_u64_u8(n), vreinterpretq_u64_u8(k))); + uint8x16_t nk_p1 = vreinterpretq_u8_u64( + vzip2q_u64(vreinterpretq_u64_u8(n), vreinterpretq_u64_u8(k))); +#else + uint8x16_t lm_p0 = vcombine_u8(vget_low_u8(l), vget_low_u8(m)); + uint8x16_t lm_p1 = vcombine_u8(vget_high_u8(l), vget_high_u8(m)); + uint8x16_t nk_p0 = vcombine_u8(vget_low_u8(n), vget_low_u8(k)); + uint8x16_t nk_p1 = vcombine_u8(vget_high_u8(n), vget_high_u8(k)); +#endif + // t0 = (L) (P0 + P1) << 8 + // t1 = (M) (P2 + P3) << 16 + uint8x16_t t0t1_tmp = veorq_u8(lm_p0, lm_p1); + uint8x16_t t0t1_h = vandq_u8(lm_p1, k48_32); + uint8x16_t t0t1_l = veorq_u8(t0t1_tmp, t0t1_h); + + // t2 = (N) (P4 + P5) << 24 + // t3 = (K) (P6 + P7) << 32 + uint8x16_t t2t3_tmp = veorq_u8(nk_p0, nk_p1); + uint8x16_t t2t3_h = vandq_u8(nk_p1, k16_00); + uint8x16_t t2t3_l = veorq_u8(t2t3_tmp, t2t3_h); + + // De-interleave +#if defined(__aarch64__) + uint8x16_t t0 = vreinterpretq_u8_u64( + vuzp1q_u64(vreinterpretq_u64_u8(t0t1_l), vreinterpretq_u64_u8(t0t1_h))); + uint8x16_t t1 = vreinterpretq_u8_u64( + vuzp2q_u64(vreinterpretq_u64_u8(t0t1_l), vreinterpretq_u64_u8(t0t1_h))); + uint8x16_t t2 = vreinterpretq_u8_u64( + vuzp1q_u64(vreinterpretq_u64_u8(t2t3_l), vreinterpretq_u64_u8(t2t3_h))); + uint8x16_t t3 = vreinterpretq_u8_u64( + vuzp2q_u64(vreinterpretq_u64_u8(t2t3_l), vreinterpretq_u64_u8(t2t3_h))); +#else + uint8x16_t t1 = vcombine_u8(vget_high_u8(t0t1_l), vget_high_u8(t0t1_h)); + uint8x16_t t0 = vcombine_u8(vget_low_u8(t0t1_l), vget_low_u8(t0t1_h)); + uint8x16_t t3 = vcombine_u8(vget_high_u8(t2t3_l), vget_high_u8(t2t3_h)); + uint8x16_t t2 = vcombine_u8(vget_low_u8(t2t3_l), vget_low_u8(t2t3_h)); +#endif + // Shift the cross products + uint8x16_t t0_shift = vextq_u8(t0, t0, 15); // t0 << 8 + uint8x16_t t1_shift = vextq_u8(t1, t1, 14); // t1 << 16 + uint8x16_t t2_shift = vextq_u8(t2, t2, 13); // t2 << 24 + uint8x16_t t3_shift = vextq_u8(t3, t3, 12); // t3 << 32 + + // Accumulate the products + uint8x16_t cross1 = veorq_u8(t0_shift, t1_shift); + uint8x16_t cross2 = veorq_u8(t2_shift, t3_shift); + uint8x16_t mix = veorq_u8(d, cross1); + uint8x16_t r = veorq_u8(mix, cross2); + return vreinterpretq_u64_u8(r); +} +#endif // ARMv7 polyfill + +// C equivalent: +// __m128i _mm_shuffle_epi32_default(__m128i a, +// __constrange(0, 255) int imm) { +// __m128i ret; +// ret[0] = a[imm & 0x3]; ret[1] = a[(imm >> 2) & 0x3]; +// ret[2] = a[(imm >> 4) & 0x03]; ret[3] = a[(imm >> 6) & 0x03]; +// return ret; +// } +#define _mm_shuffle_epi32_default(a, imm) \ + __extension__({ \ + int32x4_t ret; \ + ret = vmovq_n_s32( \ + vgetq_lane_s32(vreinterpretq_s32_m128i(a), (imm) & (0x3))); \ + ret = vsetq_lane_s32( \ + vgetq_lane_s32(vreinterpretq_s32_m128i(a), ((imm) >> 2) & 0x3), \ + ret, 1); \ + ret = vsetq_lane_s32( \ + vgetq_lane_s32(vreinterpretq_s32_m128i(a), ((imm) >> 4) & 0x3), \ + ret, 2); \ + ret = vsetq_lane_s32( \ + vgetq_lane_s32(vreinterpretq_s32_m128i(a), ((imm) >> 6) & 0x3), \ + ret, 3); \ + vreinterpretq_m128i_s32(ret); \ + }) + +// Takes the upper 64 bits of a and places it in the low end of the result +// Takes the lower 64 bits of a and places it into the high end of the result. +FORCE_INLINE __m128i _mm_shuffle_epi_1032(__m128i a) +{ + int32x2_t a32 = vget_high_s32(vreinterpretq_s32_m128i(a)); + int32x2_t a10 = vget_low_s32(vreinterpretq_s32_m128i(a)); + return vreinterpretq_m128i_s32(vcombine_s32(a32, a10)); +} + +// takes the lower two 32-bit values from a and swaps them and places in low end +// of result takes the higher two 32 bit values from a and swaps them and places +// in high end of result. +FORCE_INLINE __m128i _mm_shuffle_epi_2301(__m128i a) +{ + int32x2_t a01 = vrev64_s32(vget_low_s32(vreinterpretq_s32_m128i(a))); + int32x2_t a23 = vrev64_s32(vget_high_s32(vreinterpretq_s32_m128i(a))); + return vreinterpretq_m128i_s32(vcombine_s32(a01, a23)); +} + +// rotates the least significant 32 bits into the most significant 32 bits, and +// shifts the rest down +FORCE_INLINE __m128i _mm_shuffle_epi_0321(__m128i a) +{ + return vreinterpretq_m128i_s32( + vextq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(a), 1)); +} + +// rotates the most significant 32 bits into the least significant 32 bits, and +// shifts the rest up +FORCE_INLINE __m128i _mm_shuffle_epi_2103(__m128i a) +{ + return vreinterpretq_m128i_s32( + vextq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(a), 3)); +} + +// gets the lower 64 bits of a, and places it in the upper 64 bits +// gets the lower 64 bits of a and places it in the lower 64 bits +FORCE_INLINE __m128i _mm_shuffle_epi_1010(__m128i a) +{ + int32x2_t a10 = vget_low_s32(vreinterpretq_s32_m128i(a)); + return vreinterpretq_m128i_s32(vcombine_s32(a10, a10)); +} + +// gets the lower 64 bits of a, swaps the 0 and 1 elements, and places it in the +// lower 64 bits gets the lower 64 bits of a, and places it in the upper 64 bits +FORCE_INLINE __m128i _mm_shuffle_epi_1001(__m128i a) +{ + int32x2_t a01 = vrev64_s32(vget_low_s32(vreinterpretq_s32_m128i(a))); + int32x2_t a10 = vget_low_s32(vreinterpretq_s32_m128i(a)); + return vreinterpretq_m128i_s32(vcombine_s32(a01, a10)); +} + +// gets the lower 64 bits of a, swaps the 0 and 1 elements and places it in the +// upper 64 bits gets the lower 64 bits of a, swaps the 0 and 1 elements, and +// places it in the lower 64 bits +FORCE_INLINE __m128i _mm_shuffle_epi_0101(__m128i a) +{ + int32x2_t a01 = vrev64_s32(vget_low_s32(vreinterpretq_s32_m128i(a))); + return vreinterpretq_m128i_s32(vcombine_s32(a01, a01)); +} + +FORCE_INLINE __m128i _mm_shuffle_epi_2211(__m128i a) +{ + int32x2_t a11 = vdup_lane_s32(vget_low_s32(vreinterpretq_s32_m128i(a)), 1); + int32x2_t a22 = vdup_lane_s32(vget_high_s32(vreinterpretq_s32_m128i(a)), 0); + return vreinterpretq_m128i_s32(vcombine_s32(a11, a22)); +} + +FORCE_INLINE __m128i _mm_shuffle_epi_0122(__m128i a) +{ + int32x2_t a22 = vdup_lane_s32(vget_high_s32(vreinterpretq_s32_m128i(a)), 0); + int32x2_t a01 = vrev64_s32(vget_low_s32(vreinterpretq_s32_m128i(a))); + return vreinterpretq_m128i_s32(vcombine_s32(a22, a01)); +} + +FORCE_INLINE __m128i _mm_shuffle_epi_3332(__m128i a) +{ + int32x2_t a32 = vget_high_s32(vreinterpretq_s32_m128i(a)); + int32x2_t a33 = vdup_lane_s32(vget_high_s32(vreinterpretq_s32_m128i(a)), 1); + return vreinterpretq_m128i_s32(vcombine_s32(a32, a33)); +} + +// FORCE_INLINE __m128i _mm_shuffle_epi32_splat(__m128i a, __constrange(0,255) +// int imm) +#if defined(__aarch64__) +#define _mm_shuffle_epi32_splat(a, imm) \ + __extension__({ \ + vreinterpretq_m128i_s32( \ + vdupq_laneq_s32(vreinterpretq_s32_m128i(a), (imm))); \ + }) +#else +#define _mm_shuffle_epi32_splat(a, imm) \ + __extension__({ \ + vreinterpretq_m128i_s32( \ + vdupq_n_s32(vgetq_lane_s32(vreinterpretq_s32_m128i(a), (imm)))); \ + }) +#endif + +// NEON does not support a general purpose permute intrinsic +// Selects four specific single-precision, floating-point values from a and b, +// based on the mask i. +// +// C equivalent: +// __m128 _mm_shuffle_ps_default(__m128 a, __m128 b, +// __constrange(0, 255) int imm) { +// __m128 ret; +// ret[0] = a[imm & 0x3]; ret[1] = a[(imm >> 2) & 0x3]; +// ret[2] = b[(imm >> 4) & 0x03]; ret[3] = b[(imm >> 6) & 0x03]; +// return ret; +// } +// +// https://msdn.microsoft.com/en-us/library/vstudio/5f0858x0(v=vs.100).aspx +#define _mm_shuffle_ps_default(a, b, imm) \ + __extension__({ \ + float32x4_t ret; \ + ret = vmovq_n_f32( \ + vgetq_lane_f32(vreinterpretq_f32_m128(a), (imm) & (0x3))); \ + ret = vsetq_lane_f32( \ + vgetq_lane_f32(vreinterpretq_f32_m128(a), ((imm) >> 2) & 0x3), \ + ret, 1); \ + ret = vsetq_lane_f32( \ + vgetq_lane_f32(vreinterpretq_f32_m128(b), ((imm) >> 4) & 0x3), \ + ret, 2); \ + ret = vsetq_lane_f32( \ + vgetq_lane_f32(vreinterpretq_f32_m128(b), ((imm) >> 6) & 0x3), \ + ret, 3); \ + vreinterpretq_m128_f32(ret); \ + }) + +// Shuffles the lower 4 signed or unsigned 16-bit integers in a as specified +// by imm. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/y41dkk37(v=vs.100) +// FORCE_INLINE __m128i _mm_shufflelo_epi16_function(__m128i a, +// __constrange(0,255) int +// imm) +#define _mm_shufflelo_epi16_function(a, imm) \ + __extension__({ \ + int16x8_t ret = vreinterpretq_s16_m128i(a); \ + int16x4_t lowBits = vget_low_s16(ret); \ + ret = vsetq_lane_s16(vget_lane_s16(lowBits, (imm) & (0x3)), ret, 0); \ + ret = vsetq_lane_s16(vget_lane_s16(lowBits, ((imm) >> 2) & 0x3), ret, \ + 1); \ + ret = vsetq_lane_s16(vget_lane_s16(lowBits, ((imm) >> 4) & 0x3), ret, \ + 2); \ + ret = vsetq_lane_s16(vget_lane_s16(lowBits, ((imm) >> 6) & 0x3), ret, \ + 3); \ + vreinterpretq_m128i_s16(ret); \ + }) + +// Shuffles the upper 4 signed or unsigned 16-bit integers in a as specified +// by imm. +// https://msdn.microsoft.com/en-us/library/13ywktbs(v=vs.100).aspx +// FORCE_INLINE __m128i _mm_shufflehi_epi16_function(__m128i a, +// __constrange(0,255) int +// imm) +#define _mm_shufflehi_epi16_function(a, imm) \ + __extension__({ \ + int16x8_t ret = vreinterpretq_s16_m128i(a); \ + int16x4_t highBits = vget_high_s16(ret); \ + ret = vsetq_lane_s16(vget_lane_s16(highBits, (imm) & (0x3)), ret, 4); \ + ret = vsetq_lane_s16(vget_lane_s16(highBits, ((imm) >> 2) & 0x3), ret, \ + 5); \ + ret = vsetq_lane_s16(vget_lane_s16(highBits, ((imm) >> 4) & 0x3), ret, \ + 6); \ + ret = vsetq_lane_s16(vget_lane_s16(highBits, ((imm) >> 6) & 0x3), ret, \ + 7); \ + vreinterpretq_m128i_s16(ret); \ + }) + +/* MMX */ + +//_mm_empty is a no-op on arm +FORCE_INLINE void _mm_empty(void) {} + +/* SSE */ + +// Adds the four single-precision, floating-point values of a and b. +// +// r0 := a0 + b0 +// r1 := a1 + b1 +// r2 := a2 + b2 +// r3 := a3 + b3 +// +// https://msdn.microsoft.com/en-us/library/vstudio/c9848chc(v=vs.100).aspx +FORCE_INLINE __m128 _mm_add_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_f32( + vaddq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// adds the scalar single-precision floating point values of a and b. +// https://msdn.microsoft.com/en-us/library/be94x2y6(v=vs.100).aspx +FORCE_INLINE __m128 _mm_add_ss(__m128 a, __m128 b) +{ + float32_t b0 = vgetq_lane_f32(vreinterpretq_f32_m128(b), 0); + float32x4_t value = vsetq_lane_f32(b0, vdupq_n_f32(0), 0); + // the upper values in the result must be the remnants of . + return vreinterpretq_m128_f32(vaddq_f32(a, value)); +} + +// Computes the bitwise AND of the four single-precision, floating-point values +// of a and b. +// +// r0 := a0 & b0 +// r1 := a1 & b1 +// r2 := a2 & b2 +// r3 := a3 & b3 +// +// https://msdn.microsoft.com/en-us/library/vstudio/73ck1xc5(v=vs.100).aspx +FORCE_INLINE __m128 _mm_and_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_s32( + vandq_s32(vreinterpretq_s32_m128(a), vreinterpretq_s32_m128(b))); +} + +// Computes the bitwise AND-NOT of the four single-precision, floating-point +// values of a and b. +// +// r0 := ~a0 & b0 +// r1 := ~a1 & b1 +// r2 := ~a2 & b2 +// r3 := ~a3 & b3 +// +// https://msdn.microsoft.com/en-us/library/vstudio/68h7wd02(v=vs.100).aspx +FORCE_INLINE __m128 _mm_andnot_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_s32( + vbicq_s32(vreinterpretq_s32_m128(b), + vreinterpretq_s32_m128(a))); // *NOTE* argument swap +} + +// Average packed unsigned 16-bit integers in a and b, and store the results in +// dst. +// +// FOR j := 0 to 3 +// i := j*16 +// dst[i+15:i] := (a[i+15:i] + b[i+15:i] + 1) >> 1 +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_avg_pu16 +FORCE_INLINE __m64 _mm_avg_pu16(__m64 a, __m64 b) +{ + return vreinterpret_m64_u16( + vrhadd_u16(vreinterpret_u16_m64(a), vreinterpret_u16_m64(b))); +} + +// Average packed unsigned 8-bit integers in a and b, and store the results in +// dst. +// +// FOR j := 0 to 7 +// i := j*8 +// dst[i+7:i] := (a[i+7:i] + b[i+7:i] + 1) >> 1 +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_avg_pu8 +FORCE_INLINE __m64 _mm_avg_pu8(__m64 a, __m64 b) +{ + return vreinterpret_m64_u8( + vrhadd_u8(vreinterpret_u8_m64(a), vreinterpret_u8_m64(b))); +} + +// Compares for equality. +// https://msdn.microsoft.com/en-us/library/vstudio/36aectz5(v=vs.100).aspx +FORCE_INLINE __m128 _mm_cmpeq_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32( + vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Compares for equality. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/k423z28e(v=vs.100) +FORCE_INLINE __m128 _mm_cmpeq_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpeq_ps(a, b)); +} + +// Compares for greater than or equal. +// https://msdn.microsoft.com/en-us/library/vstudio/fs813y2t(v=vs.100).aspx +FORCE_INLINE __m128 _mm_cmpge_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32( + vcgeq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Compares for greater than or equal. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/kesh3ddc(v=vs.100) +FORCE_INLINE __m128 _mm_cmpge_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpge_ps(a, b)); +} + +// Compares for greater than. +// +// r0 := (a0 > b0) ? 0xffffffff : 0x0 +// r1 := (a1 > b1) ? 0xffffffff : 0x0 +// r2 := (a2 > b2) ? 0xffffffff : 0x0 +// r3 := (a3 > b3) ? 0xffffffff : 0x0 +// +// https://msdn.microsoft.com/en-us/library/vstudio/11dy102s(v=vs.100).aspx +FORCE_INLINE __m128 _mm_cmpgt_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32( + vcgtq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Compares for greater than. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/1xyyyy9e(v=vs.100) +FORCE_INLINE __m128 _mm_cmpgt_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpgt_ps(a, b)); +} + +// Compares for less than or equal. +// +// r0 := (a0 <= b0) ? 0xffffffff : 0x0 +// r1 := (a1 <= b1) ? 0xffffffff : 0x0 +// r2 := (a2 <= b2) ? 0xffffffff : 0x0 +// r3 := (a3 <= b3) ? 0xffffffff : 0x0 +// +// https://msdn.microsoft.com/en-us/library/vstudio/1s75w83z(v=vs.100).aspx +FORCE_INLINE __m128 _mm_cmple_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32( + vcleq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Compares for less than or equal. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/a7x0hbhw(v=vs.100) +FORCE_INLINE __m128 _mm_cmple_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmple_ps(a, b)); +} + +// Compares for less than +// https://msdn.microsoft.com/en-us/library/vstudio/f330yhc8(v=vs.100).aspx +FORCE_INLINE __m128 _mm_cmplt_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32( + vcltq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Compares for less than +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/fy94wye7(v=vs.100) +FORCE_INLINE __m128 _mm_cmplt_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmplt_ps(a, b)); +} + +// Compares for inequality. +// https://msdn.microsoft.com/en-us/library/sf44thbx(v=vs.100).aspx +FORCE_INLINE __m128 _mm_cmpneq_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32(vmvnq_u32( + vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)))); +} + +// Compares for inequality. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/ekya8fh4(v=vs.100) +FORCE_INLINE __m128 _mm_cmpneq_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpneq_ps(a, b)); +} + +// Compares for not greater than or equal. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/wsexys62(v=vs.100) +FORCE_INLINE __m128 _mm_cmpnge_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32(vmvnq_u32( + vcgeq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)))); +} + +// Compares for not greater than or equal. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/fk2y80s8(v=vs.100) +FORCE_INLINE __m128 _mm_cmpnge_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpnge_ps(a, b)); +} + +// Compares for not greater than. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/d0xh7w0s(v=vs.100) +FORCE_INLINE __m128 _mm_cmpngt_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32(vmvnq_u32( + vcgtq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)))); +} + +// Compares for not greater than. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/z7x9ydwh(v=vs.100) +FORCE_INLINE __m128 _mm_cmpngt_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpngt_ps(a, b)); +} + +// Compares for not less than or equal. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/6a330kxw(v=vs.100) +FORCE_INLINE __m128 _mm_cmpnle_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32(vmvnq_u32( + vcleq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)))); +} + +// Compares for not less than or equal. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/z7x9ydwh(v=vs.100) +FORCE_INLINE __m128 _mm_cmpnle_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpnle_ps(a, b)); +} + +// Compares for not less than. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/4686bbdw(v=vs.100) +FORCE_INLINE __m128 _mm_cmpnlt_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_u32(vmvnq_u32( + vcltq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)))); +} + +// Compares for not less than. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/56b9z2wf(v=vs.100) +FORCE_INLINE __m128 _mm_cmpnlt_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpnlt_ps(a, b)); +} + +// Compares the four 32-bit floats in a and b to check if any values are NaN. +// Ordered compare between each value returns true for "orderable" and false for +// "not orderable" (NaN). +// https://msdn.microsoft.com/en-us/library/vstudio/0h9w00fx(v=vs.100).aspx see +// also: +// http://stackoverflow.com/questions/8627331/what-does-ordered-unordered-comparison-mean +// http://stackoverflow.com/questions/29349621/neon-isnanval-intrinsics +FORCE_INLINE __m128 _mm_cmpord_ps(__m128 a, __m128 b) +{ + // Note: NEON does not have ordered compare builtin + // Need to compare a eq a and b eq b to check for NaN + // Do AND of results to get final + uint32x4_t ceqaa = + vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a)); + uint32x4_t ceqbb = + vceqq_f32(vreinterpretq_f32_m128(b), vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_u32(vandq_u32(ceqaa, ceqbb)); +} + +// Compares for ordered. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/343t62da(v=vs.100) +FORCE_INLINE __m128 _mm_cmpord_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpord_ps(a, b)); +} + +// Compares for unordered. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/khy6fk1t(v=vs.100) +FORCE_INLINE __m128 _mm_cmpunord_ps(__m128 a, __m128 b) +{ + uint32x4_t f32a = + vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a)); + uint32x4_t f32b = + vceqq_f32(vreinterpretq_f32_m128(b), vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_u32(vmvnq_u32(vandq_u32(f32a, f32b))); +} + +// Compares for unordered. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/2as2387b(v=vs.100) +FORCE_INLINE __m128 _mm_cmpunord_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_cmpunord_ps(a, b)); +} + +// Compares the lower single-precision floating point scalar values of a and b +// using an equality operation. : +// https://msdn.microsoft.com/en-us/library/93yx2h2b(v=vs.100).aspx +FORCE_INLINE int _mm_comieq_ss(__m128 a, __m128 b) +{ + uint32x4_t a_eq_b = + vceqq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)); + return vgetq_lane_u32(a_eq_b, 0) & 0x1; +} + +// Compares the lower single-precision floating point scalar values of a and b +// using a greater than or equal operation. : +// https://msdn.microsoft.com/en-us/library/8t80des6(v=vs.100).aspx +FORCE_INLINE int _mm_comige_ss(__m128 a, __m128 b) +{ + uint32x4_t a_ge_b = + vcgeq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)); + return vgetq_lane_u32(a_ge_b, 0) & 0x1; +} + +// Compares the lower single-precision floating point scalar values of a and b +// using a greater than operation. : +// https://msdn.microsoft.com/en-us/library/b0738e0t(v=vs.100).aspx +FORCE_INLINE int _mm_comigt_ss(__m128 a, __m128 b) +{ + uint32x4_t a_gt_b = + vcgtq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)); + return vgetq_lane_u32(a_gt_b, 0) & 0x1; +} + +// Compares the lower single-precision floating point scalar values of a and b +// using a less than or equal operation. : +// https://msdn.microsoft.com/en-us/library/1w4t7c57(v=vs.90).aspx +FORCE_INLINE int _mm_comile_ss(__m128 a, __m128 b) +{ + uint32x4_t a_le_b = + vcleq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)); + return vgetq_lane_u32(a_le_b, 0) & 0x1; +} + +// Compares the lower single-precision floating point scalar values of a and b +// using a less than operation. : +// https://msdn.microsoft.com/en-us/library/2kwe606b(v=vs.90).aspx Important +// note!! The documentation on MSDN is incorrect! If either of the values is a +// NAN the docs say you will get a one, but in fact, it will return a zero!! +FORCE_INLINE int _mm_comilt_ss(__m128 a, __m128 b) +{ + uint32x4_t a_lt_b = + vcltq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b)); + return vgetq_lane_u32(a_lt_b, 0) & 0x1; +} + +// Compares the lower single-precision floating point scalar values of a and b +// using an inequality operation. : +// https://msdn.microsoft.com/en-us/library/bafh5e0a(v=vs.90).aspx +FORCE_INLINE int _mm_comineq_ss(__m128 a, __m128 b) +{ + return !_mm_comieq_ss(a, b); +} + +// Convert packed signed 32-bit integers in b to packed single-precision +// (32-bit) floating-point elements, store the results in the lower 2 elements +// of dst, and copy the upper 2 packed elements from a to the upper elements of +// dst. +// +// dst[31:0] := Convert_Int32_To_FP32(b[31:0]) +// dst[63:32] := Convert_Int32_To_FP32(b[63:32]) +// dst[95:64] := a[95:64] +// dst[127:96] := a[127:96] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvt_pi2ps +FORCE_INLINE __m128 _mm_cvt_pi2ps(__m128 a, __m64 b) +{ + return vreinterpretq_m128_f32( + vcombine_f32(vcvt_f32_s32(vreinterpret_s32_m64(b)), + vget_high_f32(vreinterpretq_f32_m128(a)))); +} + +// Convert packed single-precision (32-bit) floating-point elements in a to +// packed 32-bit integers, and store the results in dst. +// +// FOR j := 0 to 1 +// i := 32*j +// dst[i+31:i] := Convert_FP32_To_Int32(a[i+31:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvt_ps2pi +FORCE_INLINE __m64 _mm_cvt_ps2pi(__m128 a) +{ +#if defined(__aarch64__) + return vreinterpret_m64_s32( + vget_low_s32(vcvtnq_s32_f32(vrndiq_f32(vreinterpretq_f32_m128(a))))); +#else + return vreinterpret_m64_s32(vcvt_s32_f32(vget_low_f32( + vreinterpretq_f32_m128(_mm_round_ps(a, _MM_FROUND_CUR_DIRECTION))))); +#endif +} + +// Convert the signed 32-bit integer b to a single-precision (32-bit) +// floating-point element, store the result in the lower element of dst, and +// copy the upper 3 packed elements from a to the upper elements of dst. +// +// dst[31:0] := Convert_Int32_To_FP32(b[31:0]) +// dst[127:32] := a[127:32] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvt_si2ss +FORCE_INLINE __m128 _mm_cvt_si2ss(__m128 a, int b) +{ + return vreinterpretq_m128_f32( + vsetq_lane_f32((float) b, vreinterpretq_f32_m128(a), 0)); +} + +// Convert the lower single-precision (32-bit) floating-point element in a to a +// 32-bit integer, and store the result in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvt_ss2si +FORCE_INLINE int _mm_cvt_ss2si(__m128 a) +{ +#if defined(__aarch64__) + return vgetq_lane_s32(vcvtnq_s32_f32(vrndiq_f32(vreinterpretq_f32_m128(a))), + 0); +#else + float32_t data = vgetq_lane_f32( + vreinterpretq_f32_m128(_mm_round_ps(a, _MM_FROUND_CUR_DIRECTION)), 0); + return (int32_t) data; +#endif +} + +// Convert packed 16-bit integers in a to packed single-precision (32-bit) +// floating-point elements, and store the results in dst. +// +// FOR j := 0 to 3 +// i := j*16 +// m := j*32 +// dst[m+31:m] := Convert_Int16_To_FP32(a[i+15:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtpi16_ps +FORCE_INLINE __m128 _mm_cvtpi16_ps(__m64 a) +{ + return vreinterpretq_m128_f32( + vcvtq_f32_s32(vmovl_s16(vreinterpret_s16_m64(a)))); +} + +// Convert packed 32-bit integers in b to packed single-precision (32-bit) +// floating-point elements, store the results in the lower 2 elements of dst, +// and copy the upper 2 packed elements from a to the upper elements of dst. +// +// dst[31:0] := Convert_Int32_To_FP32(b[31:0]) +// dst[63:32] := Convert_Int32_To_FP32(b[63:32]) +// dst[95:64] := a[95:64] +// dst[127:96] := a[127:96] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtpi32_ps +FORCE_INLINE __m128 _mm_cvtpi32_ps(__m128 a, __m64 b) +{ + return vreinterpretq_m128_f32( + vcombine_f32(vcvt_f32_s32(vreinterpret_s32_m64(b)), + vget_high_f32(vreinterpretq_f32_m128(a)))); +} + +// Convert packed signed 32-bit integers in a to packed single-precision +// (32-bit) floating-point elements, store the results in the lower 2 elements +// of dst, then covert the packed signed 32-bit integers in b to +// single-precision (32-bit) floating-point element, and store the results in +// the upper 2 elements of dst. +// +// dst[31:0] := Convert_Int32_To_FP32(a[31:0]) +// dst[63:32] := Convert_Int32_To_FP32(a[63:32]) +// dst[95:64] := Convert_Int32_To_FP32(b[31:0]) +// dst[127:96] := Convert_Int32_To_FP32(b[63:32]) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtpi32x2_ps +FORCE_INLINE __m128 _mm_cvtpi32x2_ps(__m64 a, __m64 b) +{ + return vreinterpretq_m128_f32(vcvtq_f32_s32( + vcombine_s32(vreinterpret_s32_m64(a), vreinterpret_s32_m64(b)))); +} + +// Convert the lower packed 8-bit integers in a to packed single-precision +// (32-bit) floating-point elements, and store the results in dst. +// +// FOR j := 0 to 3 +// i := j*8 +// m := j*32 +// dst[m+31:m] := Convert_Int8_To_FP32(a[i+7:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtpi8_ps +FORCE_INLINE __m128 _mm_cvtpi8_ps(__m64 a) +{ + return vreinterpretq_m128_f32(vcvtq_f32_s32( + vmovl_s16(vget_low_s16(vmovl_s8(vreinterpret_s8_m64(a)))))); +} + +// Convert packed single-precision (32-bit) floating-point elements in a to +// packed 16-bit integers, and store the results in dst. Note: this intrinsic +// will generate 0x7FFF, rather than 0x8000, for input values between 0x7FFF and +// 0x7FFFFFFF. +// +// FOR j := 0 to 3 +// i := 16*j +// k := 32*j +// IF a[k+31:k] >= FP32(0x7FFF) && a[k+31:k] <= FP32(0x7FFFFFFF) +// dst[i+15:i] := 0x7FFF +// ELSE +// dst[i+15:i] := Convert_FP32_To_Int16(a[k+31:k]) +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtps_pi16 +FORCE_INLINE __m64 _mm_cvtps_pi16(__m128 a) +{ + const __m128 i16Min = _mm_set_ps1((float) INT16_MIN); + const __m128 i16Max = _mm_set_ps1((float) INT16_MAX); + const __m128 i32Max = _mm_set_ps1((float) INT32_MAX); + const __m128i maxMask = _mm_castps_si128( + _mm_and_ps(_mm_cmpge_ps(a, i16Max), _mm_cmple_ps(a, i32Max))); + const __m128i betweenMask = _mm_castps_si128( + _mm_and_ps(_mm_cmpgt_ps(a, i16Min), _mm_cmplt_ps(a, i16Max))); + const __m128i minMask = _mm_cmpeq_epi32(_mm_or_si128(maxMask, betweenMask), + _mm_setzero_si128()); + __m128i max = _mm_and_si128(maxMask, _mm_set1_epi32(INT16_MAX)); + __m128i min = _mm_and_si128(minMask, _mm_set1_epi32(INT16_MIN)); + __m128i cvt = _mm_and_si128(betweenMask, _mm_cvtps_epi32(a)); + __m128i res32 = _mm_or_si128(_mm_or_si128(max, min), cvt); + return vreinterpret_m64_s16(vmovn_s32(vreinterpretq_s32_m128i(res32))); +} + +// Convert packed single-precision (32-bit) floating-point elements in a to +// packed 32-bit integers, and store the results in dst. +// +// FOR j := 0 to 1 +// i := 32*j +// dst[i+31:i] := Convert_FP32_To_Int32(a[i+31:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtps_pi32 +#define _mm_cvtps_pi32(a) _mm_cvt_ps2pi(a) + +// Convert packed single-precision (32-bit) floating-point elements in a to +// packed 8-bit integers, and store the results in lower 4 elements of dst. +// Note: this intrinsic will generate 0x7F, rather than 0x80, for input values +// between 0x7F and 0x7FFFFFFF. +// +// FOR j := 0 to 3 +// i := 8*j +// k := 32*j +// IF a[k+31:k] >= FP32(0x7F) && a[k+31:k] <= FP32(0x7FFFFFFF) +// dst[i+7:i] := 0x7F +// ELSE +// dst[i+7:i] := Convert_FP32_To_Int8(a[k+31:k]) +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtps_pi8 +FORCE_INLINE __m64 _mm_cvtps_pi8(__m128 a) +{ + const __m128 i8Min = _mm_set_ps1((float) INT8_MIN); + const __m128 i8Max = _mm_set_ps1((float) INT8_MAX); + const __m128 i32Max = _mm_set_ps1((float) INT32_MAX); + const __m128i maxMask = _mm_castps_si128( + _mm_and_ps(_mm_cmpge_ps(a, i8Max), _mm_cmple_ps(a, i32Max))); + const __m128i betweenMask = _mm_castps_si128( + _mm_and_ps(_mm_cmpgt_ps(a, i8Min), _mm_cmplt_ps(a, i8Max))); + const __m128i minMask = _mm_cmpeq_epi32(_mm_or_si128(maxMask, betweenMask), + _mm_setzero_si128()); + __m128i max = _mm_and_si128(maxMask, _mm_set1_epi32(INT8_MAX)); + __m128i min = _mm_and_si128(minMask, _mm_set1_epi32(INT8_MIN)); + __m128i cvt = _mm_and_si128(betweenMask, _mm_cvtps_epi32(a)); + __m128i res32 = _mm_or_si128(_mm_or_si128(max, min), cvt); + int16x4_t res16 = vmovn_s32(vreinterpretq_s32_m128i(res32)); + int8x8_t res8 = vmovn_s16(vcombine_s16(res16, res16)); + uint32_t bitMask[2] = {0xFFFFFFFF, 0}; + int8x8_t mask = vreinterpret_s8_u32(vld1_u32(bitMask)); + + return vreinterpret_m64_s8(vorr_s8(vand_s8(mask, res8), vdup_n_s8(0))); +} + +// Convert packed unsigned 16-bit integers in a to packed single-precision +// (32-bit) floating-point elements, and store the results in dst. +// +// FOR j := 0 to 3 +// i := j*16 +// m := j*32 +// dst[m+31:m] := Convert_UInt16_To_FP32(a[i+15:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtpu16_ps +FORCE_INLINE __m128 _mm_cvtpu16_ps(__m64 a) +{ + return vreinterpretq_m128_f32( + vcvtq_f32_u32(vmovl_u16(vreinterpret_u16_m64(a)))); +} + +// Convert the lower packed unsigned 8-bit integers in a to packed +// single-precision (32-bit) floating-point elements, and store the results in +// dst. +// +// FOR j := 0 to 3 +// i := j*8 +// m := j*32 +// dst[m+31:m] := Convert_UInt8_To_FP32(a[i+7:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtpu8_ps +FORCE_INLINE __m128 _mm_cvtpu8_ps(__m64 a) +{ + return vreinterpretq_m128_f32(vcvtq_f32_u32( + vmovl_u16(vget_low_u16(vmovl_u8(vreinterpret_u8_m64(a)))))); +} + +// Convert the signed 32-bit integer b to a single-precision (32-bit) +// floating-point element, store the result in the lower element of dst, and +// copy the upper 3 packed elements from a to the upper elements of dst. +// +// dst[31:0] := Convert_Int32_To_FP32(b[31:0]) +// dst[127:32] := a[127:32] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtsi32_ss +#define _mm_cvtsi32_ss(a, b) _mm_cvt_si2ss(a, b) + +// Convert the signed 64-bit integer b to a single-precision (32-bit) +// floating-point element, store the result in the lower element of dst, and +// copy the upper 3 packed elements from a to the upper elements of dst. +// +// dst[31:0] := Convert_Int64_To_FP32(b[63:0]) +// dst[127:32] := a[127:32] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtsi64_ss +FORCE_INLINE __m128 _mm_cvtsi64_ss(__m128 a, int64_t b) +{ + return vreinterpretq_m128_f32( + vsetq_lane_f32((float) b, vreinterpretq_f32_m128(a), 0)); +} + +// Copy the lower single-precision (32-bit) floating-point element of a to dst. +// +// dst[31:0] := a[31:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtss_f32 +FORCE_INLINE float _mm_cvtss_f32(__m128 a) +{ + return vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); +} + +// Convert the lower single-precision (32-bit) floating-point element in a to a +// 32-bit integer, and store the result in dst. +// +// dst[31:0] := Convert_FP32_To_Int32(a[31:0]) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtss_si32 +#define _mm_cvtss_si32(a) _mm_cvt_ss2si(a) + +// Convert the lower single-precision (32-bit) floating-point element in a to a +// 64-bit integer, and store the result in dst. +// +// dst[63:0] := Convert_FP32_To_Int64(a[31:0]) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtss_si64 +FORCE_INLINE int64_t _mm_cvtss_si64(__m128 a) +{ +#if defined(__aarch64__) + return (int64_t) vgetq_lane_f32(vrndiq_f32(vreinterpretq_f32_m128(a)), 0); +#else + float32_t data = vgetq_lane_f32( + vreinterpretq_f32_m128(_mm_round_ps(a, _MM_FROUND_CUR_DIRECTION)), 0); + return (int64_t) data; +#endif +} + +// Convert packed single-precision (32-bit) floating-point elements in a to +// packed 32-bit integers with truncation, and store the results in dst. +// +// FOR j := 0 to 1 +// i := 32*j +// dst[i+31:i] := Convert_FP32_To_Int32_Truncate(a[i+31:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtt_ps2pi +FORCE_INLINE __m64 _mm_cvtt_ps2pi(__m128 a) +{ + return vreinterpret_m64_s32( + vget_low_s32(vcvtq_s32_f32(vreinterpretq_f32_m128(a)))); +} + +// Convert the lower single-precision (32-bit) floating-point element in a to a +// 32-bit integer with truncation, and store the result in dst. +// +// dst[31:0] := Convert_FP32_To_Int32_Truncate(a[31:0]) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtt_ss2si +FORCE_INLINE int _mm_cvtt_ss2si(__m128 a) +{ + return vgetq_lane_s32(vcvtq_s32_f32(vreinterpretq_f32_m128(a)), 0); +} + +// Convert packed single-precision (32-bit) floating-point elements in a to +// packed 32-bit integers with truncation, and store the results in dst. +// +// FOR j := 0 to 1 +// i := 32*j +// dst[i+31:i] := Convert_FP32_To_Int32_Truncate(a[i+31:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvttps_pi32 +#define _mm_cvttps_pi32(a) _mm_cvtt_ps2pi(a) + +// Convert the lower single-precision (32-bit) floating-point element in a to a +// 32-bit integer with truncation, and store the result in dst. +// +// dst[31:0] := Convert_FP32_To_Int32_Truncate(a[31:0]) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvttss_si32 +#define _mm_cvttss_si32(a) _mm_cvtt_ss2si(a) + +// Convert the lower single-precision (32-bit) floating-point element in a to a +// 64-bit integer with truncation, and store the result in dst. +// +// dst[63:0] := Convert_FP32_To_Int64_Truncate(a[31:0]) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvttss_si64 +FORCE_INLINE int64_t _mm_cvttss_si64(__m128 a) +{ + return (int64_t) vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); +} + +// Divides the four single-precision, floating-point values of a and b. +// +// r0 := a0 / b0 +// r1 := a1 / b1 +// r2 := a2 / b2 +// r3 := a3 / b3 +// +// https://msdn.microsoft.com/en-us/library/edaw8147(v=vs.100).aspx +FORCE_INLINE __m128 _mm_div_ps(__m128 a, __m128 b) +{ +#if defined(__aarch64__) && !SSE2NEON_PRECISE_DIV + return vreinterpretq_m128_f32( + vdivq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +#else + float32x4_t recip = vrecpeq_f32(vreinterpretq_f32_m128(b)); + recip = vmulq_f32(recip, vrecpsq_f32(recip, vreinterpretq_f32_m128(b))); +#if SSE2NEON_PRECISE_DIV + // Additional Netwon-Raphson iteration for accuracy + recip = vmulq_f32(recip, vrecpsq_f32(recip, vreinterpretq_f32_m128(b))); +#endif + return vreinterpretq_m128_f32(vmulq_f32(vreinterpretq_f32_m128(a), recip)); +#endif +} + +// Divides the scalar single-precision floating point value of a by b. +// https://msdn.microsoft.com/en-us/library/4y73xa49(v=vs.100).aspx +FORCE_INLINE __m128 _mm_div_ss(__m128 a, __m128 b) +{ + float32_t value = + vgetq_lane_f32(vreinterpretq_f32_m128(_mm_div_ps(a, b)), 0); + return vreinterpretq_m128_f32( + vsetq_lane_f32(value, vreinterpretq_f32_m128(a), 0)); +} + +// Extract a 16-bit integer from a, selected with imm8, and store the result in +// the lower element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_extract_pi16 +#define _mm_extract_pi16(a, imm) \ + (int32_t) vget_lane_u16(vreinterpret_u16_m64(a), (imm)) + +// Free aligned memory that was allocated with _mm_malloc. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_free +FORCE_INLINE void _mm_free(void *addr) +{ + free(addr); +} + +// Macro: Get the flush zero bits from the MXCSR control and status register. +// The flush zero may contain any of the following flags: _MM_FLUSH_ZERO_ON or +// _MM_FLUSH_ZERO_OFF +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_MM_GET_FLUSH_ZERO_MODE +FORCE_INLINE unsigned int _sse2neon_mm_get_flush_zero_mode() +{ + union { + fpcr_bitfield field; +#if defined(__aarch64__) + uint64_t value; +#else + uint32_t value; +#endif + } r; + +#if defined(__aarch64__) + asm volatile("mrs %0, FPCR" : "=r"(r.value)); /* read */ +#else + asm volatile("vmrs %0, FPSCR" : "=r"(r.value)); /* read */ +#endif + + return r.field.bit24 ? _MM_FLUSH_ZERO_ON : _MM_FLUSH_ZERO_OFF; +} + +// Macro: Get the rounding mode bits from the MXCSR control and status register. +// The rounding mode may contain any of the following flags: _MM_ROUND_NEAREST, +// _MM_ROUND_DOWN, _MM_ROUND_UP, _MM_ROUND_TOWARD_ZERO +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_MM_GET_ROUNDING_MODE +FORCE_INLINE unsigned int _MM_GET_ROUNDING_MODE() +{ + union { + fpcr_bitfield field; +#if defined(__aarch64__) + uint64_t value; +#else + uint32_t value; +#endif + } r; + +#if defined(__aarch64__) + asm volatile("mrs %0, FPCR" : "=r"(r.value)); /* read */ +#else + asm volatile("vmrs %0, FPSCR" : "=r"(r.value)); /* read */ +#endif + + if (r.field.bit22) { + return r.field.bit23 ? _MM_ROUND_TOWARD_ZERO : _MM_ROUND_UP; + } else { + return r.field.bit23 ? _MM_ROUND_DOWN : _MM_ROUND_NEAREST; + } +} + +// Copy a to dst, and insert the 16-bit integer i into dst at the location +// specified by imm8. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_insert_pi16 +#define _mm_insert_pi16(a, b, imm) \ + __extension__({ \ + vreinterpret_m64_s16( \ + vset_lane_s16((b), vreinterpret_s16_m64(a), (imm))); \ + }) + +// Loads four single-precision, floating-point values. +// https://msdn.microsoft.com/en-us/library/vstudio/zzd50xxt(v=vs.100).aspx +FORCE_INLINE __m128 _mm_load_ps(const float *p) +{ + return vreinterpretq_m128_f32(vld1q_f32(p)); +} + +// Load a single-precision (32-bit) floating-point element from memory into all +// elements of dst. +// +// dst[31:0] := MEM[mem_addr+31:mem_addr] +// dst[63:32] := MEM[mem_addr+31:mem_addr] +// dst[95:64] := MEM[mem_addr+31:mem_addr] +// dst[127:96] := MEM[mem_addr+31:mem_addr] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_load_ps1 +#define _mm_load_ps1 _mm_load1_ps + +// Loads an single - precision, floating - point value into the low word and +// clears the upper three words. +// https://msdn.microsoft.com/en-us/library/548bb9h4%28v=vs.90%29.aspx +FORCE_INLINE __m128 _mm_load_ss(const float *p) +{ + return vreinterpretq_m128_f32(vsetq_lane_f32(*p, vdupq_n_f32(0), 0)); +} + +// Loads a single single-precision, floating-point value, copying it into all +// four words +// https://msdn.microsoft.com/en-us/library/vstudio/5cdkf716(v=vs.100).aspx +FORCE_INLINE __m128 _mm_load1_ps(const float *p) +{ + return vreinterpretq_m128_f32(vld1q_dup_f32(p)); +} + +// Sets the upper two single-precision, floating-point values with 64 +// bits of data loaded from the address p; the lower two values are passed +// through from a. +// +// r0 := a0 +// r1 := a1 +// r2 := *p0 +// r3 := *p1 +// +// https://msdn.microsoft.com/en-us/library/w92wta0x(v%3dvs.100).aspx +FORCE_INLINE __m128 _mm_loadh_pi(__m128 a, __m64 const *p) +{ + return vreinterpretq_m128_f32( + vcombine_f32(vget_low_f32(a), vld1_f32((const float32_t *) p))); +} + +// Sets the lower two single-precision, floating-point values with 64 +// bits of data loaded from the address p; the upper two values are passed +// through from a. +// +// Return Value +// r0 := *p0 +// r1 := *p1 +// r2 := a2 +// r3 := a3 +// +// https://msdn.microsoft.com/en-us/library/s57cyak2(v=vs.100).aspx +FORCE_INLINE __m128 _mm_loadl_pi(__m128 a, __m64 const *p) +{ + return vreinterpretq_m128_f32( + vcombine_f32(vld1_f32((const float32_t *) p), vget_high_f32(a))); +} + +// Load 4 single-precision (32-bit) floating-point elements from memory into dst +// in reverse order. mem_addr must be aligned on a 16-byte boundary or a +// general-protection exception may be generated. +// +// dst[31:0] := MEM[mem_addr+127:mem_addr+96] +// dst[63:32] := MEM[mem_addr+95:mem_addr+64] +// dst[95:64] := MEM[mem_addr+63:mem_addr+32] +// dst[127:96] := MEM[mem_addr+31:mem_addr] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_loadr_ps +FORCE_INLINE __m128 _mm_loadr_ps(const float *p) +{ + float32x4_t v = vrev64q_f32(vld1q_f32(p)); + return vreinterpretq_m128_f32(vextq_f32(v, v, 2)); +} + +// Loads four single-precision, floating-point values. +// https://msdn.microsoft.com/en-us/library/x1b16s7z%28v=vs.90%29.aspx +FORCE_INLINE __m128 _mm_loadu_ps(const float *p) +{ + // for neon, alignment doesn't matter, so _mm_load_ps and _mm_loadu_ps are + // equivalent for neon + return vreinterpretq_m128_f32(vld1q_f32(p)); +} + +// Load unaligned 16-bit integer from memory into the first element of dst. +// +// dst[15:0] := MEM[mem_addr+15:mem_addr] +// dst[MAX:16] := 0 +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_loadu_si16 +FORCE_INLINE __m128i _mm_loadu_si16(const void *p) +{ + return vreinterpretq_m128i_s16( + vsetq_lane_s16(*(const int16_t *) p, vdupq_n_s16(0), 0)); +} + +// Load unaligned 64-bit integer from memory into the first element of dst. +// +// dst[63:0] := MEM[mem_addr+63:mem_addr] +// dst[MAX:64] := 0 +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_loadu_si64 +FORCE_INLINE __m128i _mm_loadu_si64(const void *p) +{ + return vreinterpretq_m128i_s64( + vcombine_s64(vld1_s64((const int64_t *) p), vdup_n_s64(0))); +} + +// Allocate aligned blocks of memory. +// https://software.intel.com/en-us/ +// cpp-compiler-developer-guide-and-reference-allocating-and-freeing-aligned-memory-blocks +FORCE_INLINE void *_mm_malloc(size_t size, size_t align) +{ + void *ptr; + if (align == 1) + return malloc(size); + if (align == 2 || (sizeof(void *) == 8 && align == 4)) + align = sizeof(void *); + if (!posix_memalign(&ptr, align, size)) + return ptr; + return NULL; +} + +// Conditionally store 8-bit integer elements from a into memory using mask +// (elements are not stored when the highest bit is not set in the corresponding +// element) and a non-temporal memory hint. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_maskmove_si64 +FORCE_INLINE void _mm_maskmove_si64(__m64 a, __m64 mask, char *mem_addr) +{ + int8x8_t shr_mask = vshr_n_s8(vreinterpret_s8_m64(mask), 7); + __m128 b = _mm_load_ps((const float *) mem_addr); + int8x8_t masked = + vbsl_s8(vreinterpret_u8_s8(shr_mask), vreinterpret_s8_m64(a), + vreinterpret_s8_u64(vget_low_u64(vreinterpretq_u64_m128(b)))); + vst1_s8((int8_t *) mem_addr, masked); +} + +// Conditionally store 8-bit integer elements from a into memory using mask +// (elements are not stored when the highest bit is not set in the corresponding +// element) and a non-temporal memory hint. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_m_maskmovq +#define _m_maskmovq(a, mask, mem_addr) _mm_maskmove_si64(a, mask, mem_addr) + +// Compare packed signed 16-bit integers in a and b, and store packed maximum +// values in dst. +// +// FOR j := 0 to 3 +// i := j*16 +// dst[i+15:i] := MAX(a[i+15:i], b[i+15:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_max_pi16 +FORCE_INLINE __m64 _mm_max_pi16(__m64 a, __m64 b) +{ + return vreinterpret_m64_s16( + vmax_s16(vreinterpret_s16_m64(a), vreinterpret_s16_m64(b))); +} + +// Computes the maximums of the four single-precision, floating-point values of +// a and b. +// https://msdn.microsoft.com/en-us/library/vstudio/ff5d607a(v=vs.100).aspx +FORCE_INLINE __m128 _mm_max_ps(__m128 a, __m128 b) +{ +#if SSE2NEON_PRECISE_MINMAX + float32x4_t _a = vreinterpretq_f32_m128(a); + float32x4_t _b = vreinterpretq_f32_m128(b); + return vreinterpretq_m128_f32(vbslq_f32(vcgtq_f32(_a, _b), _a, _b)); +#else + return vreinterpretq_m128_f32( + vmaxq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +#endif +} + +// Compare packed unsigned 8-bit integers in a and b, and store packed maximum +// values in dst. +// +// FOR j := 0 to 7 +// i := j*8 +// dst[i+7:i] := MAX(a[i+7:i], b[i+7:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_max_pu8 +FORCE_INLINE __m64 _mm_max_pu8(__m64 a, __m64 b) +{ + return vreinterpret_m64_u8( + vmax_u8(vreinterpret_u8_m64(a), vreinterpret_u8_m64(b))); +} + +// Computes the maximum of the two lower scalar single-precision floating point +// values of a and b. +// https://msdn.microsoft.com/en-us/library/s6db5esz(v=vs.100).aspx +FORCE_INLINE __m128 _mm_max_ss(__m128 a, __m128 b) +{ + float32_t value = vgetq_lane_f32(_mm_max_ps(a, b), 0); + return vreinterpretq_m128_f32( + vsetq_lane_f32(value, vreinterpretq_f32_m128(a), 0)); +} + +// Compare packed signed 16-bit integers in a and b, and store packed minimum +// values in dst. +// +// FOR j := 0 to 3 +// i := j*16 +// dst[i+15:i] := MIN(a[i+15:i], b[i+15:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_min_pi16 +FORCE_INLINE __m64 _mm_min_pi16(__m64 a, __m64 b) +{ + return vreinterpret_m64_s16( + vmin_s16(vreinterpret_s16_m64(a), vreinterpret_s16_m64(b))); +} + +// Computes the minima of the four single-precision, floating-point values of a +// and b. +// https://msdn.microsoft.com/en-us/library/vstudio/wh13kadz(v=vs.100).aspx +FORCE_INLINE __m128 _mm_min_ps(__m128 a, __m128 b) +{ +#if SSE2NEON_PRECISE_MINMAX + float32x4_t _a = vreinterpretq_f32_m128(a); + float32x4_t _b = vreinterpretq_f32_m128(b); + return vreinterpretq_m128_f32(vbslq_f32(vcltq_f32(_a, _b), _a, _b)); +#else + return vreinterpretq_m128_f32( + vminq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +#endif +} + +// Compare packed unsigned 8-bit integers in a and b, and store packed minimum +// values in dst. +// +// FOR j := 0 to 7 +// i := j*8 +// dst[i+7:i] := MIN(a[i+7:i], b[i+7:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_min_pu8 +FORCE_INLINE __m64 _mm_min_pu8(__m64 a, __m64 b) +{ + return vreinterpret_m64_u8( + vmin_u8(vreinterpret_u8_m64(a), vreinterpret_u8_m64(b))); +} + +// Computes the minimum of the two lower scalar single-precision floating point +// values of a and b. +// https://msdn.microsoft.com/en-us/library/0a9y7xaa(v=vs.100).aspx +FORCE_INLINE __m128 _mm_min_ss(__m128 a, __m128 b) +{ + float32_t value = vgetq_lane_f32(_mm_min_ps(a, b), 0); + return vreinterpretq_m128_f32( + vsetq_lane_f32(value, vreinterpretq_f32_m128(a), 0)); +} + +// Sets the low word to the single-precision, floating-point value of b +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/35hdzazd(v=vs.100) +FORCE_INLINE __m128 _mm_move_ss(__m128 a, __m128 b) +{ + return vreinterpretq_m128_f32( + vsetq_lane_f32(vgetq_lane_f32(vreinterpretq_f32_m128(b), 0), + vreinterpretq_f32_m128(a), 0)); +} + +// Moves the upper two values of B into the lower two values of A. +// +// r3 := a3 +// r2 := a2 +// r1 := b3 +// r0 := b2 +FORCE_INLINE __m128 _mm_movehl_ps(__m128 __A, __m128 __B) +{ + float32x2_t a32 = vget_high_f32(vreinterpretq_f32_m128(__A)); + float32x2_t b32 = vget_high_f32(vreinterpretq_f32_m128(__B)); + return vreinterpretq_m128_f32(vcombine_f32(b32, a32)); +} + +// Moves the lower two values of B into the upper two values of A. +// +// r3 := b1 +// r2 := b0 +// r1 := a1 +// r0 := a0 +FORCE_INLINE __m128 _mm_movelh_ps(__m128 __A, __m128 __B) +{ + float32x2_t a10 = vget_low_f32(vreinterpretq_f32_m128(__A)); + float32x2_t b10 = vget_low_f32(vreinterpretq_f32_m128(__B)); + return vreinterpretq_m128_f32(vcombine_f32(a10, b10)); +} + +// Create mask from the most significant bit of each 8-bit element in a, and +// store the result in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_movemask_pi8 +FORCE_INLINE int _mm_movemask_pi8(__m64 a) +{ + uint8x8_t input = vreinterpret_u8_m64(a); +#if defined(__aarch64__) + static const int8x8_t shift = {0, 1, 2, 3, 4, 5, 6, 7}; + uint8x8_t tmp = vshr_n_u8(input, 7); + return vaddv_u8(vshl_u8(tmp, shift)); +#else + // Refer the implementation of `_mm_movemask_epi8` + uint16x4_t high_bits = vreinterpret_u16_u8(vshr_n_u8(input, 7)); + uint32x2_t paired16 = + vreinterpret_u32_u16(vsra_n_u16(high_bits, high_bits, 7)); + uint8x8_t paired32 = + vreinterpret_u8_u32(vsra_n_u32(paired16, paired16, 14)); + return vget_lane_u8(paired32, 0) | ((int) vget_lane_u8(paired32, 4) << 4); +#endif +} + +// NEON does not provide this method +// Creates a 4-bit mask from the most significant bits of the four +// single-precision, floating-point values. +// https://msdn.microsoft.com/en-us/library/vstudio/4490ys29(v=vs.100).aspx +FORCE_INLINE int _mm_movemask_ps(__m128 a) +{ + uint32x4_t input = vreinterpretq_u32_m128(a); +#if defined(__aarch64__) + static const int32x4_t shift = {0, 1, 2, 3}; + uint32x4_t tmp = vshrq_n_u32(input, 31); + return vaddvq_u32(vshlq_u32(tmp, shift)); +#else + // Uses the exact same method as _mm_movemask_epi8, see that for details. + // Shift out everything but the sign bits with a 32-bit unsigned shift + // right. + uint64x2_t high_bits = vreinterpretq_u64_u32(vshrq_n_u32(input, 31)); + // Merge the two pairs together with a 64-bit unsigned shift right + add. + uint8x16_t paired = + vreinterpretq_u8_u64(vsraq_n_u64(high_bits, high_bits, 31)); + // Extract the result. + return vgetq_lane_u8(paired, 0) | (vgetq_lane_u8(paired, 8) << 2); +#endif +} + +// Multiplies the four single-precision, floating-point values of a and b. +// +// r0 := a0 * b0 +// r1 := a1 * b1 +// r2 := a2 * b2 +// r3 := a3 * b3 +// +// https://msdn.microsoft.com/en-us/library/vstudio/22kbk6t9(v=vs.100).aspx +FORCE_INLINE __m128 _mm_mul_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_f32( + vmulq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Multiply the lower single-precision (32-bit) floating-point element in a and +// b, store the result in the lower element of dst, and copy the upper 3 packed +// elements from a to the upper elements of dst. +// +// dst[31:0] := a[31:0] * b[31:0] +// dst[127:32] := a[127:32] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mul_ss +FORCE_INLINE __m128 _mm_mul_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_mul_ps(a, b)); +} + +// Multiply the packed unsigned 16-bit integers in a and b, producing +// intermediate 32-bit integers, and store the high 16 bits of the intermediate +// integers in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mulhi_pu16 +FORCE_INLINE __m64 _mm_mulhi_pu16(__m64 a, __m64 b) +{ + return vreinterpret_m64_u16(vshrn_n_u32( + vmull_u16(vreinterpret_u16_m64(a), vreinterpret_u16_m64(b)), 16)); +} + +// Computes the bitwise OR of the four single-precision, floating-point values +// of a and b. +// https://msdn.microsoft.com/en-us/library/vstudio/7ctdsyy0(v=vs.100).aspx +FORCE_INLINE __m128 _mm_or_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_s32( + vorrq_s32(vreinterpretq_s32_m128(a), vreinterpretq_s32_m128(b))); +} + +// Average packed unsigned 8-bit integers in a and b, and store the results in +// dst. +// +// FOR j := 0 to 7 +// i := j*8 +// dst[i+7:i] := (a[i+7:i] + b[i+7:i] + 1) >> 1 +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_m_pavgb +#define _m_pavgb(a, b) _mm_avg_pu8(a, b) + +// Average packed unsigned 16-bit integers in a and b, and store the results in +// dst. +// +// FOR j := 0 to 3 +// i := j*16 +// dst[i+15:i] := (a[i+15:i] + b[i+15:i] + 1) >> 1 +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_m_pavgw +#define _m_pavgw(a, b) _mm_avg_pu16(a, b) + +// Extract a 16-bit integer from a, selected with imm8, and store the result in +// the lower element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_m_pextrw +#define _m_pextrw(a, imm) _mm_extract_pi16(a, imm) + +// Copy a to dst, and insert the 16-bit integer i into dst at the location +// specified by imm8. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=m_pinsrw +#define _m_pinsrw(a, i, imm) _mm_insert_pi16(a, i, imm) + +// Compare packed signed 16-bit integers in a and b, and store packed maximum +// values in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_m_pmaxsw +#define _m_pmaxsw(a, b) _mm_max_pi16(a, b) + +// Compare packed unsigned 8-bit integers in a and b, and store packed maximum +// values in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_m_pmaxub +#define _m_pmaxub(a, b) _mm_max_pu8(a, b) + +// Compare packed signed 16-bit integers in a and b, and store packed minimum +// values in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_m_pminsw +#define _m_pminsw(a, b) _mm_min_pi16(a, b) + +// Compare packed unsigned 8-bit integers in a and b, and store packed minimum +// values in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_m_pminub +#define _m_pminub(a, b) _mm_min_pu8(a, b) + +// Create mask from the most significant bit of each 8-bit element in a, and +// store the result in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_m_pmovmskb +#define _m_pmovmskb(a) _mm_movemask_pi8(a) + +// Multiply the packed unsigned 16-bit integers in a and b, producing +// intermediate 32-bit integers, and store the high 16 bits of the intermediate +// integers in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_m_pmulhuw +#define _m_pmulhuw(a, b) _mm_mulhi_pu16(a, b) + +// Loads one cache line of data from address p to a location closer to the +// processor. https://msdn.microsoft.com/en-us/library/84szxsww(v=vs.100).aspx +FORCE_INLINE void _mm_prefetch(const void *p, int i) +{ + (void) i; + __builtin_prefetch(p); +} + +// Compute the absolute differences of packed unsigned 8-bit integers in a and +// b, then horizontally sum each consecutive 8 differences to produce four +// unsigned 16-bit integers, and pack these unsigned 16-bit integers in the low +// 16 bits of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=m_psadbw +#define _m_psadbw(a, b) _mm_sad_pu8(a, b) + +// Shuffle 16-bit integers in a using the control in imm8, and store the results +// in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_m_pshufw +#define _m_pshufw(a, imm) _mm_shuffle_pi16(a, imm) + +// Compute the approximate reciprocal of packed single-precision (32-bit) +// floating-point elements in a, and store the results in dst. The maximum +// relative error for this approximation is less than 1.5*2^-12. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_rcp_ps +FORCE_INLINE __m128 _mm_rcp_ps(__m128 in) +{ + float32x4_t recip = vrecpeq_f32(vreinterpretq_f32_m128(in)); + recip = vmulq_f32(recip, vrecpsq_f32(recip, vreinterpretq_f32_m128(in))); +#if SSE2NEON_PRECISE_DIV + // Additional Netwon-Raphson iteration for accuracy + recip = vmulq_f32(recip, vrecpsq_f32(recip, vreinterpretq_f32_m128(in))); +#endif + return vreinterpretq_m128_f32(recip); +} + +// Compute the approximate reciprocal of the lower single-precision (32-bit) +// floating-point element in a, store the result in the lower element of dst, +// and copy the upper 3 packed elements from a to the upper elements of dst. The +// maximum relative error for this approximation is less than 1.5*2^-12. +// +// dst[31:0] := (1.0 / a[31:0]) +// dst[127:32] := a[127:32] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_rcp_ss +FORCE_INLINE __m128 _mm_rcp_ss(__m128 a) +{ + return _mm_move_ss(a, _mm_rcp_ps(a)); +} + +// Computes the approximations of the reciprocal square roots of the four +// single-precision floating point values of in. +// The current precision is 1% error. +// https://msdn.microsoft.com/en-us/library/22hfsh53(v=vs.100).aspx +FORCE_INLINE __m128 _mm_rsqrt_ps(__m128 in) +{ + float32x4_t out = vrsqrteq_f32(vreinterpretq_f32_m128(in)); +#if SSE2NEON_PRECISE_SQRT + // Additional Netwon-Raphson iteration for accuracy + out = vmulq_f32( + out, vrsqrtsq_f32(vmulq_f32(vreinterpretq_f32_m128(in), out), out)); + out = vmulq_f32( + out, vrsqrtsq_f32(vmulq_f32(vreinterpretq_f32_m128(in), out), out)); +#endif + return vreinterpretq_m128_f32(out); +} + +// Compute the approximate reciprocal square root of the lower single-precision +// (32-bit) floating-point element in a, store the result in the lower element +// of dst, and copy the upper 3 packed elements from a to the upper elements of +// dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_rsqrt_ss +FORCE_INLINE __m128 _mm_rsqrt_ss(__m128 in) +{ + return vsetq_lane_f32(vgetq_lane_f32(_mm_rsqrt_ps(in), 0), in, 0); +} + +// Compute the absolute differences of packed unsigned 8-bit integers in a and +// b, then horizontally sum each consecutive 8 differences to produce four +// unsigned 16-bit integers, and pack these unsigned 16-bit integers in the low +// 16 bits of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sad_pu8 +FORCE_INLINE __m64 _mm_sad_pu8(__m64 a, __m64 b) +{ + uint64x1_t t = vpaddl_u32(vpaddl_u16( + vpaddl_u8(vabd_u8(vreinterpret_u8_m64(a), vreinterpret_u8_m64(b))))); + return vreinterpret_m64_u16( + vset_lane_u16(vget_lane_u64(t, 0), vdup_n_u16(0), 0)); +} + +// Macro: Set the flush zero bits of the MXCSR control and status register to +// the value in unsigned 32-bit integer a. The flush zero may contain any of the +// following flags: _MM_FLUSH_ZERO_ON or _MM_FLUSH_ZERO_OFF +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_MM_SET_FLUSH_ZERO_MODE +FORCE_INLINE void _sse2neon_mm_set_flush_zero_mode(unsigned int flag) +{ + // AArch32 Advanced SIMD arithmetic always uses the Flush-to-zero setting, + // regardless of the value of the FZ bit. + union { + fpcr_bitfield field; +#if defined(__aarch64__) + uint64_t value; +#else + uint32_t value; +#endif + } r; + +#if defined(__aarch64__) + asm volatile("mrs %0, FPCR" : "=r"(r.value)); /* read */ +#else + asm volatile("vmrs %0, FPSCR" : "=r"(r.value)); /* read */ +#endif + + r.field.bit24 = (flag & _MM_FLUSH_ZERO_MASK) == _MM_FLUSH_ZERO_ON; + +#if defined(__aarch64__) + asm volatile("msr FPCR, %0" ::"r"(r)); /* write */ +#else + asm volatile("vmsr FPSCR, %0" ::"r"(r)); /* write */ +#endif +} + +// Sets the four single-precision, floating-point values to the four inputs. +// https://msdn.microsoft.com/en-us/library/vstudio/afh0zf75(v=vs.100).aspx +FORCE_INLINE __m128 _mm_set_ps(float w, float z, float y, float x) +{ + float ALIGN_STRUCT(16) data[4] = {x, y, z, w}; + return vreinterpretq_m128_f32(vld1q_f32(data)); +} + +// Sets the four single-precision, floating-point values to w. +// https://msdn.microsoft.com/en-us/library/vstudio/2x1se8ha(v=vs.100).aspx +FORCE_INLINE __m128 _mm_set_ps1(float _w) +{ + return vreinterpretq_m128_f32(vdupq_n_f32(_w)); +} + +// Macro: Set the rounding mode bits of the MXCSR control and status register to +// the value in unsigned 32-bit integer a. The rounding mode may contain any of +// the following flags: _MM_ROUND_NEAREST, _MM_ROUND_DOWN, _MM_ROUND_UP, +// _MM_ROUND_TOWARD_ZERO +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_MM_SET_ROUNDING_MODE +FORCE_INLINE void _MM_SET_ROUNDING_MODE(int rounding) +{ + union { + fpcr_bitfield field; +#if defined(__aarch64__) + uint64_t value; +#else + uint32_t value; +#endif + } r; + +#if defined(__aarch64__) + asm volatile("mrs %0, FPCR" : "=r"(r.value)); /* read */ +#else + asm volatile("vmrs %0, FPSCR" : "=r"(r.value)); /* read */ +#endif + + switch (rounding) { + case _MM_ROUND_TOWARD_ZERO: + r.field.bit22 = 1; + r.field.bit23 = 1; + break; + case _MM_ROUND_DOWN: + r.field.bit22 = 0; + r.field.bit23 = 1; + break; + case _MM_ROUND_UP: + r.field.bit22 = 1; + r.field.bit23 = 0; + break; + default: //_MM_ROUND_NEAREST + r.field.bit22 = 0; + r.field.bit23 = 0; + } + +#if defined(__aarch64__) + asm volatile("msr FPCR, %0" ::"r"(r)); /* write */ +#else + asm volatile("vmsr FPSCR, %0" ::"r"(r)); /* write */ +#endif +} + +// Copy single-precision (32-bit) floating-point element a to the lower element +// of dst, and zero the upper 3 elements. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_set_ss +FORCE_INLINE __m128 _mm_set_ss(float a) +{ + float ALIGN_STRUCT(16) data[4] = {a, 0, 0, 0}; + return vreinterpretq_m128_f32(vld1q_f32(data)); +} + +// Sets the four single-precision, floating-point values to w. +// +// r0 := r1 := r2 := r3 := w +// +// https://msdn.microsoft.com/en-us/library/vstudio/2x1se8ha(v=vs.100).aspx +FORCE_INLINE __m128 _mm_set1_ps(float _w) +{ + return vreinterpretq_m128_f32(vdupq_n_f32(_w)); +} + +// FIXME: _mm_setcsr() implementation supports changing the rounding mode only. +FORCE_INLINE void _mm_setcsr(unsigned int a) +{ + _MM_SET_ROUNDING_MODE(a); +} + +// FIXME: _mm_getcsr() implementation supports reading the rounding mode only. +FORCE_INLINE unsigned int _mm_getcsr() +{ + return _MM_GET_ROUNDING_MODE(); +} + +// Sets the four single-precision, floating-point values to the four inputs in +// reverse order. +// https://msdn.microsoft.com/en-us/library/vstudio/d2172ct3(v=vs.100).aspx +FORCE_INLINE __m128 _mm_setr_ps(float w, float z, float y, float x) +{ + float ALIGN_STRUCT(16) data[4] = {w, z, y, x}; + return vreinterpretq_m128_f32(vld1q_f32(data)); +} + +// Clears the four single-precision, floating-point values. +// https://msdn.microsoft.com/en-us/library/vstudio/tk1t2tbz(v=vs.100).aspx +FORCE_INLINE __m128 _mm_setzero_ps(void) +{ + return vreinterpretq_m128_f32(vdupq_n_f32(0)); +} + +// Shuffle 16-bit integers in a using the control in imm8, and store the results +// in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_shuffle_pi16 +#if __has_builtin(__builtin_shufflevector) +#define _mm_shuffle_pi16(a, imm) \ + __extension__({ \ + vreinterpret_m64_s16(__builtin_shufflevector( \ + vreinterpret_s16_m64(a), vreinterpret_s16_m64(a), (imm & 0x3), \ + ((imm >> 2) & 0x3), ((imm >> 4) & 0x3), ((imm >> 6) & 0x3))); \ + }) +#else +#define _mm_shuffle_pi16(a, imm) \ + __extension__({ \ + int16x4_t ret; \ + ret = \ + vmov_n_s16(vget_lane_s16(vreinterpret_s16_m64(a), (imm) & (0x3))); \ + ret = vset_lane_s16( \ + vget_lane_s16(vreinterpret_s16_m64(a), ((imm) >> 2) & 0x3), ret, \ + 1); \ + ret = vset_lane_s16( \ + vget_lane_s16(vreinterpret_s16_m64(a), ((imm) >> 4) & 0x3), ret, \ + 2); \ + ret = vset_lane_s16( \ + vget_lane_s16(vreinterpret_s16_m64(a), ((imm) >> 6) & 0x3), ret, \ + 3); \ + vreinterpret_m64_s16(ret); \ + }) +#endif + +// Guarantees that every preceding store is globally visible before any +// subsequent store. +// https://msdn.microsoft.com/en-us/library/5h2w73d1%28v=vs.90%29.aspx +FORCE_INLINE void _mm_sfence(void) +{ + __sync_synchronize(); +} + +// FORCE_INLINE __m128 _mm_shuffle_ps(__m128 a, __m128 b, __constrange(0,255) +// int imm) +#if __has_builtin(__builtin_shufflevector) +#define _mm_shuffle_ps(a, b, imm) \ + __extension__({ \ + float32x4_t _input1 = vreinterpretq_f32_m128(a); \ + float32x4_t _input2 = vreinterpretq_f32_m128(b); \ + float32x4_t _shuf = __builtin_shufflevector( \ + _input1, _input2, (imm) & (0x3), ((imm) >> 2) & 0x3, \ + (((imm) >> 4) & 0x3) + 4, (((imm) >> 6) & 0x3) + 4); \ + vreinterpretq_m128_f32(_shuf); \ + }) +#else // generic +#define _mm_shuffle_ps(a, b, imm) \ + __extension__({ \ + __m128 ret; \ + switch (imm) { \ + case _MM_SHUFFLE(1, 0, 3, 2): \ + ret = _mm_shuffle_ps_1032((a), (b)); \ + break; \ + case _MM_SHUFFLE(2, 3, 0, 1): \ + ret = _mm_shuffle_ps_2301((a), (b)); \ + break; \ + case _MM_SHUFFLE(0, 3, 2, 1): \ + ret = _mm_shuffle_ps_0321((a), (b)); \ + break; \ + case _MM_SHUFFLE(2, 1, 0, 3): \ + ret = _mm_shuffle_ps_2103((a), (b)); \ + break; \ + case _MM_SHUFFLE(1, 0, 1, 0): \ + ret = _mm_movelh_ps((a), (b)); \ + break; \ + case _MM_SHUFFLE(1, 0, 0, 1): \ + ret = _mm_shuffle_ps_1001((a), (b)); \ + break; \ + case _MM_SHUFFLE(0, 1, 0, 1): \ + ret = _mm_shuffle_ps_0101((a), (b)); \ + break; \ + case _MM_SHUFFLE(3, 2, 1, 0): \ + ret = _mm_shuffle_ps_3210((a), (b)); \ + break; \ + case _MM_SHUFFLE(0, 0, 1, 1): \ + ret = _mm_shuffle_ps_0011((a), (b)); \ + break; \ + case _MM_SHUFFLE(0, 0, 2, 2): \ + ret = _mm_shuffle_ps_0022((a), (b)); \ + break; \ + case _MM_SHUFFLE(2, 2, 0, 0): \ + ret = _mm_shuffle_ps_2200((a), (b)); \ + break; \ + case _MM_SHUFFLE(3, 2, 0, 2): \ + ret = _mm_shuffle_ps_3202((a), (b)); \ + break; \ + case _MM_SHUFFLE(3, 2, 3, 2): \ + ret = _mm_movehl_ps((b), (a)); \ + break; \ + case _MM_SHUFFLE(1, 1, 3, 3): \ + ret = _mm_shuffle_ps_1133((a), (b)); \ + break; \ + case _MM_SHUFFLE(2, 0, 1, 0): \ + ret = _mm_shuffle_ps_2010((a), (b)); \ + break; \ + case _MM_SHUFFLE(2, 0, 0, 1): \ + ret = _mm_shuffle_ps_2001((a), (b)); \ + break; \ + case _MM_SHUFFLE(2, 0, 3, 2): \ + ret = _mm_shuffle_ps_2032((a), (b)); \ + break; \ + default: \ + ret = _mm_shuffle_ps_default((a), (b), (imm)); \ + break; \ + } \ + ret; \ + }) +#endif + +// Computes the approximations of square roots of the four single-precision, +// floating-point values of a. First computes reciprocal square roots and then +// reciprocals of the four values. +// +// r0 := sqrt(a0) +// r1 := sqrt(a1) +// r2 := sqrt(a2) +// r3 := sqrt(a3) +// +// https://msdn.microsoft.com/en-us/library/vstudio/8z67bwwk(v=vs.100).aspx +FORCE_INLINE __m128 _mm_sqrt_ps(__m128 in) +{ +#if SSE2NEON_PRECISE_SQRT + float32x4_t recip = vrsqrteq_f32(vreinterpretq_f32_m128(in)); + + // Test for vrsqrteq_f32(0) -> positive infinity case. + // Change to zero, so that s * 1/sqrt(s) result is zero too. + const uint32x4_t pos_inf = vdupq_n_u32(0x7F800000); + const uint32x4_t div_by_zero = + vceqq_u32(pos_inf, vreinterpretq_u32_f32(recip)); + recip = vreinterpretq_f32_u32( + vandq_u32(vmvnq_u32(div_by_zero), vreinterpretq_u32_f32(recip))); + + // Additional Netwon-Raphson iteration for accuracy + recip = vmulq_f32( + vrsqrtsq_f32(vmulq_f32(recip, recip), vreinterpretq_f32_m128(in)), + recip); + recip = vmulq_f32( + vrsqrtsq_f32(vmulq_f32(recip, recip), vreinterpretq_f32_m128(in)), + recip); + + // sqrt(s) = s * 1/sqrt(s) + return vreinterpretq_m128_f32(vmulq_f32(vreinterpretq_f32_m128(in), recip)); +#elif defined(__aarch64__) + return vreinterpretq_m128_f32(vsqrtq_f32(vreinterpretq_f32_m128(in))); +#else + float32x4_t recipsq = vrsqrteq_f32(vreinterpretq_f32_m128(in)); + float32x4_t sq = vrecpeq_f32(recipsq); + return vreinterpretq_m128_f32(sq); +#endif +} + +// Computes the approximation of the square root of the scalar single-precision +// floating point value of in. +// https://msdn.microsoft.com/en-us/library/ahfsc22d(v=vs.100).aspx +FORCE_INLINE __m128 _mm_sqrt_ss(__m128 in) +{ + float32_t value = + vgetq_lane_f32(vreinterpretq_f32_m128(_mm_sqrt_ps(in)), 0); + return vreinterpretq_m128_f32( + vsetq_lane_f32(value, vreinterpretq_f32_m128(in), 0)); +} + +// Stores four single-precision, floating-point values. +// https://msdn.microsoft.com/en-us/library/vstudio/s3h4ay6y(v=vs.100).aspx +FORCE_INLINE void _mm_store_ps(float *p, __m128 a) +{ + vst1q_f32(p, vreinterpretq_f32_m128(a)); +} + +// Store the lower single-precision (32-bit) floating-point element from a into +// 4 contiguous elements in memory. mem_addr must be aligned on a 16-byte +// boundary or a general-protection exception may be generated. +// +// MEM[mem_addr+31:mem_addr] := a[31:0] +// MEM[mem_addr+63:mem_addr+32] := a[31:0] +// MEM[mem_addr+95:mem_addr+64] := a[31:0] +// MEM[mem_addr+127:mem_addr+96] := a[31:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_store_ps1 +FORCE_INLINE void _mm_store_ps1(float *p, __m128 a) +{ + float32_t a0 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); + vst1q_f32(p, vdupq_n_f32(a0)); +} + +// Stores the lower single - precision, floating - point value. +// https://msdn.microsoft.com/en-us/library/tzz10fbx(v=vs.100).aspx +FORCE_INLINE void _mm_store_ss(float *p, __m128 a) +{ + vst1q_lane_f32(p, vreinterpretq_f32_m128(a), 0); +} + +// Store the lower single-precision (32-bit) floating-point element from a into +// 4 contiguous elements in memory. mem_addr must be aligned on a 16-byte +// boundary or a general-protection exception may be generated. +// +// MEM[mem_addr+31:mem_addr] := a[31:0] +// MEM[mem_addr+63:mem_addr+32] := a[31:0] +// MEM[mem_addr+95:mem_addr+64] := a[31:0] +// MEM[mem_addr+127:mem_addr+96] := a[31:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_store1_ps +#define _mm_store1_ps _mm_store_ps1 + +// Stores the upper two single-precision, floating-point values of a to the +// address p. +// +// *p0 := a2 +// *p1 := a3 +// +// https://msdn.microsoft.com/en-us/library/a7525fs8(v%3dvs.90).aspx +FORCE_INLINE void _mm_storeh_pi(__m64 *p, __m128 a) +{ + *p = vreinterpret_m64_f32(vget_high_f32(a)); +} + +// Stores the lower two single-precision floating point values of a to the +// address p. +// +// *p0 := a0 +// *p1 := a1 +// +// https://msdn.microsoft.com/en-us/library/h54t98ks(v=vs.90).aspx +FORCE_INLINE void _mm_storel_pi(__m64 *p, __m128 a) +{ + *p = vreinterpret_m64_f32(vget_low_f32(a)); +} + +// Store 4 single-precision (32-bit) floating-point elements from a into memory +// in reverse order. mem_addr must be aligned on a 16-byte boundary or a +// general-protection exception may be generated. +// +// MEM[mem_addr+31:mem_addr] := a[127:96] +// MEM[mem_addr+63:mem_addr+32] := a[95:64] +// MEM[mem_addr+95:mem_addr+64] := a[63:32] +// MEM[mem_addr+127:mem_addr+96] := a[31:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_storer_ps +FORCE_INLINE void _mm_storer_ps(float *p, __m128 a) +{ + float32x4_t tmp = vrev64q_f32(vreinterpretq_f32_m128(a)); + float32x4_t rev = vextq_f32(tmp, tmp, 2); + vst1q_f32(p, rev); +} + +// Stores four single-precision, floating-point values. +// https://msdn.microsoft.com/en-us/library/44e30x22(v=vs.100).aspx +FORCE_INLINE void _mm_storeu_ps(float *p, __m128 a) +{ + vst1q_f32(p, vreinterpretq_f32_m128(a)); +} + +// Stores 16-bits of integer data a at the address p. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_storeu_si16 +FORCE_INLINE void _mm_storeu_si16(void *p, __m128i a) +{ + vst1q_lane_s16((int16_t *) p, vreinterpretq_s16_m128i(a), 0); +} + +// Stores 64-bits of integer data a at the address p. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_storeu_si64 +FORCE_INLINE void _mm_storeu_si64(void *p, __m128i a) +{ + vst1q_lane_s64((int64_t *) p, vreinterpretq_s64_m128i(a), 0); +} + +// Store 64-bits of integer data from a into memory using a non-temporal memory +// hint. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_stream_pi +FORCE_INLINE void _mm_stream_pi(__m64 *p, __m64 a) +{ + vst1_s64((int64_t *) p, vreinterpret_s64_m64(a)); +} + +// Store 128-bits (composed of 4 packed single-precision (32-bit) floating- +// point elements) from a into memory using a non-temporal memory hint. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_stream_ps +FORCE_INLINE void _mm_stream_ps(float *p, __m128 a) +{ +#if __has_builtin(__builtin_nontemporal_store) + __builtin_nontemporal_store(a, (float32x4_t *) p); +#else + vst1q_f32(p, vreinterpretq_f32_m128(a)); +#endif +} + +// Subtracts the four single-precision, floating-point values of a and b. +// +// r0 := a0 - b0 +// r1 := a1 - b1 +// r2 := a2 - b2 +// r3 := a3 - b3 +// +// https://msdn.microsoft.com/en-us/library/vstudio/1zad2k61(v=vs.100).aspx +FORCE_INLINE __m128 _mm_sub_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_f32( + vsubq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +} + +// Subtract the lower single-precision (32-bit) floating-point element in b from +// the lower single-precision (32-bit) floating-point element in a, store the +// result in the lower element of dst, and copy the upper 3 packed elements from +// a to the upper elements of dst. +// +// dst[31:0] := a[31:0] - b[31:0] +// dst[127:32] := a[127:32] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sub_ss +FORCE_INLINE __m128 _mm_sub_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_sub_ps(a, b)); +} + +// Macro: Transpose the 4x4 matrix formed by the 4 rows of single-precision +// (32-bit) floating-point elements in row0, row1, row2, and row3, and store the +// transposed matrix in these vectors (row0 now contains column 0, etc.). +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=MM_TRANSPOSE4_PS +#define _MM_TRANSPOSE4_PS(row0, row1, row2, row3) \ + do { \ + float32x4x2_t ROW01 = vtrnq_f32(row0, row1); \ + float32x4x2_t ROW23 = vtrnq_f32(row2, row3); \ + row0 = vcombine_f32(vget_low_f32(ROW01.val[0]), \ + vget_low_f32(ROW23.val[0])); \ + row1 = vcombine_f32(vget_low_f32(ROW01.val[1]), \ + vget_low_f32(ROW23.val[1])); \ + row2 = vcombine_f32(vget_high_f32(ROW01.val[0]), \ + vget_high_f32(ROW23.val[0])); \ + row3 = vcombine_f32(vget_high_f32(ROW01.val[1]), \ + vget_high_f32(ROW23.val[1])); \ + } while (0) + +// according to the documentation, these intrinsics behave the same as the +// non-'u' versions. We'll just alias them here. +#define _mm_ucomieq_ss _mm_comieq_ss +#define _mm_ucomige_ss _mm_comige_ss +#define _mm_ucomigt_ss _mm_comigt_ss +#define _mm_ucomile_ss _mm_comile_ss +#define _mm_ucomilt_ss _mm_comilt_ss +#define _mm_ucomineq_ss _mm_comineq_ss + +// Return vector of type __m128i with undefined elements. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=mm_undefined_si128 +FORCE_INLINE __m128i _mm_undefined_si128(void) +{ +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuninitialized" +#endif + __m128i a; + return a; +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif +} + +// Return vector of type __m128 with undefined elements. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_undefined_ps +FORCE_INLINE __m128 _mm_undefined_ps(void) +{ +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuninitialized" +#endif + __m128 a; + return a; +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif +} + +// Selects and interleaves the upper two single-precision, floating-point values +// from a and b. +// +// r0 := a2 +// r1 := b2 +// r2 := a3 +// r3 := b3 +// +// https://msdn.microsoft.com/en-us/library/skccxx7d%28v=vs.90%29.aspx +FORCE_INLINE __m128 _mm_unpackhi_ps(__m128 a, __m128 b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128_f32( + vzip2q_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +#else + float32x2_t a1 = vget_high_f32(vreinterpretq_f32_m128(a)); + float32x2_t b1 = vget_high_f32(vreinterpretq_f32_m128(b)); + float32x2x2_t result = vzip_f32(a1, b1); + return vreinterpretq_m128_f32(vcombine_f32(result.val[0], result.val[1])); +#endif +} + +// Selects and interleaves the lower two single-precision, floating-point values +// from a and b. +// +// r0 := a0 +// r1 := b0 +// r2 := a1 +// r3 := b1 +// +// https://msdn.microsoft.com/en-us/library/25st103b%28v=vs.90%29.aspx +FORCE_INLINE __m128 _mm_unpacklo_ps(__m128 a, __m128 b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128_f32( + vzip1q_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +#else + float32x2_t a1 = vget_low_f32(vreinterpretq_f32_m128(a)); + float32x2_t b1 = vget_low_f32(vreinterpretq_f32_m128(b)); + float32x2x2_t result = vzip_f32(a1, b1); + return vreinterpretq_m128_f32(vcombine_f32(result.val[0], result.val[1])); +#endif +} + +// Computes bitwise EXOR (exclusive-or) of the four single-precision, +// floating-point values of a and b. +// https://msdn.microsoft.com/en-us/library/ss6k3wk8(v=vs.100).aspx +FORCE_INLINE __m128 _mm_xor_ps(__m128 a, __m128 b) +{ + return vreinterpretq_m128_s32( + veorq_s32(vreinterpretq_s32_m128(a), vreinterpretq_s32_m128(b))); +} + +/* SSE2 */ + +// Adds the 8 signed or unsigned 16-bit integers in a to the 8 signed or +// unsigned 16-bit integers in b. +// https://msdn.microsoft.com/en-us/library/fceha5k4(v=vs.100).aspx +FORCE_INLINE __m128i _mm_add_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vaddq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Adds the 4 signed or unsigned 32-bit integers in a to the 4 signed or +// unsigned 32-bit integers in b. +// +// r0 := a0 + b0 +// r1 := a1 + b1 +// r2 := a2 + b2 +// r3 := a3 + b3 +// +// https://msdn.microsoft.com/en-us/library/vstudio/09xs4fkk(v=vs.100).aspx +FORCE_INLINE __m128i _mm_add_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vaddq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Adds the 4 signed or unsigned 64-bit integers in a to the 4 signed or +// unsigned 32-bit integers in b. +// https://msdn.microsoft.com/en-us/library/vstudio/09xs4fkk(v=vs.100).aspx +FORCE_INLINE __m128i _mm_add_epi64(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s64( + vaddq_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(b))); +} + +// Adds the 16 signed or unsigned 8-bit integers in a to the 16 signed or +// unsigned 8-bit integers in b. +// https://technet.microsoft.com/en-us/subscriptions/yc7tcyzs(v=vs.90) +FORCE_INLINE __m128i _mm_add_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s8( + vaddq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Add packed double-precision (64-bit) floating-point elements in a and b, and +// store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_add_pd +FORCE_INLINE __m128d _mm_add_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64( + vaddq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + double *da = (double *) &a; + double *db = (double *) &b; + double c[2]; + c[0] = da[0] + db[0]; + c[1] = da[1] + db[1]; + return vld1q_f32((float32_t *) c); +#endif +} + +// Add the lower double-precision (64-bit) floating-point element in a and b, +// store the result in the lower element of dst, and copy the upper element from +// a to the upper element of dst. +// +// dst[63:0] := a[63:0] + b[63:0] +// dst[127:64] := a[127:64] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_add_sd +FORCE_INLINE __m128d _mm_add_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return _mm_move_sd(a, _mm_add_pd(a, b)); +#else + double *da = (double *) &a; + double *db = (double *) &b; + double c[2]; + c[0] = da[0] + db[0]; + c[1] = da[1]; + return vld1q_f32((float32_t *) c); +#endif +} + +// Add 64-bit integers a and b, and store the result in dst. +// +// dst[63:0] := a[63:0] + b[63:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_add_si64 +FORCE_INLINE __m64 _mm_add_si64(__m64 a, __m64 b) +{ + return vreinterpret_m64_s64( + vadd_s64(vreinterpret_s64_m64(a), vreinterpret_s64_m64(b))); +} + +// Adds the 8 signed 16-bit integers in a to the 8 signed 16-bit integers in b +// and saturates. +// +// r0 := SignedSaturate(a0 + b0) +// r1 := SignedSaturate(a1 + b1) +// ... +// r7 := SignedSaturate(a7 + b7) +// +// https://msdn.microsoft.com/en-us/library/1a306ef8(v=vs.100).aspx +FORCE_INLINE __m128i _mm_adds_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vqaddq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Add packed signed 8-bit integers in a and b using saturation, and store the +// results in dst. +// +// FOR j := 0 to 15 +// i := j*8 +// dst[i+7:i] := Saturate8( a[i+7:i] + b[i+7:i] ) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_adds_epi8 +FORCE_INLINE __m128i _mm_adds_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s8( + vqaddq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Add packed unsigned 16-bit integers in a and b using saturation, and store +// the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_adds_epu16 +FORCE_INLINE __m128i _mm_adds_epu16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vqaddq_u16(vreinterpretq_u16_m128i(a), vreinterpretq_u16_m128i(b))); +} + +// Adds the 16 unsigned 8-bit integers in a to the 16 unsigned 8-bit integers in +// b and saturates.. +// https://msdn.microsoft.com/en-us/library/9hahyddy(v=vs.100).aspx +FORCE_INLINE __m128i _mm_adds_epu8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vqaddq_u8(vreinterpretq_u8_m128i(a), vreinterpretq_u8_m128i(b))); +} + +// Compute the bitwise AND of packed double-precision (64-bit) floating-point +// elements in a and b, and store the results in dst. +// +// FOR j := 0 to 1 +// i := j*64 +// dst[i+63:i] := a[i+63:i] AND b[i+63:i] +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_and_pd +FORCE_INLINE __m128d _mm_and_pd(__m128d a, __m128d b) +{ + return vreinterpretq_m128d_s64( + vandq_s64(vreinterpretq_s64_m128d(a), vreinterpretq_s64_m128d(b))); +} + +// Computes the bitwise AND of the 128-bit value in a and the 128-bit value in +// b. +// +// r := a & b +// +// https://msdn.microsoft.com/en-us/library/vstudio/6d1txsa8(v=vs.100).aspx +FORCE_INLINE __m128i _mm_and_si128(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vandq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Compute the bitwise NOT of packed double-precision (64-bit) floating-point +// elements in a and then AND with b, and store the results in dst. +// +// FOR j := 0 to 1 +// i := j*64 +// dst[i+63:i] := ((NOT a[i+63:i]) AND b[i+63:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_andnot_pd +FORCE_INLINE __m128d _mm_andnot_pd(__m128d a, __m128d b) +{ + // *NOTE* argument swap + return vreinterpretq_m128d_s64( + vbicq_s64(vreinterpretq_s64_m128d(b), vreinterpretq_s64_m128d(a))); +} + +// Computes the bitwise AND of the 128-bit value in b and the bitwise NOT of the +// 128-bit value in a. +// +// r := (~a) & b +// +// https://msdn.microsoft.com/en-us/library/vstudio/1beaceh8(v=vs.100).aspx +FORCE_INLINE __m128i _mm_andnot_si128(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vbicq_s32(vreinterpretq_s32_m128i(b), + vreinterpretq_s32_m128i(a))); // *NOTE* argument swap +} + +// Computes the average of the 8 unsigned 16-bit integers in a and the 8 +// unsigned 16-bit integers in b and rounds. +// +// r0 := (a0 + b0) / 2 +// r1 := (a1 + b1) / 2 +// ... +// r7 := (a7 + b7) / 2 +// +// https://msdn.microsoft.com/en-us/library/vstudio/y13ca3c8(v=vs.90).aspx +FORCE_INLINE __m128i _mm_avg_epu16(__m128i a, __m128i b) +{ + return (__m128i) vrhaddq_u16(vreinterpretq_u16_m128i(a), + vreinterpretq_u16_m128i(b)); +} + +// Computes the average of the 16 unsigned 8-bit integers in a and the 16 +// unsigned 8-bit integers in b and rounds. +// +// r0 := (a0 + b0) / 2 +// r1 := (a1 + b1) / 2 +// ... +// r15 := (a15 + b15) / 2 +// +// https://msdn.microsoft.com/en-us/library/vstudio/8zwh554a(v%3dvs.90).aspx +FORCE_INLINE __m128i _mm_avg_epu8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vrhaddq_u8(vreinterpretq_u8_m128i(a), vreinterpretq_u8_m128i(b))); +} + +// Shift a left by imm8 bytes while shifting in zeros, and store the results in +// dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_bslli_si128 +#define _mm_bslli_si128(a, imm) _mm_slli_si128(a, imm) + +// Shift a right by imm8 bytes while shifting in zeros, and store the results in +// dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_bsrli_si128 +#define _mm_bsrli_si128(a, imm) _mm_srli_si128(a, imm) + +// Cast vector of type __m128d to type __m128. This intrinsic is only used for +// compilation and does not generate any instructions, thus it has zero latency. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_castpd_ps +FORCE_INLINE __m128 _mm_castpd_ps(__m128d a) +{ + return vreinterpretq_m128_s64(vreinterpretq_s64_m128d(a)); +} + +// Cast vector of type __m128d to type __m128i. This intrinsic is only used for +// compilation and does not generate any instructions, thus it has zero latency. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_castpd_si128 +FORCE_INLINE __m128i _mm_castpd_si128(__m128d a) +{ + return vreinterpretq_m128i_s64(vreinterpretq_s64_m128d(a)); +} + +// Cast vector of type __m128 to type __m128d. This intrinsic is only used for +// compilation and does not generate any instructions, thus it has zero latency. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_castps_pd +FORCE_INLINE __m128d _mm_castps_pd(__m128 a) +{ + return vreinterpretq_m128d_s32(vreinterpretq_s32_m128(a)); +} + +// Applies a type cast to reinterpret four 32-bit floating point values passed +// in as a 128-bit parameter as packed 32-bit integers. +// https://msdn.microsoft.com/en-us/library/bb514099.aspx +FORCE_INLINE __m128i _mm_castps_si128(__m128 a) +{ + return vreinterpretq_m128i_s32(vreinterpretq_s32_m128(a)); +} + +// Cast vector of type __m128i to type __m128d. This intrinsic is only used for +// compilation and does not generate any instructions, thus it has zero latency. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_castsi128_pd +FORCE_INLINE __m128d _mm_castsi128_pd(__m128i a) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64(vreinterpretq_f64_m128i(a)); +#else + return vreinterpretq_m128d_f32(vreinterpretq_f32_m128i(a)); +#endif +} + +// Applies a type cast to reinterpret four 32-bit integers passed in as a +// 128-bit parameter as packed 32-bit floating point values. +// https://msdn.microsoft.com/en-us/library/bb514029.aspx +FORCE_INLINE __m128 _mm_castsi128_ps(__m128i a) +{ + return vreinterpretq_m128_s32(vreinterpretq_s32_m128i(a)); +} + +// Cache line containing p is flushed and invalidated from all caches in the +// coherency domain. : +// https://msdn.microsoft.com/en-us/library/ba08y07y(v=vs.100).aspx +FORCE_INLINE void _mm_clflush(void const *p) +{ + (void) p; + // no corollary for Neon? +} + +// Compares the 8 signed or unsigned 16-bit integers in a and the 8 signed or +// unsigned 16-bit integers in b for equality. +// https://msdn.microsoft.com/en-us/library/2ay060te(v=vs.100).aspx +FORCE_INLINE __m128i _mm_cmpeq_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vceqq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Compare packed 32-bit integers in a and b for equality, and store the results +// in dst +FORCE_INLINE __m128i _mm_cmpeq_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u32( + vceqq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Compares the 16 signed or unsigned 8-bit integers in a and the 16 signed or +// unsigned 8-bit integers in b for equality. +// https://msdn.microsoft.com/en-us/library/windows/desktop/bz5xk21a(v=vs.90).aspx +FORCE_INLINE __m128i _mm_cmpeq_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vceqq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// for equality, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmpeq_pd +FORCE_INLINE __m128d _mm_cmpeq_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_u64( + vceqq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + // (a == b) -> (a_lo == b_lo) && (a_hi == b_hi) + uint32x4_t cmp = + vceqq_u32(vreinterpretq_u32_m128d(a), vreinterpretq_u32_m128d(b)); + uint32x4_t swapped = vrev64q_u32(cmp); + return vreinterpretq_m128d_u32(vandq_u32(cmp, swapped)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b for equality, store the result in the lower element of dst, and copy the +// upper element from a to the upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmpeq_sd +FORCE_INLINE __m128d _mm_cmpeq_sd(__m128d a, __m128d b) +{ + return _mm_move_sd(a, _mm_cmpeq_pd(a, b)); +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// for greater-than-or-equal, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmpge_pd +FORCE_INLINE __m128d _mm_cmpge_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_u64( + vcgeq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = (*(double *) &a0) >= (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = (*(double *) &a1) >= (*(double *) &b1) ? ~UINT64_C(0) : UINT64_C(0); + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b for greater-than-or-equal, store the result in the lower element of dst, +// and copy the upper element from a to the upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmpge_sd +FORCE_INLINE __m128d _mm_cmpge_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return _mm_move_sd(a, _mm_cmpge_pd(a, b)); +#else + // expand "_mm_cmpge_pd()" to reduce unnecessary operations + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = (*(double *) &a0) >= (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = a1; + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compares the 8 signed 16-bit integers in a and the 8 signed 16-bit integers +// in b for greater than. +// +// r0 := (a0 > b0) ? 0xffff : 0x0 +// r1 := (a1 > b1) ? 0xffff : 0x0 +// ... +// r7 := (a7 > b7) ? 0xffff : 0x0 +// +// https://technet.microsoft.com/en-us/library/xd43yfsa(v=vs.100).aspx +FORCE_INLINE __m128i _mm_cmpgt_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vcgtq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Compares the 4 signed 32-bit integers in a and the 4 signed 32-bit integers +// in b for greater than. +// https://msdn.microsoft.com/en-us/library/vstudio/1s9f2z0y(v=vs.100).aspx +FORCE_INLINE __m128i _mm_cmpgt_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u32( + vcgtq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Compares the 16 signed 8-bit integers in a and the 16 signed 8-bit integers +// in b for greater than. +// +// r0 := (a0 > b0) ? 0xff : 0x0 +// r1 := (a1 > b1) ? 0xff : 0x0 +// ... +// r15 := (a15 > b15) ? 0xff : 0x0 +// +// https://msdn.microsoft.com/zh-tw/library/wf45zt2b(v=vs.100).aspx +FORCE_INLINE __m128i _mm_cmpgt_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vcgtq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// for greater-than, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmpgt_pd +FORCE_INLINE __m128d _mm_cmpgt_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_u64( + vcgtq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = (*(double *) &a0) > (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = (*(double *) &a1) > (*(double *) &b1) ? ~UINT64_C(0) : UINT64_C(0); + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b for greater-than, store the result in the lower element of dst, and copy +// the upper element from a to the upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmpgt_sd +FORCE_INLINE __m128d _mm_cmpgt_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return _mm_move_sd(a, _mm_cmpgt_pd(a, b)); +#else + // expand "_mm_cmpge_pd()" to reduce unnecessary operations + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = (*(double *) &a0) > (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = a1; + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// for less-than-or-equal, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmple_pd +FORCE_INLINE __m128d _mm_cmple_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_u64( + vcleq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = (*(double *) &a0) <= (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = (*(double *) &a1) <= (*(double *) &b1) ? ~UINT64_C(0) : UINT64_C(0); + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b for less-than-or-equal, store the result in the lower element of dst, and +// copy the upper element from a to the upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmple_sd +FORCE_INLINE __m128d _mm_cmple_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return _mm_move_sd(a, _mm_cmple_pd(a, b)); +#else + // expand "_mm_cmpge_pd()" to reduce unnecessary operations + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = (*(double *) &a0) <= (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = a1; + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compares the 8 signed 16-bit integers in a and the 8 signed 16-bit integers +// in b for less than. +// +// r0 := (a0 < b0) ? 0xffff : 0x0 +// r1 := (a1 < b1) ? 0xffff : 0x0 +// ... +// r7 := (a7 < b7) ? 0xffff : 0x0 +// +// https://technet.microsoft.com/en-us/library/t863edb2(v=vs.100).aspx +FORCE_INLINE __m128i _mm_cmplt_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vcltq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + + +// Compares the 4 signed 32-bit integers in a and the 4 signed 32-bit integers +// in b for less than. +// https://msdn.microsoft.com/en-us/library/vstudio/4ak0bf5d(v=vs.100).aspx +FORCE_INLINE __m128i _mm_cmplt_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u32( + vcltq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Compares the 16 signed 8-bit integers in a and the 16 signed 8-bit integers +// in b for lesser than. +// https://msdn.microsoft.com/en-us/library/windows/desktop/9s46csht(v=vs.90).aspx +FORCE_INLINE __m128i _mm_cmplt_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vcltq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// for less-than, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmplt_pd +FORCE_INLINE __m128d _mm_cmplt_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_u64( + vcltq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = (*(double *) &a0) < (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = (*(double *) &a1) < (*(double *) &b1) ? ~UINT64_C(0) : UINT64_C(0); + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b for less-than, store the result in the lower element of dst, and copy the +// upper element from a to the upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmplt_sd +FORCE_INLINE __m128d _mm_cmplt_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return _mm_move_sd(a, _mm_cmplt_pd(a, b)); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = (*(double *) &a0) < (*(double *) &b0) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = a1; + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// for not-equal, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmpneq_pd +FORCE_INLINE __m128d _mm_cmpneq_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_s32(vmvnq_s32(vreinterpretq_s32_u64( + vceqq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))))); +#else + // (a == b) -> (a_lo == b_lo) && (a_hi == b_hi) + uint32x4_t cmp = + vceqq_u32(vreinterpretq_u32_m128d(a), vreinterpretq_u32_m128d(b)); + uint32x4_t swapped = vrev64q_u32(cmp); + return vreinterpretq_m128d_u32(vmvnq_u32(vandq_u32(cmp, swapped))); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b for not-equal, store the result in the lower element of dst, and copy the +// upper element from a to the upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmpneq_sd +FORCE_INLINE __m128d _mm_cmpneq_sd(__m128d a, __m128d b) +{ + return _mm_move_sd(a, _mm_cmpneq_pd(a, b)); +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// for not-greater-than-or-equal, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmpnge_pd +FORCE_INLINE __m128d _mm_cmpnge_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_u64(veorq_u64( + vcgeq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b)), + vdupq_n_u64(UINT64_MAX))); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = + !((*(double *) &a0) >= (*(double *) &b0)) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = + !((*(double *) &a1) >= (*(double *) &b1)) ? ~UINT64_C(0) : UINT64_C(0); + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b for not-greater-than-or-equal, store the result in the lower element of +// dst, and copy the upper element from a to the upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmpnge_sd +FORCE_INLINE __m128d _mm_cmpnge_sd(__m128d a, __m128d b) +{ + return _mm_move_sd(a, _mm_cmpnge_pd(a, b)); +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// for not-greater-than, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_cmpngt_pd +FORCE_INLINE __m128d _mm_cmpngt_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_u64(veorq_u64( + vcgtq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b)), + vdupq_n_u64(UINT64_MAX))); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = + !((*(double *) &a0) > (*(double *) &b0)) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = + !((*(double *) &a1) > (*(double *) &b1)) ? ~UINT64_C(0) : UINT64_C(0); + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b for not-greater-than, store the result in the lower element of dst, and +// copy the upper element from a to the upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmpngt_sd +FORCE_INLINE __m128d _mm_cmpngt_sd(__m128d a, __m128d b) +{ + return _mm_move_sd(a, _mm_cmpngt_pd(a, b)); +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// for not-less-than-or-equal, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmpnle_pd +FORCE_INLINE __m128d _mm_cmpnle_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_u64(veorq_u64( + vcleq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b)), + vdupq_n_u64(UINT64_MAX))); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = + !((*(double *) &a0) <= (*(double *) &b0)) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = + !((*(double *) &a1) <= (*(double *) &b1)) ? ~UINT64_C(0) : UINT64_C(0); + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b for not-less-than-or-equal, store the result in the lower element of dst, +// and copy the upper element from a to the upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmpnle_sd +FORCE_INLINE __m128d _mm_cmpnle_sd(__m128d a, __m128d b) +{ + return _mm_move_sd(a, _mm_cmpnle_pd(a, b)); +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// for not-less-than, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmpnlt_pd +FORCE_INLINE __m128d _mm_cmpnlt_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_u64(veorq_u64( + vcltq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b)), + vdupq_n_u64(UINT64_MAX))); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = + !((*(double *) &a0) < (*(double *) &b0)) ? ~UINT64_C(0) : UINT64_C(0); + d[1] = + !((*(double *) &a1) < (*(double *) &b1)) ? ~UINT64_C(0) : UINT64_C(0); + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b for not-less-than, store the result in the lower element of dst, and copy +// the upper element from a to the upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmpnlt_sd +FORCE_INLINE __m128d _mm_cmpnlt_sd(__m128d a, __m128d b) +{ + return _mm_move_sd(a, _mm_cmpnlt_pd(a, b)); +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// to see if neither is NaN, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmpord_pd +FORCE_INLINE __m128d _mm_cmpord_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + // Excluding NaNs, any two floating point numbers can be compared. + uint64x2_t not_nan_a = + vceqq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(a)); + uint64x2_t not_nan_b = + vceqq_f64(vreinterpretq_f64_m128d(b), vreinterpretq_f64_m128d(b)); + return vreinterpretq_m128d_u64(vandq_u64(not_nan_a, not_nan_b)); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = ((*(double *) &a0) == (*(double *) &a0) && + (*(double *) &b0) == (*(double *) &b0)) + ? ~UINT64_C(0) + : UINT64_C(0); + d[1] = ((*(double *) &a1) == (*(double *) &a1) && + (*(double *) &b1) == (*(double *) &b1)) + ? ~UINT64_C(0) + : UINT64_C(0); + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b to see if neither is NaN, store the result in the lower element of dst, and +// copy the upper element from a to the upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmpord_sd +FORCE_INLINE __m128d _mm_cmpord_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return _mm_move_sd(a, _mm_cmpord_pd(a, b)); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t d[2]; + d[0] = ((*(double *) &a0) == (*(double *) &a0) && + (*(double *) &b0) == (*(double *) &b0)) + ? ~UINT64_C(0) + : UINT64_C(0); + d[1] = a1; + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b +// to see if either is NaN, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmpunord_pd +FORCE_INLINE __m128d _mm_cmpunord_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + // Two NaNs are not equal in comparison operation. + uint64x2_t not_nan_a = + vceqq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(a)); + uint64x2_t not_nan_b = + vceqq_f64(vreinterpretq_f64_m128d(b), vreinterpretq_f64_m128d(b)); + return vreinterpretq_m128d_s32( + vmvnq_s32(vreinterpretq_s32_u64(vandq_u64(not_nan_a, not_nan_b)))); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = ((*(double *) &a0) == (*(double *) &a0) && + (*(double *) &b0) == (*(double *) &b0)) + ? UINT64_C(0) + : ~UINT64_C(0); + d[1] = ((*(double *) &a1) == (*(double *) &a1) && + (*(double *) &b1) == (*(double *) &b1)) + ? UINT64_C(0) + : ~UINT64_C(0); + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b to see if either is NaN, store the result in the lower element of dst, and +// copy the upper element from a to the upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmpunord_sd +FORCE_INLINE __m128d _mm_cmpunord_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return _mm_move_sd(a, _mm_cmpunord_pd(a, b)); +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t d[2]; + d[0] = ((*(double *) &a0) == (*(double *) &a0) && + (*(double *) &b0) == (*(double *) &b0)) + ? UINT64_C(0) + : ~UINT64_C(0); + d[1] = a1; + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point element in a and b +// for greater-than-or-equal, and return the boolean result (0 or 1). +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_comige_sd +FORCE_INLINE int _mm_comige_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vgetq_lane_u64(vcgeq_f64(a, b), 0) & 0x1; +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + + return (*(double *) &a0 >= *(double *) &b0); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point element in a and b +// for greater-than, and return the boolean result (0 or 1). +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_comigt_sd +FORCE_INLINE int _mm_comigt_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vgetq_lane_u64(vcgtq_f64(a, b), 0) & 0x1; +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + + return (*(double *) &a0 > *(double *) &b0); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point element in a and b +// for less-than-or-equal, and return the boolean result (0 or 1). +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_comile_sd +FORCE_INLINE int _mm_comile_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vgetq_lane_u64(vcleq_f64(a, b), 0) & 0x1; +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + + return (*(double *) &a0 <= *(double *) &b0); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point element in a and b +// for less-than, and return the boolean result (0 or 1). +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_comilt_sd +FORCE_INLINE int _mm_comilt_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vgetq_lane_u64(vcltq_f64(a, b), 0) & 0x1; +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + + return (*(double *) &a0 < *(double *) &b0); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point element in a and b +// for equality, and return the boolean result (0 or 1). +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_comieq_sd +FORCE_INLINE int _mm_comieq_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vgetq_lane_u64(vceqq_f64(a, b), 0) & 0x1; +#else + uint32x4_t a_not_nan = + vceqq_u32(vreinterpretq_u32_m128d(a), vreinterpretq_u32_m128d(a)); + uint32x4_t b_not_nan = + vceqq_u32(vreinterpretq_u32_m128d(b), vreinterpretq_u32_m128d(b)); + uint32x4_t a_and_b_not_nan = vandq_u32(a_not_nan, b_not_nan); + uint32x4_t a_eq_b = + vceqq_u32(vreinterpretq_u32_m128d(a), vreinterpretq_u32_m128d(b)); + uint64x2_t and_results = vandq_u64(vreinterpretq_u64_u32(a_and_b_not_nan), + vreinterpretq_u64_u32(a_eq_b)); + return vgetq_lane_u64(and_results, 0) & 0x1; +#endif +} + +// Compare the lower double-precision (64-bit) floating-point element in a and b +// for not-equal, and return the boolean result (0 or 1). +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_comineq_sd +FORCE_INLINE int _mm_comineq_sd(__m128d a, __m128d b) +{ + return !_mm_comieq_sd(a, b); +} + +// Convert packed signed 32-bit integers in a to packed double-precision +// (64-bit) floating-point elements, and store the results in dst. +// +// FOR j := 0 to 1 +// i := j*32 +// m := j*64 +// dst[m+63:m] := Convert_Int32_To_FP64(a[i+31:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtepi32_pd +FORCE_INLINE __m128d _mm_cvtepi32_pd(__m128i a) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64( + vcvtq_f64_s64(vmovl_s32(vget_low_s32(vreinterpretq_s32_m128i(a))))); +#else + double a0 = (double) vgetq_lane_s32(vreinterpretq_s32_m128i(a), 0); + double a1 = (double) vgetq_lane_s32(vreinterpretq_s32_m128i(a), 1); + return _mm_set_pd(a1, a0); +#endif +} + +// Converts the four signed 32-bit integer values of a to single-precision, +// floating-point values +// https://msdn.microsoft.com/en-us/library/vstudio/36bwxcx5(v=vs.100).aspx +FORCE_INLINE __m128 _mm_cvtepi32_ps(__m128i a) +{ + return vreinterpretq_m128_f32(vcvtq_f32_s32(vreinterpretq_s32_m128i(a))); +} + +// Convert packed double-precision (64-bit) floating-point elements in a to +// packed 32-bit integers, and store the results in dst. +// +// FOR j := 0 to 1 +// i := 32*j +// k := 64*j +// dst[i+31:i] := Convert_FP64_To_Int32(a[k+63:k]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtpd_epi32 +FORCE_INLINE __m128i _mm_cvtpd_epi32(__m128d a) +{ + __m128d rnd = _mm_round_pd(a, _MM_FROUND_CUR_DIRECTION); + double d0 = ((double *) &rnd)[0]; + double d1 = ((double *) &rnd)[1]; + return _mm_set_epi32(0, 0, (int32_t) d1, (int32_t) d0); +} + +// Convert packed double-precision (64-bit) floating-point elements in a to +// packed 32-bit integers, and store the results in dst. +// +// FOR j := 0 to 1 +// i := 32*j +// k := 64*j +// dst[i+31:i] := Convert_FP64_To_Int32(a[k+63:k]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtpd_pi32 +FORCE_INLINE __m64 _mm_cvtpd_pi32(__m128d a) +{ + __m128d rnd = _mm_round_pd(a, _MM_FROUND_CUR_DIRECTION); + double d0 = ((double *) &rnd)[0]; + double d1 = ((double *) &rnd)[1]; + int32_t ALIGN_STRUCT(16) data[2] = {(int32_t) d0, (int32_t) d1}; + return vreinterpret_m64_s32(vld1_s32(data)); +} + +// Convert packed double-precision (64-bit) floating-point elements in a to +// packed single-precision (32-bit) floating-point elements, and store the +// results in dst. +// +// FOR j := 0 to 1 +// i := 32*j +// k := 64*j +// dst[i+31:i] := Convert_FP64_To_FP32(a[k+64:k]) +// ENDFOR +// dst[127:64] := 0 +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtpd_ps +FORCE_INLINE __m128 _mm_cvtpd_ps(__m128d a) +{ +#if defined(__aarch64__) + float32x2_t tmp = vcvt_f32_f64(vreinterpretq_f64_m128d(a)); + return vreinterpretq_m128_f32(vcombine_f32(tmp, vdup_n_f32(0))); +#else + float a0 = (float) ((double *) &a)[0]; + float a1 = (float) ((double *) &a)[1]; + return _mm_set_ps(0, 0, a1, a0); +#endif +} + +// Convert packed signed 32-bit integers in a to packed double-precision +// (64-bit) floating-point elements, and store the results in dst. +// +// FOR j := 0 to 1 +// i := j*32 +// m := j*64 +// dst[m+63:m] := Convert_Int32_To_FP64(a[i+31:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtpi32_pd +FORCE_INLINE __m128d _mm_cvtpi32_pd(__m64 a) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64( + vcvtq_f64_s64(vmovl_s32(vreinterpret_s32_m64(a)))); +#else + double a0 = (double) vget_lane_s32(vreinterpret_s32_m64(a), 0); + double a1 = (double) vget_lane_s32(vreinterpret_s32_m64(a), 1); + return _mm_set_pd(a1, a0); +#endif +} + +// Converts the four single-precision, floating-point values of a to signed +// 32-bit integer values. +// +// r0 := (int) a0 +// r1 := (int) a1 +// r2 := (int) a2 +// r3 := (int) a3 +// +// https://msdn.microsoft.com/en-us/library/vstudio/xdc42k5e(v=vs.100).aspx +// *NOTE*. The default rounding mode on SSE is 'round to even', which ARMv7-A +// does not support! It is supported on ARMv8-A however. +FORCE_INLINE __m128i _mm_cvtps_epi32(__m128 a) +{ +#if defined(__aarch64__) + switch (_MM_GET_ROUNDING_MODE()) { + case _MM_ROUND_NEAREST: + return vreinterpretq_m128i_s32(vcvtnq_s32_f32(a)); + case _MM_ROUND_DOWN: + return vreinterpretq_m128i_s32(vcvtmq_s32_f32(a)); + case _MM_ROUND_UP: + return vreinterpretq_m128i_s32(vcvtpq_s32_f32(a)); + default: // _MM_ROUND_TOWARD_ZERO + return vreinterpretq_m128i_s32(vcvtq_s32_f32(a)); + } +#else + float *f = (float *) &a; + switch (_MM_GET_ROUNDING_MODE()) { + case _MM_ROUND_NEAREST: { + uint32x4_t signmask = vdupq_n_u32(0x80000000); + float32x4_t half = vbslq_f32(signmask, vreinterpretq_f32_m128(a), + vdupq_n_f32(0.5f)); /* +/- 0.5 */ + int32x4_t r_normal = vcvtq_s32_f32(vaddq_f32( + vreinterpretq_f32_m128(a), half)); /* round to integer: [a + 0.5]*/ + int32x4_t r_trunc = vcvtq_s32_f32( + vreinterpretq_f32_m128(a)); /* truncate to integer: [a] */ + int32x4_t plusone = vreinterpretq_s32_u32(vshrq_n_u32( + vreinterpretq_u32_s32(vnegq_s32(r_trunc)), 31)); /* 1 or 0 */ + int32x4_t r_even = vbicq_s32(vaddq_s32(r_trunc, plusone), + vdupq_n_s32(1)); /* ([a] + {0,1}) & ~1 */ + float32x4_t delta = vsubq_f32( + vreinterpretq_f32_m128(a), + vcvtq_f32_s32(r_trunc)); /* compute delta: delta = (a - [a]) */ + uint32x4_t is_delta_half = + vceqq_f32(delta, half); /* delta == +/- 0.5 */ + return vreinterpretq_m128i_s32( + vbslq_s32(is_delta_half, r_even, r_normal)); + } + case _MM_ROUND_DOWN: + return _mm_set_epi32(floorf(f[3]), floorf(f[2]), floorf(f[1]), + floorf(f[0])); + case _MM_ROUND_UP: + return _mm_set_epi32(ceilf(f[3]), ceilf(f[2]), ceilf(f[1]), + ceilf(f[0])); + default: // _MM_ROUND_TOWARD_ZERO + return _mm_set_epi32((int32_t) f[3], (int32_t) f[2], (int32_t) f[1], + (int32_t) f[0]); + } +#endif +} + +// Convert packed single-precision (32-bit) floating-point elements in a to +// packed double-precision (64-bit) floating-point elements, and store the +// results in dst. +// +// FOR j := 0 to 1 +// i := 64*j +// k := 32*j +// dst[i+63:i] := Convert_FP32_To_FP64(a[k+31:k]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtps_pd +FORCE_INLINE __m128d _mm_cvtps_pd(__m128 a) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64( + vcvt_f64_f32(vget_low_f32(vreinterpretq_f32_m128(a)))); +#else + double a0 = (double) vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); + double a1 = (double) vgetq_lane_f32(vreinterpretq_f32_m128(a), 1); + return _mm_set_pd(a1, a0); +#endif +} + +// Copy the lower double-precision (64-bit) floating-point element of a to dst. +// +// dst[63:0] := a[63:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtsd_f64 +FORCE_INLINE double _mm_cvtsd_f64(__m128d a) +{ +#if defined(__aarch64__) + return (double) vgetq_lane_f64(vreinterpretq_f64_m128d(a), 0); +#else + return ((double *) &a)[0]; +#endif +} + +// Convert the lower double-precision (64-bit) floating-point element in a to a +// 32-bit integer, and store the result in dst. +// +// dst[31:0] := Convert_FP64_To_Int32(a[63:0]) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtsd_si32 +FORCE_INLINE int32_t _mm_cvtsd_si32(__m128d a) +{ +#if defined(__aarch64__) + return (int32_t) vgetq_lane_f64(vrndiq_f64(vreinterpretq_f64_m128d(a)), 0); +#else + __m128d rnd = _mm_round_pd(a, _MM_FROUND_CUR_DIRECTION); + double ret = ((double *) &rnd)[0]; + return (int32_t) ret; +#endif +} + +// Convert the lower double-precision (64-bit) floating-point element in a to a +// 64-bit integer, and store the result in dst. +// +// dst[63:0] := Convert_FP64_To_Int64(a[63:0]) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtsd_si64 +FORCE_INLINE int64_t _mm_cvtsd_si64(__m128d a) +{ +#if defined(__aarch64__) + return (int64_t) vgetq_lane_f64(vrndiq_f64(vreinterpretq_f64_m128d(a)), 0); +#else + __m128d rnd = _mm_round_pd(a, _MM_FROUND_CUR_DIRECTION); + double ret = ((double *) &rnd)[0]; + return (int64_t) ret; +#endif +} + +// Convert the lower double-precision (64-bit) floating-point element in a to a +// 64-bit integer, and store the result in dst. +// +// dst[63:0] := Convert_FP64_To_Int64(a[63:0]) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtsd_si64x +#define _mm_cvtsd_si64x _mm_cvtsd_si64 + +// Convert the lower double-precision (64-bit) floating-point element in b to a +// single-precision (32-bit) floating-point element, store the result in the +// lower element of dst, and copy the upper 3 packed elements from a to the +// upper elements of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtsd_ss +FORCE_INLINE __m128 _mm_cvtsd_ss(__m128 a, __m128d b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128_f32(vsetq_lane_f32( + vget_lane_f32(vcvt_f32_f64(vreinterpretq_f64_m128d(b)), 0), + vreinterpretq_f32_m128(a), 0)); +#else + return vreinterpretq_m128_f32(vsetq_lane_f32((float) ((double *) &b)[0], + vreinterpretq_f32_m128(a), 0)); +#endif +} + +// Copy the lower 32-bit integer in a to dst. +// +// dst[31:0] := a[31:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtsi128_si32 +FORCE_INLINE int _mm_cvtsi128_si32(__m128i a) +{ + return vgetq_lane_s32(vreinterpretq_s32_m128i(a), 0); +} + +// Copy the lower 64-bit integer in a to dst. +// +// dst[63:0] := a[63:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtsi128_si64 +FORCE_INLINE int64_t _mm_cvtsi128_si64(__m128i a) +{ + return vgetq_lane_s64(vreinterpretq_s64_m128i(a), 0); +} + +// Copy the lower 64-bit integer in a to dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtsi128_si64x +#define _mm_cvtsi128_si64x(a) _mm_cvtsi128_si64(a) + +// Convert the signed 32-bit integer b to a double-precision (64-bit) +// floating-point element, store the result in the lower element of dst, and +// copy the upper element from a to the upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtsi32_sd +FORCE_INLINE __m128d _mm_cvtsi32_sd(__m128d a, int32_t b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64( + vsetq_lane_f64((double) b, vreinterpretq_f64_m128d(a), 0)); +#else + double bf = (double) b; + return vreinterpretq_m128d_s64( + vsetq_lane_s64(*(int64_t *) &bf, vreinterpretq_s64_m128d(a), 0)); +#endif +} + +// Copy the lower 64-bit integer in a to dst. +// +// dst[63:0] := a[63:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtsi128_si64x +#define _mm_cvtsi128_si64x(a) _mm_cvtsi128_si64(a) + +// Moves 32-bit integer a to the least significant 32 bits of an __m128 object, +// zero extending the upper bits. +// +// r0 := a +// r1 := 0x0 +// r2 := 0x0 +// r3 := 0x0 +// +// https://msdn.microsoft.com/en-us/library/ct3539ha%28v=vs.90%29.aspx +FORCE_INLINE __m128i _mm_cvtsi32_si128(int a) +{ + return vreinterpretq_m128i_s32(vsetq_lane_s32(a, vdupq_n_s32(0), 0)); +} + +// Convert the signed 64-bit integer b to a double-precision (64-bit) +// floating-point element, store the result in the lower element of dst, and +// copy the upper element from a to the upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtsi64_sd +FORCE_INLINE __m128d _mm_cvtsi64_sd(__m128d a, int64_t b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64( + vsetq_lane_f64((double) b, vreinterpretq_f64_m128d(a), 0)); +#else + double bf = (double) b; + return vreinterpretq_m128d_s64( + vsetq_lane_s64(*(int64_t *) &bf, vreinterpretq_s64_m128d(a), 0)); +#endif +} + +// Moves 64-bit integer a to the least significant 64 bits of an __m128 object, +// zero extending the upper bits. +// +// r0 := a +// r1 := 0x0 +FORCE_INLINE __m128i _mm_cvtsi64_si128(int64_t a) +{ + return vreinterpretq_m128i_s64(vsetq_lane_s64(a, vdupq_n_s64(0), 0)); +} + +// Copy 64-bit integer a to the lower element of dst, and zero the upper +// element. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtsi64x_si128 +#define _mm_cvtsi64x_si128(a) _mm_cvtsi64_si128(a) + +// Convert the signed 64-bit integer b to a double-precision (64-bit) +// floating-point element, store the result in the lower element of dst, and +// copy the upper element from a to the upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtsi64x_sd +#define _mm_cvtsi64x_sd(a, b) _mm_cvtsi64_sd(a, b) + +// Convert the lower single-precision (32-bit) floating-point element in b to a +// double-precision (64-bit) floating-point element, store the result in the +// lower element of dst, and copy the upper element from a to the upper element +// of dst. +// +// dst[63:0] := Convert_FP32_To_FP64(b[31:0]) +// dst[127:64] := a[127:64] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtss_sd +FORCE_INLINE __m128d _mm_cvtss_sd(__m128d a, __m128 b) +{ + double d = (double) vgetq_lane_f32(vreinterpretq_f32_m128(b), 0); +#if defined(__aarch64__) + return vreinterpretq_m128d_f64( + vsetq_lane_f64(d, vreinterpretq_f64_m128d(a), 0)); +#else + return vreinterpretq_m128d_s64( + vsetq_lane_s64(*(int64_t *) &d, vreinterpretq_s64_m128d(a), 0)); +#endif +} + +// Convert packed double-precision (64-bit) floating-point elements in a to +// packed 32-bit integers with truncation, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvttpd_epi32 +FORCE_INLINE __m128i _mm_cvttpd_epi32(__m128d a) +{ + double a0 = ((double *) &a)[0]; + double a1 = ((double *) &a)[1]; + return _mm_set_epi32(0, 0, (int32_t) a1, (int32_t) a0); +} + +// Convert packed double-precision (64-bit) floating-point elements in a to +// packed 32-bit integers with truncation, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvttpd_pi32 +FORCE_INLINE __m64 _mm_cvttpd_pi32(__m128d a) +{ + double a0 = ((double *) &a)[0]; + double a1 = ((double *) &a)[1]; + int32_t ALIGN_STRUCT(16) data[2] = {(int32_t) a0, (int32_t) a1}; + return vreinterpret_m64_s32(vld1_s32(data)); +} + +// Converts the four single-precision, floating-point values of a to signed +// 32-bit integer values using truncate. +// https://msdn.microsoft.com/en-us/library/vstudio/1h005y6x(v=vs.100).aspx +FORCE_INLINE __m128i _mm_cvttps_epi32(__m128 a) +{ + return vreinterpretq_m128i_s32(vcvtq_s32_f32(vreinterpretq_f32_m128(a))); +} + +// Convert the lower double-precision (64-bit) floating-point element in a to a +// 32-bit integer with truncation, and store the result in dst. +// +// dst[63:0] := Convert_FP64_To_Int32_Truncate(a[63:0]) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvttsd_si32 +FORCE_INLINE int32_t _mm_cvttsd_si32(__m128d a) +{ + double ret = *((double *) &a); + return (int32_t) ret; +} + +// Convert the lower double-precision (64-bit) floating-point element in a to a +// 64-bit integer with truncation, and store the result in dst. +// +// dst[63:0] := Convert_FP64_To_Int64_Truncate(a[63:0]) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvttsd_si64 +FORCE_INLINE int64_t _mm_cvttsd_si64(__m128d a) +{ +#if defined(__aarch64__) + return vgetq_lane_s64(vcvtq_s64_f64(vreinterpretq_f64_m128d(a)), 0); +#else + double ret = *((double *) &a); + return (int64_t) ret; +#endif +} + +// Convert the lower double-precision (64-bit) floating-point element in a to a +// 64-bit integer with truncation, and store the result in dst. +// +// dst[63:0] := Convert_FP64_To_Int64_Truncate(a[63:0]) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvttsd_si64x +#define _mm_cvttsd_si64x(a) _mm_cvttsd_si64(a) + +// Divide packed double-precision (64-bit) floating-point elements in a by +// packed elements in b, and store the results in dst. +// +// FOR j := 0 to 1 +// i := 64*j +// dst[i+63:i] := a[i+63:i] / b[i+63:i] +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_div_pd +FORCE_INLINE __m128d _mm_div_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64( + vdivq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + double *da = (double *) &a; + double *db = (double *) &b; + double c[2]; + c[0] = da[0] / db[0]; + c[1] = da[1] / db[1]; + return vld1q_f32((float32_t *) c); +#endif +} + +// Divide the lower double-precision (64-bit) floating-point element in a by the +// lower double-precision (64-bit) floating-point element in b, store the result +// in the lower element of dst, and copy the upper element from a to the upper +// element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_div_sd +FORCE_INLINE __m128d _mm_div_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + float64x2_t tmp = + vdivq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b)); + return vreinterpretq_m128d_f64( + vsetq_lane_f64(vgetq_lane_f64(vreinterpretq_f64_m128d(a), 1), tmp, 1)); +#else + return _mm_move_sd(a, _mm_div_pd(a, b)); +#endif +} + +// Extracts the selected signed or unsigned 16-bit integer from a and zero +// extends. +// https://msdn.microsoft.com/en-us/library/6dceta0c(v=vs.100).aspx +// FORCE_INLINE int _mm_extract_epi16(__m128i a, __constrange(0,8) int imm) +#define _mm_extract_epi16(a, imm) \ + vgetq_lane_u16(vreinterpretq_u16_m128i(a), (imm)) + +// Inserts the least significant 16 bits of b into the selected 16-bit integer +// of a. +// https://msdn.microsoft.com/en-us/library/kaze8hz1%28v=vs.100%29.aspx +// FORCE_INLINE __m128i _mm_insert_epi16(__m128i a, int b, +// __constrange(0,8) int imm) +#define _mm_insert_epi16(a, b, imm) \ + __extension__({ \ + vreinterpretq_m128i_s16( \ + vsetq_lane_s16((b), vreinterpretq_s16_m128i(a), (imm))); \ + }) + +// Loads two double-precision from 16-byte aligned memory, floating-point +// values. +// +// dst[127:0] := MEM[mem_addr+127:mem_addr] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_load_pd +FORCE_INLINE __m128d _mm_load_pd(const double *p) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64(vld1q_f64(p)); +#else + const float *fp = (const float *) p; + float ALIGN_STRUCT(16) data[4] = {fp[0], fp[1], fp[2], fp[3]}; + return vreinterpretq_m128d_f32(vld1q_f32(data)); +#endif +} + +// Load a double-precision (64-bit) floating-point element from memory into both +// elements of dst. +// +// dst[63:0] := MEM[mem_addr+63:mem_addr] +// dst[127:64] := MEM[mem_addr+63:mem_addr] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_load_pd1 +#define _mm_load_pd1 _mm_load1_pd + +// Load a double-precision (64-bit) floating-point element from memory into the +// lower of dst, and zero the upper element. mem_addr does not need to be +// aligned on any particular boundary. +// +// dst[63:0] := MEM[mem_addr+63:mem_addr] +// dst[127:64] := 0 +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_load_sd +FORCE_INLINE __m128d _mm_load_sd(const double *p) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64(vsetq_lane_f64(*p, vdupq_n_f64(0), 0)); +#else + const float *fp = (const float *) p; + float ALIGN_STRUCT(16) data[4] = {fp[0], fp[1], 0, 0}; + return vreinterpretq_m128d_f32(vld1q_f32(data)); +#endif +} + +// Loads 128-bit value. : +// https://msdn.microsoft.com/en-us/library/atzzad1h(v=vs.80).aspx +FORCE_INLINE __m128i _mm_load_si128(const __m128i *p) +{ + return vreinterpretq_m128i_s32(vld1q_s32((const int32_t *) p)); +} + +// Load a double-precision (64-bit) floating-point element from memory into both +// elements of dst. +// +// dst[63:0] := MEM[mem_addr+63:mem_addr] +// dst[127:64] := MEM[mem_addr+63:mem_addr] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_load1_pd +FORCE_INLINE __m128d _mm_load1_pd(const double *p) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64(vld1q_dup_f64(p)); +#else + return vreinterpretq_m128d_s64(vdupq_n_s64(*(const int64_t *) p)); +#endif +} + +// Load a double-precision (64-bit) floating-point element from memory into the +// upper element of dst, and copy the lower element from a to dst. mem_addr does +// not need to be aligned on any particular boundary. +// +// dst[63:0] := a[63:0] +// dst[127:64] := MEM[mem_addr+63:mem_addr] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_loadh_pd +FORCE_INLINE __m128d _mm_loadh_pd(__m128d a, const double *p) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64( + vcombine_f64(vget_low_f64(vreinterpretq_f64_m128d(a)), vld1_f64(p))); +#else + return vreinterpretq_m128d_f32(vcombine_f32( + vget_low_f32(vreinterpretq_f32_m128d(a)), vld1_f32((const float *) p))); +#endif +} + +// Load 64-bit integer from memory into the first element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_loadl_epi64 +FORCE_INLINE __m128i _mm_loadl_epi64(__m128i const *p) +{ + /* Load the lower 64 bits of the value pointed to by p into the + * lower 64 bits of the result, zeroing the upper 64 bits of the result. + */ + return vreinterpretq_m128i_s32( + vcombine_s32(vld1_s32((int32_t const *) p), vcreate_s32(0))); +} + +// Load a double-precision (64-bit) floating-point element from memory into the +// lower element of dst, and copy the upper element from a to dst. mem_addr does +// not need to be aligned on any particular boundary. +// +// dst[63:0] := MEM[mem_addr+63:mem_addr] +// dst[127:64] := a[127:64] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_loadl_pd +FORCE_INLINE __m128d _mm_loadl_pd(__m128d a, const double *p) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64( + vcombine_f64(vld1_f64(p), vget_high_f64(vreinterpretq_f64_m128d(a)))); +#else + return vreinterpretq_m128d_f32( + vcombine_f32(vld1_f32((const float *) p), + vget_high_f32(vreinterpretq_f32_m128d(a)))); +#endif +} + +// Load 2 double-precision (64-bit) floating-point elements from memory into dst +// in reverse order. mem_addr must be aligned on a 16-byte boundary or a +// general-protection exception may be generated. +// +// dst[63:0] := MEM[mem_addr+127:mem_addr+64] +// dst[127:64] := MEM[mem_addr+63:mem_addr] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_loadr_pd +FORCE_INLINE __m128d _mm_loadr_pd(const double *p) +{ +#if defined(__aarch64__) + float64x2_t v = vld1q_f64(p); + return vreinterpretq_m128d_f64(vextq_f64(v, v, 1)); +#else + int64x2_t v = vld1q_s64((const int64_t *) p); + return vreinterpretq_m128d_s64(vextq_s64(v, v, 1)); +#endif +} + +// Loads two double-precision from unaligned memory, floating-point values. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_loadu_pd +FORCE_INLINE __m128d _mm_loadu_pd(const double *p) +{ + return _mm_load_pd(p); +} + +// Loads 128-bit value. : +// https://msdn.microsoft.com/zh-cn/library/f4k12ae8(v=vs.90).aspx +FORCE_INLINE __m128i _mm_loadu_si128(const __m128i *p) +{ + return vreinterpretq_m128i_s32(vld1q_s32((const int32_t *) p)); +} + +// Load unaligned 32-bit integer from memory into the first element of dst. +// +// dst[31:0] := MEM[mem_addr+31:mem_addr] +// dst[MAX:32] := 0 +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_loadu_si32 +FORCE_INLINE __m128i _mm_loadu_si32(const void *p) +{ + return vreinterpretq_m128i_s32( + vsetq_lane_s32(*(const int32_t *) p, vdupq_n_s32(0), 0)); +} + +// Multiplies the 8 signed 16-bit integers from a by the 8 signed 16-bit +// integers from b. +// +// r0 := (a0 * b0) + (a1 * b1) +// r1 := (a2 * b2) + (a3 * b3) +// r2 := (a4 * b4) + (a5 * b5) +// r3 := (a6 * b6) + (a7 * b7) +// https://msdn.microsoft.com/en-us/library/yht36sa6(v=vs.90).aspx +FORCE_INLINE __m128i _mm_madd_epi16(__m128i a, __m128i b) +{ + int32x4_t low = vmull_s16(vget_low_s16(vreinterpretq_s16_m128i(a)), + vget_low_s16(vreinterpretq_s16_m128i(b))); + int32x4_t high = vmull_s16(vget_high_s16(vreinterpretq_s16_m128i(a)), + vget_high_s16(vreinterpretq_s16_m128i(b))); + + int32x2_t low_sum = vpadd_s32(vget_low_s32(low), vget_high_s32(low)); + int32x2_t high_sum = vpadd_s32(vget_low_s32(high), vget_high_s32(high)); + + return vreinterpretq_m128i_s32(vcombine_s32(low_sum, high_sum)); +} + +// Conditionally store 8-bit integer elements from a into memory using mask +// (elements are not stored when the highest bit is not set in the corresponding +// element) and a non-temporal memory hint. mem_addr does not need to be aligned +// on any particular boundary. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_maskmoveu_si128 +FORCE_INLINE void _mm_maskmoveu_si128(__m128i a, __m128i mask, char *mem_addr) +{ + int8x16_t shr_mask = vshrq_n_s8(vreinterpretq_s8_m128i(mask), 7); + __m128 b = _mm_load_ps((const float *) mem_addr); + int8x16_t masked = + vbslq_s8(vreinterpretq_u8_s8(shr_mask), vreinterpretq_s8_m128i(a), + vreinterpretq_s8_m128(b)); + vst1q_s8((int8_t *) mem_addr, masked); +} + +// Computes the pairwise maxima of the 8 signed 16-bit integers from a and the 8 +// signed 16-bit integers from b. +// https://msdn.microsoft.com/en-us/LIBRary/3x060h7c(v=vs.100).aspx +FORCE_INLINE __m128i _mm_max_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vmaxq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Computes the pairwise maxima of the 16 unsigned 8-bit integers from a and the +// 16 unsigned 8-bit integers from b. +// https://msdn.microsoft.com/en-us/library/st6634za(v=vs.100).aspx +FORCE_INLINE __m128i _mm_max_epu8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vmaxq_u8(vreinterpretq_u8_m128i(a), vreinterpretq_u8_m128i(b))); +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b, +// and store packed maximum values in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_max_pd +FORCE_INLINE __m128d _mm_max_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) +#if SSE2NEON_PRECISE_MINMAX + float64x2_t _a = vreinterpretq_f64_m128d(a); + float64x2_t _b = vreinterpretq_f64_m128d(b); + return vreinterpretq_m128d_f64(vbslq_f64(vcgtq_f64(_a, _b), _a, _b)); +#else + return vreinterpretq_m128d_f64( + vmaxq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#endif +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = (*(double *) &a0) > (*(double *) &b0) ? a0 : b0; + d[1] = (*(double *) &a1) > (*(double *) &b1) ? a1 : b1; + + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b, store the maximum value in the lower element of dst, and copy the upper +// element from a to the upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_max_sd +FORCE_INLINE __m128d _mm_max_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return _mm_move_sd(a, _mm_max_pd(a, b)); +#else + double *da = (double *) &a; + double *db = (double *) &b; + double c[2] = {da[0] > db[0] ? da[0] : db[0], da[1]}; + return vreinterpretq_m128d_f32(vld1q_f32((float32_t *) c)); +#endif +} + +// Computes the pairwise minima of the 8 signed 16-bit integers from a and the 8 +// signed 16-bit integers from b. +// https://msdn.microsoft.com/en-us/library/vstudio/6te997ew(v=vs.100).aspx +FORCE_INLINE __m128i _mm_min_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vminq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Computes the pairwise minima of the 16 unsigned 8-bit integers from a and the +// 16 unsigned 8-bit integers from b. +// https://msdn.microsoft.com/ko-kr/library/17k8cf58(v=vs.100).aspxx +FORCE_INLINE __m128i _mm_min_epu8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vminq_u8(vreinterpretq_u8_m128i(a), vreinterpretq_u8_m128i(b))); +} + +// Compare packed double-precision (64-bit) floating-point elements in a and b, +// and store packed minimum values in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_min_pd +FORCE_INLINE __m128d _mm_min_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) +#if SSE2NEON_PRECISE_MINMAX + float64x2_t _a = vreinterpretq_f64_m128d(a); + float64x2_t _b = vreinterpretq_f64_m128d(b); + return vreinterpretq_m128d_f64(vbslq_f64(vcltq_f64(_a, _b), _a, _b)); +#else + return vreinterpretq_m128d_f64( + vminq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#endif +#else + uint64_t a0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(a)); + uint64_t a1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(a)); + uint64_t b0 = (uint64_t) vget_low_u64(vreinterpretq_u64_m128d(b)); + uint64_t b1 = (uint64_t) vget_high_u64(vreinterpretq_u64_m128d(b)); + uint64_t d[2]; + d[0] = (*(double *) &a0) < (*(double *) &b0) ? a0 : b0; + d[1] = (*(double *) &a1) < (*(double *) &b1) ? a1 : b1; + return vreinterpretq_m128d_u64(vld1q_u64(d)); +#endif +} + +// Compare the lower double-precision (64-bit) floating-point elements in a and +// b, store the minimum value in the lower element of dst, and copy the upper +// element from a to the upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_min_sd +FORCE_INLINE __m128d _mm_min_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return _mm_move_sd(a, _mm_min_pd(a, b)); +#else + double *da = (double *) &a; + double *db = (double *) &b; + double c[2] = {da[0] < db[0] ? da[0] : db[0], da[1]}; + return vreinterpretq_m128d_f32(vld1q_f32((float32_t *) c)); +#endif +} + +// Copy the lower 64-bit integer in a to the lower element of dst, and zero the +// upper element. +// +// dst[63:0] := a[63:0] +// dst[127:64] := 0 +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_move_epi64 +FORCE_INLINE __m128i _mm_move_epi64(__m128i a) +{ + return vreinterpretq_m128i_s64( + vsetq_lane_s64(0, vreinterpretq_s64_m128i(a), 1)); +} + +// Move the lower double-precision (64-bit) floating-point element from b to the +// lower element of dst, and copy the upper element from a to the upper element +// of dst. +// +// dst[63:0] := b[63:0] +// dst[127:64] := a[127:64] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_move_sd +FORCE_INLINE __m128d _mm_move_sd(__m128d a, __m128d b) +{ + return vreinterpretq_m128d_f32( + vcombine_f32(vget_low_f32(vreinterpretq_f32_m128d(b)), + vget_high_f32(vreinterpretq_f32_m128d(a)))); +} + +// NEON does not provide a version of this function. +// Creates a 16-bit mask from the most significant bits of the 16 signed or +// unsigned 8-bit integers in a and zero extends the upper bits. +// https://msdn.microsoft.com/en-us/library/vstudio/s090c8fk(v=vs.100).aspx +FORCE_INLINE int _mm_movemask_epi8(__m128i a) +{ + // Use increasingly wide shifts+adds to collect the sign bits + // together. + // Since the widening shifts would be rather confusing to follow in little + // endian, everything will be illustrated in big endian order instead. This + // has a different result - the bits would actually be reversed on a big + // endian machine. + + // Starting input (only half the elements are shown): + // 89 ff 1d c0 00 10 99 33 + uint8x16_t input = vreinterpretq_u8_m128i(a); + + // Shift out everything but the sign bits with an unsigned shift right. + // + // Bytes of the vector:: + // 89 ff 1d c0 00 10 99 33 + // \ \ \ \ \ \ \ \ high_bits = (uint16x4_t)(input >> 7) + // | | | | | | | | + // 01 01 00 01 00 00 01 00 + // + // Bits of first important lane(s): + // 10001001 (89) + // \______ + // | + // 00000001 (01) + uint16x8_t high_bits = vreinterpretq_u16_u8(vshrq_n_u8(input, 7)); + + // Merge the even lanes together with a 16-bit unsigned shift right + add. + // 'xx' represents garbage data which will be ignored in the final result. + // In the important bytes, the add functions like a binary OR. + // + // 01 01 00 01 00 00 01 00 + // \_ | \_ | \_ | \_ | paired16 = (uint32x4_t)(input + (input >> 7)) + // \| \| \| \| + // xx 03 xx 01 xx 00 xx 02 + // + // 00000001 00000001 (01 01) + // \_______ | + // \| + // xxxxxxxx xxxxxx11 (xx 03) + uint32x4_t paired16 = + vreinterpretq_u32_u16(vsraq_n_u16(high_bits, high_bits, 7)); + + // Repeat with a wider 32-bit shift + add. + // xx 03 xx 01 xx 00 xx 02 + // \____ | \____ | paired32 = (uint64x1_t)(paired16 + (paired16 >> + // 14)) + // \| \| + // xx xx xx 0d xx xx xx 02 + // + // 00000011 00000001 (03 01) + // \\_____ || + // '----.\|| + // xxxxxxxx xxxx1101 (xx 0d) + uint64x2_t paired32 = + vreinterpretq_u64_u32(vsraq_n_u32(paired16, paired16, 14)); + + // Last, an even wider 64-bit shift + add to get our result in the low 8 bit + // lanes. xx xx xx 0d xx xx xx 02 + // \_________ | paired64 = (uint8x8_t)(paired32 + (paired32 >> + // 28)) + // \| + // xx xx xx xx xx xx xx d2 + // + // 00001101 00000010 (0d 02) + // \ \___ | | + // '---. \| | + // xxxxxxxx 11010010 (xx d2) + uint8x16_t paired64 = + vreinterpretq_u8_u64(vsraq_n_u64(paired32, paired32, 28)); + + // Extract the low 8 bits from each 64-bit lane with 2 8-bit extracts. + // xx xx xx xx xx xx xx d2 + // || return paired64[0] + // d2 + // Note: Little endian would return the correct value 4b (01001011) instead. + return vgetq_lane_u8(paired64, 0) | ((int) vgetq_lane_u8(paired64, 8) << 8); +} + +// Set each bit of mask dst based on the most significant bit of the +// corresponding packed double-precision (64-bit) floating-point element in a. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_movemask_pd +FORCE_INLINE int _mm_movemask_pd(__m128d a) +{ + uint64x2_t input = vreinterpretq_u64_m128d(a); + uint64x2_t high_bits = vshrq_n_u64(input, 63); + return vgetq_lane_u64(high_bits, 0) | (vgetq_lane_u64(high_bits, 1) << 1); +} + +// Copy the lower 64-bit integer in a to dst. +// +// dst[63:0] := a[63:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_movepi64_pi64 +FORCE_INLINE __m64 _mm_movepi64_pi64(__m128i a) +{ + return vreinterpret_m64_s64(vget_low_s64(vreinterpretq_s64_m128i(a))); +} + +// Copy the 64-bit integer a to the lower element of dst, and zero the upper +// element. +// +// dst[63:0] := a[63:0] +// dst[127:64] := 0 +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_movpi64_epi64 +FORCE_INLINE __m128i _mm_movpi64_epi64(__m64 a) +{ + return vreinterpretq_m128i_s64( + vcombine_s64(vreinterpret_s64_m64(a), vdup_n_s64(0))); +} + +// Multiply the low unsigned 32-bit integers from each packed 64-bit element in +// a and b, and store the unsigned 64-bit results in dst. +// +// r0 := (a0 & 0xFFFFFFFF) * (b0 & 0xFFFFFFFF) +// r1 := (a2 & 0xFFFFFFFF) * (b2 & 0xFFFFFFFF) +FORCE_INLINE __m128i _mm_mul_epu32(__m128i a, __m128i b) +{ + // vmull_u32 upcasts instead of masking, so we downcast. + uint32x2_t a_lo = vmovn_u64(vreinterpretq_u64_m128i(a)); + uint32x2_t b_lo = vmovn_u64(vreinterpretq_u64_m128i(b)); + return vreinterpretq_m128i_u64(vmull_u32(a_lo, b_lo)); +} + +// Multiply packed double-precision (64-bit) floating-point elements in a and b, +// and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mul_pd +FORCE_INLINE __m128d _mm_mul_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64( + vmulq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + double *da = (double *) &a; + double *db = (double *) &b; + double c[2]; + c[0] = da[0] * db[0]; + c[1] = da[1] * db[1]; + return vld1q_f32((float32_t *) c); +#endif +} + +// Multiply the lower double-precision (64-bit) floating-point element in a and +// b, store the result in the lower element of dst, and copy the upper element +// from a to the upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=mm_mul_sd +FORCE_INLINE __m128d _mm_mul_sd(__m128d a, __m128d b) +{ + return _mm_move_sd(a, _mm_mul_pd(a, b)); +} + +// Multiply the low unsigned 32-bit integers from a and b, and store the +// unsigned 64-bit result in dst. +// +// dst[63:0] := a[31:0] * b[31:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mul_su32 +FORCE_INLINE __m64 _mm_mul_su32(__m64 a, __m64 b) +{ + return vreinterpret_m64_u64(vget_low_u64( + vmull_u32(vreinterpret_u32_m64(a), vreinterpret_u32_m64(b)))); +} + +// Multiplies the 8 signed 16-bit integers from a by the 8 signed 16-bit +// integers from b. +// +// r0 := (a0 * b0)[31:16] +// r1 := (a1 * b1)[31:16] +// ... +// r7 := (a7 * b7)[31:16] +// +// https://msdn.microsoft.com/en-us/library/vstudio/59hddw1d(v=vs.100).aspx +FORCE_INLINE __m128i _mm_mulhi_epi16(__m128i a, __m128i b) +{ + /* FIXME: issue with large values because of result saturation */ + // int16x8_t ret = vqdmulhq_s16(vreinterpretq_s16_m128i(a), + // vreinterpretq_s16_m128i(b)); /* =2*a*b */ return + // vreinterpretq_m128i_s16(vshrq_n_s16(ret, 1)); + int16x4_t a3210 = vget_low_s16(vreinterpretq_s16_m128i(a)); + int16x4_t b3210 = vget_low_s16(vreinterpretq_s16_m128i(b)); + int32x4_t ab3210 = vmull_s16(a3210, b3210); /* 3333222211110000 */ + int16x4_t a7654 = vget_high_s16(vreinterpretq_s16_m128i(a)); + int16x4_t b7654 = vget_high_s16(vreinterpretq_s16_m128i(b)); + int32x4_t ab7654 = vmull_s16(a7654, b7654); /* 7777666655554444 */ + uint16x8x2_t r = + vuzpq_u16(vreinterpretq_u16_s32(ab3210), vreinterpretq_u16_s32(ab7654)); + return vreinterpretq_m128i_u16(r.val[1]); +} + +// Multiply the packed unsigned 16-bit integers in a and b, producing +// intermediate 32-bit integers, and store the high 16 bits of the intermediate +// integers in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mulhi_epu16 +FORCE_INLINE __m128i _mm_mulhi_epu16(__m128i a, __m128i b) +{ + uint16x4_t a3210 = vget_low_u16(vreinterpretq_u16_m128i(a)); + uint16x4_t b3210 = vget_low_u16(vreinterpretq_u16_m128i(b)); + uint32x4_t ab3210 = vmull_u16(a3210, b3210); +#if defined(__aarch64__) + uint32x4_t ab7654 = + vmull_high_u16(vreinterpretq_u16_m128i(a), vreinterpretq_u16_m128i(b)); + uint16x8_t r = vuzp2q_u16(vreinterpretq_u16_u32(ab3210), + vreinterpretq_u16_u32(ab7654)); + return vreinterpretq_m128i_u16(r); +#else + uint16x4_t a7654 = vget_high_u16(vreinterpretq_u16_m128i(a)); + uint16x4_t b7654 = vget_high_u16(vreinterpretq_u16_m128i(b)); + uint32x4_t ab7654 = vmull_u16(a7654, b7654); + uint16x8x2_t r = + vuzpq_u16(vreinterpretq_u16_u32(ab3210), vreinterpretq_u16_u32(ab7654)); + return vreinterpretq_m128i_u16(r.val[1]); +#endif +} + +// Multiplies the 8 signed or unsigned 16-bit integers from a by the 8 signed or +// unsigned 16-bit integers from b. +// +// r0 := (a0 * b0)[15:0] +// r1 := (a1 * b1)[15:0] +// ... +// r7 := (a7 * b7)[15:0] +// +// https://msdn.microsoft.com/en-us/library/vstudio/9ks1472s(v=vs.100).aspx +FORCE_INLINE __m128i _mm_mullo_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vmulq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Compute the bitwise OR of packed double-precision (64-bit) floating-point +// elements in a and b, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=mm_or_pd +FORCE_INLINE __m128d _mm_or_pd(__m128d a, __m128d b) +{ + return vreinterpretq_m128d_s64( + vorrq_s64(vreinterpretq_s64_m128d(a), vreinterpretq_s64_m128d(b))); +} + +// Computes the bitwise OR of the 128-bit value in a and the 128-bit value in b. +// +// r := a | b +// +// https://msdn.microsoft.com/en-us/library/vstudio/ew8ty0db(v=vs.100).aspx +FORCE_INLINE __m128i _mm_or_si128(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vorrq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Packs the 16 signed 16-bit integers from a and b into 8-bit integers and +// saturates. +// https://msdn.microsoft.com/en-us/library/k4y4f7w5%28v=vs.90%29.aspx +FORCE_INLINE __m128i _mm_packs_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s8( + vcombine_s8(vqmovn_s16(vreinterpretq_s16_m128i(a)), + vqmovn_s16(vreinterpretq_s16_m128i(b)))); +} + +// Packs the 8 signed 32-bit integers from a and b into signed 16-bit integers +// and saturates. +// +// r0 := SignedSaturate(a0) +// r1 := SignedSaturate(a1) +// r2 := SignedSaturate(a2) +// r3 := SignedSaturate(a3) +// r4 := SignedSaturate(b0) +// r5 := SignedSaturate(b1) +// r6 := SignedSaturate(b2) +// r7 := SignedSaturate(b3) +// +// https://msdn.microsoft.com/en-us/library/393t56f9%28v=vs.90%29.aspx +FORCE_INLINE __m128i _mm_packs_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vcombine_s16(vqmovn_s32(vreinterpretq_s32_m128i(a)), + vqmovn_s32(vreinterpretq_s32_m128i(b)))); +} + +// Packs the 16 signed 16 - bit integers from a and b into 8 - bit unsigned +// integers and saturates. +// +// r0 := UnsignedSaturate(a0) +// r1 := UnsignedSaturate(a1) +// ... +// r7 := UnsignedSaturate(a7) +// r8 := UnsignedSaturate(b0) +// r9 := UnsignedSaturate(b1) +// ... +// r15 := UnsignedSaturate(b7) +// +// https://msdn.microsoft.com/en-us/library/07ad1wx4(v=vs.100).aspx +FORCE_INLINE __m128i _mm_packus_epi16(const __m128i a, const __m128i b) +{ + return vreinterpretq_m128i_u8( + vcombine_u8(vqmovun_s16(vreinterpretq_s16_m128i(a)), + vqmovun_s16(vreinterpretq_s16_m128i(b)))); +} + +// Pause the processor. This is typically used in spin-wait loops and depending +// on the x86 processor typical values are in the 40-100 cycle range. The +// 'yield' instruction isn't a good fit beacuse it's effectively a nop on most +// Arm cores. Experience with several databases has shown has shown an 'isb' is +// a reasonable approximation. +FORCE_INLINE void _mm_pause() +{ + __asm__ __volatile__("isb\n"); +} + +// Compute the absolute differences of packed unsigned 8-bit integers in a and +// b, then horizontally sum each consecutive 8 differences to produce two +// unsigned 16-bit integers, and pack these unsigned 16-bit integers in the low +// 16 bits of 64-bit elements in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sad_epu8 +FORCE_INLINE __m128i _mm_sad_epu8(__m128i a, __m128i b) +{ + uint16x8_t t = vpaddlq_u8(vabdq_u8((uint8x16_t) a, (uint8x16_t) b)); + return vreinterpretq_m128i_u64(vpaddlq_u32(vpaddlq_u16(t))); +} + +// Sets the 8 signed 16-bit integer values. +// https://msdn.microsoft.com/en-au/library/3e0fek84(v=vs.90).aspx +FORCE_INLINE __m128i _mm_set_epi16(short i7, + short i6, + short i5, + short i4, + short i3, + short i2, + short i1, + short i0) +{ + int16_t ALIGN_STRUCT(16) data[8] = {i0, i1, i2, i3, i4, i5, i6, i7}; + return vreinterpretq_m128i_s16(vld1q_s16(data)); +} + +// Sets the 4 signed 32-bit integer values. +// https://msdn.microsoft.com/en-us/library/vstudio/019beekt(v=vs.100).aspx +FORCE_INLINE __m128i _mm_set_epi32(int i3, int i2, int i1, int i0) +{ + int32_t ALIGN_STRUCT(16) data[4] = {i0, i1, i2, i3}; + return vreinterpretq_m128i_s32(vld1q_s32(data)); +} + +// Returns the __m128i structure with its two 64-bit integer values +// initialized to the values of the two 64-bit integers passed in. +// https://msdn.microsoft.com/en-us/library/dk2sdw0h(v=vs.120).aspx +FORCE_INLINE __m128i _mm_set_epi64(__m64 i1, __m64 i2) +{ + return _mm_set_epi64x((int64_t) i1, (int64_t) i2); +} + +// Returns the __m128i structure with its two 64-bit integer values +// initialized to the values of the two 64-bit integers passed in. +// https://msdn.microsoft.com/en-us/library/dk2sdw0h(v=vs.120).aspx +FORCE_INLINE __m128i _mm_set_epi64x(int64_t i1, int64_t i2) +{ + return vreinterpretq_m128i_s64( + vcombine_s64(vcreate_s64(i2), vcreate_s64(i1))); +} + +// Sets the 16 signed 8-bit integer values. +// https://msdn.microsoft.com/en-us/library/x0cx8zd3(v=vs.90).aspx +FORCE_INLINE __m128i _mm_set_epi8(signed char b15, + signed char b14, + signed char b13, + signed char b12, + signed char b11, + signed char b10, + signed char b9, + signed char b8, + signed char b7, + signed char b6, + signed char b5, + signed char b4, + signed char b3, + signed char b2, + signed char b1, + signed char b0) +{ + int8_t ALIGN_STRUCT(16) + data[16] = {(int8_t) b0, (int8_t) b1, (int8_t) b2, (int8_t) b3, + (int8_t) b4, (int8_t) b5, (int8_t) b6, (int8_t) b7, + (int8_t) b8, (int8_t) b9, (int8_t) b10, (int8_t) b11, + (int8_t) b12, (int8_t) b13, (int8_t) b14, (int8_t) b15}; + return (__m128i) vld1q_s8(data); +} + +// Set packed double-precision (64-bit) floating-point elements in dst with the +// supplied values. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_set_pd +FORCE_INLINE __m128d _mm_set_pd(double e1, double e0) +{ + double ALIGN_STRUCT(16) data[2] = {e0, e1}; +#if defined(__aarch64__) + return vreinterpretq_m128d_f64(vld1q_f64((float64_t *) data)); +#else + return vreinterpretq_m128d_f32(vld1q_f32((float32_t *) data)); +#endif +} + +// Broadcast double-precision (64-bit) floating-point value a to all elements of +// dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_set_pd1 +#define _mm_set_pd1 _mm_set1_pd + +// Copy double-precision (64-bit) floating-point element a to the lower element +// of dst, and zero the upper element. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_set_sd +FORCE_INLINE __m128d _mm_set_sd(double a) +{ + return _mm_set_pd(0, a); +} + +// Sets the 8 signed 16-bit integer values to w. +// +// r0 := w +// r1 := w +// ... +// r7 := w +// +// https://msdn.microsoft.com/en-us/library/k0ya3x0e(v=vs.90).aspx +FORCE_INLINE __m128i _mm_set1_epi16(short w) +{ + return vreinterpretq_m128i_s16(vdupq_n_s16(w)); +} + +// Sets the 4 signed 32-bit integer values to i. +// +// r0 := i +// r1 := i +// r2 := i +// r3 := I +// +// https://msdn.microsoft.com/en-us/library/vstudio/h4xscxat(v=vs.100).aspx +FORCE_INLINE __m128i _mm_set1_epi32(int _i) +{ + return vreinterpretq_m128i_s32(vdupq_n_s32(_i)); +} + +// Sets the 2 signed 64-bit integer values to i. +// https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/whtfzhzk(v=vs.100) +FORCE_INLINE __m128i _mm_set1_epi64(__m64 _i) +{ + return vreinterpretq_m128i_s64(vdupq_n_s64((int64_t) _i)); +} + +// Sets the 2 signed 64-bit integer values to i. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_set1_epi64x +FORCE_INLINE __m128i _mm_set1_epi64x(int64_t _i) +{ + return vreinterpretq_m128i_s64(vdupq_n_s64(_i)); +} + +// Sets the 16 signed 8-bit integer values to b. +// +// r0 := b +// r1 := b +// ... +// r15 := b +// +// https://msdn.microsoft.com/en-us/library/6e14xhyf(v=vs.100).aspx +FORCE_INLINE __m128i _mm_set1_epi8(signed char w) +{ + return vreinterpretq_m128i_s8(vdupq_n_s8(w)); +} + +// Broadcast double-precision (64-bit) floating-point value a to all elements of +// dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_set1_pd +FORCE_INLINE __m128d _mm_set1_pd(double d) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64(vdupq_n_f64(d)); +#else + return vreinterpretq_m128d_s64(vdupq_n_s64(*(int64_t *) &d)); +#endif +} + +// Sets the 8 signed 16-bit integer values in reverse order. +// +// Return Value +// r0 := w0 +// r1 := w1 +// ... +// r7 := w7 +FORCE_INLINE __m128i _mm_setr_epi16(short w0, + short w1, + short w2, + short w3, + short w4, + short w5, + short w6, + short w7) +{ + int16_t ALIGN_STRUCT(16) data[8] = {w0, w1, w2, w3, w4, w5, w6, w7}; + return vreinterpretq_m128i_s16(vld1q_s16((int16_t *) data)); +} + +// Sets the 4 signed 32-bit integer values in reverse order +// https://technet.microsoft.com/en-us/library/security/27yb3ee5(v=vs.90).aspx +FORCE_INLINE __m128i _mm_setr_epi32(int i3, int i2, int i1, int i0) +{ + int32_t ALIGN_STRUCT(16) data[4] = {i3, i2, i1, i0}; + return vreinterpretq_m128i_s32(vld1q_s32(data)); +} + +// Set packed 64-bit integers in dst with the supplied values in reverse order. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_setr_epi64 +FORCE_INLINE __m128i _mm_setr_epi64(__m64 e1, __m64 e0) +{ + return vreinterpretq_m128i_s64(vcombine_s64(e1, e0)); +} + +// Sets the 16 signed 8-bit integer values in reverse order. +// https://msdn.microsoft.com/en-us/library/2khb9c7k(v=vs.90).aspx +FORCE_INLINE __m128i _mm_setr_epi8(signed char b0, + signed char b1, + signed char b2, + signed char b3, + signed char b4, + signed char b5, + signed char b6, + signed char b7, + signed char b8, + signed char b9, + signed char b10, + signed char b11, + signed char b12, + signed char b13, + signed char b14, + signed char b15) +{ + int8_t ALIGN_STRUCT(16) + data[16] = {(int8_t) b0, (int8_t) b1, (int8_t) b2, (int8_t) b3, + (int8_t) b4, (int8_t) b5, (int8_t) b6, (int8_t) b7, + (int8_t) b8, (int8_t) b9, (int8_t) b10, (int8_t) b11, + (int8_t) b12, (int8_t) b13, (int8_t) b14, (int8_t) b15}; + return (__m128i) vld1q_s8(data); +} + +// Set packed double-precision (64-bit) floating-point elements in dst with the +// supplied values in reverse order. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_setr_pd +FORCE_INLINE __m128d _mm_setr_pd(double e1, double e0) +{ + return _mm_set_pd(e0, e1); +} + +// Return vector of type __m128d with all elements set to zero. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_setzero_pd +FORCE_INLINE __m128d _mm_setzero_pd(void) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64(vdupq_n_f64(0)); +#else + return vreinterpretq_m128d_f32(vdupq_n_f32(0)); +#endif +} + +// Sets the 128-bit value to zero +// https://msdn.microsoft.com/en-us/library/vstudio/ys7dw0kh(v=vs.100).aspx +FORCE_INLINE __m128i _mm_setzero_si128(void) +{ + return vreinterpretq_m128i_s32(vdupq_n_s32(0)); +} + +// Shuffles the 4 signed or unsigned 32-bit integers in a as specified by imm. +// https://msdn.microsoft.com/en-us/library/56f67xbk%28v=vs.90%29.aspx +// FORCE_INLINE __m128i _mm_shuffle_epi32(__m128i a, +// __constrange(0,255) int imm) +#if __has_builtin(__builtin_shufflevector) +#define _mm_shuffle_epi32(a, imm) \ + __extension__({ \ + int32x4_t _input = vreinterpretq_s32_m128i(a); \ + int32x4_t _shuf = __builtin_shufflevector( \ + _input, _input, (imm) & (0x3), ((imm) >> 2) & 0x3, \ + ((imm) >> 4) & 0x3, ((imm) >> 6) & 0x3); \ + vreinterpretq_m128i_s32(_shuf); \ + }) +#else // generic +#define _mm_shuffle_epi32(a, imm) \ + __extension__({ \ + __m128i ret; \ + switch (imm) { \ + case _MM_SHUFFLE(1, 0, 3, 2): \ + ret = _mm_shuffle_epi_1032((a)); \ + break; \ + case _MM_SHUFFLE(2, 3, 0, 1): \ + ret = _mm_shuffle_epi_2301((a)); \ + break; \ + case _MM_SHUFFLE(0, 3, 2, 1): \ + ret = _mm_shuffle_epi_0321((a)); \ + break; \ + case _MM_SHUFFLE(2, 1, 0, 3): \ + ret = _mm_shuffle_epi_2103((a)); \ + break; \ + case _MM_SHUFFLE(1, 0, 1, 0): \ + ret = _mm_shuffle_epi_1010((a)); \ + break; \ + case _MM_SHUFFLE(1, 0, 0, 1): \ + ret = _mm_shuffle_epi_1001((a)); \ + break; \ + case _MM_SHUFFLE(0, 1, 0, 1): \ + ret = _mm_shuffle_epi_0101((a)); \ + break; \ + case _MM_SHUFFLE(2, 2, 1, 1): \ + ret = _mm_shuffle_epi_2211((a)); \ + break; \ + case _MM_SHUFFLE(0, 1, 2, 2): \ + ret = _mm_shuffle_epi_0122((a)); \ + break; \ + case _MM_SHUFFLE(3, 3, 3, 2): \ + ret = _mm_shuffle_epi_3332((a)); \ + break; \ + case _MM_SHUFFLE(0, 0, 0, 0): \ + ret = _mm_shuffle_epi32_splat((a), 0); \ + break; \ + case _MM_SHUFFLE(1, 1, 1, 1): \ + ret = _mm_shuffle_epi32_splat((a), 1); \ + break; \ + case _MM_SHUFFLE(2, 2, 2, 2): \ + ret = _mm_shuffle_epi32_splat((a), 2); \ + break; \ + case _MM_SHUFFLE(3, 3, 3, 3): \ + ret = _mm_shuffle_epi32_splat((a), 3); \ + break; \ + default: \ + ret = _mm_shuffle_epi32_default((a), (imm)); \ + break; \ + } \ + ret; \ + }) +#endif + +// Shuffle double-precision (64-bit) floating-point elements using the control +// in imm8, and store the results in dst. +// +// dst[63:0] := (imm8[0] == 0) ? a[63:0] : a[127:64] +// dst[127:64] := (imm8[1] == 0) ? b[63:0] : b[127:64] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_shuffle_pd +#if __has_builtin(__builtin_shufflevector) +#define _mm_shuffle_pd(a, b, imm8) \ + vreinterpretq_m128d_s64(__builtin_shufflevector( \ + vreinterpretq_s64_m128d(a), vreinterpretq_s64_m128d(b), imm8 & 0x1, \ + ((imm8 & 0x2) >> 1) + 2)) +#else +#define _mm_shuffle_pd(a, b, imm8) \ + _mm_castsi128_pd(_mm_set_epi64x( \ + vgetq_lane_s64(vreinterpretq_s64_m128d(b), (imm8 & 0x2) >> 1), \ + vgetq_lane_s64(vreinterpretq_s64_m128d(a), imm8 & 0x1))) +#endif + +// FORCE_INLINE __m128i _mm_shufflehi_epi16(__m128i a, +// __constrange(0,255) int imm) +#if __has_builtin(__builtin_shufflevector) +#define _mm_shufflehi_epi16(a, imm) \ + __extension__({ \ + int16x8_t _input = vreinterpretq_s16_m128i(a); \ + int16x8_t _shuf = __builtin_shufflevector( \ + _input, _input, 0, 1, 2, 3, ((imm) & (0x3)) + 4, \ + (((imm) >> 2) & 0x3) + 4, (((imm) >> 4) & 0x3) + 4, \ + (((imm) >> 6) & 0x3) + 4); \ + vreinterpretq_m128i_s16(_shuf); \ + }) +#else // generic +#define _mm_shufflehi_epi16(a, imm) _mm_shufflehi_epi16_function((a), (imm)) +#endif + +// FORCE_INLINE __m128i _mm_shufflelo_epi16(__m128i a, +// __constrange(0,255) int imm) +#if __has_builtin(__builtin_shufflevector) +#define _mm_shufflelo_epi16(a, imm) \ + __extension__({ \ + int16x8_t _input = vreinterpretq_s16_m128i(a); \ + int16x8_t _shuf = __builtin_shufflevector( \ + _input, _input, ((imm) & (0x3)), (((imm) >> 2) & 0x3), \ + (((imm) >> 4) & 0x3), (((imm) >> 6) & 0x3), 4, 5, 6, 7); \ + vreinterpretq_m128i_s16(_shuf); \ + }) +#else // generic +#define _mm_shufflelo_epi16(a, imm) _mm_shufflelo_epi16_function((a), (imm)) +#endif + +// Shift packed 16-bit integers in a left by count while shifting in zeros, and +// store the results in dst. +// +// FOR j := 0 to 7 +// i := j*16 +// IF count[63:0] > 15 +// dst[i+15:i] := 0 +// ELSE +// dst[i+15:i] := ZeroExtend16(a[i+15:i] << count[63:0]) +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sll_epi16 +FORCE_INLINE __m128i _mm_sll_epi16(__m128i a, __m128i count) +{ + uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); + if (_sse2neon_unlikely(c & ~15)) + return _mm_setzero_si128(); + + int16x8_t vc = vdupq_n_s16((int16_t) c); + return vreinterpretq_m128i_s16(vshlq_s16(vreinterpretq_s16_m128i(a), vc)); +} + +// Shift packed 32-bit integers in a left by count while shifting in zeros, and +// store the results in dst. +// +// FOR j := 0 to 3 +// i := j*32 +// IF count[63:0] > 31 +// dst[i+31:i] := 0 +// ELSE +// dst[i+31:i] := ZeroExtend32(a[i+31:i] << count[63:0]) +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sll_epi32 +FORCE_INLINE __m128i _mm_sll_epi32(__m128i a, __m128i count) +{ + uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); + if (_sse2neon_unlikely(c & ~31)) + return _mm_setzero_si128(); + + int32x4_t vc = vdupq_n_s32((int32_t) c); + return vreinterpretq_m128i_s32(vshlq_s32(vreinterpretq_s32_m128i(a), vc)); +} + +// Shift packed 64-bit integers in a left by count while shifting in zeros, and +// store the results in dst. +// +// FOR j := 0 to 1 +// i := j*64 +// IF count[63:0] > 63 +// dst[i+63:i] := 0 +// ELSE +// dst[i+63:i] := ZeroExtend64(a[i+63:i] << count[63:0]) +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sll_epi64 +FORCE_INLINE __m128i _mm_sll_epi64(__m128i a, __m128i count) +{ + uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); + if (_sse2neon_unlikely(c & ~63)) + return _mm_setzero_si128(); + + int64x2_t vc = vdupq_n_s64((int64_t) c); + return vreinterpretq_m128i_s64(vshlq_s64(vreinterpretq_s64_m128i(a), vc)); +} + +// Shift packed 16-bit integers in a left by imm8 while shifting in zeros, and +// store the results in dst. +// +// FOR j := 0 to 7 +// i := j*16 +// IF imm8[7:0] > 15 +// dst[i+15:i] := 0 +// ELSE +// dst[i+15:i] := ZeroExtend16(a[i+15:i] << imm8[7:0]) +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_slli_epi16 +FORCE_INLINE __m128i _mm_slli_epi16(__m128i a, int imm) +{ + if (_sse2neon_unlikely(imm & ~15)) + return _mm_setzero_si128(); + return vreinterpretq_m128i_s16( + vshlq_s16(vreinterpretq_s16_m128i(a), vdupq_n_s16(imm))); +} + +// Shift packed 32-bit integers in a left by imm8 while shifting in zeros, and +// store the results in dst. +// +// FOR j := 0 to 3 +// i := j*32 +// IF imm8[7:0] > 31 +// dst[i+31:i] := 0 +// ELSE +// dst[i+31:i] := ZeroExtend32(a[i+31:i] << imm8[7:0]) +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_slli_epi32 +FORCE_INLINE __m128i _mm_slli_epi32(__m128i a, int imm) +{ + if (_sse2neon_unlikely(imm & ~31)) + return _mm_setzero_si128(); + return vreinterpretq_m128i_s32( + vshlq_s32(vreinterpretq_s32_m128i(a), vdupq_n_s32(imm))); +} + +// Shift packed 64-bit integers in a left by imm8 while shifting in zeros, and +// store the results in dst. +// +// FOR j := 0 to 1 +// i := j*64 +// IF imm8[7:0] > 63 +// dst[i+63:i] := 0 +// ELSE +// dst[i+63:i] := ZeroExtend64(a[i+63:i] << imm8[7:0]) +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_slli_epi64 +FORCE_INLINE __m128i _mm_slli_epi64(__m128i a, int imm) +{ + if (_sse2neon_unlikely(imm & ~63)) + return _mm_setzero_si128(); + return vreinterpretq_m128i_s64( + vshlq_s64(vreinterpretq_s64_m128i(a), vdupq_n_s64(imm))); +} + +// Shift a left by imm8 bytes while shifting in zeros, and store the results in +// dst. +// +// tmp := imm8[7:0] +// IF tmp > 15 +// tmp := 16 +// FI +// dst[127:0] := a[127:0] << (tmp*8) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_slli_si128 +FORCE_INLINE __m128i _mm_slli_si128(__m128i a, int imm) +{ + if (_sse2neon_unlikely(imm & ~15)) + return _mm_setzero_si128(); + uint8x16_t tmp[2] = {vdupq_n_u8(0), vreinterpretq_u8_m128i(a)}; + return vreinterpretq_m128i_u8( + vld1q_u8(((uint8_t const *) tmp) + (16 - imm))); +} + +// Compute the square root of packed double-precision (64-bit) floating-point +// elements in a, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sqrt_pd +FORCE_INLINE __m128d _mm_sqrt_pd(__m128d a) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64(vsqrtq_f64(vreinterpretq_f64_m128d(a))); +#else + double a0 = sqrt(((double *) &a)[0]); + double a1 = sqrt(((double *) &a)[1]); + return _mm_set_pd(a1, a0); +#endif +} + +// Compute the square root of the lower double-precision (64-bit) floating-point +// element in b, store the result in the lower element of dst, and copy the +// upper element from a to the upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sqrt_sd +FORCE_INLINE __m128d _mm_sqrt_sd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return _mm_move_sd(a, _mm_sqrt_pd(b)); +#else + return _mm_set_pd(((double *) &a)[1], sqrt(((double *) &b)[0])); +#endif +} + +// Shift packed 16-bit integers in a right by count while shifting in sign bits, +// and store the results in dst. +// +// FOR j := 0 to 7 +// i := j*16 +// IF count[63:0] > 15 +// dst[i+15:i] := (a[i+15] ? 0xFFFF : 0x0) +// ELSE +// dst[i+15:i] := SignExtend16(a[i+15:i] >> count[63:0]) +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sra_epi16 +FORCE_INLINE __m128i _mm_sra_epi16(__m128i a, __m128i count) +{ + int64_t c = (int64_t) vget_low_s64((int64x2_t) count); + if (_sse2neon_unlikely(c & ~15)) + return _mm_cmplt_epi16(a, _mm_setzero_si128()); + return vreinterpretq_m128i_s16(vshlq_s16((int16x8_t) a, vdupq_n_s16(-c))); +} + +// Shift packed 32-bit integers in a right by count while shifting in sign bits, +// and store the results in dst. +// +// FOR j := 0 to 3 +// i := j*32 +// IF count[63:0] > 31 +// dst[i+31:i] := (a[i+31] ? 0xFFFFFFFF : 0x0) +// ELSE +// dst[i+31:i] := SignExtend32(a[i+31:i] >> count[63:0]) +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sra_epi32 +FORCE_INLINE __m128i _mm_sra_epi32(__m128i a, __m128i count) +{ + int64_t c = (int64_t) vget_low_s64((int64x2_t) count); + if (_sse2neon_unlikely(c & ~31)) + return _mm_cmplt_epi32(a, _mm_setzero_si128()); + return vreinterpretq_m128i_s32(vshlq_s32((int32x4_t) a, vdupq_n_s32(-c))); +} + +// Shift packed 16-bit integers in a right by imm8 while shifting in sign +// bits, and store the results in dst. +// +// FOR j := 0 to 7 +// i := j*16 +// IF imm8[7:0] > 15 +// dst[i+15:i] := (a[i+15] ? 0xFFFF : 0x0) +// ELSE +// dst[i+15:i] := SignExtend16(a[i+15:i] >> imm8[7:0]) +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_srai_epi16 +FORCE_INLINE __m128i _mm_srai_epi16(__m128i a, int imm) +{ + const int count = (imm & ~15) ? 15 : imm; + return (__m128i) vshlq_s16((int16x8_t) a, vdupq_n_s16(-count)); +} + +// Shift packed 32-bit integers in a right by imm8 while shifting in sign bits, +// and store the results in dst. +// +// FOR j := 0 to 3 +// i := j*32 +// IF imm8[7:0] > 31 +// dst[i+31:i] := (a[i+31] ? 0xFFFFFFFF : 0x0) +// ELSE +// dst[i+31:i] := SignExtend32(a[i+31:i] >> imm8[7:0]) +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_srai_epi32 +// FORCE_INLINE __m128i _mm_srai_epi32(__m128i a, __constrange(0,255) int imm) +#define _mm_srai_epi32(a, imm) \ + __extension__({ \ + __m128i ret; \ + if (_sse2neon_unlikely((imm) == 0)) { \ + ret = a; \ + } else if (_sse2neon_likely(0 < (imm) && (imm) < 32)) { \ + ret = vreinterpretq_m128i_s32( \ + vshlq_s32(vreinterpretq_s32_m128i(a), vdupq_n_s32(-imm))); \ + } else { \ + ret = vreinterpretq_m128i_s32( \ + vshrq_n_s32(vreinterpretq_s32_m128i(a), 31)); \ + } \ + ret; \ + }) + +// Shift packed 16-bit integers in a right by count while shifting in zeros, and +// store the results in dst. +// +// FOR j := 0 to 7 +// i := j*16 +// IF count[63:0] > 15 +// dst[i+15:i] := 0 +// ELSE +// dst[i+15:i] := ZeroExtend16(a[i+15:i] >> count[63:0]) +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_srl_epi16 +FORCE_INLINE __m128i _mm_srl_epi16(__m128i a, __m128i count) +{ + uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); + if (_sse2neon_unlikely(c & ~15)) + return _mm_setzero_si128(); + + int16x8_t vc = vdupq_n_s16(-(int16_t) c); + return vreinterpretq_m128i_u16(vshlq_u16(vreinterpretq_u16_m128i(a), vc)); +} + +// Shift packed 32-bit integers in a right by count while shifting in zeros, and +// store the results in dst. +// +// FOR j := 0 to 3 +// i := j*32 +// IF count[63:0] > 31 +// dst[i+31:i] := 0 +// ELSE +// dst[i+31:i] := ZeroExtend32(a[i+31:i] >> count[63:0]) +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_srl_epi32 +FORCE_INLINE __m128i _mm_srl_epi32(__m128i a, __m128i count) +{ + uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); + if (_sse2neon_unlikely(c & ~31)) + return _mm_setzero_si128(); + + int32x4_t vc = vdupq_n_s32(-(int32_t) c); + return vreinterpretq_m128i_u32(vshlq_u32(vreinterpretq_u32_m128i(a), vc)); +} + +// Shift packed 64-bit integers in a right by count while shifting in zeros, and +// store the results in dst. +// +// FOR j := 0 to 1 +// i := j*64 +// IF count[63:0] > 63 +// dst[i+63:i] := 0 +// ELSE +// dst[i+63:i] := ZeroExtend64(a[i+63:i] >> count[63:0]) +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_srl_epi64 +FORCE_INLINE __m128i _mm_srl_epi64(__m128i a, __m128i count) +{ + uint64_t c = vreinterpretq_nth_u64_m128i(count, 0); + if (_sse2neon_unlikely(c & ~63)) + return _mm_setzero_si128(); + + int64x2_t vc = vdupq_n_s64(-(int64_t) c); + return vreinterpretq_m128i_u64(vshlq_u64(vreinterpretq_u64_m128i(a), vc)); +} + +// Shift packed 16-bit integers in a right by imm8 while shifting in zeros, and +// store the results in dst. +// +// FOR j := 0 to 7 +// i := j*16 +// IF imm8[7:0] > 15 +// dst[i+15:i] := 0 +// ELSE +// dst[i+15:i] := ZeroExtend16(a[i+15:i] >> imm8[7:0]) +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_srli_epi16 +#define _mm_srli_epi16(a, imm) \ + __extension__({ \ + __m128i ret; \ + if (_sse2neon_unlikely(imm & ~15)) { \ + ret = _mm_setzero_si128(); \ + } else { \ + ret = vreinterpretq_m128i_u16( \ + vshlq_u16(vreinterpretq_u16_m128i(a), vdupq_n_s16(-imm))); \ + } \ + ret; \ + }) + +// Shift packed 32-bit integers in a right by imm8 while shifting in zeros, and +// store the results in dst. +// +// FOR j := 0 to 3 +// i := j*32 +// IF imm8[7:0] > 31 +// dst[i+31:i] := 0 +// ELSE +// dst[i+31:i] := ZeroExtend32(a[i+31:i] >> imm8[7:0]) +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_srli_epi32 +// FORCE_INLINE __m128i _mm_srli_epi32(__m128i a, __constrange(0,255) int imm) +#define _mm_srli_epi32(a, imm) \ + __extension__({ \ + __m128i ret; \ + if (_sse2neon_unlikely(imm & ~31)) { \ + ret = _mm_setzero_si128(); \ + } else { \ + ret = vreinterpretq_m128i_u32( \ + vshlq_u32(vreinterpretq_u32_m128i(a), vdupq_n_s32(-imm))); \ + } \ + ret; \ + }) + +// Shift packed 64-bit integers in a right by imm8 while shifting in zeros, and +// store the results in dst. +// +// FOR j := 0 to 1 +// i := j*64 +// IF imm8[7:0] > 63 +// dst[i+63:i] := 0 +// ELSE +// dst[i+63:i] := ZeroExtend64(a[i+63:i] >> imm8[7:0]) +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_srli_epi64 +#define _mm_srli_epi64(a, imm) \ + __extension__({ \ + __m128i ret; \ + if (_sse2neon_unlikely(imm & ~63)) { \ + ret = _mm_setzero_si128(); \ + } else { \ + ret = vreinterpretq_m128i_u64( \ + vshlq_u64(vreinterpretq_u64_m128i(a), vdupq_n_s64(-imm))); \ + } \ + ret; \ + }) + +// Shift a right by imm8 bytes while shifting in zeros, and store the results in +// dst. +// +// tmp := imm8[7:0] +// IF tmp > 15 +// tmp := 16 +// FI +// dst[127:0] := a[127:0] >> (tmp*8) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_srli_si128 +FORCE_INLINE __m128i _mm_srli_si128(__m128i a, int imm) +{ + if (_sse2neon_unlikely(imm & ~15)) + return _mm_setzero_si128(); + uint8x16_t tmp[2] = {vreinterpretq_u8_m128i(a), vdupq_n_u8(0)}; + return vreinterpretq_m128i_u8(vld1q_u8(((uint8_t const *) tmp) + imm)); +} + +// Store 128-bits (composed of 2 packed double-precision (64-bit) floating-point +// elements) from a into memory. mem_addr must be aligned on a 16-byte boundary +// or a general-protection exception may be generated. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_store_pd +FORCE_INLINE void _mm_store_pd(double *mem_addr, __m128d a) +{ +#if defined(__aarch64__) + vst1q_f64((float64_t *) mem_addr, vreinterpretq_f64_m128d(a)); +#else + vst1q_f32((float32_t *) mem_addr, vreinterpretq_f32_m128d(a)); +#endif +} + +// Store the lower double-precision (64-bit) floating-point element from a into +// 2 contiguous elements in memory. mem_addr must be aligned on a 16-byte +// boundary or a general-protection exception may be generated. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_store_pd1 +FORCE_INLINE void _mm_store_pd1(double *mem_addr, __m128d a) +{ +#if defined(__aarch64__) + float64x1_t a_low = vget_low_f64(vreinterpretq_f64_m128d(a)); + vst1q_f64((float64_t *) mem_addr, + vreinterpretq_f64_m128d(vcombine_f64(a_low, a_low))); +#else + float32x2_t a_low = vget_low_f32(vreinterpretq_f32_m128d(a)); + vst1q_f32((float32_t *) mem_addr, + vreinterpretq_f32_m128d(vcombine_f32(a_low, a_low))); +#endif +} + +// Store the lower double-precision (64-bit) floating-point element from a into +// memory. mem_addr does not need to be aligned on any particular boundary. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=mm_store_sd +FORCE_INLINE void _mm_store_sd(double *mem_addr, __m128d a) +{ +#if defined(__aarch64__) + vst1_f64((float64_t *) mem_addr, vget_low_f64(vreinterpretq_f64_m128d(a))); +#else + vst1_u64((uint64_t *) mem_addr, vget_low_u64(vreinterpretq_u64_m128d(a))); +#endif +} + +// Stores four 32-bit integer values as (as a __m128i value) at the address p. +// https://msdn.microsoft.com/en-us/library/vstudio/edk11s13(v=vs.100).aspx +FORCE_INLINE void _mm_store_si128(__m128i *p, __m128i a) +{ + vst1q_s32((int32_t *) p, vreinterpretq_s32_m128i(a)); +} + +// Store the lower double-precision (64-bit) floating-point element from a into +// 2 contiguous elements in memory. mem_addr must be aligned on a 16-byte +// boundary or a general-protection exception may be generated. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=9,526,5601&text=_mm_store1_pd +#define _mm_store1_pd _mm_store_pd1 + +// Store the upper double-precision (64-bit) floating-point element from a into +// memory. +// +// MEM[mem_addr+63:mem_addr] := a[127:64] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_storeh_pd +FORCE_INLINE void _mm_storeh_pd(double *mem_addr, __m128d a) +{ +#if defined(__aarch64__) + vst1_f64((float64_t *) mem_addr, vget_high_f64(vreinterpretq_f64_m128d(a))); +#else + vst1_f32((float32_t *) mem_addr, vget_high_f32(vreinterpretq_f32_m128d(a))); +#endif +} + +// Reads the lower 64 bits of b and stores them into the lower 64 bits of a. +// https://msdn.microsoft.com/en-us/library/hhwf428f%28v=vs.90%29.aspx +FORCE_INLINE void _mm_storel_epi64(__m128i *a, __m128i b) +{ + uint64x1_t hi = vget_high_u64(vreinterpretq_u64_m128i(*a)); + uint64x1_t lo = vget_low_u64(vreinterpretq_u64_m128i(b)); + *a = vreinterpretq_m128i_u64(vcombine_u64(lo, hi)); +} + +// Store the lower double-precision (64-bit) floating-point element from a into +// memory. +// +// MEM[mem_addr+63:mem_addr] := a[63:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_storel_pd +FORCE_INLINE void _mm_storel_pd(double *mem_addr, __m128d a) +{ +#if defined(__aarch64__) + vst1_f64((float64_t *) mem_addr, vget_low_f64(vreinterpretq_f64_m128d(a))); +#else + vst1_f32((float32_t *) mem_addr, vget_low_f32(vreinterpretq_f32_m128d(a))); +#endif +} + +// Store 2 double-precision (64-bit) floating-point elements from a into memory +// in reverse order. mem_addr must be aligned on a 16-byte boundary or a +// general-protection exception may be generated. +// +// MEM[mem_addr+63:mem_addr] := a[127:64] +// MEM[mem_addr+127:mem_addr+64] := a[63:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_storer_pd +FORCE_INLINE void _mm_storer_pd(double *mem_addr, __m128d a) +{ + float32x4_t f = vreinterpretq_f32_m128d(a); + _mm_store_pd(mem_addr, vreinterpretq_m128d_f32(vextq_f32(f, f, 2))); +} + +// Store 128-bits (composed of 2 packed double-precision (64-bit) floating-point +// elements) from a into memory. mem_addr does not need to be aligned on any +// particular boundary. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_storeu_pd +FORCE_INLINE void _mm_storeu_pd(double *mem_addr, __m128d a) +{ + _mm_store_pd(mem_addr, a); +} + +// Stores 128-bits of integer data a at the address p. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_storeu_si128 +FORCE_INLINE void _mm_storeu_si128(__m128i *p, __m128i a) +{ + vst1q_s32((int32_t *) p, vreinterpretq_s32_m128i(a)); +} + +// Stores 32-bits of integer data a at the address p. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_storeu_si32 +FORCE_INLINE void _mm_storeu_si32(void *p, __m128i a) +{ + vst1q_lane_s32((int32_t *) p, vreinterpretq_s32_m128i(a), 0); +} + +// Store 128-bits (composed of 2 packed double-precision (64-bit) floating-point +// elements) from a into memory using a non-temporal memory hint. mem_addr must +// be aligned on a 16-byte boundary or a general-protection exception may be +// generated. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_stream_pd +FORCE_INLINE void _mm_stream_pd(double *p, __m128d a) +{ +#if __has_builtin(__builtin_nontemporal_store) + __builtin_nontemporal_store(a, (float32x4_t *) p); +#elif defined(__aarch64__) + vst1q_f64(p, vreinterpretq_f64_m128d(a)); +#else + vst1q_s64((int64_t *) p, vreinterpretq_s64_m128d(a)); +#endif +} + +// Stores the data in a to the address p without polluting the caches. If the +// cache line containing address p is already in the cache, the cache will be +// updated. +// https://msdn.microsoft.com/en-us/library/ba08y07y%28v=vs.90%29.aspx +FORCE_INLINE void _mm_stream_si128(__m128i *p, __m128i a) +{ +#if __has_builtin(__builtin_nontemporal_store) + __builtin_nontemporal_store(a, p); +#else + vst1q_s64((int64_t *) p, vreinterpretq_s64_m128i(a)); +#endif +} + +// Store 32-bit integer a into memory using a non-temporal hint to minimize +// cache pollution. If the cache line containing address mem_addr is already in +// the cache, the cache will be updated. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_stream_si32 +FORCE_INLINE void _mm_stream_si32(int *p, int a) +{ + vst1q_lane_s32((int32_t *) p, vdupq_n_s32(a), 0); +} + +// Subtract packed 16-bit integers in b from packed 16-bit integers in a, and +// store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sub_epi16 +FORCE_INLINE __m128i _mm_sub_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vsubq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Subtracts the 4 signed or unsigned 32-bit integers of b from the 4 signed or +// unsigned 32-bit integers of a. +// +// r0 := a0 - b0 +// r1 := a1 - b1 +// r2 := a2 - b2 +// r3 := a3 - b3 +// +// https://msdn.microsoft.com/en-us/library/vstudio/fhh866h0(v=vs.100).aspx +FORCE_INLINE __m128i _mm_sub_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vsubq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Subtract 2 packed 64-bit integers in b from 2 packed 64-bit integers in a, +// and store the results in dst. +// r0 := a0 - b0 +// r1 := a1 - b1 +FORCE_INLINE __m128i _mm_sub_epi64(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s64( + vsubq_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(b))); +} + +// Subtract packed 8-bit integers in b from packed 8-bit integers in a, and +// store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sub_epi8 +FORCE_INLINE __m128i _mm_sub_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s8( + vsubq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Subtract packed double-precision (64-bit) floating-point elements in b from +// packed double-precision (64-bit) floating-point elements in a, and store the +// results in dst. +// +// FOR j := 0 to 1 +// i := j*64 +// dst[i+63:i] := a[i+63:i] - b[i+63:i] +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=mm_sub_pd +FORCE_INLINE __m128d _mm_sub_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64( + vsubq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + double *da = (double *) &a; + double *db = (double *) &b; + double c[2]; + c[0] = da[0] - db[0]; + c[1] = da[1] - db[1]; + return vld1q_f32((float32_t *) c); +#endif +} + +// Subtract the lower double-precision (64-bit) floating-point element in b from +// the lower double-precision (64-bit) floating-point element in a, store the +// result in the lower element of dst, and copy the upper element from a to the +// upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sub_sd +FORCE_INLINE __m128d _mm_sub_sd(__m128d a, __m128d b) +{ + return _mm_move_sd(a, _mm_sub_pd(a, b)); +} + +// Subtract 64-bit integer b from 64-bit integer a, and store the result in dst. +// +// dst[63:0] := a[63:0] - b[63:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sub_si64 +FORCE_INLINE __m64 _mm_sub_si64(__m64 a, __m64 b) +{ + return vreinterpret_m64_s64( + vsub_s64(vreinterpret_s64_m64(a), vreinterpret_s64_m64(b))); +} + +// Subtracts the 8 signed 16-bit integers of b from the 8 signed 16-bit integers +// of a and saturates. +// +// r0 := SignedSaturate(a0 - b0) +// r1 := SignedSaturate(a1 - b1) +// ... +// r7 := SignedSaturate(a7 - b7) +// +// https://technet.microsoft.com/en-us/subscriptions/3247z5b8(v=vs.90) +FORCE_INLINE __m128i _mm_subs_epi16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s16( + vqsubq_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +} + +// Subtracts the 16 signed 8-bit integers of b from the 16 signed 8-bit integers +// of a and saturates. +// +// r0 := SignedSaturate(a0 - b0) +// r1 := SignedSaturate(a1 - b1) +// ... +// r15 := SignedSaturate(a15 - b15) +// +// https://technet.microsoft.com/en-us/subscriptions/by7kzks1(v=vs.90) +FORCE_INLINE __m128i _mm_subs_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s8( + vqsubq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Subtracts the 8 unsigned 16-bit integers of bfrom the 8 unsigned 16-bit +// integers of a and saturates.. +// https://technet.microsoft.com/en-us/subscriptions/index/f44y0s19(v=vs.90).aspx +FORCE_INLINE __m128i _mm_subs_epu16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vqsubq_u16(vreinterpretq_u16_m128i(a), vreinterpretq_u16_m128i(b))); +} + +// Subtracts the 16 unsigned 8-bit integers of b from the 16 unsigned 8-bit +// integers of a and saturates. +// +// r0 := UnsignedSaturate(a0 - b0) +// r1 := UnsignedSaturate(a1 - b1) +// ... +// r15 := UnsignedSaturate(a15 - b15) +// +// https://technet.microsoft.com/en-us/subscriptions/yadkxc18(v=vs.90) +FORCE_INLINE __m128i _mm_subs_epu8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vqsubq_u8(vreinterpretq_u8_m128i(a), vreinterpretq_u8_m128i(b))); +} + +#define _mm_ucomieq_sd _mm_comieq_sd +#define _mm_ucomige_sd _mm_comige_sd +#define _mm_ucomigt_sd _mm_comigt_sd +#define _mm_ucomile_sd _mm_comile_sd +#define _mm_ucomilt_sd _mm_comilt_sd +#define _mm_ucomineq_sd _mm_comineq_sd + +// Return vector of type __m128d with undefined elements. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_undefined_pd +FORCE_INLINE __m128d _mm_undefined_pd(void) +{ +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuninitialized" +#endif + __m128d a; + return a; +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif +} + +// Interleaves the upper 4 signed or unsigned 16-bit integers in a with the +// upper 4 signed or unsigned 16-bit integers in b. +// +// r0 := a4 +// r1 := b4 +// r2 := a5 +// r3 := b5 +// r4 := a6 +// r5 := b6 +// r6 := a7 +// r7 := b7 +// +// https://msdn.microsoft.com/en-us/library/03196cz7(v=vs.100).aspx +FORCE_INLINE __m128i _mm_unpackhi_epi16(__m128i a, __m128i b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128i_s16( + vzip2q_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +#else + int16x4_t a1 = vget_high_s16(vreinterpretq_s16_m128i(a)); + int16x4_t b1 = vget_high_s16(vreinterpretq_s16_m128i(b)); + int16x4x2_t result = vzip_s16(a1, b1); + return vreinterpretq_m128i_s16(vcombine_s16(result.val[0], result.val[1])); +#endif +} + +// Interleaves the upper 2 signed or unsigned 32-bit integers in a with the +// upper 2 signed or unsigned 32-bit integers in b. +// https://msdn.microsoft.com/en-us/library/65sa7cbs(v=vs.100).aspx +FORCE_INLINE __m128i _mm_unpackhi_epi32(__m128i a, __m128i b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128i_s32( + vzip2q_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +#else + int32x2_t a1 = vget_high_s32(vreinterpretq_s32_m128i(a)); + int32x2_t b1 = vget_high_s32(vreinterpretq_s32_m128i(b)); + int32x2x2_t result = vzip_s32(a1, b1); + return vreinterpretq_m128i_s32(vcombine_s32(result.val[0], result.val[1])); +#endif +} + +// Interleaves the upper signed or unsigned 64-bit integer in a with the +// upper signed or unsigned 64-bit integer in b. +// +// r0 := a1 +// r1 := b1 +FORCE_INLINE __m128i _mm_unpackhi_epi64(__m128i a, __m128i b) +{ + int64x1_t a_h = vget_high_s64(vreinterpretq_s64_m128i(a)); + int64x1_t b_h = vget_high_s64(vreinterpretq_s64_m128i(b)); + return vreinterpretq_m128i_s64(vcombine_s64(a_h, b_h)); +} + +// Interleaves the upper 8 signed or unsigned 8-bit integers in a with the upper +// 8 signed or unsigned 8-bit integers in b. +// +// r0 := a8 +// r1 := b8 +// r2 := a9 +// r3 := b9 +// ... +// r14 := a15 +// r15 := b15 +// +// https://msdn.microsoft.com/en-us/library/t5h7783k(v=vs.100).aspx +FORCE_INLINE __m128i _mm_unpackhi_epi8(__m128i a, __m128i b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128i_s8( + vzip2q_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +#else + int8x8_t a1 = + vreinterpret_s8_s16(vget_high_s16(vreinterpretq_s16_m128i(a))); + int8x8_t b1 = + vreinterpret_s8_s16(vget_high_s16(vreinterpretq_s16_m128i(b))); + int8x8x2_t result = vzip_s8(a1, b1); + return vreinterpretq_m128i_s8(vcombine_s8(result.val[0], result.val[1])); +#endif +} + +// Unpack and interleave double-precision (64-bit) floating-point elements from +// the high half of a and b, and store the results in dst. +// +// DEFINE INTERLEAVE_HIGH_QWORDS(src1[127:0], src2[127:0]) { +// dst[63:0] := src1[127:64] +// dst[127:64] := src2[127:64] +// RETURN dst[127:0] +// } +// dst[127:0] := INTERLEAVE_HIGH_QWORDS(a[127:0], b[127:0]) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_unpackhi_pd +FORCE_INLINE __m128d _mm_unpackhi_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64( + vzip2q_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + return vreinterpretq_m128d_s64( + vcombine_s64(vget_high_s64(vreinterpretq_s64_m128d(a)), + vget_high_s64(vreinterpretq_s64_m128d(b)))); +#endif +} + +// Interleaves the lower 4 signed or unsigned 16-bit integers in a with the +// lower 4 signed or unsigned 16-bit integers in b. +// +// r0 := a0 +// r1 := b0 +// r2 := a1 +// r3 := b1 +// r4 := a2 +// r5 := b2 +// r6 := a3 +// r7 := b3 +// +// https://msdn.microsoft.com/en-us/library/btxb17bw%28v=vs.90%29.aspx +FORCE_INLINE __m128i _mm_unpacklo_epi16(__m128i a, __m128i b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128i_s16( + vzip1q_s16(vreinterpretq_s16_m128i(a), vreinterpretq_s16_m128i(b))); +#else + int16x4_t a1 = vget_low_s16(vreinterpretq_s16_m128i(a)); + int16x4_t b1 = vget_low_s16(vreinterpretq_s16_m128i(b)); + int16x4x2_t result = vzip_s16(a1, b1); + return vreinterpretq_m128i_s16(vcombine_s16(result.val[0], result.val[1])); +#endif +} + +// Interleaves the lower 2 signed or unsigned 32 - bit integers in a with the +// lower 2 signed or unsigned 32 - bit integers in b. +// +// r0 := a0 +// r1 := b0 +// r2 := a1 +// r3 := b1 +// +// https://msdn.microsoft.com/en-us/library/x8atst9d(v=vs.100).aspx +FORCE_INLINE __m128i _mm_unpacklo_epi32(__m128i a, __m128i b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128i_s32( + vzip1q_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +#else + int32x2_t a1 = vget_low_s32(vreinterpretq_s32_m128i(a)); + int32x2_t b1 = vget_low_s32(vreinterpretq_s32_m128i(b)); + int32x2x2_t result = vzip_s32(a1, b1); + return vreinterpretq_m128i_s32(vcombine_s32(result.val[0], result.val[1])); +#endif +} + +FORCE_INLINE __m128i _mm_unpacklo_epi64(__m128i a, __m128i b) +{ + int64x1_t a_l = vget_low_s64(vreinterpretq_s64_m128i(a)); + int64x1_t b_l = vget_low_s64(vreinterpretq_s64_m128i(b)); + return vreinterpretq_m128i_s64(vcombine_s64(a_l, b_l)); +} + +// Interleaves the lower 8 signed or unsigned 8-bit integers in a with the lower +// 8 signed or unsigned 8-bit integers in b. +// +// r0 := a0 +// r1 := b0 +// r2 := a1 +// r3 := b1 +// ... +// r14 := a7 +// r15 := b7 +// +// https://msdn.microsoft.com/en-us/library/xf7k860c%28v=vs.90%29.aspx +FORCE_INLINE __m128i _mm_unpacklo_epi8(__m128i a, __m128i b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128i_s8( + vzip1q_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +#else + int8x8_t a1 = vreinterpret_s8_s16(vget_low_s16(vreinterpretq_s16_m128i(a))); + int8x8_t b1 = vreinterpret_s8_s16(vget_low_s16(vreinterpretq_s16_m128i(b))); + int8x8x2_t result = vzip_s8(a1, b1); + return vreinterpretq_m128i_s8(vcombine_s8(result.val[0], result.val[1])); +#endif +} + +// Unpack and interleave double-precision (64-bit) floating-point elements from +// the low half of a and b, and store the results in dst. +// +// DEFINE INTERLEAVE_QWORDS(src1[127:0], src2[127:0]) { +// dst[63:0] := src1[63:0] +// dst[127:64] := src2[63:0] +// RETURN dst[127:0] +// } +// dst[127:0] := INTERLEAVE_QWORDS(a[127:0], b[127:0]) +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_unpacklo_pd +FORCE_INLINE __m128d _mm_unpacklo_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64( + vzip1q_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + return vreinterpretq_m128d_s64( + vcombine_s64(vget_low_s64(vreinterpretq_s64_m128d(a)), + vget_low_s64(vreinterpretq_s64_m128d(b)))); +#endif +} + +// Compute the bitwise XOR of packed double-precision (64-bit) floating-point +// elements in a and b, and store the results in dst. +// +// FOR j := 0 to 1 +// i := j*64 +// dst[i+63:i] := a[i+63:i] XOR b[i+63:i] +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_xor_pd +FORCE_INLINE __m128d _mm_xor_pd(__m128d a, __m128d b) +{ + return vreinterpretq_m128d_s64( + veorq_s64(vreinterpretq_s64_m128d(a), vreinterpretq_s64_m128d(b))); +} + +// Computes the bitwise XOR of the 128-bit value in a and the 128-bit value in +// b. https://msdn.microsoft.com/en-us/library/fzt08www(v=vs.100).aspx +FORCE_INLINE __m128i _mm_xor_si128(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + veorq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +/* SSE3 */ + +// Alternatively add and subtract packed double-precision (64-bit) +// floating-point elements in a to/from packed elements in b, and store the +// results in dst. +// +// FOR j := 0 to 1 +// i := j*64 +// IF ((j & 1) == 0) +// dst[i+63:i] := a[i+63:i] - b[i+63:i] +// ELSE +// dst[i+63:i] := a[i+63:i] + b[i+63:i] +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_addsub_pd +FORCE_INLINE __m128d _mm_addsub_pd(__m128d a, __m128d b) +{ + __m128d mask = _mm_set_pd(1.0f, -1.0f); +#if defined(__aarch64__) + return vreinterpretq_m128d_f64(vfmaq_f64(vreinterpretq_f64_m128d(a), + vreinterpretq_f64_m128d(b), + vreinterpretq_f64_m128d(mask))); +#else + return _mm_add_pd(_mm_mul_pd(b, mask), a); +#endif +} + +// Alternatively add and subtract packed single-precision (32-bit) +// floating-point elements in a to/from packed elements in b, and store the +// results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=addsub_ps +FORCE_INLINE __m128 _mm_addsub_ps(__m128 a, __m128 b) +{ + __m128 mask = {-1.0f, 1.0f, -1.0f, 1.0f}; +#if defined(__aarch64__) || defined(__ARM_FEATURE_FMA) /* VFPv4+ */ + return vreinterpretq_m128_f32(vfmaq_f32(vreinterpretq_f32_m128(a), + vreinterpretq_f32_m128(mask), + vreinterpretq_f32_m128(b))); +#else + return _mm_add_ps(_mm_mul_ps(b, mask), a); +#endif +} + +// Horizontally add adjacent pairs of double-precision (64-bit) floating-point +// elements in a and b, and pack the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_hadd_pd +FORCE_INLINE __m128d _mm_hadd_pd(__m128d a, __m128d b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64( + vpaddq_f64(vreinterpretq_f64_m128d(a), vreinterpretq_f64_m128d(b))); +#else + double *da = (double *) &a; + double *db = (double *) &b; + double c[] = {da[0] + da[1], db[0] + db[1]}; + return vreinterpretq_m128d_u64(vld1q_u64((uint64_t *) c)); +#endif +} + +// Computes pairwise add of each argument as single-precision, floating-point +// values a and b. +// https://msdn.microsoft.com/en-us/library/yd9wecaa.aspx +FORCE_INLINE __m128 _mm_hadd_ps(__m128 a, __m128 b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128_f32( + vpaddq_f32(vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(b))); +#else + float32x2_t a10 = vget_low_f32(vreinterpretq_f32_m128(a)); + float32x2_t a32 = vget_high_f32(vreinterpretq_f32_m128(a)); + float32x2_t b10 = vget_low_f32(vreinterpretq_f32_m128(b)); + float32x2_t b32 = vget_high_f32(vreinterpretq_f32_m128(b)); + return vreinterpretq_m128_f32( + vcombine_f32(vpadd_f32(a10, a32), vpadd_f32(b10, b32))); +#endif +} + +// Horizontally subtract adjacent pairs of double-precision (64-bit) +// floating-point elements in a and b, and pack the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_hsub_pd +FORCE_INLINE __m128d _mm_hsub_pd(__m128d _a, __m128d _b) +{ +#if defined(__aarch64__) + float64x2_t a = vreinterpretq_f64_m128d(_a); + float64x2_t b = vreinterpretq_f64_m128d(_b); + return vreinterpretq_m128d_f64( + vsubq_f64(vuzp1q_f64(a, b), vuzp2q_f64(a, b))); +#else + double *da = (double *) &_a; + double *db = (double *) &_b; + double c[] = {da[0] - da[1], db[0] - db[1]}; + return vreinterpretq_m128d_u64(vld1q_u64((uint64_t *) c)); +#endif +} + +// Horizontally substract adjacent pairs of single-precision (32-bit) +// floating-point elements in a and b, and pack the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_hsub_ps +FORCE_INLINE __m128 _mm_hsub_ps(__m128 _a, __m128 _b) +{ + float32x4_t a = vreinterpretq_f32_m128(_a); + float32x4_t b = vreinterpretq_f32_m128(_b); +#if defined(__aarch64__) + return vreinterpretq_m128_f32( + vsubq_f32(vuzp1q_f32(a, b), vuzp2q_f32(a, b))); +#else + float32x4x2_t c = vuzpq_f32(a, b); + return vreinterpretq_m128_f32(vsubq_f32(c.val[0], c.val[1])); +#endif +} + +// Load 128-bits of integer data from unaligned memory into dst. This intrinsic +// may perform better than _mm_loadu_si128 when the data crosses a cache line +// boundary. +// +// dst[127:0] := MEM[mem_addr+127:mem_addr] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_lddqu_si128 +#define _mm_lddqu_si128 _mm_loadu_si128 + +// Load a double-precision (64-bit) floating-point element from memory into both +// elements of dst. +// +// dst[63:0] := MEM[mem_addr+63:mem_addr] +// dst[127:64] := MEM[mem_addr+63:mem_addr] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_loaddup_pd +#define _mm_loaddup_pd _mm_load1_pd + +// Duplicate the low double-precision (64-bit) floating-point element from a, +// and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_movedup_pd +FORCE_INLINE __m128d _mm_movedup_pd(__m128d a) +{ +#if (__aarch64__) + return vreinterpretq_m128d_f64( + vdupq_laneq_f64(vreinterpretq_f64_m128d(a), 0)); +#else + return vreinterpretq_m128d_u64( + vdupq_n_u64(vgetq_lane_u64(vreinterpretq_u64_m128d(a), 0))); +#endif +} + +// Duplicate odd-indexed single-precision (32-bit) floating-point elements +// from a, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_movehdup_ps +FORCE_INLINE __m128 _mm_movehdup_ps(__m128 a) +{ +#if __has_builtin(__builtin_shufflevector) + return vreinterpretq_m128_f32(__builtin_shufflevector( + vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a), 1, 1, 3, 3)); +#else + float32_t a1 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 1); + float32_t a3 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 3); + float ALIGN_STRUCT(16) data[4] = {a1, a1, a3, a3}; + return vreinterpretq_m128_f32(vld1q_f32(data)); +#endif +} + +// Duplicate even-indexed single-precision (32-bit) floating-point elements +// from a, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_moveldup_ps +FORCE_INLINE __m128 _mm_moveldup_ps(__m128 a) +{ +#if __has_builtin(__builtin_shufflevector) + return vreinterpretq_m128_f32(__builtin_shufflevector( + vreinterpretq_f32_m128(a), vreinterpretq_f32_m128(a), 0, 0, 2, 2)); +#else + float32_t a0 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 0); + float32_t a2 = vgetq_lane_f32(vreinterpretq_f32_m128(a), 2); + float ALIGN_STRUCT(16) data[4] = {a0, a0, a2, a2}; + return vreinterpretq_m128_f32(vld1q_f32(data)); +#endif +} + +/* SSSE3 */ + +// Compute the absolute value of packed signed 16-bit integers in a, and store +// the unsigned results in dst. +// +// FOR j := 0 to 7 +// i := j*16 +// dst[i+15:i] := ABS(a[i+15:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_abs_epi16 +FORCE_INLINE __m128i _mm_abs_epi16(__m128i a) +{ + return vreinterpretq_m128i_s16(vabsq_s16(vreinterpretq_s16_m128i(a))); +} + +// Compute the absolute value of packed signed 32-bit integers in a, and store +// the unsigned results in dst. +// +// FOR j := 0 to 3 +// i := j*32 +// dst[i+31:i] := ABS(a[i+31:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_abs_epi32 +FORCE_INLINE __m128i _mm_abs_epi32(__m128i a) +{ + return vreinterpretq_m128i_s32(vabsq_s32(vreinterpretq_s32_m128i(a))); +} + +// Compute the absolute value of packed signed 8-bit integers in a, and store +// the unsigned results in dst. +// +// FOR j := 0 to 15 +// i := j*8 +// dst[i+7:i] := ABS(a[i+7:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_abs_epi8 +FORCE_INLINE __m128i _mm_abs_epi8(__m128i a) +{ + return vreinterpretq_m128i_s8(vabsq_s8(vreinterpretq_s8_m128i(a))); +} + +// Compute the absolute value of packed signed 16-bit integers in a, and store +// the unsigned results in dst. +// +// FOR j := 0 to 3 +// i := j*16 +// dst[i+15:i] := ABS(a[i+15:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_abs_pi16 +FORCE_INLINE __m64 _mm_abs_pi16(__m64 a) +{ + return vreinterpret_m64_s16(vabs_s16(vreinterpret_s16_m64(a))); +} + +// Compute the absolute value of packed signed 32-bit integers in a, and store +// the unsigned results in dst. +// +// FOR j := 0 to 1 +// i := j*32 +// dst[i+31:i] := ABS(a[i+31:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_abs_pi32 +FORCE_INLINE __m64 _mm_abs_pi32(__m64 a) +{ + return vreinterpret_m64_s32(vabs_s32(vreinterpret_s32_m64(a))); +} + +// Compute the absolute value of packed signed 8-bit integers in a, and store +// the unsigned results in dst. +// +// FOR j := 0 to 7 +// i := j*8 +// dst[i+7:i] := ABS(a[i+7:i]) +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_abs_pi8 +FORCE_INLINE __m64 _mm_abs_pi8(__m64 a) +{ + return vreinterpret_m64_s8(vabs_s8(vreinterpret_s8_m64(a))); +} + +// Concatenate 16-byte blocks in a and b into a 32-byte temporary result, shift +// the result right by imm8 bytes, and store the low 16 bytes in dst. +// +// tmp[255:0] := ((a[127:0] << 128)[255:0] OR b[127:0]) >> (imm8*8) +// dst[127:0] := tmp[127:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_alignr_epi8 +FORCE_INLINE __m128i _mm_alignr_epi8(__m128i a, __m128i b, int imm) +{ + if (_sse2neon_unlikely(imm & ~31)) + return _mm_setzero_si128(); + int idx; + uint8x16_t tmp[2]; + if (imm >= 16) { + idx = imm - 16; + tmp[0] = vreinterpretq_u8_m128i(a); + tmp[1] = vdupq_n_u8(0); + } else { + idx = imm; + tmp[0] = vreinterpretq_u8_m128i(b); + tmp[1] = vreinterpretq_u8_m128i(a); + } + return vreinterpretq_m128i_u8(vld1q_u8(((uint8_t const *) tmp) + idx)); +} + +// Concatenate 8-byte blocks in a and b into a 16-byte temporary result, shift +// the result right by imm8 bytes, and store the low 8 bytes in dst. +// +// tmp[127:0] := ((a[63:0] << 64)[127:0] OR b[63:0]) >> (imm8*8) +// dst[63:0] := tmp[63:0] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_alignr_pi8 +#define _mm_alignr_pi8(a, b, imm) \ + __extension__({ \ + __m64 ret; \ + if (_sse2neon_unlikely((imm) >= 16)) { \ + ret = vreinterpret_m64_s8(vdup_n_s8(0)); \ + } else { \ + uint8x8_t tmp_low, tmp_high; \ + if (imm >= 8) { \ + const int idx = imm - 8; \ + tmp_low = vreinterpret_u8_m64(a); \ + tmp_high = vdup_n_u8(0); \ + ret = vreinterpret_m64_u8(vext_u8(tmp_low, tmp_high, idx)); \ + } else { \ + const int idx = imm; \ + tmp_low = vreinterpret_u8_m64(b); \ + tmp_high = vreinterpret_u8_m64(a); \ + ret = vreinterpret_m64_u8(vext_u8(tmp_low, tmp_high, idx)); \ + } \ + } \ + ret; \ + }) + +// Computes pairwise add of each argument as a 16-bit signed or unsigned integer +// values a and b. +FORCE_INLINE __m128i _mm_hadd_epi16(__m128i _a, __m128i _b) +{ + int16x8_t a = vreinterpretq_s16_m128i(_a); + int16x8_t b = vreinterpretq_s16_m128i(_b); +#if defined(__aarch64__) + return vreinterpretq_m128i_s16(vpaddq_s16(a, b)); +#else + return vreinterpretq_m128i_s16( + vcombine_s16(vpadd_s16(vget_low_s16(a), vget_high_s16(a)), + vpadd_s16(vget_low_s16(b), vget_high_s16(b)))); +#endif +} + +// Computes pairwise add of each argument as a 32-bit signed or unsigned integer +// values a and b. +FORCE_INLINE __m128i _mm_hadd_epi32(__m128i _a, __m128i _b) +{ + int32x4_t a = vreinterpretq_s32_m128i(_a); + int32x4_t b = vreinterpretq_s32_m128i(_b); + return vreinterpretq_m128i_s32( + vcombine_s32(vpadd_s32(vget_low_s32(a), vget_high_s32(a)), + vpadd_s32(vget_low_s32(b), vget_high_s32(b)))); +} + +// Horizontally add adjacent pairs of 16-bit integers in a and b, and pack the +// signed 16-bit results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_hadd_pi16 +FORCE_INLINE __m64 _mm_hadd_pi16(__m64 a, __m64 b) +{ + return vreinterpret_m64_s16( + vpadd_s16(vreinterpret_s16_m64(a), vreinterpret_s16_m64(b))); +} + +// Horizontally add adjacent pairs of 32-bit integers in a and b, and pack the +// signed 32-bit results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_hadd_pi32 +FORCE_INLINE __m64 _mm_hadd_pi32(__m64 a, __m64 b) +{ + return vreinterpret_m64_s32( + vpadd_s32(vreinterpret_s32_m64(a), vreinterpret_s32_m64(b))); +} + +// Computes saturated pairwise sub of each argument as a 16-bit signed +// integer values a and b. +FORCE_INLINE __m128i _mm_hadds_epi16(__m128i _a, __m128i _b) +{ +#if defined(__aarch64__) + int16x8_t a = vreinterpretq_s16_m128i(_a); + int16x8_t b = vreinterpretq_s16_m128i(_b); + return vreinterpretq_s64_s16( + vqaddq_s16(vuzp1q_s16(a, b), vuzp2q_s16(a, b))); +#else + int32x4_t a = vreinterpretq_s32_m128i(_a); + int32x4_t b = vreinterpretq_s32_m128i(_b); + // Interleave using vshrn/vmovn + // [a0|a2|a4|a6|b0|b2|b4|b6] + // [a1|a3|a5|a7|b1|b3|b5|b7] + int16x8_t ab0246 = vcombine_s16(vmovn_s32(a), vmovn_s32(b)); + int16x8_t ab1357 = vcombine_s16(vshrn_n_s32(a, 16), vshrn_n_s32(b, 16)); + // Saturated add + return vreinterpretq_m128i_s16(vqaddq_s16(ab0246, ab1357)); +#endif +} + +// Horizontally add adjacent pairs of signed 16-bit integers in a and b using +// saturation, and pack the signed 16-bit results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_hadds_pi16 +FORCE_INLINE __m64 _mm_hadds_pi16(__m64 _a, __m64 _b) +{ + int16x4_t a = vreinterpret_s16_m64(_a); + int16x4_t b = vreinterpret_s16_m64(_b); +#if defined(__aarch64__) + return vreinterpret_s64_s16(vqadd_s16(vuzp1_s16(a, b), vuzp2_s16(a, b))); +#else + int16x4x2_t res = vuzp_s16(a, b); + return vreinterpret_s64_s16(vqadd_s16(res.val[0], res.val[1])); +#endif +} + +// Horizontally subtract adjacent pairs of 16-bit integers in a and b, and pack +// the signed 16-bit results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_hsub_epi16 +FORCE_INLINE __m128i _mm_hsub_epi16(__m128i _a, __m128i _b) +{ + int16x8_t a = vreinterpretq_s16_m128i(_a); + int16x8_t b = vreinterpretq_s16_m128i(_b); +#if defined(__aarch64__) + return vreinterpretq_m128i_s16( + vsubq_s16(vuzp1q_s16(a, b), vuzp2q_s16(a, b))); +#else + int16x8x2_t c = vuzpq_s16(a, b); + return vreinterpretq_m128i_s16(vsubq_s16(c.val[0], c.val[1])); +#endif +} + +// Horizontally subtract adjacent pairs of 32-bit integers in a and b, and pack +// the signed 32-bit results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_hsub_epi32 +FORCE_INLINE __m128i _mm_hsub_epi32(__m128i _a, __m128i _b) +{ + int32x4_t a = vreinterpretq_s32_m128i(_a); + int32x4_t b = vreinterpretq_s32_m128i(_b); +#if defined(__aarch64__) + return vreinterpretq_m128i_s32( + vsubq_s32(vuzp1q_s32(a, b), vuzp2q_s32(a, b))); +#else + int32x4x2_t c = vuzpq_s32(a, b); + return vreinterpretq_m128i_s32(vsubq_s32(c.val[0], c.val[1])); +#endif +} + +// Horizontally subtract adjacent pairs of 16-bit integers in a and b, and pack +// the signed 16-bit results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_hsub_pi16 +FORCE_INLINE __m64 _mm_hsub_pi16(__m64 _a, __m64 _b) +{ + int16x4_t a = vreinterpret_s16_m64(_a); + int16x4_t b = vreinterpret_s16_m64(_b); +#if defined(__aarch64__) + return vreinterpret_m64_s16(vsub_s16(vuzp1_s16(a, b), vuzp2_s16(a, b))); +#else + int16x4x2_t c = vuzp_s16(a, b); + return vreinterpret_m64_s16(vsub_s16(c.val[0], c.val[1])); +#endif +} + +// Horizontally subtract adjacent pairs of 32-bit integers in a and b, and pack +// the signed 32-bit results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=mm_hsub_pi32 +FORCE_INLINE __m64 _mm_hsub_pi32(__m64 _a, __m64 _b) +{ + int32x2_t a = vreinterpret_s32_m64(_a); + int32x2_t b = vreinterpret_s32_m64(_b); +#if defined(__aarch64__) + return vreinterpret_m64_s32(vsub_s32(vuzp1_s32(a, b), vuzp2_s32(a, b))); +#else + int32x2x2_t c = vuzp_s32(a, b); + return vreinterpret_m64_s32(vsub_s32(c.val[0], c.val[1])); +#endif +} + +// Computes saturated pairwise difference of each argument as a 16-bit signed +// integer values a and b. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_hsubs_epi16 +FORCE_INLINE __m128i _mm_hsubs_epi16(__m128i _a, __m128i _b) +{ + int16x8_t a = vreinterpretq_s16_m128i(_a); + int16x8_t b = vreinterpretq_s16_m128i(_b); +#if defined(__aarch64__) + return vreinterpretq_m128i_s16( + vqsubq_s16(vuzp1q_s16(a, b), vuzp2q_s16(a, b))); +#else + int16x8x2_t c = vuzpq_s16(a, b); + return vreinterpretq_m128i_s16(vqsubq_s16(c.val[0], c.val[1])); +#endif +} + +// Horizontally subtract adjacent pairs of signed 16-bit integers in a and b +// using saturation, and pack the signed 16-bit results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_hsubs_pi16 +FORCE_INLINE __m64 _mm_hsubs_pi16(__m64 _a, __m64 _b) +{ + int16x4_t a = vreinterpret_s16_m64(_a); + int16x4_t b = vreinterpret_s16_m64(_b); +#if defined(__aarch64__) + return vreinterpret_m64_s16(vqsub_s16(vuzp1_s16(a, b), vuzp2_s16(a, b))); +#else + int16x4x2_t c = vuzp_s16(a, b); + return vreinterpret_m64_s16(vqsub_s16(c.val[0], c.val[1])); +#endif +} + +// Vertically multiply each unsigned 8-bit integer from a with the corresponding +// signed 8-bit integer from b, producing intermediate signed 16-bit integers. +// Horizontally add adjacent pairs of intermediate signed 16-bit integers, +// and pack the saturated results in dst. +// +// FOR j := 0 to 7 +// i := j*16 +// dst[i+15:i] := Saturate_To_Int16( a[i+15:i+8]*b[i+15:i+8] + +// a[i+7:i]*b[i+7:i] ) +// ENDFOR +FORCE_INLINE __m128i _mm_maddubs_epi16(__m128i _a, __m128i _b) +{ +#if defined(__aarch64__) + uint8x16_t a = vreinterpretq_u8_m128i(_a); + int8x16_t b = vreinterpretq_s8_m128i(_b); + int16x8_t tl = vmulq_s16(vreinterpretq_s16_u16(vmovl_u8(vget_low_u8(a))), + vmovl_s8(vget_low_s8(b))); + int16x8_t th = vmulq_s16(vreinterpretq_s16_u16(vmovl_u8(vget_high_u8(a))), + vmovl_s8(vget_high_s8(b))); + return vreinterpretq_m128i_s16( + vqaddq_s16(vuzp1q_s16(tl, th), vuzp2q_s16(tl, th))); +#else + // This would be much simpler if x86 would choose to zero extend OR sign + // extend, not both. This could probably be optimized better. + uint16x8_t a = vreinterpretq_u16_m128i(_a); + int16x8_t b = vreinterpretq_s16_m128i(_b); + + // Zero extend a + int16x8_t a_odd = vreinterpretq_s16_u16(vshrq_n_u16(a, 8)); + int16x8_t a_even = vreinterpretq_s16_u16(vbicq_u16(a, vdupq_n_u16(0xff00))); + + // Sign extend by shifting left then shifting right. + int16x8_t b_even = vshrq_n_s16(vshlq_n_s16(b, 8), 8); + int16x8_t b_odd = vshrq_n_s16(b, 8); + + // multiply + int16x8_t prod1 = vmulq_s16(a_even, b_even); + int16x8_t prod2 = vmulq_s16(a_odd, b_odd); + + // saturated add + return vreinterpretq_m128i_s16(vqaddq_s16(prod1, prod2)); +#endif +} + +// Vertically multiply each unsigned 8-bit integer from a with the corresponding +// signed 8-bit integer from b, producing intermediate signed 16-bit integers. +// Horizontally add adjacent pairs of intermediate signed 16-bit integers, and +// pack the saturated results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_maddubs_pi16 +FORCE_INLINE __m64 _mm_maddubs_pi16(__m64 _a, __m64 _b) +{ + uint16x4_t a = vreinterpret_u16_m64(_a); + int16x4_t b = vreinterpret_s16_m64(_b); + + // Zero extend a + int16x4_t a_odd = vreinterpret_s16_u16(vshr_n_u16(a, 8)); + int16x4_t a_even = vreinterpret_s16_u16(vand_u16(a, vdup_n_u16(0xff))); + + // Sign extend by shifting left then shifting right. + int16x4_t b_even = vshr_n_s16(vshl_n_s16(b, 8), 8); + int16x4_t b_odd = vshr_n_s16(b, 8); + + // multiply + int16x4_t prod1 = vmul_s16(a_even, b_even); + int16x4_t prod2 = vmul_s16(a_odd, b_odd); + + // saturated add + return vreinterpret_m64_s16(vqadd_s16(prod1, prod2)); +} + +// Multiply packed signed 16-bit integers in a and b, producing intermediate +// signed 32-bit integers. Shift right by 15 bits while rounding up, and store +// the packed 16-bit integers in dst. +// +// r0 := Round(((int32_t)a0 * (int32_t)b0) >> 15) +// r1 := Round(((int32_t)a1 * (int32_t)b1) >> 15) +// r2 := Round(((int32_t)a2 * (int32_t)b2) >> 15) +// ... +// r7 := Round(((int32_t)a7 * (int32_t)b7) >> 15) +FORCE_INLINE __m128i _mm_mulhrs_epi16(__m128i a, __m128i b) +{ + // Has issues due to saturation + // return vreinterpretq_m128i_s16(vqrdmulhq_s16(a, b)); + + // Multiply + int32x4_t mul_lo = vmull_s16(vget_low_s16(vreinterpretq_s16_m128i(a)), + vget_low_s16(vreinterpretq_s16_m128i(b))); + int32x4_t mul_hi = vmull_s16(vget_high_s16(vreinterpretq_s16_m128i(a)), + vget_high_s16(vreinterpretq_s16_m128i(b))); + + // Rounding narrowing shift right + // narrow = (int16_t)((mul + 16384) >> 15); + int16x4_t narrow_lo = vrshrn_n_s32(mul_lo, 15); + int16x4_t narrow_hi = vrshrn_n_s32(mul_hi, 15); + + // Join together + return vreinterpretq_m128i_s16(vcombine_s16(narrow_lo, narrow_hi)); +} + +// Multiply packed signed 16-bit integers in a and b, producing intermediate +// signed 32-bit integers. Truncate each intermediate integer to the 18 most +// significant bits, round by adding 1, and store bits [16:1] to dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mulhrs_pi16 +FORCE_INLINE __m64 _mm_mulhrs_pi16(__m64 a, __m64 b) +{ + int32x4_t mul_extend = + vmull_s16((vreinterpret_s16_m64(a)), (vreinterpret_s16_m64(b))); + + // Rounding narrowing shift right + return vreinterpret_m64_s16(vrshrn_n_s32(mul_extend, 15)); +} + +// Shuffle packed 8-bit integers in a according to shuffle control mask in the +// corresponding 8-bit element of b, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_shuffle_epi8 +FORCE_INLINE __m128i _mm_shuffle_epi8(__m128i a, __m128i b) +{ + int8x16_t tbl = vreinterpretq_s8_m128i(a); // input a + uint8x16_t idx = vreinterpretq_u8_m128i(b); // input b + uint8x16_t idx_masked = + vandq_u8(idx, vdupq_n_u8(0x8F)); // avoid using meaningless bits +#if defined(__aarch64__) + return vreinterpretq_m128i_s8(vqtbl1q_s8(tbl, idx_masked)); +#elif defined(__GNUC__) + int8x16_t ret; + // %e and %f represent the even and odd D registers + // respectively. + __asm__ __volatile__( + "vtbl.8 %e[ret], {%e[tbl], %f[tbl]}, %e[idx]\n" + "vtbl.8 %f[ret], {%e[tbl], %f[tbl]}, %f[idx]\n" + : [ret] "=&w"(ret) + : [tbl] "w"(tbl), [idx] "w"(idx_masked)); + return vreinterpretq_m128i_s8(ret); +#else + // use this line if testing on aarch64 + int8x8x2_t a_split = {vget_low_s8(tbl), vget_high_s8(tbl)}; + return vreinterpretq_m128i_s8( + vcombine_s8(vtbl2_s8(a_split, vget_low_u8(idx_masked)), + vtbl2_s8(a_split, vget_high_u8(idx_masked)))); +#endif +} + +// Shuffle packed 8-bit integers in a according to shuffle control mask in the +// corresponding 8-bit element of b, and store the results in dst. +// +// FOR j := 0 to 7 +// i := j*8 +// IF b[i+7] == 1 +// dst[i+7:i] := 0 +// ELSE +// index[2:0] := b[i+2:i] +// dst[i+7:i] := a[index*8+7:index*8] +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_shuffle_pi8 +FORCE_INLINE __m64 _mm_shuffle_pi8(__m64 a, __m64 b) +{ + const int8x8_t controlMask = + vand_s8(vreinterpret_s8_m64(b), vdup_n_s8((int8_t)(0x1 << 7 | 0x07))); + int8x8_t res = vtbl1_s8(vreinterpret_s8_m64(a), controlMask); + return vreinterpret_m64_s8(res); +} + +// Negate packed 16-bit integers in a when the corresponding signed +// 16-bit integer in b is negative, and store the results in dst. +// Element in dst are zeroed out when the corresponding element +// in b is zero. +// +// for i in 0..7 +// if b[i] < 0 +// r[i] := -a[i] +// else if b[i] == 0 +// r[i] := 0 +// else +// r[i] := a[i] +// fi +// done +FORCE_INLINE __m128i _mm_sign_epi16(__m128i _a, __m128i _b) +{ + int16x8_t a = vreinterpretq_s16_m128i(_a); + int16x8_t b = vreinterpretq_s16_m128i(_b); + + // signed shift right: faster than vclt + // (b < 0) ? 0xFFFF : 0 + uint16x8_t ltMask = vreinterpretq_u16_s16(vshrq_n_s16(b, 15)); + // (b == 0) ? 0xFFFF : 0 +#if defined(__aarch64__) + int16x8_t zeroMask = vreinterpretq_s16_u16(vceqzq_s16(b)); +#else + int16x8_t zeroMask = vreinterpretq_s16_u16(vceqq_s16(b, vdupq_n_s16(0))); +#endif + + // bitwise select either a or negative 'a' (vnegq_s16(a) equals to negative + // 'a') based on ltMask + int16x8_t masked = vbslq_s16(ltMask, vnegq_s16(a), a); + // res = masked & (~zeroMask) + int16x8_t res = vbicq_s16(masked, zeroMask); + return vreinterpretq_m128i_s16(res); +} + +// Negate packed 32-bit integers in a when the corresponding signed +// 32-bit integer in b is negative, and store the results in dst. +// Element in dst are zeroed out when the corresponding element +// in b is zero. +// +// for i in 0..3 +// if b[i] < 0 +// r[i] := -a[i] +// else if b[i] == 0 +// r[i] := 0 +// else +// r[i] := a[i] +// fi +// done +FORCE_INLINE __m128i _mm_sign_epi32(__m128i _a, __m128i _b) +{ + int32x4_t a = vreinterpretq_s32_m128i(_a); + int32x4_t b = vreinterpretq_s32_m128i(_b); + + // signed shift right: faster than vclt + // (b < 0) ? 0xFFFFFFFF : 0 + uint32x4_t ltMask = vreinterpretq_u32_s32(vshrq_n_s32(b, 31)); + + // (b == 0) ? 0xFFFFFFFF : 0 +#if defined(__aarch64__) + int32x4_t zeroMask = vreinterpretq_s32_u32(vceqzq_s32(b)); +#else + int32x4_t zeroMask = vreinterpretq_s32_u32(vceqq_s32(b, vdupq_n_s32(0))); +#endif + + // bitwise select either a or negative 'a' (vnegq_s32(a) equals to negative + // 'a') based on ltMask + int32x4_t masked = vbslq_s32(ltMask, vnegq_s32(a), a); + // res = masked & (~zeroMask) + int32x4_t res = vbicq_s32(masked, zeroMask); + return vreinterpretq_m128i_s32(res); +} + +// Negate packed 8-bit integers in a when the corresponding signed +// 8-bit integer in b is negative, and store the results in dst. +// Element in dst are zeroed out when the corresponding element +// in b is zero. +// +// for i in 0..15 +// if b[i] < 0 +// r[i] := -a[i] +// else if b[i] == 0 +// r[i] := 0 +// else +// r[i] := a[i] +// fi +// done +FORCE_INLINE __m128i _mm_sign_epi8(__m128i _a, __m128i _b) +{ + int8x16_t a = vreinterpretq_s8_m128i(_a); + int8x16_t b = vreinterpretq_s8_m128i(_b); + + // signed shift right: faster than vclt + // (b < 0) ? 0xFF : 0 + uint8x16_t ltMask = vreinterpretq_u8_s8(vshrq_n_s8(b, 7)); + + // (b == 0) ? 0xFF : 0 +#if defined(__aarch64__) + int8x16_t zeroMask = vreinterpretq_s8_u8(vceqzq_s8(b)); +#else + int8x16_t zeroMask = vreinterpretq_s8_u8(vceqq_s8(b, vdupq_n_s8(0))); +#endif + + // bitwise select either a or nagative 'a' (vnegq_s8(a) return nagative 'a') + // based on ltMask + int8x16_t masked = vbslq_s8(ltMask, vnegq_s8(a), a); + // res = masked & (~zeroMask) + int8x16_t res = vbicq_s8(masked, zeroMask); + + return vreinterpretq_m128i_s8(res); +} + +// Negate packed 16-bit integers in a when the corresponding signed 16-bit +// integer in b is negative, and store the results in dst. Element in dst are +// zeroed out when the corresponding element in b is zero. +// +// FOR j := 0 to 3 +// i := j*16 +// IF b[i+15:i] < 0 +// dst[i+15:i] := -(a[i+15:i]) +// ELSE IF b[i+15:i] == 0 +// dst[i+15:i] := 0 +// ELSE +// dst[i+15:i] := a[i+15:i] +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sign_pi16 +FORCE_INLINE __m64 _mm_sign_pi16(__m64 _a, __m64 _b) +{ + int16x4_t a = vreinterpret_s16_m64(_a); + int16x4_t b = vreinterpret_s16_m64(_b); + + // signed shift right: faster than vclt + // (b < 0) ? 0xFFFF : 0 + uint16x4_t ltMask = vreinterpret_u16_s16(vshr_n_s16(b, 15)); + + // (b == 0) ? 0xFFFF : 0 +#if defined(__aarch64__) + int16x4_t zeroMask = vreinterpret_s16_u16(vceqz_s16(b)); +#else + int16x4_t zeroMask = vreinterpret_s16_u16(vceq_s16(b, vdup_n_s16(0))); +#endif + + // bitwise select either a or nagative 'a' (vneg_s16(a) return nagative 'a') + // based on ltMask + int16x4_t masked = vbsl_s16(ltMask, vneg_s16(a), a); + // res = masked & (~zeroMask) + int16x4_t res = vbic_s16(masked, zeroMask); + + return vreinterpret_m64_s16(res); +} + +// Negate packed 32-bit integers in a when the corresponding signed 32-bit +// integer in b is negative, and store the results in dst. Element in dst are +// zeroed out when the corresponding element in b is zero. +// +// FOR j := 0 to 1 +// i := j*32 +// IF b[i+31:i] < 0 +// dst[i+31:i] := -(a[i+31:i]) +// ELSE IF b[i+31:i] == 0 +// dst[i+31:i] := 0 +// ELSE +// dst[i+31:i] := a[i+31:i] +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sign_pi32 +FORCE_INLINE __m64 _mm_sign_pi32(__m64 _a, __m64 _b) +{ + int32x2_t a = vreinterpret_s32_m64(_a); + int32x2_t b = vreinterpret_s32_m64(_b); + + // signed shift right: faster than vclt + // (b < 0) ? 0xFFFFFFFF : 0 + uint32x2_t ltMask = vreinterpret_u32_s32(vshr_n_s32(b, 31)); + + // (b == 0) ? 0xFFFFFFFF : 0 +#if defined(__aarch64__) + int32x2_t zeroMask = vreinterpret_s32_u32(vceqz_s32(b)); +#else + int32x2_t zeroMask = vreinterpret_s32_u32(vceq_s32(b, vdup_n_s32(0))); +#endif + + // bitwise select either a or nagative 'a' (vneg_s32(a) return nagative 'a') + // based on ltMask + int32x2_t masked = vbsl_s32(ltMask, vneg_s32(a), a); + // res = masked & (~zeroMask) + int32x2_t res = vbic_s32(masked, zeroMask); + + return vreinterpret_m64_s32(res); +} + +// Negate packed 8-bit integers in a when the corresponding signed 8-bit integer +// in b is negative, and store the results in dst. Element in dst are zeroed out +// when the corresponding element in b is zero. +// +// FOR j := 0 to 7 +// i := j*8 +// IF b[i+7:i] < 0 +// dst[i+7:i] := -(a[i+7:i]) +// ELSE IF b[i+7:i] == 0 +// dst[i+7:i] := 0 +// ELSE +// dst[i+7:i] := a[i+7:i] +// FI +// ENDFOR +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_sign_pi8 +FORCE_INLINE __m64 _mm_sign_pi8(__m64 _a, __m64 _b) +{ + int8x8_t a = vreinterpret_s8_m64(_a); + int8x8_t b = vreinterpret_s8_m64(_b); + + // signed shift right: faster than vclt + // (b < 0) ? 0xFF : 0 + uint8x8_t ltMask = vreinterpret_u8_s8(vshr_n_s8(b, 7)); + + // (b == 0) ? 0xFF : 0 +#if defined(__aarch64__) + int8x8_t zeroMask = vreinterpret_s8_u8(vceqz_s8(b)); +#else + int8x8_t zeroMask = vreinterpret_s8_u8(vceq_s8(b, vdup_n_s8(0))); +#endif + + // bitwise select either a or nagative 'a' (vneg_s8(a) return nagative 'a') + // based on ltMask + int8x8_t masked = vbsl_s8(ltMask, vneg_s8(a), a); + // res = masked & (~zeroMask) + int8x8_t res = vbic_s8(masked, zeroMask); + + return vreinterpret_m64_s8(res); +} + +/* SSE4.1 */ + +// Blend packed 16-bit integers from a and b using control mask imm8, and store +// the results in dst. +// +// FOR j := 0 to 7 +// i := j*16 +// IF imm8[j] +// dst[i+15:i] := b[i+15:i] +// ELSE +// dst[i+15:i] := a[i+15:i] +// FI +// ENDFOR +// FORCE_INLINE __m128i _mm_blend_epi16(__m128i a, __m128i b, +// __constrange(0,255) int imm) +#define _mm_blend_epi16(a, b, imm) \ + __extension__({ \ + const uint16_t _mask[8] = {((imm) & (1 << 0)) ? (uint16_t) -1 : 0x0, \ + ((imm) & (1 << 1)) ? (uint16_t) -1 : 0x0, \ + ((imm) & (1 << 2)) ? (uint16_t) -1 : 0x0, \ + ((imm) & (1 << 3)) ? (uint16_t) -1 : 0x0, \ + ((imm) & (1 << 4)) ? (uint16_t) -1 : 0x0, \ + ((imm) & (1 << 5)) ? (uint16_t) -1 : 0x0, \ + ((imm) & (1 << 6)) ? (uint16_t) -1 : 0x0, \ + ((imm) & (1 << 7)) ? (uint16_t) -1 : 0x0}; \ + uint16x8_t _mask_vec = vld1q_u16(_mask); \ + uint16x8_t _a = vreinterpretq_u16_m128i(a); \ + uint16x8_t _b = vreinterpretq_u16_m128i(b); \ + vreinterpretq_m128i_u16(vbslq_u16(_mask_vec, _b, _a)); \ + }) + +// Blend packed double-precision (64-bit) floating-point elements from a and b +// using control mask imm8, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_blend_pd +#define _mm_blend_pd(a, b, imm) \ + __extension__({ \ + const uint64_t _mask[2] = { \ + ((imm) & (1 << 0)) ? ~UINT64_C(0) : UINT64_C(0), \ + ((imm) & (1 << 1)) ? ~UINT64_C(0) : UINT64_C(0)}; \ + uint64x2_t _mask_vec = vld1q_u64(_mask); \ + uint64x2_t _a = vreinterpretq_u64_m128d(a); \ + uint64x2_t _b = vreinterpretq_u64_m128d(b); \ + vreinterpretq_m128d_u64(vbslq_u64(_mask_vec, _b, _a)); \ + }) + +// Blend packed single-precision (32-bit) floating-point elements from a and b +// using mask, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_blend_ps +FORCE_INLINE __m128 _mm_blend_ps(__m128 _a, __m128 _b, const char imm8) +{ + const uint32_t ALIGN_STRUCT(16) + data[4] = {((imm8) & (1 << 0)) ? UINT32_MAX : 0, + ((imm8) & (1 << 1)) ? UINT32_MAX : 0, + ((imm8) & (1 << 2)) ? UINT32_MAX : 0, + ((imm8) & (1 << 3)) ? UINT32_MAX : 0}; + uint32x4_t mask = vld1q_u32(data); + float32x4_t a = vreinterpretq_f32_m128(_a); + float32x4_t b = vreinterpretq_f32_m128(_b); + return vreinterpretq_m128_f32(vbslq_f32(mask, b, a)); +} + +// Blend packed 8-bit integers from a and b using mask, and store the results in +// dst. +// +// FOR j := 0 to 15 +// i := j*8 +// IF mask[i+7] +// dst[i+7:i] := b[i+7:i] +// ELSE +// dst[i+7:i] := a[i+7:i] +// FI +// ENDFOR +FORCE_INLINE __m128i _mm_blendv_epi8(__m128i _a, __m128i _b, __m128i _mask) +{ + // Use a signed shift right to create a mask with the sign bit + uint8x16_t mask = + vreinterpretq_u8_s8(vshrq_n_s8(vreinterpretq_s8_m128i(_mask), 7)); + uint8x16_t a = vreinterpretq_u8_m128i(_a); + uint8x16_t b = vreinterpretq_u8_m128i(_b); + return vreinterpretq_m128i_u8(vbslq_u8(mask, b, a)); +} + +// Blend packed double-precision (64-bit) floating-point elements from a and b +// using mask, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_blendv_pd +FORCE_INLINE __m128d _mm_blendv_pd(__m128d _a, __m128d _b, __m128d _mask) +{ + uint64x2_t mask = + vreinterpretq_u64_s64(vshrq_n_s64(vreinterpretq_s64_m128d(_mask), 63)); +#if defined(__aarch64__) + float64x2_t a = vreinterpretq_f64_m128d(_a); + float64x2_t b = vreinterpretq_f64_m128d(_b); + return vreinterpretq_m128d_f64(vbslq_f64(mask, b, a)); +#else + uint64x2_t a = vreinterpretq_u64_m128d(_a); + uint64x2_t b = vreinterpretq_u64_m128d(_b); + return vreinterpretq_m128d_u64(vbslq_u64(mask, b, a)); +#endif +} + +// Blend packed single-precision (32-bit) floating-point elements from a and b +// using mask, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_blendv_ps +FORCE_INLINE __m128 _mm_blendv_ps(__m128 _a, __m128 _b, __m128 _mask) +{ + // Use a signed shift right to create a mask with the sign bit + uint32x4_t mask = + vreinterpretq_u32_s32(vshrq_n_s32(vreinterpretq_s32_m128(_mask), 31)); + float32x4_t a = vreinterpretq_f32_m128(_a); + float32x4_t b = vreinterpretq_f32_m128(_b); + return vreinterpretq_m128_f32(vbslq_f32(mask, b, a)); +} + +// Round the packed double-precision (64-bit) floating-point elements in a up +// to an integer value, and store the results as packed double-precision +// floating-point elements in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_ceil_pd +FORCE_INLINE __m128d _mm_ceil_pd(__m128d a) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64(vrndpq_f64(vreinterpretq_f64_m128d(a))); +#else + double *f = (double *) &a; + return _mm_set_pd(ceil(f[1]), ceil(f[0])); +#endif +} + +// Round the packed single-precision (32-bit) floating-point elements in a up to +// an integer value, and store the results as packed single-precision +// floating-point elements in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_ceil_ps +FORCE_INLINE __m128 _mm_ceil_ps(__m128 a) +{ +#if defined(__aarch64__) + return vreinterpretq_m128_f32(vrndpq_f32(vreinterpretq_f32_m128(a))); +#else + float *f = (float *) &a; + return _mm_set_ps(ceilf(f[3]), ceilf(f[2]), ceilf(f[1]), ceilf(f[0])); +#endif +} + +// Round the lower double-precision (64-bit) floating-point element in b up to +// an integer value, store the result as a double-precision floating-point +// element in the lower element of dst, and copy the upper element from a to the +// upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_ceil_sd +FORCE_INLINE __m128d _mm_ceil_sd(__m128d a, __m128d b) +{ + return _mm_move_sd(a, _mm_ceil_pd(b)); +} + +// Round the lower single-precision (32-bit) floating-point element in b up to +// an integer value, store the result as a single-precision floating-point +// element in the lower element of dst, and copy the upper 3 packed elements +// from a to the upper elements of dst. +// +// dst[31:0] := CEIL(b[31:0]) +// dst[127:32] := a[127:32] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_ceil_ss +FORCE_INLINE __m128 _mm_ceil_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_ceil_ps(b)); +} + +// Compare packed 64-bit integers in a and b for equality, and store the results +// in dst +FORCE_INLINE __m128i _mm_cmpeq_epi64(__m128i a, __m128i b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128i_u64( + vceqq_u64(vreinterpretq_u64_m128i(a), vreinterpretq_u64_m128i(b))); +#else + // ARMv7 lacks vceqq_u64 + // (a == b) -> (a_lo == b_lo) && (a_hi == b_hi) + uint32x4_t cmp = + vceqq_u32(vreinterpretq_u32_m128i(a), vreinterpretq_u32_m128i(b)); + uint32x4_t swapped = vrev64q_u32(cmp); + return vreinterpretq_m128i_u32(vandq_u32(cmp, swapped)); +#endif +} + +// Converts the four signed 16-bit integers in the lower 64 bits to four signed +// 32-bit integers. +FORCE_INLINE __m128i _mm_cvtepi16_epi32(__m128i a) +{ + return vreinterpretq_m128i_s32( + vmovl_s16(vget_low_s16(vreinterpretq_s16_m128i(a)))); +} + +// Converts the two signed 16-bit integers in the lower 32 bits two signed +// 32-bit integers. +FORCE_INLINE __m128i _mm_cvtepi16_epi64(__m128i a) +{ + int16x8_t s16x8 = vreinterpretq_s16_m128i(a); /* xxxx xxxx xxxx 0B0A */ + int32x4_t s32x4 = vmovl_s16(vget_low_s16(s16x8)); /* 000x 000x 000B 000A */ + int64x2_t s64x2 = vmovl_s32(vget_low_s32(s32x4)); /* 0000 000B 0000 000A */ + return vreinterpretq_m128i_s64(s64x2); +} + +// Converts the two signed 32-bit integers in the lower 64 bits to two signed +// 64-bit integers. +FORCE_INLINE __m128i _mm_cvtepi32_epi64(__m128i a) +{ + return vreinterpretq_m128i_s64( + vmovl_s32(vget_low_s32(vreinterpretq_s32_m128i(a)))); +} + +// Converts the four unsigned 8-bit integers in the lower 16 bits to four +// unsigned 32-bit integers. +FORCE_INLINE __m128i _mm_cvtepi8_epi16(__m128i a) +{ + int8x16_t s8x16 = vreinterpretq_s8_m128i(a); /* xxxx xxxx xxxx DCBA */ + int16x8_t s16x8 = vmovl_s8(vget_low_s8(s8x16)); /* 0x0x 0x0x 0D0C 0B0A */ + return vreinterpretq_m128i_s16(s16x8); +} + +// Converts the four unsigned 8-bit integers in the lower 32 bits to four +// unsigned 32-bit integers. +FORCE_INLINE __m128i _mm_cvtepi8_epi32(__m128i a) +{ + int8x16_t s8x16 = vreinterpretq_s8_m128i(a); /* xxxx xxxx xxxx DCBA */ + int16x8_t s16x8 = vmovl_s8(vget_low_s8(s8x16)); /* 0x0x 0x0x 0D0C 0B0A */ + int32x4_t s32x4 = vmovl_s16(vget_low_s16(s16x8)); /* 000D 000C 000B 000A */ + return vreinterpretq_m128i_s32(s32x4); +} + +// Converts the two signed 8-bit integers in the lower 32 bits to four +// signed 64-bit integers. +FORCE_INLINE __m128i _mm_cvtepi8_epi64(__m128i a) +{ + int8x16_t s8x16 = vreinterpretq_s8_m128i(a); /* xxxx xxxx xxxx xxBA */ + int16x8_t s16x8 = vmovl_s8(vget_low_s8(s8x16)); /* 0x0x 0x0x 0x0x 0B0A */ + int32x4_t s32x4 = vmovl_s16(vget_low_s16(s16x8)); /* 000x 000x 000B 000A */ + int64x2_t s64x2 = vmovl_s32(vget_low_s32(s32x4)); /* 0000 000B 0000 000A */ + return vreinterpretq_m128i_s64(s64x2); +} + +// Converts the four unsigned 16-bit integers in the lower 64 bits to four +// unsigned 32-bit integers. +FORCE_INLINE __m128i _mm_cvtepu16_epi32(__m128i a) +{ + return vreinterpretq_m128i_u32( + vmovl_u16(vget_low_u16(vreinterpretq_u16_m128i(a)))); +} + +// Converts the two unsigned 16-bit integers in the lower 32 bits to two +// unsigned 64-bit integers. +FORCE_INLINE __m128i _mm_cvtepu16_epi64(__m128i a) +{ + uint16x8_t u16x8 = vreinterpretq_u16_m128i(a); /* xxxx xxxx xxxx 0B0A */ + uint32x4_t u32x4 = vmovl_u16(vget_low_u16(u16x8)); /* 000x 000x 000B 000A */ + uint64x2_t u64x2 = vmovl_u32(vget_low_u32(u32x4)); /* 0000 000B 0000 000A */ + return vreinterpretq_m128i_u64(u64x2); +} + +// Converts the two unsigned 32-bit integers in the lower 64 bits to two +// unsigned 64-bit integers. +FORCE_INLINE __m128i _mm_cvtepu32_epi64(__m128i a) +{ + return vreinterpretq_m128i_u64( + vmovl_u32(vget_low_u32(vreinterpretq_u32_m128i(a)))); +} + +// Zero extend packed unsigned 8-bit integers in a to packed 16-bit integers, +// and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cvtepu8_epi16 +FORCE_INLINE __m128i _mm_cvtepu8_epi16(__m128i a) +{ + uint8x16_t u8x16 = vreinterpretq_u8_m128i(a); /* xxxx xxxx HGFE DCBA */ + uint16x8_t u16x8 = vmovl_u8(vget_low_u8(u8x16)); /* 0H0G 0F0E 0D0C 0B0A */ + return vreinterpretq_m128i_u16(u16x8); +} + +// Converts the four unsigned 8-bit integers in the lower 32 bits to four +// unsigned 32-bit integers. +// https://msdn.microsoft.com/en-us/library/bb531467%28v=vs.100%29.aspx +FORCE_INLINE __m128i _mm_cvtepu8_epi32(__m128i a) +{ + uint8x16_t u8x16 = vreinterpretq_u8_m128i(a); /* xxxx xxxx xxxx DCBA */ + uint16x8_t u16x8 = vmovl_u8(vget_low_u8(u8x16)); /* 0x0x 0x0x 0D0C 0B0A */ + uint32x4_t u32x4 = vmovl_u16(vget_low_u16(u16x8)); /* 000D 000C 000B 000A */ + return vreinterpretq_m128i_u32(u32x4); +} + +// Converts the two unsigned 8-bit integers in the lower 16 bits to two +// unsigned 64-bit integers. +FORCE_INLINE __m128i _mm_cvtepu8_epi64(__m128i a) +{ + uint8x16_t u8x16 = vreinterpretq_u8_m128i(a); /* xxxx xxxx xxxx xxBA */ + uint16x8_t u16x8 = vmovl_u8(vget_low_u8(u8x16)); /* 0x0x 0x0x 0x0x 0B0A */ + uint32x4_t u32x4 = vmovl_u16(vget_low_u16(u16x8)); /* 000x 000x 000B 000A */ + uint64x2_t u64x2 = vmovl_u32(vget_low_u32(u32x4)); /* 0000 000B 0000 000A */ + return vreinterpretq_m128i_u64(u64x2); +} + +// Conditionally multiply the packed double-precision (64-bit) floating-point +// elements in a and b using the high 4 bits in imm8, sum the four products, and +// conditionally store the sum in dst using the low 4 bits of imm8. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_dp_pd +FORCE_INLINE __m128d _mm_dp_pd(__m128d a, __m128d b, const int imm) +{ + // Generate mask value from constant immediate bit value + const int64_t bit0Mask = imm & 0x01 ? UINT64_MAX : 0; + const int64_t bit1Mask = imm & 0x02 ? UINT64_MAX : 0; +#if !SSE2NEON_PRECISE_DP + const int64_t bit4Mask = imm & 0x10 ? UINT64_MAX : 0; + const int64_t bit5Mask = imm & 0x20 ? UINT64_MAX : 0; +#endif + // Conditional multiplication +#if !SSE2NEON_PRECISE_DP + __m128d mul = _mm_mul_pd(a, b); + const __m128d mulMask = + _mm_castsi128_pd(_mm_set_epi64x(bit5Mask, bit4Mask)); + __m128d tmp = _mm_and_pd(mul, mulMask); +#else +#if defined(__aarch64__) + double d0 = (imm & 0x10) ? vgetq_lane_f64(vreinterpretq_f64_m128d(a), 0) * + vgetq_lane_f64(vreinterpretq_f64_m128d(b), 0) + : 0; + double d1 = (imm & 0x20) ? vgetq_lane_f64(vreinterpretq_f64_m128d(a), 1) * + vgetq_lane_f64(vreinterpretq_f64_m128d(b), 1) + : 0; +#else + double d0 = (imm & 0x10) ? ((double *) &a)[0] * ((double *) &b)[0] : 0; + double d1 = (imm & 0x20) ? ((double *) &a)[1] * ((double *) &b)[1] : 0; +#endif + __m128d tmp = _mm_set_pd(d1, d0); +#endif + // Sum the products +#if defined(__aarch64__) + double sum = vpaddd_f64(vreinterpretq_f64_m128d(tmp)); +#else + double sum = *((double *) &tmp) + *(((double *) &tmp) + 1); +#endif + // Conditionally store the sum + const __m128d sumMask = + _mm_castsi128_pd(_mm_set_epi64x(bit1Mask, bit0Mask)); + __m128d res = _mm_and_pd(_mm_set_pd1(sum), sumMask); + return res; +} + +// Conditionally multiply the packed single-precision (32-bit) floating-point +// elements in a and b using the high 4 bits in imm8, sum the four products, +// and conditionally store the sum in dst using the low 4 bits of imm. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_dp_ps +FORCE_INLINE __m128 _mm_dp_ps(__m128 a, __m128 b, const int imm) +{ +#if defined(__aarch64__) + /* shortcuts */ + if (imm == 0xFF) { + return _mm_set1_ps(vaddvq_f32(_mm_mul_ps(a, b))); + } + if (imm == 0x7F) { + float32x4_t m = _mm_mul_ps(a, b); + m[3] = 0; + return _mm_set1_ps(vaddvq_f32(m)); + } +#endif + + float s = 0, c = 0; + float32x4_t f32a = vreinterpretq_f32_m128(a); + float32x4_t f32b = vreinterpretq_f32_m128(b); + + /* To improve the accuracy of floating-point summation, Kahan algorithm + * is used for each operation. + */ + if (imm & (1 << 4)) + _sse2neon_kadd_f32(&s, &c, f32a[0] * f32b[0]); + if (imm & (1 << 5)) + _sse2neon_kadd_f32(&s, &c, f32a[1] * f32b[1]); + if (imm & (1 << 6)) + _sse2neon_kadd_f32(&s, &c, f32a[2] * f32b[2]); + if (imm & (1 << 7)) + _sse2neon_kadd_f32(&s, &c, f32a[3] * f32b[3]); + s += c; + + float32x4_t res = { + (imm & 0x1) ? s : 0, + (imm & 0x2) ? s : 0, + (imm & 0x4) ? s : 0, + (imm & 0x8) ? s : 0, + }; + return vreinterpretq_m128_f32(res); +} + +// Extracts the selected signed or unsigned 32-bit integer from a and zero +// extends. +// FORCE_INLINE int _mm_extract_epi32(__m128i a, __constrange(0,4) int imm) +#define _mm_extract_epi32(a, imm) \ + vgetq_lane_s32(vreinterpretq_s32_m128i(a), (imm)) + +// Extracts the selected signed or unsigned 64-bit integer from a and zero +// extends. +// FORCE_INLINE __int64 _mm_extract_epi64(__m128i a, __constrange(0,2) int imm) +#define _mm_extract_epi64(a, imm) \ + vgetq_lane_s64(vreinterpretq_s64_m128i(a), (imm)) + +// Extracts the selected signed or unsigned 8-bit integer from a and zero +// extends. +// FORCE_INLINE int _mm_extract_epi8(__m128i a, __constrange(0,16) int imm) +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_extract_epi8 +#define _mm_extract_epi8(a, imm) vgetq_lane_u8(vreinterpretq_u8_m128i(a), (imm)) + +// Extracts the selected single-precision (32-bit) floating-point from a. +// FORCE_INLINE int _mm_extract_ps(__m128 a, __constrange(0,4) int imm) +#define _mm_extract_ps(a, imm) vgetq_lane_s32(vreinterpretq_s32_m128(a), (imm)) + +// Round the packed double-precision (64-bit) floating-point elements in a down +// to an integer value, and store the results as packed double-precision +// floating-point elements in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_floor_pd +FORCE_INLINE __m128d _mm_floor_pd(__m128d a) +{ +#if defined(__aarch64__) + return vreinterpretq_m128d_f64(vrndmq_f64(vreinterpretq_f64_m128d(a))); +#else + double *f = (double *) &a; + return _mm_set_pd(floor(f[1]), floor(f[0])); +#endif +} + +// Round the packed single-precision (32-bit) floating-point elements in a down +// to an integer value, and store the results as packed single-precision +// floating-point elements in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_floor_ps +FORCE_INLINE __m128 _mm_floor_ps(__m128 a) +{ +#if defined(__aarch64__) + return vreinterpretq_m128_f32(vrndmq_f32(vreinterpretq_f32_m128(a))); +#else + float *f = (float *) &a; + return _mm_set_ps(floorf(f[3]), floorf(f[2]), floorf(f[1]), floorf(f[0])); +#endif +} + +// Round the lower double-precision (64-bit) floating-point element in b down to +// an integer value, store the result as a double-precision floating-point +// element in the lower element of dst, and copy the upper element from a to the +// upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_floor_sd +FORCE_INLINE __m128d _mm_floor_sd(__m128d a, __m128d b) +{ + return _mm_move_sd(a, _mm_floor_pd(b)); +} + +// Round the lower single-precision (32-bit) floating-point element in b down to +// an integer value, store the result as a single-precision floating-point +// element in the lower element of dst, and copy the upper 3 packed elements +// from a to the upper elements of dst. +// +// dst[31:0] := FLOOR(b[31:0]) +// dst[127:32] := a[127:32] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_floor_ss +FORCE_INLINE __m128 _mm_floor_ss(__m128 a, __m128 b) +{ + return _mm_move_ss(a, _mm_floor_ps(b)); +} + +// Inserts the least significant 32 bits of b into the selected 32-bit integer +// of a. +// FORCE_INLINE __m128i _mm_insert_epi32(__m128i a, int b, +// __constrange(0,4) int imm) +#define _mm_insert_epi32(a, b, imm) \ + __extension__({ \ + vreinterpretq_m128i_s32( \ + vsetq_lane_s32((b), vreinterpretq_s32_m128i(a), (imm))); \ + }) + +// Inserts the least significant 64 bits of b into the selected 64-bit integer +// of a. +// FORCE_INLINE __m128i _mm_insert_epi64(__m128i a, __int64 b, +// __constrange(0,2) int imm) +#define _mm_insert_epi64(a, b, imm) \ + __extension__({ \ + vreinterpretq_m128i_s64( \ + vsetq_lane_s64((b), vreinterpretq_s64_m128i(a), (imm))); \ + }) + +// Inserts the least significant 8 bits of b into the selected 8-bit integer +// of a. +// FORCE_INLINE __m128i _mm_insert_epi8(__m128i a, int b, +// __constrange(0,16) int imm) +#define _mm_insert_epi8(a, b, imm) \ + __extension__({ \ + vreinterpretq_m128i_s8( \ + vsetq_lane_s8((b), vreinterpretq_s8_m128i(a), (imm))); \ + }) + +// Copy a to tmp, then insert a single-precision (32-bit) floating-point +// element from b into tmp using the control in imm8. Store tmp to dst using +// the mask in imm8 (elements are zeroed out when the corresponding bit is set). +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=insert_ps +#define _mm_insert_ps(a, b, imm8) \ + __extension__({ \ + float32x4_t tmp1 = \ + vsetq_lane_f32(vgetq_lane_f32(b, (imm8 >> 6) & 0x3), \ + vreinterpretq_f32_m128(a), 0); \ + float32x4_t tmp2 = \ + vsetq_lane_f32(vgetq_lane_f32(tmp1, 0), vreinterpretq_f32_m128(a), \ + ((imm8 >> 4) & 0x3)); \ + const uint32_t data[4] = {((imm8) & (1 << 0)) ? UINT32_MAX : 0, \ + ((imm8) & (1 << 1)) ? UINT32_MAX : 0, \ + ((imm8) & (1 << 2)) ? UINT32_MAX : 0, \ + ((imm8) & (1 << 3)) ? UINT32_MAX : 0}; \ + uint32x4_t mask = vld1q_u32(data); \ + float32x4_t all_zeros = vdupq_n_f32(0); \ + \ + vreinterpretq_m128_f32( \ + vbslq_f32(mask, all_zeros, vreinterpretq_f32_m128(tmp2))); \ + }) + +// epi versions of min/max +// Computes the pariwise maximums of the four signed 32-bit integer values of a +// and b. +// +// A 128-bit parameter that can be defined with the following equations: +// r0 := (a0 > b0) ? a0 : b0 +// r1 := (a1 > b1) ? a1 : b1 +// r2 := (a2 > b2) ? a2 : b2 +// r3 := (a3 > b3) ? a3 : b3 +// +// https://msdn.microsoft.com/en-us/library/vstudio/bb514055(v=vs.100).aspx +FORCE_INLINE __m128i _mm_max_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vmaxq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Compare packed signed 8-bit integers in a and b, and store packed maximum +// values in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_max_epi8 +FORCE_INLINE __m128i _mm_max_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s8( + vmaxq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Compare packed unsigned 16-bit integers in a and b, and store packed maximum +// values in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_max_epu16 +FORCE_INLINE __m128i _mm_max_epu16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vmaxq_u16(vreinterpretq_u16_m128i(a), vreinterpretq_u16_m128i(b))); +} + +// Compare packed unsigned 32-bit integers in a and b, and store packed maximum +// values in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_max_epu32 +FORCE_INLINE __m128i _mm_max_epu32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u32( + vmaxq_u32(vreinterpretq_u32_m128i(a), vreinterpretq_u32_m128i(b))); +} + +// Computes the pariwise minima of the four signed 32-bit integer values of a +// and b. +// +// A 128-bit parameter that can be defined with the following equations: +// r0 := (a0 < b0) ? a0 : b0 +// r1 := (a1 < b1) ? a1 : b1 +// r2 := (a2 < b2) ? a2 : b2 +// r3 := (a3 < b3) ? a3 : b3 +// +// https://msdn.microsoft.com/en-us/library/vstudio/bb531476(v=vs.100).aspx +FORCE_INLINE __m128i _mm_min_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vminq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Compare packed signed 8-bit integers in a and b, and store packed minimum +// values in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_min_epi8 +FORCE_INLINE __m128i _mm_min_epi8(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s8( + vminq_s8(vreinterpretq_s8_m128i(a), vreinterpretq_s8_m128i(b))); +} + +// Compare packed unsigned 16-bit integers in a and b, and store packed minimum +// values in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_min_epu16 +FORCE_INLINE __m128i _mm_min_epu16(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vminq_u16(vreinterpretq_u16_m128i(a), vreinterpretq_u16_m128i(b))); +} + +// Compare packed unsigned 32-bit integers in a and b, and store packed minimum +// values in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_max_epu32 +FORCE_INLINE __m128i _mm_min_epu32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u32( + vminq_u32(vreinterpretq_u32_m128i(a), vreinterpretq_u32_m128i(b))); +} + +// Horizontally compute the minimum amongst the packed unsigned 16-bit integers +// in a, store the minimum and index in dst, and zero the remaining bits in dst. +// +// index[2:0] := 0 +// min[15:0] := a[15:0] +// FOR j := 0 to 7 +// i := j*16 +// IF a[i+15:i] < min[15:0] +// index[2:0] := j +// min[15:0] := a[i+15:i] +// FI +// ENDFOR +// dst[15:0] := min[15:0] +// dst[18:16] := index[2:0] +// dst[127:19] := 0 +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_minpos_epu16 +FORCE_INLINE __m128i _mm_minpos_epu16(__m128i a) +{ + __m128i dst; + uint16_t min, idx = 0; + // Find the minimum value +#if defined(__aarch64__) + min = vminvq_u16(vreinterpretq_u16_m128i(a)); +#else + __m64 tmp; + tmp = vreinterpret_m64_u16( + vmin_u16(vget_low_u16(vreinterpretq_u16_m128i(a)), + vget_high_u16(vreinterpretq_u16_m128i(a)))); + tmp = vreinterpret_m64_u16( + vpmin_u16(vreinterpret_u16_m64(tmp), vreinterpret_u16_m64(tmp))); + tmp = vreinterpret_m64_u16( + vpmin_u16(vreinterpret_u16_m64(tmp), vreinterpret_u16_m64(tmp))); + min = vget_lane_u16(vreinterpret_u16_m64(tmp), 0); +#endif + // Get the index of the minimum value + int i; + for (i = 0; i < 8; i++) { + if (min == vgetq_lane_u16(vreinterpretq_u16_m128i(a), 0)) { + idx = (uint16_t) i; + break; + } + a = _mm_srli_si128(a, 2); + } + // Generate result + dst = _mm_setzero_si128(); + dst = vreinterpretq_m128i_u16( + vsetq_lane_u16(min, vreinterpretq_u16_m128i(dst), 0)); + dst = vreinterpretq_m128i_u16( + vsetq_lane_u16(idx, vreinterpretq_u16_m128i(dst), 1)); + return dst; +} + +// Compute the sum of absolute differences (SADs) of quadruplets of unsigned +// 8-bit integers in a compared to those in b, and store the 16-bit results in +// dst. Eight SADs are performed using one quadruplet from b and eight +// quadruplets from a. One quadruplet is selected from b starting at on the +// offset specified in imm8. Eight quadruplets are formed from sequential 8-bit +// integers selected from a starting at the offset specified in imm8. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mpsadbw_epu8 +FORCE_INLINE __m128i _mm_mpsadbw_epu8(__m128i a, __m128i b, const int imm) +{ + uint8x16_t _a, _b; + + switch (imm & 0x4) { + case 0: + // do nothing + _a = vreinterpretq_u8_m128i(a); + break; + case 4: + _a = vreinterpretq_u8_u32(vextq_u32(vreinterpretq_u32_m128i(a), + vreinterpretq_u32_m128i(a), 1)); + break; + default: +#if defined(__GNUC__) || defined(__clang__) + __builtin_unreachable(); +#endif + break; + } + + switch (imm & 0x3) { + case 0: + _b = vreinterpretq_u8_u32( + vdupq_n_u32(vgetq_lane_u32(vreinterpretq_u32_m128i(b), 0))); + break; + case 1: + _b = vreinterpretq_u8_u32( + vdupq_n_u32(vgetq_lane_u32(vreinterpretq_u32_m128i(b), 1))); + break; + case 2: + _b = vreinterpretq_u8_u32( + vdupq_n_u32(vgetq_lane_u32(vreinterpretq_u32_m128i(b), 2))); + break; + case 3: + _b = vreinterpretq_u8_u32( + vdupq_n_u32(vgetq_lane_u32(vreinterpretq_u32_m128i(b), 3))); + break; + default: +#if defined(__GNUC__) || defined(__clang__) + __builtin_unreachable(); +#endif + break; + } + + int16x8_t c04, c15, c26, c37; + uint8x8_t low_b = vget_low_u8(_b); + c04 = vabsq_s16(vreinterpretq_s16_u16(vsubl_u8(vget_low_u8(_a), low_b))); + _a = vextq_u8(_a, _a, 1); + c15 = vabsq_s16(vreinterpretq_s16_u16(vsubl_u8(vget_low_u8(_a), low_b))); + _a = vextq_u8(_a, _a, 1); + c26 = vabsq_s16(vreinterpretq_s16_u16(vsubl_u8(vget_low_u8(_a), low_b))); + _a = vextq_u8(_a, _a, 1); + c37 = vabsq_s16(vreinterpretq_s16_u16(vsubl_u8(vget_low_u8(_a), low_b))); +#if defined(__aarch64__) + // |0|4|2|6| + c04 = vpaddq_s16(c04, c26); + // |1|5|3|7| + c15 = vpaddq_s16(c15, c37); + + int32x4_t trn1_c = + vtrn1q_s32(vreinterpretq_s32_s16(c04), vreinterpretq_s32_s16(c15)); + int32x4_t trn2_c = + vtrn2q_s32(vreinterpretq_s32_s16(c04), vreinterpretq_s32_s16(c15)); + return vreinterpretq_m128i_s16(vpaddq_s16(vreinterpretq_s16_s32(trn1_c), + vreinterpretq_s16_s32(trn2_c))); +#else + int16x4_t c01, c23, c45, c67; + c01 = vpadd_s16(vget_low_s16(c04), vget_low_s16(c15)); + c23 = vpadd_s16(vget_low_s16(c26), vget_low_s16(c37)); + c45 = vpadd_s16(vget_high_s16(c04), vget_high_s16(c15)); + c67 = vpadd_s16(vget_high_s16(c26), vget_high_s16(c37)); + + return vreinterpretq_m128i_s16( + vcombine_s16(vpadd_s16(c01, c23), vpadd_s16(c45, c67))); +#endif +} + +// Multiply the low signed 32-bit integers from each packed 64-bit element in +// a and b, and store the signed 64-bit results in dst. +// +// r0 := (int64_t)(int32_t)a0 * (int64_t)(int32_t)b0 +// r1 := (int64_t)(int32_t)a2 * (int64_t)(int32_t)b2 +FORCE_INLINE __m128i _mm_mul_epi32(__m128i a, __m128i b) +{ + // vmull_s32 upcasts instead of masking, so we downcast. + int32x2_t a_lo = vmovn_s64(vreinterpretq_s64_m128i(a)); + int32x2_t b_lo = vmovn_s64(vreinterpretq_s64_m128i(b)); + return vreinterpretq_m128i_s64(vmull_s32(a_lo, b_lo)); +} + +// Multiplies the 4 signed or unsigned 32-bit integers from a by the 4 signed or +// unsigned 32-bit integers from b. +// https://msdn.microsoft.com/en-us/library/vstudio/bb531409(v=vs.100).aspx +FORCE_INLINE __m128i _mm_mullo_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_s32( + vmulq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); +} + +// Packs the 8 unsigned 32-bit integers from a and b into unsigned 16-bit +// integers and saturates. +// +// r0 := UnsignedSaturate(a0) +// r1 := UnsignedSaturate(a1) +// r2 := UnsignedSaturate(a2) +// r3 := UnsignedSaturate(a3) +// r4 := UnsignedSaturate(b0) +// r5 := UnsignedSaturate(b1) +// r6 := UnsignedSaturate(b2) +// r7 := UnsignedSaturate(b3) +FORCE_INLINE __m128i _mm_packus_epi32(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u16( + vcombine_u16(vqmovun_s32(vreinterpretq_s32_m128i(a)), + vqmovun_s32(vreinterpretq_s32_m128i(b)))); +} + +// Round the packed double-precision (64-bit) floating-point elements in a using +// the rounding parameter, and store the results as packed double-precision +// floating-point elements in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_round_pd +FORCE_INLINE __m128d _mm_round_pd(__m128d a, int rounding) +{ +#if defined(__aarch64__) + switch (rounding) { + case (_MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC): + return vreinterpretq_m128d_f64(vrndnq_f64(vreinterpretq_f64_m128d(a))); + case (_MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC): + return _mm_floor_pd(a); + case (_MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC): + return _mm_ceil_pd(a); + case (_MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC): + return vreinterpretq_m128d_f64(vrndq_f64(vreinterpretq_f64_m128d(a))); + default: //_MM_FROUND_CUR_DIRECTION + return vreinterpretq_m128d_f64(vrndiq_f64(vreinterpretq_f64_m128d(a))); + } +#else + double *v_double = (double *) &a; + + if (rounding == (_MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC) || + (rounding == _MM_FROUND_CUR_DIRECTION && + _MM_GET_ROUNDING_MODE() == _MM_ROUND_NEAREST)) { + double res[2], tmp; + for (int i = 0; i < 2; i++) { + tmp = (v_double[i] < 0) ? -v_double[i] : v_double[i]; + double roundDown = floor(tmp); // Round down value + double roundUp = ceil(tmp); // Round up value + double diffDown = tmp - roundDown; + double diffUp = roundUp - tmp; + if (diffDown < diffUp) { + /* If it's closer to the round down value, then use it */ + res[i] = roundDown; + } else if (diffDown > diffUp) { + /* If it's closer to the round up value, then use it */ + res[i] = roundUp; + } else { + /* If it's equidistant between round up and round down value, + * pick the one which is an even number */ + double half = roundDown / 2; + if (half != floor(half)) { + /* If the round down value is odd, return the round up value + */ + res[i] = roundUp; + } else { + /* If the round up value is odd, return the round down value + */ + res[i] = roundDown; + } + } + res[i] = (v_double[i] < 0) ? -res[i] : res[i]; + } + return _mm_set_pd(res[1], res[0]); + } else if (rounding == (_MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC) || + (rounding == _MM_FROUND_CUR_DIRECTION && + _MM_GET_ROUNDING_MODE() == _MM_ROUND_DOWN)) { + return _mm_floor_pd(a); + } else if (rounding == (_MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC) || + (rounding == _MM_FROUND_CUR_DIRECTION && + _MM_GET_ROUNDING_MODE() == _MM_ROUND_UP)) { + return _mm_ceil_pd(a); + } + return _mm_set_pd(v_double[1] > 0 ? floor(v_double[1]) : ceil(v_double[1]), + v_double[0] > 0 ? floor(v_double[0]) : ceil(v_double[0])); +#endif +} + +// Round the packed single-precision (32-bit) floating-point elements in a using +// the rounding parameter, and store the results as packed single-precision +// floating-point elements in dst. +// software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_round_ps +FORCE_INLINE __m128 _mm_round_ps(__m128 a, int rounding) +{ +#if defined(__aarch64__) + switch (rounding) { + case (_MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC): + return vreinterpretq_m128_f32(vrndnq_f32(vreinterpretq_f32_m128(a))); + case (_MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC): + return _mm_floor_ps(a); + case (_MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC): + return _mm_ceil_ps(a); + case (_MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC): + return vreinterpretq_m128_f32(vrndq_f32(vreinterpretq_f32_m128(a))); + default: //_MM_FROUND_CUR_DIRECTION + return vreinterpretq_m128_f32(vrndiq_f32(vreinterpretq_f32_m128(a))); + } +#else + float *v_float = (float *) &a; + + if (rounding == (_MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC) || + (rounding == _MM_FROUND_CUR_DIRECTION && + _MM_GET_ROUNDING_MODE() == _MM_ROUND_NEAREST)) { + uint32x4_t signmask = vdupq_n_u32(0x80000000); + float32x4_t half = vbslq_f32(signmask, vreinterpretq_f32_m128(a), + vdupq_n_f32(0.5f)); /* +/- 0.5 */ + int32x4_t r_normal = vcvtq_s32_f32(vaddq_f32( + vreinterpretq_f32_m128(a), half)); /* round to integer: [a + 0.5]*/ + int32x4_t r_trunc = vcvtq_s32_f32( + vreinterpretq_f32_m128(a)); /* truncate to integer: [a] */ + int32x4_t plusone = vreinterpretq_s32_u32(vshrq_n_u32( + vreinterpretq_u32_s32(vnegq_s32(r_trunc)), 31)); /* 1 or 0 */ + int32x4_t r_even = vbicq_s32(vaddq_s32(r_trunc, plusone), + vdupq_n_s32(1)); /* ([a] + {0,1}) & ~1 */ + float32x4_t delta = vsubq_f32( + vreinterpretq_f32_m128(a), + vcvtq_f32_s32(r_trunc)); /* compute delta: delta = (a - [a]) */ + uint32x4_t is_delta_half = + vceqq_f32(delta, half); /* delta == +/- 0.5 */ + return vreinterpretq_m128_f32( + vcvtq_f32_s32(vbslq_s32(is_delta_half, r_even, r_normal))); + } else if (rounding == (_MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC) || + (rounding == _MM_FROUND_CUR_DIRECTION && + _MM_GET_ROUNDING_MODE() == _MM_ROUND_DOWN)) { + return _mm_floor_ps(a); + } else if (rounding == (_MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC) || + (rounding == _MM_FROUND_CUR_DIRECTION && + _MM_GET_ROUNDING_MODE() == _MM_ROUND_UP)) { + return _mm_ceil_ps(a); + } + return _mm_set_ps(v_float[3] > 0 ? floorf(v_float[3]) : ceilf(v_float[3]), + v_float[2] > 0 ? floorf(v_float[2]) : ceilf(v_float[2]), + v_float[1] > 0 ? floorf(v_float[1]) : ceilf(v_float[1]), + v_float[0] > 0 ? floorf(v_float[0]) : ceilf(v_float[0])); +#endif +} + +// Round the lower double-precision (64-bit) floating-point element in b using +// the rounding parameter, store the result as a double-precision floating-point +// element in the lower element of dst, and copy the upper element from a to the +// upper element of dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_round_sd +FORCE_INLINE __m128d _mm_round_sd(__m128d a, __m128d b, int rounding) +{ + return _mm_move_sd(a, _mm_round_pd(b, rounding)); +} + +// Round the lower single-precision (32-bit) floating-point element in b using +// the rounding parameter, store the result as a single-precision floating-point +// element in the lower element of dst, and copy the upper 3 packed elements +// from a to the upper elements of dst. Rounding is done according to the +// rounding[3:0] parameter, which can be one of: +// (_MM_FROUND_TO_NEAREST_INT |_MM_FROUND_NO_EXC) // round to nearest, and +// suppress exceptions +// (_MM_FROUND_TO_NEG_INF |_MM_FROUND_NO_EXC) // round down, and +// suppress exceptions +// (_MM_FROUND_TO_POS_INF |_MM_FROUND_NO_EXC) // round up, and suppress +// exceptions +// (_MM_FROUND_TO_ZERO |_MM_FROUND_NO_EXC) // truncate, and suppress +// exceptions _MM_FROUND_CUR_DIRECTION // use MXCSR.RC; see +// _MM_SET_ROUNDING_MODE +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_round_ss +FORCE_INLINE __m128 _mm_round_ss(__m128 a, __m128 b, int rounding) +{ + return _mm_move_ss(a, _mm_round_ps(b, rounding)); +} + +// Load 128-bits of integer data from memory into dst using a non-temporal +// memory hint. mem_addr must be aligned on a 16-byte boundary or a +// general-protection exception may be generated. +// +// dst[127:0] := MEM[mem_addr+127:mem_addr] +// +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_stream_load_si128 +FORCE_INLINE __m128i _mm_stream_load_si128(__m128i *p) +{ +#if __has_builtin(__builtin_nontemporal_store) + return __builtin_nontemporal_load(p); +#else + return vreinterpretq_m128i_s64(vld1q_s64((int64_t *) p)); +#endif +} + +// Compute the bitwise NOT of a and then AND with a 128-bit vector containing +// all 1's, and return 1 if the result is zero, otherwise return 0. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_test_all_ones +FORCE_INLINE int _mm_test_all_ones(__m128i a) +{ + return (uint64_t)(vgetq_lane_s64(a, 0) & vgetq_lane_s64(a, 1)) == + ~(uint64_t) 0; +} + +// Compute the bitwise AND of 128 bits (representing integer data) in a and +// mask, and return 1 if the result is zero, otherwise return 0. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_test_all_zeros +FORCE_INLINE int _mm_test_all_zeros(__m128i a, __m128i mask) +{ + int64x2_t a_and_mask = + vandq_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(mask)); + return !(vgetq_lane_s64(a_and_mask, 0) | vgetq_lane_s64(a_and_mask, 1)); +} + +// Compute the bitwise AND of 128 bits (representing integer data) in a and +// mask, and set ZF to 1 if the result is zero, otherwise set ZF to 0. Compute +// the bitwise NOT of a and then AND with mask, and set CF to 1 if the result is +// zero, otherwise set CF to 0. Return 1 if both the ZF and CF values are zero, +// otherwise return 0. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=mm_test_mix_ones_zero +FORCE_INLINE int _mm_test_mix_ones_zeros(__m128i a, __m128i mask) +{ + uint64x2_t zf = + vandq_u64(vreinterpretq_u64_m128i(mask), vreinterpretq_u64_m128i(a)); + uint64x2_t cf = + vbicq_u64(vreinterpretq_u64_m128i(mask), vreinterpretq_u64_m128i(a)); + uint64x2_t result = vandq_u64(zf, cf); + return !(vgetq_lane_u64(result, 0) | vgetq_lane_u64(result, 1)); +} + +// Compute the bitwise AND of 128 bits (representing integer data) in a and b, +// and set ZF to 1 if the result is zero, otherwise set ZF to 0. Compute the +// bitwise NOT of a and then AND with b, and set CF to 1 if the result is zero, +// otherwise set CF to 0. Return the CF value. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_testc_si128 +FORCE_INLINE int _mm_testc_si128(__m128i a, __m128i b) +{ + int64x2_t s64 = + vandq_s64(vreinterpretq_s64_s32(vmvnq_s32(vreinterpretq_s32_m128i(a))), + vreinterpretq_s64_m128i(b)); + return !(vgetq_lane_s64(s64, 0) | vgetq_lane_s64(s64, 1)); +} + +// Compute the bitwise AND of 128 bits (representing integer data) in a and b, +// and set ZF to 1 if the result is zero, otherwise set ZF to 0. Compute the +// bitwise NOT of a and then AND with b, and set CF to 1 if the result is zero, +// otherwise set CF to 0. Return 1 if both the ZF and CF values are zero, +// otherwise return 0. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_testnzc_si128 +#define _mm_testnzc_si128(a, b) _mm_test_mix_ones_zeros(a, b) + +// Compute the bitwise AND of 128 bits (representing integer data) in a and b, +// and set ZF to 1 if the result is zero, otherwise set ZF to 0. Compute the +// bitwise NOT of a and then AND with b, and set CF to 1 if the result is zero, +// otherwise set CF to 0. Return the ZF value. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_testz_si128 +FORCE_INLINE int _mm_testz_si128(__m128i a, __m128i b) +{ + int64x2_t s64 = + vandq_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(b)); + return !(vgetq_lane_s64(s64, 0) | vgetq_lane_s64(s64, 1)); +} + +/* SSE4.2 */ + +// Compares the 2 signed 64-bit integers in a and the 2 signed 64-bit integers +// in b for greater than. +FORCE_INLINE __m128i _mm_cmpgt_epi64(__m128i a, __m128i b) +{ +#if defined(__aarch64__) + return vreinterpretq_m128i_u64( + vcgtq_s64(vreinterpretq_s64_m128i(a), vreinterpretq_s64_m128i(b))); +#else + return vreinterpretq_m128i_s64(vshrq_n_s64( + vqsubq_s64(vreinterpretq_s64_m128i(b), vreinterpretq_s64_m128i(a)), + 63)); +#endif +} + +// Starting with the initial value in crc, accumulates a CRC32 value for +// unsigned 16-bit integer v. +// https://msdn.microsoft.com/en-us/library/bb531411(v=vs.100) +FORCE_INLINE uint32_t _mm_crc32_u16(uint32_t crc, uint16_t v) +{ +#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) + __asm__ __volatile__("crc32ch %w[c], %w[c], %w[v]\n\t" + : [c] "+r"(crc) + : [v] "r"(v)); +#else + crc = _mm_crc32_u8(crc, v & 0xff); + crc = _mm_crc32_u8(crc, (v >> 8) & 0xff); +#endif + return crc; +} + +// Starting with the initial value in crc, accumulates a CRC32 value for +// unsigned 32-bit integer v. +// https://msdn.microsoft.com/en-us/library/bb531394(v=vs.100) +FORCE_INLINE uint32_t _mm_crc32_u32(uint32_t crc, uint32_t v) +{ +#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) + __asm__ __volatile__("crc32cw %w[c], %w[c], %w[v]\n\t" + : [c] "+r"(crc) + : [v] "r"(v)); +#else + crc = _mm_crc32_u16(crc, v & 0xffff); + crc = _mm_crc32_u16(crc, (v >> 16) & 0xffff); +#endif + return crc; +} + +// Starting with the initial value in crc, accumulates a CRC32 value for +// unsigned 64-bit integer v. +// https://msdn.microsoft.com/en-us/library/bb514033(v=vs.100) +FORCE_INLINE uint64_t _mm_crc32_u64(uint64_t crc, uint64_t v) +{ +#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) + __asm__ __volatile__("crc32cx %w[c], %w[c], %x[v]\n\t" + : [c] "+r"(crc) + : [v] "r"(v)); +#else + crc = _mm_crc32_u32((uint32_t)(crc), v & 0xffffffff); + crc = _mm_crc32_u32((uint32_t)(crc), (v >> 32) & 0xffffffff); +#endif + return crc; +} + +// Starting with the initial value in crc, accumulates a CRC32 value for +// unsigned 8-bit integer v. +// https://msdn.microsoft.com/en-us/library/bb514036(v=vs.100) +FORCE_INLINE uint32_t _mm_crc32_u8(uint32_t crc, uint8_t v) +{ +#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) + __asm__ __volatile__("crc32cb %w[c], %w[c], %w[v]\n\t" + : [c] "+r"(crc) + : [v] "r"(v)); +#else + crc ^= v; + for (int bit = 0; bit < 8; bit++) { + if (crc & 1) + crc = (crc >> 1) ^ UINT32_C(0x82f63b78); + else + crc = (crc >> 1); + } +#endif + return crc; +} + +/* AES */ + +#if !defined(__ARM_FEATURE_CRYPTO) +/* clang-format off */ +#define SSE2NEON_AES_DATA(w) \ + { \ + w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), \ + w(0xc5), w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), \ + w(0xab), w(0x76), w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), \ + w(0x59), w(0x47), w(0xf0), w(0xad), w(0xd4), w(0xa2), w(0xaf), \ + w(0x9c), w(0xa4), w(0x72), w(0xc0), w(0xb7), w(0xfd), w(0x93), \ + w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc), w(0x34), w(0xa5), \ + w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15), w(0x04), \ + w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a), \ + w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), \ + w(0x75), w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), \ + w(0x5a), w(0xa0), w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), \ + w(0xe3), w(0x2f), w(0x84), w(0x53), w(0xd1), w(0x00), w(0xed), \ + w(0x20), w(0xfc), w(0xb1), w(0x5b), w(0x6a), w(0xcb), w(0xbe), \ + w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf), w(0xd0), w(0xef), \ + w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85), w(0x45), \ + w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8), \ + w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), \ + w(0xf5), w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), \ + w(0xf3), w(0xd2), w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), \ + w(0x97), w(0x44), w(0x17), w(0xc4), w(0xa7), w(0x7e), w(0x3d), \ + w(0x64), w(0x5d), w(0x19), w(0x73), w(0x60), w(0x81), w(0x4f), \ + w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88), w(0x46), w(0xee), \ + w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb), w(0xe0), \ + w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c), \ + w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), \ + w(0x79), w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), \ + w(0x4e), w(0xa9), w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), \ + w(0x7a), w(0xae), w(0x08), w(0xba), w(0x78), w(0x25), w(0x2e), \ + w(0x1c), w(0xa6), w(0xb4), w(0xc6), w(0xe8), w(0xdd), w(0x74), \ + w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a), w(0x70), w(0x3e), \ + w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e), w(0x61), \ + w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e), \ + w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), \ + w(0x94), w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), \ + w(0x28), w(0xdf), w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), \ + w(0xe6), w(0x42), w(0x68), w(0x41), w(0x99), w(0x2d), w(0x0f), \ + w(0xb0), w(0x54), w(0xbb), w(0x16) \ + } +/* clang-format on */ + +/* X Macro trick. See https://en.wikipedia.org/wiki/X_Macro */ +#define SSE2NEON_AES_H0(x) (x) +static const uint8_t SSE2NEON_sbox[256] = SSE2NEON_AES_DATA(SSE2NEON_AES_H0); +#undef SSE2NEON_AES_H0 + +// In the absence of crypto extensions, implement aesenc using regular neon +// intrinsics instead. See: +// https://www.workofard.com/2017/01/accelerated-aes-for-the-arm64-linux-kernel/ +// https://www.workofard.com/2017/07/ghash-for-low-end-cores/ and +// https://github.com/ColinIanKing/linux-next-mirror/blob/b5f466091e130caaf0735976648f72bd5e09aa84/crypto/aegis128-neon-inner.c#L52 +// for more information Reproduced with permission of the author. +FORCE_INLINE __m128i _mm_aesenc_si128(__m128i EncBlock, __m128i RoundKey) +{ +#if defined(__aarch64__) + static const uint8_t shift_rows[] = {0x0, 0x5, 0xa, 0xf, 0x4, 0x9, + 0xe, 0x3, 0x8, 0xd, 0x2, 0x7, + 0xc, 0x1, 0x6, 0xb}; + static const uint8_t ror32by8[] = {0x1, 0x2, 0x3, 0x0, 0x5, 0x6, 0x7, 0x4, + 0x9, 0xa, 0xb, 0x8, 0xd, 0xe, 0xf, 0xc}; + + uint8x16_t v; + uint8x16_t w = vreinterpretq_u8_m128i(EncBlock); + + // shift rows + w = vqtbl1q_u8(w, vld1q_u8(shift_rows)); + + // sub bytes + v = vqtbl4q_u8(_sse2neon_vld1q_u8_x4(SSE2NEON_sbox), w); + v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(SSE2NEON_sbox + 0x40), w - 0x40); + v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(SSE2NEON_sbox + 0x80), w - 0x80); + v = vqtbx4q_u8(v, _sse2neon_vld1q_u8_x4(SSE2NEON_sbox + 0xc0), w - 0xc0); + + // mix columns + w = (v << 1) ^ (uint8x16_t)(((int8x16_t) v >> 7) & 0x1b); + w ^= (uint8x16_t) vrev32q_u16((uint16x8_t) v); + w ^= vqtbl1q_u8(v ^ w, vld1q_u8(ror32by8)); + + // add round key + return vreinterpretq_m128i_u8(w) ^ RoundKey; + +#else /* ARMv7-A NEON implementation */ +#define SSE2NEON_AES_B2W(b0, b1, b2, b3) \ + (((uint32_t)(b3) << 24) | ((uint32_t)(b2) << 16) | ((uint32_t)(b1) << 8) | \ + (b0)) +#define SSE2NEON_AES_F2(x) ((x << 1) ^ (((x >> 7) & 1) * 0x011b /* WPOLY */)) +#define SSE2NEON_AES_F3(x) (SSE2NEON_AES_F2(x) ^ x) +#define SSE2NEON_AES_U0(p) \ + SSE2NEON_AES_B2W(SSE2NEON_AES_F2(p), p, p, SSE2NEON_AES_F3(p)) +#define SSE2NEON_AES_U1(p) \ + SSE2NEON_AES_B2W(SSE2NEON_AES_F3(p), SSE2NEON_AES_F2(p), p, p) +#define SSE2NEON_AES_U2(p) \ + SSE2NEON_AES_B2W(p, SSE2NEON_AES_F3(p), SSE2NEON_AES_F2(p), p) +#define SSE2NEON_AES_U3(p) \ + SSE2NEON_AES_B2W(p, p, SSE2NEON_AES_F3(p), SSE2NEON_AES_F2(p)) + static const uint32_t ALIGN_STRUCT(16) aes_table[4][256] = { + SSE2NEON_AES_DATA(SSE2NEON_AES_U0), + SSE2NEON_AES_DATA(SSE2NEON_AES_U1), + SSE2NEON_AES_DATA(SSE2NEON_AES_U2), + SSE2NEON_AES_DATA(SSE2NEON_AES_U3), + }; +#undef SSE2NEON_AES_B2W +#undef SSE2NEON_AES_F2 +#undef SSE2NEON_AES_F3 +#undef SSE2NEON_AES_U0 +#undef SSE2NEON_AES_U1 +#undef SSE2NEON_AES_U2 +#undef SSE2NEON_AES_U3 + + uint32_t x0 = _mm_cvtsi128_si32(EncBlock); + uint32_t x1 = _mm_cvtsi128_si32(_mm_shuffle_epi32(EncBlock, 0x55)); + uint32_t x2 = _mm_cvtsi128_si32(_mm_shuffle_epi32(EncBlock, 0xAA)); + uint32_t x3 = _mm_cvtsi128_si32(_mm_shuffle_epi32(EncBlock, 0xFF)); + + __m128i out = _mm_set_epi32( + (aes_table[0][x3 & 0xff] ^ aes_table[1][(x0 >> 8) & 0xff] ^ + aes_table[2][(x1 >> 16) & 0xff] ^ aes_table[3][x2 >> 24]), + (aes_table[0][x2 & 0xff] ^ aes_table[1][(x3 >> 8) & 0xff] ^ + aes_table[2][(x0 >> 16) & 0xff] ^ aes_table[3][x1 >> 24]), + (aes_table[0][x1 & 0xff] ^ aes_table[1][(x2 >> 8) & 0xff] ^ + aes_table[2][(x3 >> 16) & 0xff] ^ aes_table[3][x0 >> 24]), + (aes_table[0][x0 & 0xff] ^ aes_table[1][(x1 >> 8) & 0xff] ^ + aes_table[2][(x2 >> 16) & 0xff] ^ aes_table[3][x3 >> 24])); + + return _mm_xor_si128(out, RoundKey); +#endif +} + +// Perform the last round of an AES encryption flow on data (state) in a using +// the round key in RoundKey, and store the result in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_aesenclast_si128 +FORCE_INLINE __m128i _mm_aesenclast_si128(__m128i a, __m128i RoundKey) +{ + /* FIXME: optimized for NEON */ + uint8_t v[4][4] = { + {SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 0)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 5)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 10)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 15)]}, + {SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 4)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 9)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 14)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 3)]}, + {SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 8)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 13)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 2)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 7)]}, + {SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 12)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 1)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 6)], + SSE2NEON_sbox[vreinterpretq_nth_u8_m128i(a, 11)]}, + }; + for (int i = 0; i < 16; i++) + vreinterpretq_nth_u8_m128i(a, i) = + v[i / 4][i % 4] ^ vreinterpretq_nth_u8_m128i(RoundKey, i); + return a; +} + +// Emits the Advanced Encryption Standard (AES) instruction aeskeygenassist. +// This instruction generates a round key for AES encryption. See +// https://kazakov.life/2017/11/01/cryptocurrency-mining-on-ios-devices/ +// for details. +// +// https://msdn.microsoft.com/en-us/library/cc714138(v=vs.120).aspx +FORCE_INLINE __m128i _mm_aeskeygenassist_si128(__m128i key, const int rcon) +{ + uint32_t X1 = _mm_cvtsi128_si32(_mm_shuffle_epi32(key, 0x55)); + uint32_t X3 = _mm_cvtsi128_si32(_mm_shuffle_epi32(key, 0xFF)); + for (int i = 0; i < 4; ++i) { + ((uint8_t *) &X1)[i] = SSE2NEON_sbox[((uint8_t *) &X1)[i]]; + ((uint8_t *) &X3)[i] = SSE2NEON_sbox[((uint8_t *) &X3)[i]]; + } + return _mm_set_epi32(((X3 >> 8) | (X3 << 24)) ^ rcon, X3, + ((X1 >> 8) | (X1 << 24)) ^ rcon, X1); +} +#undef SSE2NEON_AES_DATA + +#else /* __ARM_FEATURE_CRYPTO */ +// Implements equivalent of 'aesenc' by combining AESE (with an empty key) and +// AESMC and then manually applying the real key as an xor operation. This +// unfortunately means an additional xor op; the compiler should be able to +// optimize this away for repeated calls however. See +// https://blog.michaelbrase.com/2018/05/08/emulating-x86-aes-intrinsics-on-armv8-a +// for more details. +FORCE_INLINE __m128i _mm_aesenc_si128(__m128i a, __m128i b) +{ + return vreinterpretq_m128i_u8( + vaesmcq_u8(vaeseq_u8(vreinterpretq_u8_m128i(a), vdupq_n_u8(0))) ^ + vreinterpretq_u8_m128i(b)); +} + +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_aesenclast_si128 +FORCE_INLINE __m128i _mm_aesenclast_si128(__m128i a, __m128i RoundKey) +{ + return _mm_xor_si128(vreinterpretq_m128i_u8(vaeseq_u8( + vreinterpretq_u8_m128i(a), vdupq_n_u8(0))), + RoundKey); +} + +FORCE_INLINE __m128i _mm_aeskeygenassist_si128(__m128i a, const int rcon) +{ + // AESE does ShiftRows and SubBytes on A + uint8x16_t u8 = vaeseq_u8(vreinterpretq_u8_m128i(a), vdupq_n_u8(0)); + + uint8x16_t dest = { + // Undo ShiftRows step from AESE and extract X1 and X3 + u8[0x4], u8[0x1], u8[0xE], u8[0xB], // SubBytes(X1) + u8[0x1], u8[0xE], u8[0xB], u8[0x4], // ROT(SubBytes(X1)) + u8[0xC], u8[0x9], u8[0x6], u8[0x3], // SubBytes(X3) + u8[0x9], u8[0x6], u8[0x3], u8[0xC], // ROT(SubBytes(X3)) + }; + uint32x4_t r = {0, (unsigned) rcon, 0, (unsigned) rcon}; + return vreinterpretq_m128i_u8(dest) ^ vreinterpretq_m128i_u32(r); +} +#endif + +/* Others */ + +// Perform a carry-less multiplication of two 64-bit integers, selected from a +// and b according to imm8, and store the results in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_clmulepi64_si128 +FORCE_INLINE __m128i _mm_clmulepi64_si128(__m128i _a, __m128i _b, const int imm) +{ + uint64x2_t a = vreinterpretq_u64_m128i(_a); + uint64x2_t b = vreinterpretq_u64_m128i(_b); + switch (imm & 0x11) { + case 0x00: + return vreinterpretq_m128i_u64( + _sse2neon_vmull_p64(vget_low_u64(a), vget_low_u64(b))); + case 0x01: + return vreinterpretq_m128i_u64( + _sse2neon_vmull_p64(vget_high_u64(a), vget_low_u64(b))); + case 0x10: + return vreinterpretq_m128i_u64( + _sse2neon_vmull_p64(vget_low_u64(a), vget_high_u64(b))); + case 0x11: + return vreinterpretq_m128i_u64( + _sse2neon_vmull_p64(vget_high_u64(a), vget_high_u64(b))); + default: + abort(); + } +} + +FORCE_INLINE unsigned int _sse2neon_mm_get_denormals_zero_mode() +{ + union { + fpcr_bitfield field; +#if defined(__aarch64__) + uint64_t value; +#else + uint32_t value; +#endif + } r; + +#if defined(__aarch64__) + asm volatile("mrs %0, FPCR" : "=r"(r.value)); /* read */ +#else + asm volatile("vmrs %0, FPSCR" : "=r"(r.value)); /* read */ +#endif + + return r.field.bit24 ? _MM_DENORMALS_ZERO_ON : _MM_DENORMALS_ZERO_OFF; +} + +// Count the number of bits set to 1 in unsigned 32-bit integer a, and +// return that count in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_popcnt_u32 +FORCE_INLINE int _mm_popcnt_u32(unsigned int a) +{ +#if defined(__aarch64__) +#if __has_builtin(__builtin_popcount) + return __builtin_popcount(a); +#else + return (int) vaddlv_u8(vcnt_u8(vcreate_u8((uint64_t) a))); +#endif +#else + uint32_t count = 0; + uint8x8_t input_val, count8x8_val; + uint16x4_t count16x4_val; + uint32x2_t count32x2_val; + + input_val = vld1_u8((uint8_t *) &a); + count8x8_val = vcnt_u8(input_val); + count16x4_val = vpaddl_u8(count8x8_val); + count32x2_val = vpaddl_u16(count16x4_val); + + vst1_u32(&count, count32x2_val); + return count; +#endif +} + +// Count the number of bits set to 1 in unsigned 64-bit integer a, and +// return that count in dst. +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_popcnt_u64 +FORCE_INLINE int64_t _mm_popcnt_u64(uint64_t a) +{ +#if defined(__aarch64__) +#if __has_builtin(__builtin_popcountll) + return __builtin_popcountll(a); +#else + return (int64_t) vaddlv_u8(vcnt_u8(vcreate_u8(a))); +#endif +#else + uint64_t count = 0; + uint8x8_t input_val, count8x8_val; + uint16x4_t count16x4_val; + uint32x2_t count32x2_val; + uint64x1_t count64x1_val; + + input_val = vld1_u8((uint8_t *) &a); + count8x8_val = vcnt_u8(input_val); + count16x4_val = vpaddl_u8(count8x8_val); + count32x2_val = vpaddl_u16(count16x4_val); + count64x1_val = vpaddl_u32(count32x2_val); + vst1_u64(&count, count64x1_val); + return count; +#endif +} + +FORCE_INLINE void _sse2neon_mm_set_denormals_zero_mode(unsigned int flag) +{ + // AArch32 Advanced SIMD arithmetic always uses the Flush-to-zero setting, + // regardless of the value of the FZ bit. + union { + fpcr_bitfield field; +#if defined(__aarch64__) + uint64_t value; +#else + uint32_t value; +#endif + } r; + +#if defined(__aarch64__) + asm volatile("mrs %0, FPCR" : "=r"(r.value)); /* read */ +#else + asm volatile("vmrs %0, FPSCR" : "=r"(r.value)); /* read */ +#endif + + r.field.bit24 = (flag & _MM_DENORMALS_ZERO_MASK) == _MM_DENORMALS_ZERO_ON; + +#if defined(__aarch64__) + asm volatile("msr FPCR, %0" ::"r"(r)); /* write */ +#else + asm volatile("vmsr FPSCR, %0" ::"r"(r)); /* write */ +#endif +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma pop_macro("ALIGN_STRUCT") +#pragma pop_macro("FORCE_INLINE") +#endif + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC pop_options +#endif + +#endif diff --git a/lib/olive/include/util/tests.h b/lib/olive/include/util/tests.h new file mode 100644 index 0000000000..0a035341fb --- /dev/null +++ b/lib/olive/include/util/tests.h @@ -0,0 +1,62 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef LIBOLIVECORE_TESTS_H +#define LIBOLIVECORE_TESTS_H + +#include + +namespace olive { + +class Tester +{ +public: + Tester() = default; + + typedef bool (*test_t)(); + + void add(const char *name, test_t test_function) + { + test_names_.push_back(name); + test_functions_.push_back(test_function); + } + + bool run(); + + int exec() + { + if (run()) { + return 0; + } else { + return 1; + } + } + + static void echo(const char *fmt, ...); + +private: + std::list test_names_; + std::list test_functions_; + +}; + +} + +#endif // LIBOLIVECORE_TESTS_H diff --git a/lib/olive/include/util/timecodefunctions.h b/lib/olive/include/util/timecodefunctions.h new file mode 100644 index 0000000000..34f6351aa2 --- /dev/null +++ b/lib/olive/include/util/timecodefunctions.h @@ -0,0 +1,78 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef LIBOLIVECORE_TIMECODEFUNCTIONS_H +#define LIBOLIVECORE_TIMECODEFUNCTIONS_H + +#include "rational.h" + +namespace olive { + +/** + * @brief Functions for converting times/timecodes/timestamps + * + * Olive uses the following terminology through its code: + * + * `time` - time in seconds presented in a rational form + * `timebase` - the base time unit of an audio/video stream in seconds + * `timestamp` - an integer representation of a time in timebase units (in many cases is used like a frame number) + * `timecode` a user-friendly string representation of a time according to Timecode::Display + */ +class Timecode { +public: + enum Display { + kTimecodeDropFrame, + kTimecodeNonDropFrame, + kTimecodeSeconds, + kFrames, + kMilliseconds + }; + + enum Rounding { + kCeil, + kFloor, + kRound + }; + + /** + * @brief Convert a timestamp (according to a rational timebase) to a user-friendly string representation + */ + static QString time_to_timecode(const rational& time, const rational& timebase, const Display &display, bool show_plus_if_positive = false); + static rational timecode_to_time(QString timecode, const rational& timebase, const Display& display, bool *ok = nullptr); + + static QString time_to_string(int64_t ms); + + static rational snap_time_to_timebase(const rational& time, const rational& timebase, Rounding floor = kRound); + + static int64_t time_to_timestamp(const rational& time, const rational& timebase, Rounding floor = kRound); + static int64_t time_to_timestamp(const double& time, const rational& timebase, Rounding floor = kRound); + + static int64_t rescale_timestamp(const int64_t& ts, const rational& source, const rational& dest); + static int64_t rescale_timestamp_ceil(const int64_t& ts, const rational& source, const rational& dest); + + static rational timestamp_to_time(const int64_t& timestamp, const rational& timebase); + + static bool timebase_is_drop_frame(const rational& timebase); + +}; + +} + +#endif // LIBOLIVECORE_TIMECODEFUNCTIONS_H diff --git a/lib/olive/include/util/timerange.h b/lib/olive/include/util/timerange.h new file mode 100644 index 0000000000..af92d0f124 --- /dev/null +++ b/lib/olive/include/util/timerange.h @@ -0,0 +1,310 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef LIBOLIVECORE_TIMERANGE_H +#define LIBOLIVECORE_TIMERANGE_H + +#include +#include + +#include "rational.h" + +namespace olive { + +class TimeRange { +public: + TimeRange() = default; + TimeRange(const rational& in, const rational& out); + TimeRange(const TimeRange& r) : + TimeRange(r.in(), r.out()) + { + } + + TimeRange &operator=(const TimeRange &r) + { + set_range(r.in(), r.out()); + return *this; + } + + const rational& in() const; + const rational& out() const; + const rational& length() const; + + void set_in(const rational& in); + void set_out(const rational& out); + void set_range(const rational& in, const rational& out); + + bool operator==(const TimeRange& r) const; + bool operator!=(const TimeRange& r) const; + + bool OverlapsWith(const TimeRange& a, bool in_inclusive = true, bool out_inclusive = true) const; + bool Contains(const TimeRange& a, bool in_inclusive = true, bool out_inclusive = true) const; + bool Contains(const rational& r) const; + + TimeRange Combined(const TimeRange& a) const; + static TimeRange Combine(const TimeRange &a, const TimeRange &b); + TimeRange Intersected(const TimeRange& a) const; + static TimeRange Intersect(const TimeRange &a, const TimeRange &b); + + TimeRange operator+(const rational& rhs) const; + TimeRange operator-(const rational& rhs) const; + + const TimeRange& operator+=(const rational &rhs); + const TimeRange& operator-=(const rational &rhs); + + std::list Split(const int &chunk_size) const; + +private: + void normalize(); + + rational in_; + rational out_; + rational length_; + +}; + +class TimeRangeList { +public: + TimeRangeList() = default; + + TimeRangeList(std::initializer_list r) : + array_(r) + { + } + + void insert(const TimeRangeList &list_to_add); + void insert(TimeRange range_to_add); + + void remove(const TimeRange& remove); + void remove(const TimeRangeList &list); + + template + static void util_remove(std::vector *list, const TimeRange &remove) + { + std::vector additions; + + for (auto it = list->begin(); it != list->end(); ) { + T& compare = *it; + + if (remove.Contains(compare)) { + // This element is entirely encompassed in this range, remove it + it = list->erase(it); + } else { + if (compare.Contains(remove, false, false)) { + // The remove range is within this element, only choice is to split the element into two + T new_range = compare; + new_range.set_in(remove.out()); + compare.set_out(remove.in()); + + additions.push_back(new_range); + break; + } else { + if (compare.in() < remove.in() && compare.out() > remove.in()) { + // This element's out point overlaps the range's in, we'll trim it + compare.set_out(remove.in()); + } else if (compare.in() < remove.out() && compare.out() > remove.out()) { + // This element's in point overlaps the range's out, we'll trim it + compare.set_in(remove.out()); + } + + it++; + } + } + } + + list->insert(list->end(), additions.begin(), additions.end()); + } + + bool contains(const TimeRange& range, bool in_inclusive = true, bool out_inclusive = true) const; + + bool contains(const rational &r) const + { + for (const TimeRange &range : array_) { + if (range.Contains(r)) { + return true; + } + } + + return false; + } + + bool OverlapsWith(const TimeRange& r, bool in_inclusive = true, bool out_inclusive = true) const + { + for (const TimeRange &range : array_) { + if (range.OverlapsWith(r, in_inclusive, out_inclusive)) { + return true; + } + } + + return false; + } + + bool isEmpty() const + { + return array_.empty(); + } + + void clear() + { + array_.clear(); + } + + int size() const + { + return array_.size(); + } + + void shift(const rational& diff); + + void trim_in(const rational& diff); + + void trim_out(const rational& diff); + + TimeRangeList Intersects(const TimeRange& range) const; + + using const_iterator = std::vector::const_iterator; + + const_iterator begin() const + { + return array_.cbegin(); + } + + const_iterator end() const + { + return array_.cend(); + } + + const_iterator cbegin() const + { + return begin(); + } + + const_iterator cend() const + { + return end(); + } + + const TimeRange& first() const + { + return array_.front(); + } + + const TimeRange& last() const + { + return array_.back(); + } + + const TimeRange& at(int index) const + { + return array_.at(index); + } + + const std::vector& internal_array() const + { + return array_; + } + + bool operator==(const TimeRangeList &rhs) const + { + return array_ == rhs.array_; + } + +private: + std::vector array_; + +}; + +class TimeRangeListFrameIterator +{ +public: + TimeRangeListFrameIterator(); + TimeRangeListFrameIterator(const TimeRangeList &list, const rational &timebase); + + rational Snap(const rational &r) const; + + bool GetNext(rational *out); + + bool HasNext() const; + + std::vector ToVector() const + { + TimeRangeListFrameIterator copy(list_, timebase_); + std::vector times; + rational r; + while (copy.GetNext(&r)) { + times.push_back(r); + } + return times; + } + + int size(); + + void reset() + { + *this = TimeRangeListFrameIterator(); + } + + void insert(const TimeRange &range) + { + list_.insert(range); + } + + void insert(const TimeRangeList &list) + { + list_.insert(list); + } + + bool IsCustomRange() const + { + return custom_range_; + } + + void SetCustomRange(bool e) + { + custom_range_ = e; + } + + int frame_index() const + { + return frame_index_; + } + +private: + void UpdateIndexIfNecessary(); + + TimeRangeList list_; + + rational timebase_; + + rational current_; + + int range_index_; + + int size_; + + int frame_index_; + + bool custom_range_; + +}; + +} + +#endif // LIBOLIVECORE_TIMERANGE_H diff --git a/lib/olive/src/CMakeLists.txt b/lib/olive/src/CMakeLists.txt new file mode 100644 index 0000000000..8552d18823 --- /dev/null +++ b/lib/olive/src/CMakeLists.txt @@ -0,0 +1,24 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2022 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +add_subdirectory(render) +add_subdirectory(util) + +set(OLIVECORE_SOURCES + ${OLIVECORE_SOURCES} + PARENT_SCOPE +) + diff --git a/lib/olive/src/render/CMakeLists.txt b/lib/olive/src/render/CMakeLists.txt new file mode 100644 index 0000000000..aeafd8e8e2 --- /dev/null +++ b/lib/olive/src/render/CMakeLists.txt @@ -0,0 +1,23 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2022 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(OLIVECORE_SOURCES + ${OLIVECORE_SOURCES} + src/render/audioparams.cpp + src/render/samplebuffer.cpp + PARENT_SCOPE +) + diff --git a/lib/olive/src/render/audioparams.cpp b/lib/olive/src/render/audioparams.cpp new file mode 100644 index 0000000000..eeed783f1d --- /dev/null +++ b/lib/olive/src/render/audioparams.cpp @@ -0,0 +1,199 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "render/audioparams.h" + +#include + +#include "common/xmlutils.h" + +namespace olive { + +const std::vector AudioParams::kSupportedSampleRates = { + 8000, // 8000 Hz + 11025, // 11025 Hz + 16000, // 16000 Hz + 22050, // 22050 Hz + 24000, // 24000 Hz + 32000, // 32000 Hz + 44100, // 44100 Hz + 48000, // 48000 Hz + 88200, // 88200 Hz + 96000 // 96000 Hz +}; + +const std::vector AudioParams::kSupportedChannelLayouts = { + AV_CH_LAYOUT_MONO, + AV_CH_LAYOUT_STEREO, + AV_CH_LAYOUT_2_1, + AV_CH_LAYOUT_5POINT1, + AV_CH_LAYOUT_7POINT1 +}; + +bool AudioParams::operator==(const AudioParams &other) const +{ + return (format() == other.format() + && sample_rate() == other.sample_rate() + && time_base() == other.time_base() + && channel_layout() == other.channel_layout()); +} + +bool AudioParams::operator!=(const AudioParams &other) const +{ + return !(*this == other); +} + +int64_t AudioParams::time_to_bytes(const double &time) const +{ + return time_to_bytes_per_channel(time) * channel_count(); +} + +int64_t AudioParams::time_to_bytes(const rational &time) const +{ + return time_to_bytes(time.toDouble()); +} + +int64_t AudioParams::time_to_bytes_per_channel(const double &time) const +{ + assert(is_valid()); + + return int64_t(time_to_samples(time)) * bytes_per_sample_per_channel(); +} + +int64_t AudioParams::time_to_bytes_per_channel(const rational &time) const +{ + return time_to_bytes_per_channel(time.toDouble()); +} + +int64_t AudioParams::time_to_samples(const double &time) const +{ + assert(is_valid()); + + return std::round(double(sample_rate()) * time); +} + +int64_t AudioParams::time_to_samples(const rational &time) const +{ + return time_to_samples(time.toDouble()); +} + +int64_t AudioParams::samples_to_bytes(const int64_t &samples) const +{ + assert(is_valid()); + + return samples_to_bytes_per_channel(samples) * channel_count(); +} + +int64_t AudioParams::samples_to_bytes_per_channel(const int64_t &samples) const +{ + assert(is_valid()); + + return samples * bytes_per_sample_per_channel(); +} + +rational AudioParams::samples_to_time(const int64_t &samples) const +{ + return sample_rate_as_time_base() * samples; +} + +int64_t AudioParams::bytes_to_samples(const int64_t &bytes) const +{ + assert(is_valid()); + + return bytes / (channel_count() * bytes_per_sample_per_channel()); +} + +rational AudioParams::bytes_to_time(const int64_t &bytes) const +{ + assert(is_valid()); + + return samples_to_time(bytes_to_samples(bytes)); +} + +rational AudioParams::bytes_per_channel_to_time(const int64_t &bytes) const +{ + assert(is_valid()); + + return samples_to_time(bytes_to_samples(bytes * channel_count())); +} + +int AudioParams::channel_count() const +{ + return channel_count_; +} + +int AudioParams::bytes_per_sample_per_channel() const +{ + return format_.byte_count(); +} + +int AudioParams::bits_per_sample() const +{ + return bytes_per_sample_per_channel() * 8; +} + +bool AudioParams::is_valid() const +{ + return (!time_base().isNull() + && channel_layout() > 0 + && format_ > SampleFormat::INVALID + && format_ < SampleFormat::COUNT); +} + +void AudioParams::load(QXmlStreamReader *reader) +{ + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("samplerate")) { + set_sample_rate(reader->readElementText().toInt()); + } else if (reader->name() == QStringLiteral("channellayout")) { + set_channel_layout(reader->readElementText().toULongLong()); + } else if (reader->name() == QStringLiteral("format")) { + set_format(SampleFormat::from_string(reader->readElementText())); + } else if (reader->name() == QStringLiteral("enabled")) { + set_enabled(reader->readElementText().toInt()); + } else if (reader->name() == QStringLiteral("streamindex")) { + set_stream_index(reader->readElementText().toInt()); + } else if (reader->name() == QStringLiteral("duration")) { + set_duration(reader->readElementText().toLongLong()); + } else if (reader->name() == QStringLiteral("timebase")) { + set_time_base(rational::fromString(reader->readElementText())); + } else { + reader->skipCurrentElement(); + } + } +} + +void AudioParams::save(QXmlStreamWriter *writer) const +{ + writer->writeTextElement(QStringLiteral("samplerate"), QString::number(sample_rate())); + writer->writeTextElement(QStringLiteral("channellayout"), QString::number(channel_layout())); + writer->writeTextElement(QStringLiteral("format"), format().to_string()); + writer->writeTextElement(QStringLiteral("enabled"), QString::number(enabled())); + writer->writeTextElement(QStringLiteral("streamindex"), QString::number(stream_index())); + writer->writeTextElement(QStringLiteral("duration"), QString::number(duration())); + writer->writeTextElement(QStringLiteral("timebase"), time_base().toString()); +} + +void AudioParams::calculate_channel_count() +{ + channel_count_ = av_get_channel_layout_nb_channels(channel_layout()); +} + +} diff --git a/lib/olive/src/render/samplebuffer.cpp b/lib/olive/src/render/samplebuffer.cpp new file mode 100644 index 0000000000..f419334043 --- /dev/null +++ b/lib/olive/src/render/samplebuffer.cpp @@ -0,0 +1,301 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "render/samplebuffer.h" + +#include +#include +#include +#include + +#include "util/cpuoptimize.h" +#include "util/log.h" + +namespace olive { + +SampleBuffer::SampleBuffer() : + sample_count_per_channel_(0) +{ +} + +SampleBuffer::SampleBuffer(const AudioParams &audio_params, const rational &length) : + audio_params_(audio_params) +{ + sample_count_per_channel_ = audio_params_.time_to_samples(length); + allocate(); +} + +SampleBuffer::SampleBuffer(const AudioParams &audio_params, size_t samples_per_channel) : + audio_params_(audio_params), + sample_count_per_channel_(samples_per_channel) +{ + allocate(); +} + +SampleBuffer SampleBuffer::rip_channel(int channel) const +{ + AudioParams p = this->audio_params_; + p.set_channel_layout(AV_CH_LAYOUT_MONO); + + SampleBuffer b(p, this->sample_count_per_channel_); + b.fast_set(*this, 0, channel); + return b; +} + +std::vector SampleBuffer::rip_channel_vector(int channel) const +{ + return data_.at(channel); +} + +const AudioParams &SampleBuffer::audio_params() const +{ + return audio_params_; +} + +void SampleBuffer::set_audio_params(const AudioParams ¶ms) +{ + if (is_allocated()) { + Log::Warning() << "Tried to set parameters on allocated sample buffer"; + return; + } + + audio_params_ = params; +} + +void SampleBuffer::set_sample_count(const size_t &sample_count) +{ + if (is_allocated()) { + Log::Warning() << "Tried to set sample count on allocated sample buffer"; + return; + } + + sample_count_per_channel_ = sample_count; +} + +void SampleBuffer::allocate() +{ + if (!audio_params_.is_valid()) { + Log::Warning() << "Tried to allocate sample buffer with invalid audio parameters"; + return; + } + + if (!sample_count_per_channel_) { + Log::Warning() << "Tried to allocate sample buffer with zero sample count"; + return; + } + + if (is_allocated()) { + Log::Warning() << "Tried to allocate already allocated sample buffer"; + return; + } + + data_.resize(audio_params_.channel_count()); + for (int i=0; i(sample_count_per_channel_) / speed); + + std::vector< std::vector > output_data; + + output_data.resize(audio_params_.channel_count()); + for (int i=0; i(i) * speed); + + for (int j=0;jchannel_count() == output->channel_count()); + assert(input->sample_count_per_channel_ == output->sample_count_per_channel_); + + for (int i=0;iaudio_params().channel_count();i++) { + transform_volume_for_channel(i, f, input, output); + } +} + +void SampleBuffer::transform_volume_for_channel(int channel, float volume, const SampleBuffer *input, SampleBuffer *output) +{ + const float *cdat = input->data_[channel].data(); + float *odat = output->data_[channel].data(); + size_t unopt_start = 0; + + assert(input->channel_count() == output->channel_count()); + assert(input->sample_count_per_channel_ == output->sample_count_per_channel_); + +#if defined(OLIVE_PROCESSOR_X86) || defined(OLIVE_PROCESSOR_ARM) + __m128 mult = _mm_load1_ps(&volume); + unopt_start = (input->sample_count_per_channel_ / 4) * 4; + for (size_t j=0; jsample_count_per_channel_; j++) { + odat[j] = cdat[j] * volume; + } +} + +void SampleBuffer::transform_volume_for_sample(size_t sample_index, float volume) +{ + for (int i=0;i(data_[i].data()) + start_byte, 0, end_byte - start_byte); + } +} + +void SampleBuffer::set(int channel, const float *data, size_t sample_offset, size_t sample_length) +{ + if (!is_allocated()) { + Log::Warning() << "Tried to fill an unallocated sample buffer"; + return; + } + + memcpy(&data_[channel].data()[sample_offset], data, sizeof(float) * sample_length); +} + +void SampleBuffer::fast_set(const SampleBuffer &other, int to, int from) +{ + if (from == -1) { + from = to; + } + + data_[to] = other.data_[from]; +} + +void SampleBuffer::clamp_channel(int channel) +{ + const float min = -1.0f; + const float max = 1.0f; + + float *cdat = data_[channel].data(); + size_t unopt_start = 0; + +#if defined(OLIVE_PROCESSOR_X86) || defined(OLIVE_PROCESSOR_ARM) + __m128 min_sse = _mm_load1_ps(&min); + __m128 max_sse = _mm_load1_ps(&max); + + unopt_start = (sample_count_per_channel_ / 4) * 4; + for (size_t j=0; j. + +set(OLIVECORE_SOURCES + ${OLIVECORE_SOURCES} + src/util/bezier.cpp + src/util/color.cpp + src/util/rational.cpp + src/util/tests.cpp + src/util/timecodefunctions.cpp + src/util/timerange.cpp + PARENT_SCOPE +) + diff --git a/lib/olive/src/util/bezier.cpp b/lib/olive/src/util/bezier.cpp new file mode 100644 index 0000000000..9673ff6cf1 --- /dev/null +++ b/lib/olive/src/util/bezier.cpp @@ -0,0 +1,108 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "util/bezier.h" + +#include + +namespace olive { + +Bezier::Bezier() : + x_(0), + y_(0), + cp1_x_(0), + cp1_y_(0), + cp2_x_(0), + cp2_y_(0) +{ +} + +Bezier::Bezier(double x, double y) : + x_(x), + y_(y), + cp1_x_(0), + cp1_y_(0), + cp2_x_(0), + cp2_y_(0) +{ +} + +Bezier::Bezier(double x, double y, double cp1_x, double cp1_y, double cp2_x, double cp2_y) : + x_(x), + y_(y), + cp1_x_(cp1_x), + cp1_y_(cp1_y), + cp2_x_(cp2_x), + cp2_y_(cp2_y) +{ +} + +double Bezier::QuadraticXtoT(double x, double a, double b, double c) +{ + // Clamp to prevent infinite loop + x = std::clamp(x, a, c); + + return CalculateTFromX(false, x, a, b, c, 0); +} + +double Bezier::QuadraticTtoY(double a, double b, double c, double t) +{ + return std::pow(1.0 - t, 2)*a + 2*(1.0 - t)*t*b + std::pow(t, 2)*c; +} + +double Bezier::CubicXtoT(double x, double a, double b, double c, double d) +{ + // Clamp to prevent infinite loop + x = std::clamp(x, a, d); + + return CalculateTFromX(true, x, a, b, c, d); +} + +double Bezier::CubicTtoY(double a, double b, double c, double d, double t) +{ + return std::pow(1.0 - t, 3)*a + 3*std::pow(1.0 - t, 2)*t*b + 3*(1.0 - t)*std::pow(t, 2)*c + std::pow(t, 3)*d; +} + +double Bezier::CalculateTFromX(bool cubic, double x, double a, double b, double c, double d) +{ + double bottom = 0.0; + double top = 1.0; + + while (true) { + if (bottom == top) { + return bottom; + } + + double mid = (bottom + top) * 0.5; + double test = cubic ? CubicTtoY(a, b, c, d, mid) : QuadraticTtoY(a, b, c, mid); + + if (std::abs(test - x) < 0.000001) { + return mid; + } else if (x > test) { + bottom = mid; + } else { + top = mid; + } + } + + return NAN; +} + +} diff --git a/lib/olive/src/util/color.cpp b/lib/olive/src/util/color.cpp new file mode 100644 index 0000000000..88fa24836a --- /dev/null +++ b/lib/olive/src/util/color.cpp @@ -0,0 +1,309 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "util/color.h" + +#include +#include +#include +#include + +namespace olive { + +Color Color::fromHsv(const DataType &h, const DataType &s, const DataType &v) +{ + DataType C = s * v; + DataType X = C * (1.0 - std::abs(std::fmod(h / 60.0, 2.0) - 1.0)); + DataType m = v - C; + DataType Rs, Gs, Bs; + + if(h >= 0.0 && h < 60.0) { + Rs = C; + Gs = X; + Bs = 0.0; + } + else if(h >= 60.0 && h < 120.0) { + Rs = X; + Gs = C; + Bs = 0.0; + } + else if(h >= 120.0 && h < 180.0) { + Rs = 0.0; + Gs = C; + Bs = X; + } + else if(h >= 180.0 && h < 240.0) { + Rs = 0.0; + Gs = X; + Bs = C; + } + else if(h >= 240.0 && h < 300.0) { + Rs = X; + Gs = 0.0; + Bs = C; + } + else { + Rs = C; + Gs = 0.0; + Bs = X; + } + + return Color(Rs + m, Gs + m, Bs + m); +} + +Color::Color(const char *data, const PixelFormat &format, int ch_layout) +{ + *this = fromData(data, format, ch_layout); +} + +void Color::toHsv(DataType *hue, DataType *sat, DataType *val) const +{ + DataType fCMax = std::max(std::max(red(), green()), blue()); + DataType fCMin = std::min(std::min(red(), green()), blue()); + DataType fDelta = fCMax - fCMin; + + if(fDelta > 0) { + if(fCMax == red()) { + *hue = 60 * (fmod(((green() - blue()) / fDelta), 6)); + } else if(fCMax == green()) { + *hue = 60 * (((blue() - red()) / fDelta) + 2); + } else if(fCMax == blue()) { + *hue = 60 * (((red() - green()) / fDelta) + 4); + } + + if(fCMax > 0) { + *sat = fDelta / fCMax; + } else { + *sat = 0; + } + + *val = fCMax; + } else { + *hue = 0; + *sat = 0; + *val = fCMax; + } + + if(*hue < 0) { + *hue = 360 + *hue; + } +} + +Color::DataType Color::hsv_hue() const +{ + DataType h, s, v; + toHsv(&h, &s, &v); + return h; +} + +Color::DataType Color::hsv_saturation() const +{ + DataType h, s, v; + toHsv(&h, &s, &v); + return s; +} + +Color::DataType Color::value() const +{ + DataType h, s, v; + toHsv(&h, &s, &v); + return v; +} + +void Color::toHsl(DataType *hue, DataType *sat, DataType *lightness) const +{ + DataType fCMin = std::min(red(), std::min(green(), blue())); + DataType fCMax = std::max(red(), std::max(green(), blue())); + + *lightness = 0.5 * (fCMin + fCMax); + + if (fCMin == fCMax) + { + *sat = 0; + *hue = 0; + return; + + } + else if (*lightness < 0.5) + { + *sat = (fCMax - fCMin) / (fCMax + fCMin); + } + else + { + *sat = (fCMax - fCMin) / (2.0 - fCMax - fCMin); + } + + if (fCMax == red()) + { + *hue = 60 * (green() - blue()) / (fCMax - fCMin); + } + if (fCMax == green()) + { + *hue = 60 * (blue() - red()) / (fCMax - fCMin) + 120; + } + if (fCMax == blue()) + { + *hue = 60 * (red() - green()) / (fCMax - fCMin) + 240; + } + if (*hue < 0) + { + *hue = *hue + 360; + } +} + +Color::DataType Color::hsl_hue() const +{ + DataType h, s, l; + toHsl(&h, &s, &l); + return h; +} + +Color::DataType Color::hsl_saturation() const +{ + DataType h, s, l; + toHsl(&h, &s, &l); + return s; +} + +Color::DataType Color::lightness() const +{ + DataType h, s, l; + toHsl(&h, &s, &l); + return l; +} + +void Color::toData(char *out, const PixelFormat &format, unsigned int nb_channels) const +{ + unsigned int count = std::min(RGBA, nb_channels); + + for (unsigned int i = 0; i < count; i++) { + DataType f = data_[i]; + + switch (format) { + case PixelFormat::INVALID: + case PixelFormat::COUNT: + break; + case PixelFormat::U8: + reinterpret_cast(out)[i] = f * 255.0; + break; + case PixelFormat::U16: + reinterpret_cast(out)[i] = f * 65535.0; + break; + case PixelFormat::F16: + reinterpret_cast(out)[i] = f; + break; + case PixelFormat::F32: + reinterpret_cast(out)[i] = f; + break; + } + } +} + +Color Color::fromData(const char *in, const PixelFormat &format, unsigned int nb_channels) +{ + Color c; + + unsigned int count = std::min(RGBA, nb_channels); + + for (unsigned int i = 0; i < count; i++) { + DataType &f = c.data_[i]; + + switch (format) { + case PixelFormat::INVALID: + case PixelFormat::COUNT: + break; + case PixelFormat::U8: + f = DataType(reinterpret_cast(in)[i]) / 255.0; + break; + case PixelFormat::U16: + f = DataType(reinterpret_cast(in)[i]) / 65535.0; + break; + case PixelFormat::F16: + f = DataType(reinterpret_cast(in)[i]); + break; + case PixelFormat::F32: + f = DataType(reinterpret_cast(in)[i]); + break; + } + } + + return c; +} + +Color::DataType Color::GetRoughLuminance() const +{ + return (2*red()+blue()+3*green())/6.0; +} + +Color &Color::operator+=(const Color &rhs) +{ + for (int i = 0; i < RGBA; i++) { + data_[i] += rhs.data_[i]; + } + + return *this; +} + +Color &Color::operator-=(const Color &rhs) +{ + for (int i = 0; i < RGBA; i++) { + data_[i] -= rhs.data_[i]; + } + + return *this; +} + +Color &Color::operator+=(const DataType &rhs) +{ + for (int i = 0; i < RGBA; i++) { + data_[i] += rhs; + } + + return *this; +} + +Color &Color::operator-=(const DataType &rhs) +{ + for (int i = 0; i < RGBA; i++) { + data_[i] -= rhs; + } + + return *this; +} + +Color &Color::operator*=(const DataType &rhs) +{ + for (int i = 0; i < RGBA; i++) { + data_[i] *= rhs; + } + + return *this; +} + +Color &Color::operator/=(const DataType &rhs) +{ + for (int i = 0; i < RGBA; i++) { + data_[i] /= rhs; + } + + return *this; +} + +} diff --git a/lib/olive/src/util/rational.cpp b/lib/olive/src/util/rational.cpp new file mode 100644 index 0000000000..4f6dd51502 --- /dev/null +++ b/lib/olive/src/util/rational.cpp @@ -0,0 +1,294 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "util/rational.h" + +#include +#include + +namespace olive { + +const rational rational::NaN = rational(0, 0); + +rational rational::fromDouble(const double &flt, bool* ok) +{ + if (isnan(flt)) { + // Return NaN rational + if (ok) *ok = false; + return NaN; + } + + // Use FFmpeg function for the time being + AVRational r = av_d2q(flt, INT_MAX); + + if (r.den == 0) { + // If den == 0, we were unable to convert to a rational + if (ok) { + *ok = false; + } + } else { + // Otherwise, assume we received a real rational + if (ok) { + *ok = true; + } + } + + return r; +} + +rational rational::fromString(const QString &str, bool* ok) +{ + QStringList elements = str.split('/'); + + bool ok2; + switch (elements.size()) { + case 1: + { + int num = elements.front().toInt(&ok2); + if (ok2) { + if (ok) *ok = true; + return num; + } + } + case 2: + { + int num = elements.at(0).toInt(&ok2); + if (ok2) { + int den = elements.at(1).toInt(&ok2); + if (ok2) { + if (ok) *ok = true; + return rational(num, den); + } + } + } + default: + break; + } + + // Returns NaN with ok set to false + if (ok) { + *ok = false; + } + return NaN; +} + +//Function: convert to double + +double rational::toDouble() const +{ + if (r_.den != 0) { + return av_q2d(r_); + } else { + return std::numeric_limits::quiet_NaN(); + } +} + +AVRational rational::toAVRational() const +{ + return r_; +} + +#ifdef USE_OTIO +opentime::RationalTime rational::toRationalTime(double framerate) const +{ + // Is this the best way of doing this? + // Olive can store rationals as 0/0 which causes errors in OTIO + opentime::RationalTime time = opentime::RationalTime(r_.num, r_.den == 0 ? 1 : r_.den); + return time.rescaled_to(framerate); +} +#endif + +rational rational::flipped() const +{ + rational r = *this; + r.flip(); + return r; +} + +void rational::flip() +{ + if (!isNull()) { + std::swap(r_.den, r_.num); + fix_signs(); + } +} + +QString rational::toString() const +{ + return QStringLiteral("%1/%2").arg(QString::number(r_.num), QString::number(r_.den)); +} + +void rational::fix_signs() +{ + if (r_.den < 0) { + // Normalize so that denominator is always positive + r_.den = -r_.den; + r_.num = -r_.num; + } else if (r_.den == 0) { + // Normalize to 0/0 (aka NaN) if denominator is zero + r_.num = 0; + } else if (r_.num == 0) { + // Normalize to 0/1 if numerator is zero + r_.den = 1; + } +} + +void rational::reduce() +{ + av_reduce(&r_.num, &r_.den, r_.num, r_.den, INT_MAX); +} + +//Assignment Operators + +const rational& rational::operator=(const rational &rhs) +{ + r_ = rhs.r_; + return *this; +} + +const rational& rational::operator+=(const rational &rhs) +{ + if(*this == RATIONAL_MIN || *this == RATIONAL_MAX || rhs == RATIONAL_MIN || rhs == RATIONAL_MAX) { + *this = NaN; + } else if (!isNaN()) { + if (rhs.isNaN()) { + *this = NaN; + } else { + r_ = av_add_q(r_, rhs.r_); + fix_signs(); + } + } + + return *this; +} + +const rational& rational::operator-=(const rational &rhs) +{ + if (*this == RATIONAL_MIN || *this == RATIONAL_MAX || rhs == RATIONAL_MIN || rhs == RATIONAL_MAX) { + *this = NaN; + } else if (!isNaN()) { + if (rhs.isNaN()) { + *this = NaN; + } else { + r_ = av_sub_q(r_, rhs.r_); + fix_signs(); + } + } + + return *this; +} + +const rational& rational::operator*=(const rational &rhs) +{ + if (*this == RATIONAL_MIN || *this == RATIONAL_MAX || rhs == RATIONAL_MIN || rhs == RATIONAL_MAX) { + *this = NaN; + } else if (!isNaN()) { + if (rhs.isNaN()) { + *this = NaN; + } else { + r_ = av_mul_q(r_, rhs.r_); + fix_signs(); + } + } + + return *this; +} + +const rational& rational::operator/=(const rational &rhs) +{ + if (*this == RATIONAL_MIN || *this == RATIONAL_MAX || rhs == RATIONAL_MIN || rhs == RATIONAL_MAX) { + *this = NaN; + } else if (!isNaN()) { + if (rhs.isNaN()) { + *this = NaN; + } else { + r_ = av_div_q(r_, rhs.r_); + fix_signs(); + } + } + + return *this; +} + +//Binary math operators + +rational rational::operator+(const rational &rhs) const +{ + rational answer(*this); + answer += rhs; + return answer; +} + +rational rational::operator-(const rational &rhs) const +{ + rational answer(*this); + answer -= rhs; + return answer; +} + +rational rational::operator/(const rational &rhs) const +{ + rational answer(*this); + answer /= rhs; + return answer; +} + +rational rational::operator*(const rational &rhs) const +{ + rational answer(*this); + answer *= rhs; + return answer; +} + +//Relational and equality operators + +bool rational::operator<(const rational &rhs) const +{ + return av_cmp_q(r_, rhs.r_) == -1; +} + +bool rational::operator<=(const rational &rhs) const +{ + int cmp = av_cmp_q(r_, rhs.r_); + return cmp == 0 || cmp == -1; +} + +bool rational::operator>(const rational &rhs) const +{ + return av_cmp_q(r_, rhs.r_) == 1; +} + +bool rational::operator>=(const rational &rhs) const +{ + int cmp = av_cmp_q(r_, rhs.r_); + return cmp == 0 || cmp == 1; +} + +bool rational::operator==(const rational &rhs) const +{ + return av_cmp_q(r_, rhs.r_) == 0; +} + +bool rational::operator!=(const rational &rhs) const +{ + return !(*this == rhs); +} + +} diff --git a/lib/olive/src/util/tests.cpp b/lib/olive/src/util/tests.cpp new file mode 100644 index 0000000000..ef8647757f --- /dev/null +++ b/lib/olive/src/util/tests.cpp @@ -0,0 +1,61 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "util/tests.h" + +#include +#include +#include + +namespace olive { + +bool Tester::run() +{ + size_t index = 1; + size_t count = test_functions_.size(); + + while (!test_functions_.empty()) { + echo("[%lu/%lu] %s :: ", index, count, test_names_.front()); + + if (test_functions_.front()()) { + echo("PASSED\n"); + } else { + echo("FAILED\n"); + return false; + } + + test_names_.pop_front(); + test_functions_.pop_front(); + } + + return true; +} + +void Tester::echo(const char *fmt, ...) +{ + va_list a; + va_start(a, fmt); + + vfprintf(stderr, fmt, a); + + va_end(a); +} + +} diff --git a/lib/olive/src/util/timecodefunctions.cpp b/lib/olive/src/util/timecodefunctions.cpp new file mode 100644 index 0000000000..0d91b428c9 --- /dev/null +++ b/lib/olive/src/util/timecodefunctions.cpp @@ -0,0 +1,333 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "util/timecodefunctions.h" + +extern "C" { +#include +} + +#include +#include + +namespace olive { + +QString leftpad(int64_t t, int count) +{ + return QStringLiteral("%1").arg(qlonglong(t), count, 10, QChar('0')); +} + +QString Timecode::time_to_timecode(const rational &time, const rational &timebase, const Timecode::Display &display, bool show_plus_if_positive) +{ + if (timebase.isNull() || timebase.flipped().toDouble() < 1) { + return "INVALID TIMEBASE"; + } + + double time_dbl = time.toDouble(); + + switch (display) { + case kTimecodeNonDropFrame: + case kTimecodeDropFrame: + case kTimecodeSeconds: + { + const char *prefix = ""; + + if (time_dbl < 0) { + prefix = "-"; + } else if (show_plus_if_positive) { + prefix = "+"; + } + + if (display == kTimecodeSeconds) { + time_dbl = std::abs(time_dbl); + + int64_t total_seconds = std::floor(time_dbl); + + int64_t hours = total_seconds / 3600; + int64_t mins = total_seconds / 60 - hours * 60; + int64_t secs = total_seconds - mins * 60; + int64_t fraction = std::llround((time_dbl - static_cast(total_seconds)) * 1000); + + return QStringLiteral("%1%2:%3:%4.%5").arg(prefix, + leftpad(hours, 2), + leftpad(mins, 2), + leftpad(secs, 2), + leftpad(fraction, 3)); + } else { + // Determine what symbol to separate frames (";" is used for drop frame, ":" is non-drop frame) + const char *frame_token; + double frame_rate = timebase.flipped().toDouble(); + int rounded_frame_rate = std::llround(frame_rate); + int64_t frames, secs, mins, hours; + int64_t f = std::abs(time_to_timestamp(time, timebase)); + + if (display == kTimecodeDropFrame && timebase_is_drop_frame(timebase)) { + frame_token = ";"; + + /** + * CONVERT A FRAME NUMBER TO DROP FRAME TIMECODE + * + * Code by David Heidelberger, adapted from Andrew Duncan, further adapted for Olive by Olive Team + * Given an int called framenumber and a double called framerate + * Framerate should be 29.97, 59.94, or 23.976, otherwise the calculations will be off. + */ + + // If frame number is greater than 24 hrs, next operation will rollover clock + f %= (std::llround(frame_rate*3600)*24); + + // Number of frames per ten minutes + int64_t framesPer10Minutes = std::llround(frame_rate * 600); + int64_t d = f / framesPer10Minutes; + int64_t m = f % framesPer10Minutes; + + // Number of frames to drop on the minute marks is the nearest integer to 6% of the framerate + int64_t dropFrames = std::llround(frame_rate * (2.0/30.0)); + + // Number of frames per minute is the round of the framerate * 60 minus the number of dropped frames + f += dropFrames*9*d; + if (m > dropFrames) { + f += dropFrames * ((m - dropFrames) / (std::llround(frame_rate)*60 - dropFrames)); + } + } else { + frame_token = ":"; + } + + // non-drop timecode + hours = f / (3600*rounded_frame_rate); + mins = f / (60*rounded_frame_rate) % 60; + secs = f / rounded_frame_rate % 60; + frames = f % rounded_frame_rate; + + return QStringLiteral("%1%2:%3:%4%5%6").arg(prefix, + leftpad(hours, 2), + leftpad(mins, 2), + leftpad(secs, 2), + frame_token, + leftpad(frames, 2)); + } + } + case kFrames: + return QString::number(time_to_timestamp(time, timebase)); + case kMilliseconds: + return QString::number(std::llround(time_dbl * 1000)); + } + + return "INVALID TIMECODE MODE"; +} + +int64_t StrToInt64EmptyTolerant(const QString &s, bool *ok) +{ + return s.toLongLong(ok); +} + +double StrToDoubleEmptyTolerant(const QString &s, bool *ok) +{ + return s.toDouble(ok); +} + +rational Timecode::timecode_to_time(QString timecode, const rational &timebase, const Timecode::Display &display, bool *ok) +{ + timecode = timecode.trimmed(); + if (timecode.isEmpty()) { + goto err_fatal; + } + + switch (display) { + case kTimecodeNonDropFrame: + case kTimecodeDropFrame: + case kTimecodeSeconds: + { + QStringList timecode_split = timecode.split(QRegExp("(:)|(;)")); + + const int element_count = display == kTimecodeSeconds ? 3 : 4; + + // Remove excess tokens (we're only interested in HH:MM:SS.FF) + while (timecode_split.size() > element_count) { + timecode_split.removeLast(); + } + + // For easier index calculations, ensure minimum size + while (timecode_split.size() < element_count) { + timecode_split.prepend(QString()); + } + + bool negative = (timecode.at(0) == '-'); + + double frame_rate = timebase.flipped().toDouble(); + int rounded_frame_rate = std::lround(frame_rate); + + bool valid; + rational time; + + int64_t hours = StrToInt64EmptyTolerant(timecode_split.at(0), &valid); + if (!valid) goto err_fatal; + int64_t mins = StrToInt64EmptyTolerant(timecode_split.at(1), &valid); + if (!valid) goto err_fatal; + + if (display == kTimecodeSeconds) { + double secs = StrToDoubleEmptyTolerant(timecode_split.at(2), &valid); + if (!valid) goto err_fatal; + + time = rational::fromDouble(hours * 3600 + mins * 60 + secs); + } else { + int64_t secs = StrToInt64EmptyTolerant(timecode_split.at(2), &valid); + if (!valid) goto err_fatal; + int64_t frames = StrToInt64EmptyTolerant(timecode_split.at(3), &valid); + if (!valid) goto err_fatal; + + int64_t sec_count = (hours*3600 + mins*60 + secs); + int64_t frame_count = sec_count*rounded_frame_rate + frames; + + if (display == kTimecodeDropFrame && timebase_is_drop_frame(timebase)) { + + // Number of frames to drop on the minute marks is the nearest integer to 6% of the framerate + int64_t dropFrames = std::llround(frame_rate * (2.0/30.0)); + + // d and m need to be calculated from + int64_t real_fr_ts = std::llround(static_cast(sec_count)*frame_rate) + frames; + + int64_t framesPer10Minutes = std::llround(frame_rate * 600); + int64_t d = real_fr_ts / framesPer10Minutes; + int64_t m = real_fr_ts % framesPer10Minutes; + + if (m > dropFrames) { + frame_count -= dropFrames * ((m - dropFrames) / (std::llround(frame_rate)*60 - dropFrames)); + } + frame_count -= dropFrames*9*d; + } + + time = timestamp_to_time(frame_count, timebase); + } + + if (ok) *ok = true; + + if (negative) time = -time; + + return time; + } + case kMilliseconds: + { + double timecode_secs = timecode.toDouble(ok); + + // Convert milliseconds to seconds + timecode_secs *= 0.001; + + // Convert seconds to rational + return rational::fromDouble(timecode_secs, ok); + } + case kFrames: + return timecode.toLongLong(ok); + } + +err_fatal: + if (ok) *ok = false; + return 0; +} + +QString Timecode::time_to_string(int64_t ms) +{ + int64_t total_seconds = ms / 1000; + int64_t ss = total_seconds % 60; + int64_t mm = (total_seconds / 60) % 60; + int64_t hh = total_seconds / 3600; + + return QStringLiteral("%1:%2:%3").arg(leftpad(hh, 2), + leftpad(mm, 2), + leftpad(ss, 2)); +} + +rational Timecode::snap_time_to_timebase(const rational &time, const rational &timebase, Rounding floor) +{ + // Just convert to a timestamp in timebase units and back + int64_t timestamp = time_to_timestamp(time, timebase, floor); + + return timestamp_to_time(timestamp, timebase); +} + +rational Timecode::timestamp_to_time(const int64_t ×tamp, const rational &timebase) +{ + int64_t num = int64_t(timebase.numerator()) * timestamp; + int64_t den = timebase.denominator(); + + int num_r, den_r; + + av_reduce(&num_r, &den_r, num, den, INT_MAX); + + return rational(num_r, den_r); +} + +bool Timecode::timebase_is_drop_frame(const rational &timebase) +{ + return (timebase.numerator() != 1); +} + +int64_t Timecode::time_to_timestamp(const rational &time, const rational &timebase, Rounding floor) +{ + return time_to_timestamp(time.toDouble(), timebase, floor); +} + +int64_t Timecode::time_to_timestamp(const double &time, const rational &timebase, Rounding floor) +{ + const double d = time * timebase.flipped().toDouble(); + + if (std::isnan(d)) { + return 0; + } + + const double eps = 0.000000000001; + + switch (floor) { + case kRound: + default: + return std::llround(d); + case kFloor: + if (d > std::ceil(d)-eps) { + return std::ceil(d); + } else { + return std::floor(d); + } + case kCeil: + if (d < std::floor(d)+eps) { + return std::floor(d); + } else { + return std::ceil(d); + } + } +} + +int64_t Timecode::rescale_timestamp(const int64_t &ts, const rational &source, const rational &dest) +{ + if (source == dest) { + return ts; + } + + return av_rescale_q(ts, source.toAVRational(), dest.toAVRational()); +} + +int64_t Timecode::rescale_timestamp_ceil(const int64_t &ts, const rational &source, const rational &dest) +{ + if (source == dest) { + return ts; + } + + return av_rescale_q_rnd(ts, source.toAVRational(), dest.toAVRational(), AV_ROUND_UP); +} + +} diff --git a/lib/olive/src/util/timerange.cpp b/lib/olive/src/util/timerange.cpp new file mode 100644 index 0000000000..c07ba1fdcc --- /dev/null +++ b/lib/olive/src/util/timerange.cpp @@ -0,0 +1,381 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "util/timerange.h" + +#include +#include +#include + +#include "util/timecodefunctions.h" + +namespace olive { + +TimeRange::TimeRange(const rational &in, const rational &out) : + in_(in), + out_(out) +{ + normalize(); +} + +const rational &TimeRange::in() const +{ + return in_; +} + +const rational &TimeRange::out() const +{ + return out_; +} + +const rational &TimeRange::length() const +{ + return length_; +} + +void TimeRange::set_in(const rational &in) +{ + in_ = in; + normalize(); +} + +void TimeRange::set_out(const rational &out) +{ + out_ = out; + normalize(); +} + +void TimeRange::set_range(const rational &in, const rational &out) +{ + in_ = in; + out_ = out; + normalize(); +} + +bool TimeRange::operator==(const TimeRange &r) const +{ + return in() == r.in() && out() == r.out(); +} + +bool TimeRange::operator!=(const TimeRange &r) const +{ + return in() != r.in() || out() != r.out(); +} + +bool TimeRange::OverlapsWith(const TimeRange &a, bool in_inclusive, bool out_inclusive) const +{ + bool doesnt_overlap_in = (in_inclusive) ? (a.out() < in()) : (a.out() <= in()); + + bool doesnt_overlap_out = (out_inclusive) ? (a.in() > out()) : (a.in() >= out()); + + return !doesnt_overlap_in && !doesnt_overlap_out; +} + +TimeRange TimeRange::Combined(const TimeRange &a) const +{ + return Combine(a, *this); +} + +bool TimeRange::Contains(const TimeRange &compare, bool in_inclusive, bool out_inclusive) const +{ + bool contains_in = (in_inclusive) ? (compare.in() >= in()) : (compare.in() > in()); + + bool contains_out = (out_inclusive) ? (compare.out() <= out()) : (compare.out() < out()); + + return contains_in && contains_out; +} + +bool TimeRange::Contains(const rational &r) const +{ + return r >= in_ && r < out_; +} + +TimeRange TimeRange::Combine(const TimeRange &a, const TimeRange &b) +{ + return TimeRange(std::min(a.in(), b.in()), + std::max(a.out(), b.out())); +} + +TimeRange TimeRange::Intersected(const TimeRange &a) const +{ + return Intersect(a, *this); +} + +TimeRange TimeRange::Intersect(const TimeRange &a, const TimeRange &b) +{ + return TimeRange(std::max(a.in(), b.in()), + std::min(a.out(), b.out())); +} + +TimeRange TimeRange::operator+(const rational &rhs) const +{ + TimeRange answer(*this); + answer += rhs; + return answer; +} + +TimeRange TimeRange::operator-(const rational &rhs) const +{ + TimeRange answer(*this); + answer -= rhs; + return answer; +} + +const TimeRange &TimeRange::operator+=(const rational &rhs) +{ + set_range(in_ + rhs, out_ + rhs); + + return *this; +} + +const TimeRange &TimeRange::operator-=(const rational &rhs) +{ + set_range(in_ - rhs, out_ - rhs); + + return *this; +} + +std::list TimeRange::Split(const int &chunk_size) const +{ + std::list split_ranges; + + int start_time = std::floor(this->in().toDouble() / static_cast(chunk_size)) * chunk_size; + int end_time = std::ceil(this->out().toDouble() / static_cast(chunk_size)) * chunk_size; + + for (int i=start_time; iin(), rational(i)), + std::min(this->out(), rational(i + chunk_size)))); + } + + return split_ranges; +} + +void TimeRange::normalize() +{ + // If `out` is earlier than `in`, swap them + if (out_ < in_) + { + std::swap(out_, in_); + } + + // Calculate length + if (out_ == RATIONAL_MIN || out_ == RATIONAL_MAX || in_ == RATIONAL_MIN || in_ == RATIONAL_MAX) { + length_ = rational::NaN; + } else { + length_ = out_ - in_; + } +} + +void TimeRangeList::insert(const TimeRangeList &list_to_add) +{ + for (auto it=list_to_add.cbegin(); it!=list_to_add.cend(); it++) { + insert(*it); + } +} + +void TimeRangeList::insert(TimeRange range_to_add) +{ + // See if list contains this range + if (contains(range_to_add)) { + return; + } + + // Does not contain range, so we'll almost certainly be adding it in some way + for (auto it = array_.begin(); it != array_.end(); ) { + const TimeRange& compare = *it; + + if (compare.OverlapsWith(range_to_add)) { + range_to_add = TimeRange::Combine(range_to_add, compare); + it = array_.erase(it); + } else { + it++; + } + } + + array_.push_back(range_to_add); +} + +void TimeRangeList::remove(const TimeRange &remove) +{ + util_remove(&array_, remove); +} + +void TimeRangeList::remove(const TimeRangeList &list) +{ + for (const TimeRange &r : list) { + remove(r); + } +} + +bool TimeRangeList::contains(const TimeRange &range, bool in_inclusive, bool out_inclusive) const +{ + for (int i=0;i= range.out()) { + // No intersect + continue; + } else { + // Crop the time range to the range and add it to the list + TimeRange cropped(std::max(range.in(), compare.in()), + std::min(range.out(), compare.out())); + + intersect_list.insert(cropped); + } + } + + return intersect_list; +} + +TimeRangeListFrameIterator::TimeRangeListFrameIterator() : + TimeRangeListFrameIterator(TimeRangeList(), rational::NaN) +{ +} + +TimeRangeListFrameIterator::TimeRangeListFrameIterator(const TimeRangeList &list, const rational &timebase) : + list_(list), + timebase_(timebase), + range_index_(-1), + size_(-1), + frame_index_(0), + custom_range_(false) +{ + if (!list_.isEmpty() && timebase_.isNull()) { + std::cerr << "TimeRangeListFrameIterator created with null timebase but non-empty list, this will likely lead to infinite loops" << std::endl; + } + + UpdateIndexIfNecessary(); +} + +rational TimeRangeListFrameIterator::Snap(const rational &r) const +{ + return Timecode::snap_time_to_timebase(r, timebase_, Timecode::kFloor); +} + +bool TimeRangeListFrameIterator::GetNext(rational *out) +{ + if (!HasNext()) { + return false; + } + + // Output current value + *out = current_; + + // Determine next value by adding timebase + current_ += timebase_; + + // If this time is outside the current range, jump to the next one + UpdateIndexIfNecessary(); + + // Increment frame index + frame_index_++; + + return true; +} + +bool TimeRangeListFrameIterator::HasNext() const +{ + return range_index_ < list_.size(); +} + +int TimeRangeListFrameIterator::size() +{ + if (size_ == -1) { + // Size isn't calculated automatically for optimization, so we'll calculate it now + size_ = 0; + + for (const TimeRange &range : list_) { + rational start = Snap(range.in()); + rational end = Timecode::snap_time_to_timebase(range.out(), timebase_, Timecode::kFloor); + + if (end == range.out()) { + end -= timebase_; + } + + int64_t start_ts = Timecode::time_to_timestamp(start, timebase_); + int64_t end_ts = Timecode::time_to_timestamp(end, timebase_); + + size_ += 1 + (end_ts - start_ts); + } + } + + return size_; +} + +void TimeRangeListFrameIterator::UpdateIndexIfNecessary() +{ + while (range_index_ < list_.size() && (range_index_ == -1 || current_ >= list_.at(range_index_).out())) { + range_index_++; + + if (range_index_ < list_.size()) { + current_ = Snap(list_.at(range_index_).in()); + } + } +} + +} diff --git a/tests/general/CMakeLists.txt b/tests/general/CMakeLists.txt index a41a69ea46..ac940576e3 100644 --- a/tests/general/CMakeLists.txt +++ b/tests/general/CMakeLists.txt @@ -15,3 +15,7 @@ # along with this program. If not, see . olive_add_test(General common-tests common-tests.cpp) +olive_add_test(General rational-tests rational-tests.cpp) +olive_add_test(General stringutils-tests stringutils-tests.cpp) +olive_add_test(General timecode-tests timecode-tests.cpp) +olive_add_test(General timerange-tests timerange-tests.cpp) diff --git a/tests/general/rational-test.cpp b/tests/general/rational-test.cpp new file mode 100644 index 0000000000..c67a1a55f5 --- /dev/null +++ b/tests/general/rational-test.cpp @@ -0,0 +1,102 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include + +#include "util/rational.h" +#include "util/tests.h" + +using namespace olive::core; + +bool rational_to_from_string_test() +{ + rational r(1, 30); + + std::string s = r.toString(); + + rational r2 = rational::fromString(s); + + return r == r2; +} + +bool rational_to_from_string_test2() +{ + rational r(69, 420); + + std::string s = r.toString(); + + rational r2 = rational::fromString(s); + + return r == r2; +} + +bool rational_defaults() +{ + // By default, rationals are valid 0/1 + rational basic_constructor; + + if (!basic_constructor.isNull()) { + return false; + } + + if (basic_constructor.isNaN()) { + return false; + } + + return true; +} + +bool rational_nan() +{ + // Create a NaN with a 0 denominator + rational nan = rational(0, 0); + if (!nan.isNaN()) return false; + if (!nan.isNull()) return false; + + // Create a non-NaN with a zero numerator + rational zero_nonnan(0, 999); + if (!zero_nonnan.isNull()) return false; + if (zero_nonnan.isNaN()) return false; + + // Create a non-NaN with a non-zero numerator + rational nonzer_nonnan(1, 30); + if (nonzer_nonnan.isNull()) return false; + if (nonzer_nonnan.isNaN()) return false; + + return true; +} + +bool rational_nan_constant() +{ + return rational::NaN.isNaN(); +} + +int main() +{ + Tester t; + + t.add("rational::defaults", rational_defaults); + t.add("rational::NaN", rational_nan); + t.add("rational::NaN_constant", rational_nan_constant); + t.add("rational::toString/fromString", rational_to_from_string_test); + t.add("rational::toString/fromString2", rational_to_from_string_test2); + + return t.exec(); +} diff --git a/tests/general/stringutils-test.cpp b/tests/general/stringutils-test.cpp new file mode 100644 index 0000000000..fe96c5509e --- /dev/null +++ b/tests/general/stringutils-test.cpp @@ -0,0 +1,46 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include + +#include "util/stringutils.h" +#include "util/tests.h" + +using namespace olive::core; + +bool stringutils_format_test() +{ + const char *expected = "Hello, world!"; + std::string f = StringUtils::format("%s, %s!", "Hello", "world"); + if (strcmp(f.c_str(), expected) != 0) { + return false; + } + + return true; +} + +int main() +{ + Tester t; + + t.add("StringUtils::format", stringutils_format_test); + + return t.exec(); +} diff --git a/tests/general/timecode-test.cpp b/tests/general/timecode-test.cpp new file mode 100644 index 0000000000..29ce5a78d2 --- /dev/null +++ b/tests/general/timecode-test.cpp @@ -0,0 +1,60 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include + +#include "util/timecodefunctions.h" +#include "util/tests.h" + +using namespace olive::core; + +bool timecodefunctions_time_to_timecode_test() +{ + rational drop_frame_30(1001, 30000); + + std::string timecode = Timecode::time_to_timecode(rational(1), drop_frame_30, Timecode::kTimecodeDropFrame); + if (strcmp(timecode.c_str(), "00:00:01;00") != 0) { + return false; + } + + return true; +} + +bool timecodefunctions_time_to_timecode_test2() +{ + rational bizarre_timebase(156632219); + + std::string timecode = Timecode::time_to_timecode(rational(0), bizarre_timebase, Timecode::kTimecodeDropFrame); + if (strcmp(timecode.c_str(), "INVALID TIMEBASE") != 0) { + return false; + } + + return true; +} + +int main() +{ + Tester t; + + t.add("Timecode::time_to_timecode", timecodefunctions_time_to_timecode_test); + t.add("Timecode::time_to_timecode2", timecodefunctions_time_to_timecode_test2); + + return t.exec(); +} diff --git a/tests/general/timerange-test.cpp b/tests/general/timerange-test.cpp new file mode 100644 index 0000000000..fc13277ec0 --- /dev/null +++ b/tests/general/timerange-test.cpp @@ -0,0 +1,95 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include + +#include "util/timerange.h" +#include "util/tests.h" + +using namespace olive::core; + +bool timerangelist_remove_test() +{ + TimeRangeList t; + + t.insert(TimeRange(0, 30)); + t.remove(TimeRange(2, 5)); + + return true; +} + +bool timerangelist_mergeadjacent_test() +{ + TimeRangeList t; + + // TimeRangeList should merge 1 and 3 together since they're adjacent + t.insert(TimeRange(0, 6)); + t.insert(TimeRange(20, 30)); + t.insert(TimeRange(6, 10)); + + if (!(t.size() == 2)) { return false; } + if (!(t.first() == TimeRange(20, 30))) { return false; } + if (!(t.at(1) == TimeRange(0, 10))) { return false; } + + // TimeRangeList should ignore these because it's already contained + TimeRangeList noop_test = t; + + noop_test.insert(TimeRange(4, 7)); + if (!(noop_test == t)) { return false; } + + noop_test.insert(TimeRange(0, 3)); + if (!(noop_test == t)) { return false; } + + noop_test.insert(TimeRange(25, 30)); + if (!(noop_test == t)) { return false; } + + // TimeRangeList should combine all these together + TimeRangeList combine_test_no_overlap = t; + combine_test_no_overlap.insert(TimeRange(10, 20)); + if (!(combine_test_no_overlap.size() == 1)) { return false; } + if (!(combine_test_no_overlap.first() == TimeRange(0, 30))) { return false; } + + TimeRangeList combine_test_in_overlap = t; + combine_test_in_overlap.insert(TimeRange(9, 20)); + if (!(combine_test_in_overlap.size() == 1)) { return false; } + if (!(combine_test_in_overlap.first() == TimeRange(0, 30))) { return false; } + + TimeRangeList combine_test_out_overlap = t; + combine_test_out_overlap.insert(TimeRange(10, 21)); + if (!(combine_test_out_overlap.size() == 1)) { return false; } + if (!(combine_test_out_overlap.first() == TimeRange(0, 30))) { return false; } + + TimeRangeList combine_test_both_overlap = t; + combine_test_both_overlap.insert(TimeRange(9, 21)); + if (!(combine_test_both_overlap.size() == 1)) { return false; } + if (!(combine_test_both_overlap.first() == TimeRange(0, 30))) { return false; } + + return true; +} + +int main() +{ + Tester t; + + t.add("TimeRangeList::remove", timerangelist_remove_test); + t.add("TimeRangeList::merge_adjacent", timerangelist_mergeadjacent_test); + + return t.exec(); +} From b42256e57a318d929f18674fda397b81159160bd Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 27 Jun 2023 00:45:29 -0700 Subject: [PATCH 65/71] minor adjustments for merge --- app/node/generator/polygon/polygon.h | 1 + app/node/math/math/math.cpp | 1 + app/node/value.cpp | 2 +- app/render/job/audiojob.h | 4 ++-- app/widget/nodeview/curvedconnectoritem.cpp | 5 ++--- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/node/generator/polygon/polygon.h b/app/node/generator/polygon/polygon.h index 7463a825fd..87b9f6c596 100644 --- a/app/node/generator/polygon/polygon.h +++ b/app/node/generator/polygon/polygon.h @@ -29,6 +29,7 @@ #include "node/gizmo/point.h" #include "node/node.h" #include "node/inputdragger.h" +#include "util/bezier.h" namespace olive { diff --git a/app/node/math/math/math.cpp b/app/node/math/math/math.cpp index d392f4d527..5c2f967c75 100644 --- a/app/node/math/math/math.cpp +++ b/app/node/math/math/math.cpp @@ -21,6 +21,7 @@ #include "math.h" #include "mathfunctions.h" +#include "util/cpuoptimize.h" namespace olive { diff --git a/app/node/value.cpp b/app/node/value.cpp index 3438c2456e..56a1a2d3a6 100644 --- a/app/node/value.cpp +++ b/app/node/value.cpp @@ -289,7 +289,7 @@ value_t::component_t converter_RationalToString(const value_t::component_t &v) value_t::component_t converter_StringToRational(const value_t::component_t &v) { - return v.value().toStdString(); + return rational::fromString(v.value()); } value_t::component_t converter_BinaryToString(const value_t::component_t &v) diff --git a/app/render/job/audiojob.h b/app/render/job/audiojob.h index 43604c0170..015d24a026 100644 --- a/app/render/job/audiojob.h +++ b/app/render/job/audiojob.h @@ -21,15 +21,15 @@ #ifndef AUDIOJOB_H #define AUDIOJOB_H -#include #include #include "common/define.h" +#include "render/audioparams.h" +#include "util/timerange.h" namespace olive { class AcceleratedJob; -using namespace core; class AudioJob; using AudioJobPtr = std::shared_ptr; diff --git a/app/widget/nodeview/curvedconnectoritem.cpp b/app/widget/nodeview/curvedconnectoritem.cpp index 3b73128242..af8557f86b 100644 --- a/app/widget/nodeview/curvedconnectoritem.cpp +++ b/app/widget/nodeview/curvedconnectoritem.cpp @@ -20,7 +20,7 @@ #include "curvedconnectoritem.h" -#include +#include #include #include #include @@ -29,11 +29,10 @@ #include #include "common/lerp.h" +#include "util/bezier.h" namespace olive { -using namespace core; - CurvedConnectorItem::CurvedConnectorItem(QGraphicsItem* parent) : QGraphicsPathItem(parent) { From 82ee885e6a8e5d5cb06890dad3848db85402621a Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 5 Jul 2023 10:34:40 -0700 Subject: [PATCH 66/71] improve channel system --- CMakeLists.txt | 1 - app/audio/audioprocessor.cpp | 55 ++++--- app/audio/audioprocessor.h | 2 - app/codec/conformmanager.cpp | 2 +- app/codec/encoder.cpp | 4 +- app/codec/ffmpeg/ffmpegdecoder.cpp | 61 +++----- app/codec/ffmpeg/ffmpegdecoder.h | 6 +- app/codec/ffmpeg/ffmpegencoder.cpp | 35 ++--- app/codec/oiio/oiiodecoder.cpp | 4 +- app/common/qtutils.h | 9 +- app/config/config.cpp | 12 +- .../preferences/tabs/preferencesaudiotab.cpp | 8 +- app/dialog/sequence/sequence.cpp | 2 +- .../sequence/sequencedialogparametertab.h | 2 +- .../sequence/sequencedialogpresettab.cpp | 14 +- app/dialog/sequence/sequencepreset.h | 10 +- app/node/input/value/valuenode.cpp | 68 ++++----- app/node/input/value/valuenode.h | 10 +- app/node/inputimmediate.cpp | 12 +- app/node/inputimmediate.h | 7 +- app/node/node.cpp | 86 +++++++---- app/node/node.h | 12 +- app/node/output/viewer/viewer.cpp | 2 +- app/node/swizzlemap.h | 2 + app/node/type.h | 60 ++++++++ app/node/value.cpp | 32 +++- app/node/value.h | 69 +++------ app/render/opengl/openglrenderer.cpp | 12 -- app/render/videoparams.cpp | 8 +- app/render/videoparams.h | 21 ++- app/widget/nodevaluetree/nodevaluetree.cpp | 8 +- .../nodevaluetree/valueswizzlewidget.cpp | 137 ++++++++--------- app/widget/nodevaluetree/valueswizzlewidget.h | 34 ++--- .../standardcombos/channellayoutcombobox.h | 13 +- app/widget/viewer/viewer.cpp | 4 +- lib/olive/include/render/audiochannellayout.h | 138 ++++++++++++++++++ lib/olive/include/render/audioparams.h | 23 +-- lib/olive/src/render/CMakeLists.txt | 1 + lib/olive/src/render/audiochannellayout.cpp | 61 ++++++++ lib/olive/src/render/audioparams.cpp | 25 ++-- lib/olive/src/render/samplebuffer.cpp | 2 +- lib/olive/src/util/color.cpp | 19 +++ 42 files changed, 682 insertions(+), 411 deletions(-) create mode 100644 app/node/type.h create mode 100644 lib/olive/include/render/audiochannellayout.h create mode 100644 lib/olive/src/render/audiochannellayout.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9be21e6b20..8a7cb2c74e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,7 +64,6 @@ else() -Wextra -Wno-unused-parameter -Wshadow - -Wno-deprecated-declarations ) if (USE_WERROR) list(APPEND OLIVE_COMPILE_OPTIONS "-Werror") diff --git a/app/audio/audioprocessor.cpp b/app/audio/audioprocessor.cpp index cddf5aed90..701c3e8b02 100644 --- a/app/audio/audioprocessor.cpp +++ b/app/audio/audioprocessor.cpp @@ -34,7 +34,6 @@ namespace olive { AudioProcessor::AudioProcessor() { filter_graph_ = nullptr; - in_frame_ = nullptr; out_frame_ = nullptr; } @@ -61,12 +60,12 @@ bool AudioProcessor::Open(const AudioParams &from, const AudioParams &to, double // 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, + snprintf(filter_args, 200, "time_base=%d/%d:sample_rate=%d:sample_fmt=%d:channel_layout=%s", 1, from.sample_rate(), from.sample_rate(), from_fmt_, - from.channel_layout()); + from.channel_layout().toString().toUtf8().constData()); int r; @@ -120,10 +119,10 @@ bool AudioProcessor::Open(const AudioParams &from, const AudioParams &to, double || (to.format().is_planar() && 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, + snprintf(filter_args, 200, "sample_fmts=%s:sample_rates=%d:channel_layouts=%s", av_get_sample_fmt_name(to_fmt_), to.sample_rate(), - to.channel_layout()); + to.channel_layout().toString().toUtf8().constData()); AVFilterContext *c; r = avfilter_graph_create_filter(&c, avfilter_get_by_name("aformat"), "fmt", filter_args, nullptr, filter_graph_); @@ -165,19 +164,6 @@ bool AudioProcessor::Open(const AudioParams &from, const AudioParams &to, double 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"; @@ -200,11 +186,6 @@ void AudioProcessor::Close() 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; @@ -221,14 +202,32 @@ int AudioProcessor::Convert(float **in, int nb_in_samples, AudioProcessor::Buffe int r = 0; if (in && nb_in_samples) { - // Set frame parameters - in_frame_->nb_samples = nb_in_samples; + // Pass pointers to AVFilter through an AVFrame + AVFrame *in_frame = av_frame_alloc(); + if (!in_frame) { + qCritical() << "Failed to allocate input frame"; + return -1; + } + + in_frame->sample_rate = from_.sample_rate(); + in_frame->format = from_fmt_; + from_.channel_layout().exportTo(&in_frame->ch_layout); + in_frame->pts = 0; + + in_frame->nb_samples = nb_in_samples; + for (int i=0; idata[i] = reinterpret_cast(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); + for (int i=0; idata[i] = reinterpret_cast(in[i]); - in_frame_->linesize[i] = from_.samples_to_bytes(nb_in_samples); + in_frame->data[i] = nullptr; + in_frame->linesize[i] = 0; } - r = av_buffersrc_add_frame_flags(buffersrc_ctx_, in_frame_, AV_BUFFERSRC_FLAG_KEEP_REF); + av_frame_free(&in_frame); if (r < 0) { qCritical() << "Failed to add frame to buffersrc:" << r; return r; diff --git a/app/audio/audioprocessor.h b/app/audio/audioprocessor.h index 36e6f587fe..0e730fd1eb 100644 --- a/app/audio/audioprocessor.h +++ b/app/audio/audioprocessor.h @@ -71,8 +71,6 @@ class AudioProcessor AudioParams to_; AVSampleFormat to_fmt_; - AVFrame *in_frame_; - AVFrame *out_frame_; }; diff --git a/app/codec/conformmanager.cpp b/app/codec/conformmanager.cpp index 760b82daed..8a9330d941 100644 --- a/app/codec/conformmanager.cpp +++ b/app/codec/conformmanager.cpp @@ -67,7 +67,7 @@ QVector ConformManager::GetConformedFilename(const QString &cache_path, QString::number(stream.stream()), QString::number(params.sample_rate()), QString::number(params.format()), - QString::number(params.channel_layout()), + params.channel_layout().toString(), QString::number(i)); filenames[i] = QDir(cache_path).filePath(index_fn); diff --git a/app/codec/encoder.cpp b/app/codec/encoder.cpp index 0787d71ebc..56fa88ffc8 100644 --- a/app/codec/encoder.cpp +++ b/app/codec/encoder.cpp @@ -257,7 +257,7 @@ void EncodingParams::Save(QXmlStreamWriter *writer) const if (audio_enabled_) { writer->writeTextElement(QStringLiteral("codec"), QString::number(audio_codec_)); writer->writeTextElement(QStringLiteral("samplerate"), QString::number(audio_params_.sample_rate())); - writer->writeTextElement(QStringLiteral("channellayout"), QString::number(audio_params_.channel_layout())); + writer->writeTextElement(QStringLiteral("channellayout"), audio_params_.channel_layout().toString()); writer->writeTextElement(QStringLiteral("format"), audio_params_.format().to_string()); writer->writeTextElement(QStringLiteral("bitrate"), QString::number(audio_bit_rate_)); } @@ -470,7 +470,7 @@ bool EncodingParams::LoadV1(QXmlStreamReader *reader) } else if (reader->name() == QStringLiteral("samplerate")) { audio_params_.set_sample_rate(reader->readElementText().toInt()); } else if (reader->name() == QStringLiteral("channellayout")) { - audio_params_.set_channel_layout(reader->readElementText().toULongLong()); + audio_params_.set_channel_layout(AudioChannelLayout::fromString(reader->readElementText())); } else if (reader->name() == QStringLiteral("format")) { audio_params_.set_format(SampleFormat::from_string(reader->readElementText())); } else if (reader->name() == QStringLiteral("bitrate")) { diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index 41a0a0b899..1eb5c2e428 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -447,7 +447,7 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can } else if (avstream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { // Create an audio stream object - uint64_t channel_layout = ValidateChannelLayout(avstream); + AudioChannelLayout channel_layout = avstream->codecpar->ch_layout; if (avstream->duration == AV_NOPTS_VALUE || duration_guessed_from_bitrate) { // Loop through stream until we get the whole duration @@ -555,29 +555,23 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector &filenames, cons // Seek to starting point instance_.Seek(0); - // Handle NULL channel layout - uint64_t channel_layout = ValidateChannelLayout(instance_.avstream()); - if (!channel_layout) { - qCritical() << "Failed to determine channel layout of audio file, could not conform"; - return false; - } - // Create resampling context - SwrContext* resampler = swr_alloc_set_opts(nullptr, - params.channel_layout(), - FFmpegUtils::GetFFmpegSampleFormat(params.format()), - params.sample_rate(), - channel_layout, - static_cast(instance_.avstream()->codecpar->format), - instance_.avstream()->codecpar->sample_rate, - 0, - nullptr); + int ret; + SwrContext* resampler; + ret = swr_alloc_set_opts2(&resampler, + params.channel_layout().ref(), + FFmpegUtils::GetFFmpegSampleFormat(params.format()), + params.sample_rate(), + &instance_.avstream()->codecpar->ch_layout, + static_cast(instance_.avstream()->codecpar->format), + instance_.avstream()->codecpar->sample_rate, + 0, + nullptr); swr_init(resampler); AVPacket* pkt = av_packet_alloc(); AVFrame* frame = av_frame_alloc(); - int ret; bool success = false; @@ -787,15 +781,6 @@ int FFmpegDecoder::GetNativeChannelCount(AVPixelFormat pix_fmt) } } -uint64_t FFmpegDecoder::ValidateChannelLayout(AVStream* stream) -{ - if (stream->codecpar->channel_layout) { - return stream->codecpar->channel_layout; - } - - return av_get_default_channel_layout(stream->codecpar->channels); -} - const char *FFmpegDecoder::GetInterlacingModeInFFmpeg(VideoParams::Interlacing interlacing) { if (interlacing == VideoParams::kInterlacedTopFirst) { @@ -1116,11 +1101,9 @@ Decoder::RetrieveAudioStatus FFmpegDecoder::ValidateResampler(const AudioParams auto src_fmt_native = FFmpegUtils::GetNativeSampleFormat(static_cast(instance_.avstream()->codecpar->format)); // We need a resampler. Check if one already exists. - int64_t input_ch_layout = ValidateChannelLayout(instance_.avstream()); - if (swr_ctx_ && swr_irate_ == instance_.avstream()->codecpar->sample_rate - && swr_ichannels_ == input_ch_layout + && swr_ichannels_ == instance_.avstream()->codecpar->ch_layout && swr_iformat_ == src_fmt_native && swr_orate_ == output.sample_rate() && swr_ochannels_ == output.channel_layout() @@ -1132,14 +1115,14 @@ Decoder::RetrieveAudioStatus FFmpegDecoder::ValidateResampler(const AudioParams int r; - swr_ctx_ = swr_alloc_set_opts(nullptr, - output.channel_layout(), - FFmpegUtils::GetFFmpegSampleFormat(output.format()), - output.sample_rate(), - input_ch_layout, - static_cast(instance_.avstream()->codecpar->format), - instance_.avstream()->codecpar->sample_rate, - 0, nullptr); + r = swr_alloc_set_opts2(&swr_ctx_, + output.channel_layout().ref(), + FFmpegUtils::GetFFmpegSampleFormat(output.format()), + output.sample_rate(), + &instance_.avstream()->codecpar->ch_layout, + static_cast(instance_.avstream()->codecpar->format), + instance_.avstream()->codecpar->sample_rate, + 0, nullptr); if (!swr_ctx_) { qCritical() << "Failed to allocate SwrContext"; @@ -1153,7 +1136,7 @@ Decoder::RetrieveAudioStatus FFmpegDecoder::ValidateResampler(const AudioParams } swr_irate_ = instance_.avstream()->codecpar->sample_rate; - swr_ichannels_ = input_ch_layout; + swr_ichannels_ = instance_.avstream()->codecpar->ch_layout; swr_iformat_ = src_fmt_native; swr_orate_ = output.sample_rate(); swr_ochannels_ = output.channel_layout(); diff --git a/app/codec/ffmpeg/ffmpegdecoder.h b/app/codec/ffmpeg/ffmpegdecoder.h index 2676e7e660..f8b3c9ec8e 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.h +++ b/app/codec/ffmpeg/ffmpegdecoder.h @@ -144,8 +144,6 @@ class FFmpegDecoder : public Decoder static int GetNativeChannelCount(AVPixelFormat pix_fmt); - static uint64_t ValidateChannelLayout(AVStream *stream); - static const char* GetInterlacingModeInFFmpeg(VideoParams::Interlacing interlacing); static bool IsPixelFormatGLSLCompatible(AVPixelFormat f); @@ -181,10 +179,10 @@ class FFmpegDecoder : public Decoder SwrContext *swr_ctx_; int swr_irate_; - int64_t swr_ichannels_; + AudioChannelLayout swr_ichannels_; SampleFormat swr_iformat_; int swr_orate_; - int64_t swr_ochannels_; + AudioChannelLayout swr_ochannels_; SampleFormat swr_oformat_; int64_t swr_time_; diff --git a/app/codec/ffmpeg/ffmpegencoder.cpp b/app/codec/ffmpeg/ffmpegencoder.cpp index f5ca225a4d..5089c7482c 100644 --- a/app/codec/ffmpeg/ffmpegencoder.cpp +++ b/app/codec/ffmpeg/ffmpegencoder.cpp @@ -334,7 +334,7 @@ bool FFmpegEncoder::WriteAudioData(const AudioParams &audio_params, const uint8_ int output_sample_count = input_sample_count ? swr_get_out_samples(audio_resample_ctx_, input_sample_count) : 102400; uint8_t** output_data = nullptr; int output_linesize; - av_samples_alloc_array_and_samples(&output_data, &output_linesize, audio_stream_->codecpar->channels, + av_samples_alloc_array_and_samples(&output_data, &output_linesize, audio_stream_->codecpar->ch_layout.nb_channels, output_sample_count, static_cast(audio_stream_->codecpar->format), 0); // Perform conversion @@ -349,7 +349,7 @@ bool FFmpegEncoder::WriteAudioData(const AudioParams &audio_params, const uint8_ av_samples_copy(audio_frame_->data, output_data, audio_frame_offset_, i, copy_length, - audio_frame_->channels, static_cast(audio_frame_->format)); + audio_frame_->ch_layout.nb_channels, static_cast(audio_frame_->format)); audio_frame_offset_ += copy_length; i += copy_length; @@ -690,8 +690,7 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV // Assume audio stream codec_ctx->sample_rate = params().audio_params().sample_rate(); - codec_ctx->channel_layout = params().audio_params().channel_layout(); - codec_ctx->channels = av_get_channel_layout_nb_channels(codec_ctx->channel_layout); + params().audio_params().channel_layout().exportTo(codec_ctx->ch_layout); codec_ctx->sample_fmt = FFmpegUtils::GetFFmpegSampleFormat(params().audio_params().format()); codec_ctx->time_base = {1, codec_ctx->sample_rate}; @@ -829,22 +828,24 @@ bool FFmpegEncoder::InitializeResampleContext(const AudioParams &audio) } // Create resample context - audio_resample_ctx_ = swr_alloc_set_opts(nullptr, - static_cast(audio_codec_ctx_->channel_layout), - audio_codec_ctx_->sample_fmt, - audio_codec_ctx_->sample_rate, - static_cast(audio.channel_layout()), - FFmpegUtils::GetFFmpegSampleFormat(audio.format()), - audio.sample_rate(), - 0, - nullptr); - if (!audio_resample_ctx_) { + int err; + err = swr_alloc_set_opts2(&audio_resample_ctx_, + &audio_codec_ctx_->ch_layout, + audio_codec_ctx_->sample_fmt, + audio_codec_ctx_->sample_rate, + audio.channel_layout().ref(), + FFmpegUtils::GetFFmpegSampleFormat(audio.format()), + audio.sample_rate(), + 0, + nullptr); + if (err < 0) { + FFmpegError(tr("Failed to create resampling context"), err); return false; } - int err = swr_init(audio_resample_ctx_); + err = swr_init(audio_resample_ctx_); if (err < 0) { - FFmpegError(tr("Failed to create resampling context"), err); + FFmpegError(tr("Failed to initialize resampling context"), err); return false; } @@ -865,7 +866,7 @@ bool FFmpegEncoder::InitializeResampleContext(const AudioParams &audio) return false; } - audio_frame_->channel_layout = audio_codec_ctx_->channel_layout; + audio_frame_->ch_layout = audio_codec_ctx_->ch_layout; audio_frame_->format = audio_codec_ctx_->sample_fmt; audio_frame_->nb_samples = audio_max_samples_; diff --git a/app/codec/oiio/oiiodecoder.cpp b/app/codec/oiio/oiiodecoder.cpp index e3efc41086..82f9ae6dd6 100644 --- a/app/codec/oiio/oiiodecoder.cpp +++ b/app/codec/oiio/oiiodecoder.cpp @@ -134,10 +134,10 @@ TexturePtr OIIODecoder::RetrieveVideoInternal(const RetrieveVideoParams &p) if (p.divider == 1) { // Just upload straight to the buffer - image_->read_image(oiio_pix_fmt_, buffer_.data(), OIIO::AutoStride, buffer_.linesize_bytes()); + image_->read_image(0, 0, 0, -1, oiio_pix_fmt_, buffer_.data(), OIIO::AutoStride, buffer_.linesize_bytes()); } else { OIIO::ImageBuf buf(image_->spec()); - image_->read_image(image_->spec().format, buf.localpixels(), buf.pixel_stride(), buf.scanline_stride(), buf.z_stride()); + image_->read_image(0, 0, 0, -1, image_->spec().format, buf.localpixels(), buf.pixel_stride(), buf.scanline_stride(), buf.z_stride()); // Roughly downsample image for divider (for some reason OIIO::ImageBufAlgo::resample failed here) int px_sz = vp.GetBytesPerPixel(); diff --git a/app/common/qtutils.h b/app/common/qtutils.h index 092d045394..a3a0f0279c 100644 --- a/app/common/qtutils.h +++ b/app/common/qtutils.h @@ -106,11 +106,12 @@ uint qHash(const AudioParams& r, uint seed = 0); } -Q_DECLARE_METATYPE(olive::rational); -Q_DECLARE_METATYPE(olive::Color); -Q_DECLARE_METATYPE(olive::TimeRange); -Q_DECLARE_METATYPE(olive::Bezier); +Q_DECLARE_METATYPE(olive::AudioChannelLayout); Q_DECLARE_METATYPE(olive::AudioParams); +Q_DECLARE_METATYPE(olive::Bezier); +Q_DECLARE_METATYPE(olive::Color); Q_DECLARE_METATYPE(olive::SampleBuffer); +Q_DECLARE_METATYPE(olive::TimeRange); +Q_DECLARE_METATYPE(olive::rational); #endif // QTVERSIONABSTRACTION_H diff --git a/app/config/config.cpp b/app/config/config.cpp index d6c4ac341c..dac3d2c249 100644 --- a/app/config/config.cpp +++ b/app/config/config.cpp @@ -133,13 +133,13 @@ void Config::SetDefaults() OLIVE_CONFIG("AudioInput") = QString(); OLIVE_CONFIG("AudioOutputSampleRate") = 48000; - OLIVE_CONFIG("AudioOutputChannelLayout") = int64_t(AV_CH_LAYOUT_STEREO); + OLIVE_CONFIG("AudioOutputChannelLayout") = AudioChannelLayout::STEREO.toString(); OLIVE_CONFIG("AudioOutputSampleFormat") = SampleFormat(SampleFormat::S16).to_string(); OLIVE_CONFIG("AudioRecordingFormat") = ExportFormat::kFormatWAV; OLIVE_CONFIG("AudioRecordingCodec") = ExportCodec::kCodecPCM; OLIVE_CONFIG("AudioRecordingSampleRate") = 48000; - OLIVE_CONFIG("AudioRecordingChannelLayout") = int64_t(AV_CH_LAYOUT_STEREO); + OLIVE_CONFIG("AudioRecordingChannelLayout") = AudioChannelLayout::STEREO.toString(); OLIVE_CONFIG("AudioRecordingSampleFormat") = SampleFormat(SampleFormat::S16).to_string(); OLIVE_CONFIG("AudioRecordingBitRate") = 320; @@ -153,7 +153,7 @@ void Config::SetDefaults() OLIVE_CONFIG("DefaultSequenceInterlacing") = VideoParams::kInterlaceNone; OLIVE_CONFIG("DefaultSequenceAutoCache2") = false; OLIVE_CONFIG("DefaultSequenceAudioFrequency") = 48000; - OLIVE_CONFIG("DefaultSequenceAudioLayout") = int64_t(AV_CH_LAYOUT_STEREO); + OLIVE_CONFIG("DefaultSequenceAudioLayout") = AudioChannelLayout::STEREO.toString(); // Online/offline settings OLIVE_CONFIG("OnlinePixelFormat") = PixelFormat::F32; @@ -164,6 +164,9 @@ void Config::SetDefaults() void Config::Load() { + // Reset to defaults + current_config_.SetDefaults(); + QFile config_file(GetConfigFilePath()); if (!config_file.exists()) { @@ -175,9 +178,6 @@ void Config::Load() return; } - // Reset to defaults - current_config_.SetDefaults(); - QXmlStreamReader reader(&config_file); QString config_version; diff --git a/app/dialog/preferences/tabs/preferencesaudiotab.cpp b/app/dialog/preferences/tabs/preferencesaudiotab.cpp index d64cd781c1..7fc4a7bdbb 100644 --- a/app/dialog/preferences/tabs/preferencesaudiotab.cpp +++ b/app/dialog/preferences/tabs/preferencesaudiotab.cpp @@ -91,7 +91,7 @@ PreferencesAudioTab::PreferencesAudioTab() 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").toInt()); + output_ch_layout_combo_->SetChannelLayout(AudioChannelLayout::fromString(OLIVE_CONFIG("AudioOutputChannelLayout").toString())); output_param_layout->addWidget(output_ch_layout_combo_, output_row, 1); output_row++; @@ -140,7 +140,7 @@ PreferencesAudioTab::PreferencesAudioTab() record_options_->SetFormat(record_format_combo_->GetFormat()); record_options_->SetCodec(static_cast(OLIVE_CONFIG("AudioRecordingCodec").toInt())); record_options_->sample_rate_combobox()->SetSampleRate(OLIVE_CONFIG("AudioRecordingSampleRate").toInt()); - record_options_->channel_layout_combobox()->SetChannelLayout(OLIVE_CONFIG("AudioRecordingChannelLayout").toInt()); + record_options_->channel_layout_combobox()->SetChannelLayout(AudioChannelLayout::fromString(OLIVE_CONFIG("AudioRecordingChannelLayout").toString())); record_options_->bit_rate_slider()->SetValue(OLIVE_CONFIG("AudioRecordingBitRate").toInt()); record_options_->sample_format_combobox()->SetSampleFormat(SampleFormat::from_string(OLIVE_CONFIG("AudioRecordingSampleFormat").toString())); recording_layout->addWidget(record_options_); @@ -181,13 +181,13 @@ void PreferencesAudioTab::Accept(MultiUndoCommand *command) AudioManager::instance()->SetInputDevice(input_device); OLIVE_CONFIG("AudioOutputSampleRate") = output_rate_combo_->GetSampleRate(); - OLIVE_CONFIG("AudioOutputChannelLayout") = output_ch_layout_combo_->GetChannelLayout(); + OLIVE_CONFIG("AudioOutputChannelLayout") = output_ch_layout_combo_->GetChannelLayout().toString(); OLIVE_CONFIG("AudioOutputSampleFormat") = output_fmt_combo_->GetSampleFormat().to_string(); 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") = record_options_->channel_layout_combobox()->GetChannelLayout(); + OLIVE_CONFIG("AudioRecordingChannelLayout") = record_options_->channel_layout_combobox()->GetChannelLayout().toString(); OLIVE_CONFIG("AudioRecordingBitRate") = record_options_->bit_rate_slider()->GetValue(); OLIVE_CONFIG("AudioRecordingSampleFormat") = record_options_->sample_format_combobox()->GetSampleFormat().to_string(); diff --git a/app/dialog/sequence/sequence.cpp b/app/dialog/sequence/sequence.cpp index 04a8b351a2..d3c7180351 100644 --- a/app/dialog/sequence/sequence.cpp +++ b/app/dialog/sequence/sequence.cpp @@ -176,7 +176,7 @@ void SequenceDialog::SetAsDefaultClicked() OLIVE_CONFIG("DefaultSequenceFrameRate") = parameter_tab_->GetSelectedVideoFrameRate().flipped(); OLIVE_CONFIG("DefaultSequenceInterlacing") = parameter_tab_->GetSelectedVideoInterlacingMode(); OLIVE_CONFIG("DefaultSequenceAudioFrequency") = parameter_tab_->GetSelectedAudioSampleRate(); - OLIVE_CONFIG("DefaultSequenceAudioLayout") = parameter_tab_->GetSelectedAudioChannelLayout(); + OLIVE_CONFIG("DefaultSequenceAudioLayout") = parameter_tab_->GetSelectedAudioChannelLayout().toString(); } } diff --git a/app/dialog/sequence/sequencedialogparametertab.h b/app/dialog/sequence/sequencedialogparametertab.h index 0efef7e7c6..887af3e472 100644 --- a/app/dialog/sequence/sequencedialogparametertab.h +++ b/app/dialog/sequence/sequencedialogparametertab.h @@ -49,7 +49,7 @@ class SequenceDialogParameterTab : public QWidget return audio_sample_rate_field_->GetSampleRate(); } - uint64_t GetSelectedAudioChannelLayout() const + AudioChannelLayout GetSelectedAudioChannelLayout() const { return audio_channels_field_->GetChannelLayout(); } diff --git a/app/dialog/sequence/sequencedialogpresettab.cpp b/app/dialog/sequence/sequencedialogpresettab.cpp index b027066c1a..ea064e7a07 100644 --- a/app/dialog/sequence/sequencedialogpresettab.cpp +++ b/app/dialog/sequence/sequencedialogpresettab.cpp @@ -109,7 +109,7 @@ QTreeWidgetItem *SequenceDialogPresetTab::CreateHDPresetFolder(const QString &na VideoParams::kPixelAspectSquare, VideoParams::kInterlaceNone, 48000, - AV_CH_LAYOUT_STEREO, + AudioChannelLayout::STEREO, divider, default_format, default_autocache)); @@ -120,7 +120,7 @@ QTreeWidgetItem *SequenceDialogPresetTab::CreateHDPresetFolder(const QString &na VideoParams::kPixelAspectSquare, VideoParams::kInterlaceNone, 48000, - AV_CH_LAYOUT_STEREO, + AudioChannelLayout::STEREO, divider, default_format, default_autocache)); @@ -131,7 +131,7 @@ QTreeWidgetItem *SequenceDialogPresetTab::CreateHDPresetFolder(const QString &na VideoParams::kPixelAspectSquare, VideoParams::kInterlaceNone, 48000, - AV_CH_LAYOUT_STEREO, + AudioChannelLayout::STEREO, divider, default_format, default_autocache)); @@ -142,7 +142,7 @@ QTreeWidgetItem *SequenceDialogPresetTab::CreateHDPresetFolder(const QString &na VideoParams::kPixelAspectSquare, VideoParams::kInterlaceNone, 48000, - AV_CH_LAYOUT_STEREO, + AudioChannelLayout::STEREO, divider, default_format, default_autocache)); @@ -153,7 +153,7 @@ QTreeWidgetItem *SequenceDialogPresetTab::CreateHDPresetFolder(const QString &na VideoParams::kPixelAspectSquare, VideoParams::kInterlaceNone, 48000, - AV_CH_LAYOUT_STEREO, + AudioChannelLayout::STEREO, divider, default_format, default_autocache)); @@ -173,7 +173,7 @@ QTreeWidgetItem *SequenceDialogPresetTab::CreateSDPresetFolder(const QString &na standard_par, VideoParams::kInterlacedBottomFirst, 48000, - AV_CH_LAYOUT_STEREO, + AudioChannelLayout::STEREO, divider, default_format, default_autocache)); @@ -184,7 +184,7 @@ QTreeWidgetItem *SequenceDialogPresetTab::CreateSDPresetFolder(const QString &na wide_par, VideoParams::kInterlacedBottomFirst, 48000, - AV_CH_LAYOUT_STEREO, + AudioChannelLayout::STEREO, divider, default_format, default_autocache)); diff --git a/app/dialog/sequence/sequencepreset.h b/app/dialog/sequence/sequencepreset.h index a172ca6d1e..932333a659 100644 --- a/app/dialog/sequence/sequencepreset.h +++ b/app/dialog/sequence/sequencepreset.h @@ -40,7 +40,7 @@ class SequencePreset : public Preset { const rational& pixel_aspect, VideoParams::Interlacing interlacing, int sample_rate, - uint64_t channel_layout, + const AudioChannelLayout &channel_layout, int preview_divider, PixelFormat preview_format, bool preview_autocache) : @@ -76,7 +76,7 @@ class SequencePreset : public Preset { } else if (reader->name() == QStringLiteral("samplerate")) { sample_rate_ = reader->readElementText().toInt(); } else if (reader->name() == QStringLiteral("chlayout")) { - channel_layout_ = reader->readElementText().toULongLong(); + channel_layout_ = AudioChannelLayout::fromString(reader->readElementText()); } else if (reader->name() == QStringLiteral("divider")) { preview_divider_ = reader->readElementText().toInt(); } else if (reader->name() == QStringLiteral("format")) { @@ -98,7 +98,7 @@ class SequencePreset : public Preset { writer->writeTextElement(QStringLiteral("pixelaspect"), pixel_aspect_.toString()); writer->writeTextElement(QStringLiteral("interlacing_"), QString::number(interlacing_)); writer->writeTextElement(QStringLiteral("samplerate"), QString::number(sample_rate_)); - writer->writeTextElement(QStringLiteral("chlayout"), QString::number(channel_layout_)); + writer->writeTextElement(QStringLiteral("chlayout"), channel_layout_.toString()); writer->writeTextElement(QStringLiteral("divider"), QString::number(preview_divider_)); writer->writeTextElement(QStringLiteral("format"), QString::number(preview_format_)); writer->writeTextElement(QStringLiteral("autocache"), QString::number(preview_autocache_)); @@ -134,7 +134,7 @@ class SequencePreset : public Preset { return sample_rate_; } - uint64_t channel_layout() const + const AudioChannelLayout &channel_layout() const { return channel_layout_; } @@ -161,7 +161,7 @@ class SequencePreset : public Preset { rational pixel_aspect_; VideoParams::Interlacing interlacing_; int sample_rate_; - uint64_t channel_layout_; + AudioChannelLayout channel_layout_; int preview_divider_; PixelFormat preview_format_; bool preview_autocache_; diff --git a/app/node/input/value/valuenode.cpp b/app/node/input/value/valuenode.cpp index 8b4d35514f..f8e752e13b 100644 --- a/app/node/input/value/valuenode.cpp +++ b/app/node/input/value/valuenode.cpp @@ -27,31 +27,18 @@ const QString ValueNode::kValueInput = QStringLiteral("value_in"); #define super Node -constexpr type_t OUR_DOUBLE = "dbl"; -constexpr type_t OUR_INTEGER = "int"; -constexpr type_t OUR_RATIONAL = "rational"; -constexpr type_t OUR_VEC2 = "vec2"; -constexpr type_t OUR_VEC3 = "vec3"; -constexpr type_t OUR_VEC4 = "vec4"; -constexpr type_t OUR_COLOR = "color"; -constexpr type_t OUR_STRING = "str"; -constexpr type_t OUR_MATRIX = "mat"; -constexpr type_t OUR_FONT = "font"; -constexpr type_t OUR_BOOL = "bool"; - -const QVector ValueNode::kSupportedTypes = -{ - {OUR_DOUBLE, TYPE_DOUBLE, QString(), 1}, - {OUR_INTEGER, TYPE_INTEGER, QString(), 1}, - {OUR_RATIONAL, TYPE_RATIONAL, QString(), 1}, - {OUR_VEC2, TYPE_DOUBLE, QString(), 2}, - {OUR_VEC3, TYPE_DOUBLE, QString(), 3}, - {OUR_VEC4, TYPE_DOUBLE, QString(), 4}, - {OUR_COLOR, TYPE_DOUBLE, QStringLiteral("color"), 4}, - {OUR_STRING, TYPE_STRING, QString(), 1}, - {OUR_MATRIX, TYPE_MATRIX, QString(), 1}, - {OUR_FONT, TYPE_STRING, QStringLiteral("font"), 1}, - {OUR_BOOL, TYPE_INTEGER, QStringLiteral("bool"), 1}, +const QVector ValueNode::kSupportedTypes = { + TYPE_DOUBLE, + TYPE_INTEGER, + TYPE_RATIONAL, + TYPE_VEC2, + TYPE_VEC3, + TYPE_VEC4, + TYPE_COLOR, + TYPE_STRING, + TYPE_MATRIX, + TYPE_FONT, + TYPE_BOOL }; ValueNode::ValueNode() @@ -70,8 +57,8 @@ void ValueNode::Retranslate() QStringList type_names; type_names.reserve(kSupportedTypes.size()); - for (const Type &type : kSupportedTypes) { - type_names.append(GetPrettyTypeName(type.our_type)); + for (const type_t &type : kSupportedTypes) { + type_names.append(GetPrettyTypeName(type)); } SetComboBoxStrings(kTypeInput, type_names); @@ -90,10 +77,9 @@ void ValueNode::InputValueChangedEvent(const QString &input, int element) if (input == kTypeInput) { int64_t k = GetStandardValue(kTypeInput).toInt(); - const Type &t = kSupportedTypes.at(k); + const type_t &t = kSupportedTypes.at(k); - SetInputProperty(kValueInput, QStringLiteral("subtype"), t.subtype); - SetInputDataType(kValueInput, t.base_type, t.channel_count); + SetInputDataType(kValueInput, t); } super::InputValueChangedEvent(input, element); @@ -101,27 +87,27 @@ void ValueNode::InputValueChangedEvent(const QString &input, int element) QString ValueNode::GetPrettyTypeName(const type_t &id) { - if (id == OUR_DOUBLE) { + if (id == TYPE_DOUBLE) { return tr("Double"); - } else if (id == OUR_INTEGER) { + } else if (id == TYPE_INTEGER) { return tr("Integer"); - } else if (id == OUR_RATIONAL) { + } else if (id == TYPE_RATIONAL) { return tr("Rational"); - } else if (id == OUR_VEC2) { + } else if (id == TYPE_VEC2) { return tr("Vector 2D"); - } else if (id == OUR_VEC3) { + } else if (id == TYPE_VEC3) { return tr("Vector 3D"); - } else if (id == OUR_VEC4) { + } else if (id == TYPE_VEC4) { return tr("Vector 4D"); - } else if (id == OUR_COLOR) { + } else if (id == TYPE_COLOR) { return tr("Color"); - } else if (id == OUR_STRING) { + } else if (id == TYPE_STRING) { return tr("Text"); - } else if (id == OUR_MATRIX) { + } else if (id == TYPE_MATRIX) { return tr("Matrix"); - } else if (id == OUR_FONT) { + } else if (id == TYPE_FONT) { return tr("Font"); - } else if (id == OUR_BOOL) { + } else if (id == TYPE_BOOL) { return tr("Boolean"); } diff --git a/app/node/input/value/valuenode.h b/app/node/input/value/valuenode.h index 57d4f71648..1c00b6234c 100644 --- a/app/node/input/value/valuenode.h +++ b/app/node/input/value/valuenode.h @@ -66,15 +66,7 @@ class ValueNode : public Node private: static QString GetPrettyTypeName(const type_t &id); - struct Type - { - type_t our_type; - type_t base_type; - QString subtype; - size_t channel_count; - }; - - static const QVector kSupportedTypes; + static const QVector kSupportedTypes; }; diff --git a/app/node/inputimmediate.cpp b/app/node/inputimmediate.cpp index 779814c247..f5c2ef2e94 100644 --- a/app/node/inputimmediate.cpp +++ b/app/node/inputimmediate.cpp @@ -25,20 +25,26 @@ namespace olive { -NodeInputImmediate::NodeInputImmediate(type_t type, size_t channels) : +NodeInputImmediate::NodeInputImmediate() : keyframing_(false) { - standard_value_.resize(channels); } void NodeInputImmediate::set_standard_value_on_track(const value_t::component_t &value, size_t track) { + if (track >= standard_value_.size()) { + standard_value_.resize(track + 1); + } standard_value_[track] = value; } void NodeInputImmediate::set_split_standard_value(const value_t &value) { - for (size_t i=0; i Node::GetConnectedOutputs(const QString &input, int element) const +{ + std::vector l; + + for (auto it = input_connections_.cbegin(); it != input_connections_.cend(); it++) { + if (it->second.input() == input && it->second.element() == element) { + l.push_back(it->first); + } + } + + return l; +} + bool Node::IsUsingStandardValue(const QString &input, int track, int element) const { NodeInputImmediate* imm = GetImmediate(input, element); @@ -364,15 +377,7 @@ void Node::SetInputDataType(const QString &id, const type_t &type, size_t channe Input* input_meta = GetInternalInputData(id); if (input_meta) { - input_meta->type = type; - input_meta->channel_count = channels; - - /*int array_sz = InputArraySize(id); - for (int i=-1; iset_data_type(type, input_meta->channel_count); - }*/ - - emit InputDataTypeChanged(id, type); + SetInputDataTypeInternal(input_meta, id, type, channels); } else { ReportInvalidInput("set data type of", id, -1); } @@ -419,9 +424,7 @@ void Node::SetInputProperty(const QString &id, const QString &name, const value_ Input* i = GetInternalInputData(id); if (i) { - i->properties.insert(name, value); - - emit InputPropertyChanged(id, name, value); + SetInputPropertyInternal(i, id, name, value); } else { ReportInvalidInput("set property of", id, -1); } @@ -429,10 +432,20 @@ void Node::SetInputProperty(const QString &id, const QString &name, const value_ value_t Node::GetValueAtTime(const QString &input, const rational &time, int element) const { - value_t v(GetInputDataType(input), GetNumberOfKeyframeTracks(input)); + const Input* in = GetInternalInputData(input); + if (!in) { + ReportInvalidInput("get value at time of", input, element); + return value_t(); + } + + value_t v(in->type, in->channel_count); for (size_t i = 0; i < v.data().size(); i++) { - v.data()[i] = GetSplitValueAtTimeOnTrack(input, time, i, element); + value_t::component_t &c = v.data()[i]; + c = GetSplitValueAtTimeOnTrack(input, time, i, element); + if (i < in->id_map.size() && in->id_map.at(i) != type_t()) { + c.set_id(in->id_map.at(i)); + } } return v; @@ -1804,6 +1817,31 @@ void Node::SaveImmediate(QXmlStreamWriter *writer, const QString &input, int ele } } +void Node::SetInputDataTypeInternal(Node::Input *i, const QString &id, type_t type, size_t channel_count) +{ + QString subtype; + std::vector id_map; + + type = ResolveSpecialType(type, channel_count, subtype, id_map); + + i->type = type; + i->channel_count = channel_count; + i->id_map = id_map; + + if (!subtype.isEmpty()) { + SetInputPropertyInternal(i, id, QStringLiteral("subtype"), subtype); + } + + emit InputDataTypeChanged(id, type); +} + +void Node::SetInputPropertyInternal(Input *i, const QString &id, const QString &name, const value_t &value) +{ + i->properties.insert(name, value); + + emit InputPropertyChanged(id, name, value); +} + void Node::InsertInput(const QString &id, type_t type, size_t channel_count, const value_t &default_value, InputFlag flags, int index) { if (id.isEmpty()) { @@ -1816,23 +1854,13 @@ void Node::InsertInput(const QString &id, type_t type, size_t channel_count, con return; } - QString subtype; - - type = ResolveSpecialType(type, channel_count, subtype); - Node::Input i; - i.type = type; i.default_value = default_value; i.flags = flags; i.array_size = 0; - i.channel_count = channel_count; - - //qDebug() << "creating" << id << "with channels" << i.channel_count; - if (!subtype.isEmpty()) { - i.properties.insert(QStringLiteral("subtype"), subtype); - } + SetInputDataTypeInternal(&i, id, type, channel_count); input_ids_.insert(index, id); input_data_.insert(index, i); @@ -1902,7 +1930,7 @@ NodeInputImmediate *Node::CreateImmediate(const QString &input) const Input* i = GetInternalInputData(input); if (i) { - return new NodeInputImmediate(i->type, i->channel_count); + return new NodeInputImmediate(); } else { ReportInvalidInput("create immediate", input, -1); return nullptr; @@ -2372,20 +2400,24 @@ void Node::ClearElement(const QString& input, int index) SetStandardValue(input, GetDefaultValue(input), index); } -type_t Node::ResolveSpecialType(type_t type, size_t &channel_count, QString &subtype) +type_t Node::ResolveSpecialType(type_t type, size_t &channel_count, QString &subtype, std::vector &id_map) { if (type == TYPE_VEC2) { channel_count = 2; + id_map = value_t::XYZW_IDS; return TYPE_DOUBLE; } else if (type == TYPE_VEC3) { channel_count = 3; + id_map = value_t::XYZW_IDS; return TYPE_DOUBLE; } else if (type == TYPE_VEC4) { channel_count = 4; + id_map = value_t::XYZW_IDS; return TYPE_DOUBLE; } else if (type == TYPE_COLOR) { channel_count = 4; subtype = QStringLiteral("color"); + id_map = value_t::RGBA_IDS; return TYPE_DOUBLE; } else if (type == TYPE_BOOL) { subtype = QStringLiteral("bool"); diff --git a/app/node/node.h b/app/node/node.h index 6712d1d581..7c8a5ca56e 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -401,6 +401,12 @@ class Node : public QObject return GetConnectedOutput(input.input(), input.element()); } + std::vector GetConnectedOutputs(const QString &input, int element = -1) const; + std::vector GetConnectedOutputs(const NodeInput &input) const + { + return GetConnectedOutputs(input.input(), input.element()); + } + bool is_enabled() const { return GetStandardValue(kEnabledInput).toBool(); } void set_enabled(bool e) { SetStandardValue(kEnabledInput, e); } @@ -1065,6 +1071,7 @@ protected slots: QString human_name; size_t array_size; size_t channel_count; + std::vector id_map; }; NodeInputImmediate* CreateImmediate(const QString& input); @@ -1096,6 +1103,9 @@ protected slots: } } + void SetInputDataTypeInternal(Node::Input *i, const QString &id, type_t type, size_t channel_count); + void SetInputPropertyInternal(Node::Input *i, const QString &id, const QString& name, const value_t &value); + void ReportInvalidInput(const char* attempted_action, const QString &id, int element) const; static Node *CopyNodeAndDependencyGraphMinusItemsInternal(QMap &created, Node *node, MultiUndoCommand *command); @@ -1144,7 +1154,7 @@ protected slots: void ClearElement(const QString &input, int index); - type_t ResolveSpecialType(type_t type, size_t &channel_count, QString &subtype); + type_t ResolveSpecialType(type_t type, size_t &channel_count, QString &subtype, std::vector &id_map); /** * @brief Custom user label for node diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index d4a2ff3f2a..c118f36103 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -222,7 +222,7 @@ void ViewerOutput::set_default_parameters() )); SetAudioParams(AudioParams( OLIVE_CONFIG("DefaultSequenceAudioFrequency").toInt(), - OLIVE_CONFIG("DefaultSequenceAudioLayout").toInt(), + AudioChannelLayout::fromString(OLIVE_CONFIG("DefaultSequenceAudioLayout").toString()), kDefaultSampleFormat )); } diff --git a/app/node/swizzlemap.h b/app/node/swizzlemap.h index 0a57c0f2a5..047e7990f5 100644 --- a/app/node/swizzlemap.h +++ b/app/node/swizzlemap.h @@ -92,6 +92,8 @@ class SwizzleMap std::map::const_iterator cbegin() const { return map_.cbegin(); } std::map::const_iterator cend() const { return map_.cend(); } + size_t size() const { return map_.size(); } + private: std::map map_; diff --git a/app/node/type.h b/app/node/type.h new file mode 100644 index 0000000000..2719542e3e --- /dev/null +++ b/app/node/type.h @@ -0,0 +1,60 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef TYPE_H +#define TYPE_H + +#include + +namespace olive { + +class type_t +{ + public: + constexpr type_t() : type_(0) {} + constexpr type_t(const char *x) : type_(insn_to_num(x)) {} + + static type_t fromString(const QStringView &s) { return s.toUtf8().constData(); } + QString toString() const + { + const char *c = reinterpret_cast(&type_); + return QString::fromUtf8(c, strnlen(c, sizeof(type_))); + } + + bool operator==(const type_t &t) const { return type_ == t.type_; } + bool operator!=(const type_t &t) const { return !(*this == t); } + bool operator<(const type_t &t) const { return type_ < t.type_; } + bool operator<=(const type_t &t) const { return type_ <= t.type_; } + bool operator>(const type_t &t) const { return type_ > t.type_; } + bool operator>=(const type_t &t) const { return type_ >= t.type_; } + + private: + constexpr uint64_t insn_to_num(const char* x) + { + return (x && *x) ? *x + (insn_to_num(x+1) << 8) : 0; + } + + uint64_t type_; + +}; + +} + +#endif // TYPE_H diff --git a/app/node/value.cpp b/app/node/value.cpp index 56a1a2d3a6..9a9f7fbfa8 100644 --- a/app/node/value.cpp +++ b/app/node/value.cpp @@ -36,6 +36,9 @@ namespace olive { std::map > value_t::converters_; +const std::vector value_t::XYZW_IDS = {"x", "y", "z", "w"}; +const std::vector value_t::RGBA_IDS = {"r", "g", "b", "a"}; + QChar CHANNEL_SPLITTER = ':'; struct TextureChannel @@ -59,16 +62,33 @@ value_t::value_t(TexturePtr texture) : for (size_t i = 0; i < sz; i++) { data_[i] = TextureChannel({texture, i}); } + + switch (sz) { + case 2: + data_[1].set_id("a"); + /* fall-through */ + case 1: + data_[0].set_id("l"); + break; + case 4: + data_[3].set_id("a"); + case 3: + data_[0].set_id("r"); + data_[1].set_id("g"); + data_[2].set_id("b"); + break; + } } } value_t::value_t(AudioJobPtr job) { if (job) { + std::vector channel_names = job->params().channel_layout().getChannelNames(); size_t sz = job->params().channel_count(); data_.resize(sz); for (size_t i = 0; i < sz; i++) { - data_[i] = SampleJobChannel({job, i}); + data_[i] = component_t(SampleJobChannel({job, i}), channel_names.at(i)); } type_ = TYPE_SAMPLES; } @@ -79,6 +99,14 @@ value_t::value_t(const SampleJob &job) : { } +QString value_t::toString() const +{ + if (type_ != TYPE_STRING) { + return this->converted(TYPE_STRING).value(); + } + return value(); +} + ShaderCode GetSwizzleShaderCode(const QString &id) { return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/swizzle.frag"))); @@ -178,7 +206,7 @@ AudioJobPtr value_t::toAudioJob() const if (swizzled) { if (ap.is_valid()) { // Return texture(s) wrapped in a swizzle shader - ap.set_channel_layout(av_get_default_channel_layout(data_.size())); + ap.set_channel_layout(AudioChannelLayout::fromMask(av_get_default_channel_layout(data_.size()))); SampleJob swizzle(ValueParams(VideoParams(), ap, time, QString(), LoopMode(), nullptr, nullptr)); diff --git a/app/node/value.h b/app/node/value.h index cff859230b..15790946f1 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -27,6 +27,7 @@ #include "render/job/audiojob.h" #include "render/samplebuffer.h" #include "render/texture.h" +#include "type.h" #include "util/color.h" namespace olive { @@ -37,36 +38,6 @@ class SampleJob; using NodeValueArray = std::vector; -class type_t -{ -public: - constexpr type_t() : type_(0) {} - constexpr type_t(const char *x) : type_(insn_to_num(x)) {} - - static type_t fromString(const QStringView &s) { return s.toUtf8().constData(); } - QString toString() const - { - const char *c = reinterpret_cast(&type_); - return QString::fromUtf8(c, strnlen(c, sizeof(type_))); - } - - bool operator==(const type_t &t) const { return type_ == t.type_; } - bool operator!=(const type_t &t) const { return !(*this == t); } - bool operator<(const type_t &t) const { return type_ < t.type_; } - bool operator<=(const type_t &t) const { return type_ <= t.type_; } - bool operator>(const type_t &t) const { return type_ > t.type_; } - bool operator>=(const type_t &t) const { return type_ >= t.type_; } - -private: - constexpr uint64_t insn_to_num(const char* x) - { - return *x ? *x + (insn_to_num(x+1) << 8) : 0; - } - - uint64_t type_; - -}; - QDebug operator<<(QDebug dbg, const type_t &t); constexpr type_t TYPE_NONE; @@ -89,9 +60,10 @@ class value_t component_t() = default; template - component_t(const T &t) + component_t(const T &t, const type_t &id = type_t()) { set(t); + id_ = id; } // Bad initializers, must catch these at runtime @@ -135,6 +107,8 @@ class value_t } const std::any &data() const { return data_; } + const type_t &id() const { return id_; } + void set_id(const type_t &id) { id_ = id; } component_t converted(type_t from, type_t to, bool *ok = nullptr) const; @@ -143,6 +117,7 @@ class value_t private: std::any data_; + type_t id_; }; @@ -183,25 +158,25 @@ class value_t value_t(const QVector2D &vec) : value_t(TYPE_DOUBLE, size_t(2)) { - data_[0] = double(vec.x()); - data_[1] = double(vec.y()); + data_[0] = component_t(double(vec.x()), XYZW_IDS.at(0)); + data_[1] = component_t(double(vec.y()), XYZW_IDS.at(1)); } value_t(const QVector3D &vec) : value_t(TYPE_DOUBLE, size_t(3)) { - data_[0] = double(vec.x()); - data_[1] = double(vec.y()); - data_[2] = double(vec.z()); + data_[0] = component_t(double(vec.x()), XYZW_IDS.at(0)); + data_[1] = component_t(double(vec.y()), XYZW_IDS.at(1)); + data_[2] = component_t(double(vec.z()), XYZW_IDS.at(2)); } value_t(const QVector4D &vec) : value_t(TYPE_DOUBLE, size_t(4)) { - data_[0] = double(vec.x()); - data_[1] = double(vec.y()); - data_[2] = double(vec.z()); - data_[3] = double(vec.w()); + data_[0] = component_t(double(vec.x()), XYZW_IDS.at(0)); + data_[1] = component_t(double(vec.y()), XYZW_IDS.at(1)); + data_[2] = component_t(double(vec.z()), XYZW_IDS.at(2)); + data_[3] = component_t(double(vec.w()), XYZW_IDS.at(3)); } value_t(const float &f) : @@ -237,10 +212,10 @@ class value_t value_t(const Color &i) : value_t(TYPE_DOUBLE, size_t(4)) { - data_[0] = double(i.red()); - data_[1] = double(i.green()); - data_[2] = double(i.blue()); - data_[3] = double(i.alpha()); + data_[0] = component_t(double(i.red()), RGBA_IDS.at(0)); + data_[1] = component_t(double(i.green()), RGBA_IDS.at(1)); + data_[2] = component_t(double(i.blue()), RGBA_IDS.at(2)); + data_[3] = component_t(double(i.alpha()), RGBA_IDS.at(3)); } value_t(const QString &i) : @@ -313,7 +288,7 @@ class value_t double toDouble() const { return value(); } int64_t toInt() const { return value(); } rational toRational() const { return value(); } - QString toString() const { return value(); } + QString toString() const; Color toColor() const { return Color(value(0), value(1), value(2), value(3)); } QVector2D toVec2() const { return QVector2D(value(0), value(1)); } QVector3D toVec3() const { return QVector3D(value(0), value(1), value(2)); } @@ -334,9 +309,13 @@ class value_t static void registerConverter(const type_t &from, const type_t &to, Converter_t converter); static void registerDefaultConverters(); + static const std::vector XYZW_IDS; + static const std::vector RGBA_IDS; + private: type_t type_; std::vector data_; + component_t meta_; static std::map > converters_; diff --git a/app/render/opengl/openglrenderer.cpp b/app/render/opengl/openglrenderer.cpp index 143d85d6f0..8585871fcb 100644 --- a/app/render/opengl/openglrenderer.cpp +++ b/app/render/opengl/openglrenderer.cpp @@ -383,11 +383,6 @@ Color OpenGLRenderer::GetPixelFromTexture(Texture *texture, const QPointF &pt) Color c = Color::fromData(data.data(), texture->format(), texture->channel_count()); - if (texture->channel_count() == VideoParams::kRGBChannelCount) { - // No alpha channel, set to 1.0 - c.set_alpha(1.0); - } - DetachTextureAsDestination(); return c; @@ -475,13 +470,6 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video if (tex_id) { PrepareInputTexture(target, t.interpolation); - - if (texture->channel_count() == 1 && destination_params.channel_count() != 1) { - // Interpret this texture as a grayscale texture - functions_->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED); - functions_->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED); - functions_->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); - } } } diff --git a/app/render/videoparams.cpp b/app/render/videoparams.cpp index d20e70dffd..6f9bbc9e9c 100644 --- a/app/render/videoparams.cpp +++ b/app/render/videoparams.cpp @@ -72,7 +72,7 @@ VideoParams::VideoParams() : height_(0), depth_(0), format_(PixelFormat::INVALID), - channel_count_(0), + channel_count_(kNoChannels), interlacing_(Interlacing::kInterlaceNone), divider_(1) { @@ -84,7 +84,7 @@ VideoParams::VideoParams(int width, int height, PixelFormat format, int nb_chann height_(height), depth_(1), format_(format), - channel_count_(nb_channels), + channel_count_(static_cast(nb_channels)), pixel_aspect_ratio_(pixel_aspect_ratio), interlacing_(interlacing), divider_(divider) @@ -99,7 +99,7 @@ VideoParams::VideoParams(int width, int height, int depth, PixelFormat format, i height_(height), depth_(depth), format_(format), - channel_count_(nb_channels), + channel_count_(static_cast(nb_channels)), pixel_aspect_ratio_(pixel_aspect_ratio), interlacing_(interlacing), divider_(divider) @@ -115,7 +115,7 @@ VideoParams::VideoParams(int width, int height, const rational &time_base, Pixel depth_(1), time_base_(time_base), format_(format), - channel_count_(nb_channels), + channel_count_(static_cast(nb_channels)), pixel_aspect_ratio_(pixel_aspect_ratio), interlacing_(interlacing), divider_(divider), diff --git a/app/render/videoparams.h b/app/render/videoparams.h index 0f01d88028..59a4b74b93 100644 --- a/app/render/videoparams.h +++ b/app/render/videoparams.h @@ -51,6 +51,14 @@ class VideoParams { kColorRangeDefault = kColorRangeLimited }; + enum ChannelCount + { + kNoChannels, + kGrayOnlyChannelCount, + kGrayAndAlphaChannelCount, + kRGBChannelCount, + kRGBAChannelCount + }; VideoParams(); VideoParams(int width, int height, PixelFormat format, int nb_channels, @@ -169,16 +177,21 @@ class VideoParams { format_ = f; } - int channel_count() const + ChannelCount channel_count() const { return channel_count_; } - void set_channel_count(int c) + void set_channel_count(ChannelCount c) { channel_count_ = c; } + void set_channel_count(int c) + { + set_channel_count(static_cast(c)); + } + const rational& pixel_aspect_ratio() const { return pixel_aspect_ratio_; @@ -253,8 +266,6 @@ class VideoParams { static const QVector kSupportedDividers; static const int kHSVChannelCount = 3; - static const int kRGBChannelCount = 3; - static const int kRGBAChannelCount = 4; /** * @brief Convert rational frame rate (i.e. flipped timebase) to a user-friendly string @@ -377,7 +388,7 @@ class VideoParams { PixelFormat format_; - int channel_count_; + ChannelCount channel_count_; rational pixel_aspect_ratio_; diff --git a/app/widget/nodevaluetree/nodevaluetree.cpp b/app/widget/nodevaluetree/nodevaluetree.cpp index 0df1f79601..a4fc35bcf1 100644 --- a/app/widget/nodevaluetree/nodevaluetree.cpp +++ b/app/widget/nodevaluetree/nodevaluetree.cpp @@ -72,10 +72,7 @@ void NodeValueTree::SetNode(const NodeInput &input) swizzler_items->setFlags(Qt::NoItemFlags); ValueSwizzleWidget *b = new ValueSwizzleWidget(); - //b->set_channels(4, input.GetChannelCount()); - b->set_channels(4, 4); - b->set_type(input_.GetDataType()); - b->set(vh.swizzle()); + b->set(ValueParams(), input_); connect(b, &ValueSwizzleWidget::value_changed, this, &NodeValueTree::SwizzleChanged); this->setItemWidget(swizzler_items, 1, b); } @@ -158,9 +155,6 @@ void NodeValueTree::ValueHintChanged(const NodeInput &input) if (rb->property("output").toString() == output.output()) { rb->setChecked(true); - - ValueSwizzleWidget *b = GetSwizzleWidgetFromTopLevelItem(i); - b->set(vh.swizzle()); } } } diff --git a/app/widget/nodevaluetree/valueswizzlewidget.cpp b/app/widget/nodevaluetree/valueswizzlewidget.cpp index 4cf6a5bf41..f5d6ec7ff9 100644 --- a/app/widget/nodevaluetree/valueswizzlewidget.cpp +++ b/app/widget/nodevaluetree/valueswizzlewidget.cpp @@ -25,6 +25,7 @@ #include #include "config/config.h" +#include "node/node.h" namespace olive { @@ -44,12 +45,9 @@ ValueSwizzleWidget::ValueSwizzleWidget(QWidget *parent) : setScene(scene_); new_item_ = nullptr; - labels_ = kNumberLabels; channel_height_ = this->fontMetrics().height() * 3 / 2; channel_width_ = channel_height_ * 2; - from_count_ = 0; - to_count_ = 0; setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); @@ -69,63 +67,56 @@ bool ValueSwizzleWidget::delete_selected() map.remove(conn->to()); } - set(map); + set_map(map); emit value_changed(map); return true; } -void ValueSwizzleWidget::set(const SwizzleMap &map) +void ValueSwizzleWidget::set(const ValueParams &g, const NodeInput &input) { - clear_all(); + if (input_.IsValid()) { + disconnect(input_.node(), &Node::InputValueHintChanged, this, &ValueSwizzleWidget::hint_changed); + } - cached_map_ = map; + input_ = input; + from_.clear(); + outputs_.clear(); - if (cached_map_.empty()) { - // Connect everything directly for empty maps - size_t lim = std::min(from_count_, to_count_); - for (size_t i = 0; i < lim; i++) { - make_item(i, i); - } - } else { - for (auto it = cached_map_.cbegin(); it != cached_map_.cend(); it++) { - make_item(it->second, it->first); + if (input_.IsValid()) { + Node *n = input_.node(); + outputs_ = n->GetConnectedOutputs(input_); + + from_.resize(outputs_.size()); + for (size_t i = 0; i < from_.size(); i++) { + from_[i] = n->GetFakeConnectedValue(g, outputs_[i], input_.input(), input_.element()); } - } - adjust_all(); -} + set_map(n->GetValueHintForInput(input.input(), input.element()).swizzle()); -void ValueSwizzleWidget::set_channels(size_t from, size_t to) -{ - from_count_ = from; - to_count_ = to; - setFixedHeight(std::max(from, to) * channel_height_); + connect(input_.node(), &Node::InputValueHintChanged, this, &ValueSwizzleWidget::hint_changed); + } - set(cached_map_); + setFixedHeight(std::max(from_count(), to_count()) * channel_height_); } -void ValueSwizzleWidget::set_type(type_t t) +void ValueSwizzleWidget::drawBackground(QPainter *p, const QRectF &r) { - if (t == TYPE_TEXTURE) { - labels_ = kRGBALabels; - } else if (t == TYPE_DOUBLE) { - labels_ = kXYZWLabels; - } else { - labels_ = kNumberLabels; + if (!input_.IsValid()) { + return; } - viewport()->update(); -} + QFontMetrics fm = p->fontMetrics(); + p->drawText(0, 0, viewport()->width(), fm.height(), Qt::AlignLeft | Qt::AlignTop, ); -void ValueSwizzleWidget::drawBackground(QPainter *p, const QRectF &r) -{ - for (size_t i = 0; i < from_count_; i++) { - draw_channel(p, i, 0); + + for (size_t i = 0; i < from_count(); i++) { + draw_channel(p, i, 0, from_.at(i).id().toString()); } - for (size_t i = 0; i < to_count_; i++) { - draw_channel(p, i, width() - channel_width_ - 1); + for (size_t i = 0; i < to_count(); i++) { + // FIXME: Get naming scheme for this... + draw_channel(p, i, width() - channel_width_ - 1, QString()); } } @@ -183,7 +174,7 @@ void ValueSwizzleWidget::mouseMoveEvent(QMouseEvent *e) bool is_from = channel_is_from(e->x()); if (drag_from_ != is_from) { size_t index = get_channel_index_from_y(e->y()); - if ((is_from && index < from_count_) || (!is_from && index < to_count_)) { + if ((is_from && index < from_count()) || (!is_from && index < to_count())) { end_point = get_connect_point_of_channel(is_from, index); new_item_connected_ = true; if (drag_from_) { @@ -218,7 +209,7 @@ void ValueSwizzleWidget::mouseReleaseEvent(QMouseEvent *e) delete new_item_; new_item_ = nullptr; - set(map); + set_map(map); emit value_changed(map); } else { super::mouseReleaseEvent(e); @@ -234,7 +225,7 @@ void ValueSwizzleWidget::resizeEvent(QResizeEvent *e) super::resizeEvent(e); } -void ValueSwizzleWidget::draw_channel(QPainter *p, size_t i, int x) +void ValueSwizzleWidget::draw_channel(QPainter *p, size_t i, int x, const QString &name) { static const size_t kChannelColorCount = 4; static const QColor kDefaultColor = QColor(16, 16, 16); @@ -254,7 +245,8 @@ void ValueSwizzleWidget::draw_channel(QPainter *p, size_t i, int x) QRect r(x, i * channel_height_, channel_width_, channel_height_); - const QColor *kChannelColors = labels_ == kRGBALabels ? kRGBAChannelColors : kGrayChannelColors; + //const QColor *kChannelColors = type_ == TYPE_TEXTURE || type_ == TYPE_COLOR ? kRGBAChannelColors : kGrayChannelColors; + const QColor *kChannelColors = kGrayChannelColors; const QColor &main_col = i < kChannelColorCount ? kChannelColors[i] : kDefaultColor; if (OLIVE_CONFIG("UseGradients").toBool()) { @@ -272,7 +264,36 @@ void ValueSwizzleWidget::draw_channel(QPainter *p, size_t i, int x) p->drawRect(r); p->setPen(Qt::white); - p->drawText(r, Qt::AlignCenter, get_label_text(i)); + + QString t = (!name.isEmpty()) ? name : QString::number(i); + p->drawText(r, Qt::AlignCenter, t); +} + +void ValueSwizzleWidget::set_map(const SwizzleMap &map) +{ + clear_all(); + + cached_map_ = map; + + if (cached_map_.empty()) { + // Connect everything directly for empty maps + size_t lim = std::min(from_count(), to_count()); + for (size_t i = 0; i < lim; i++) { + make_item(i, i); + } + } else { + for (auto it = cached_map_.cbegin(); it != cached_map_.cend(); it++) { + make_item(it->second, it->first); + } + } + + adjust_all(); +} + +size_t ValueSwizzleWidget::to_count() const +{ + return 4; + //return std::max(input_.GetChannelCount(), cached_map_.size()); } QPoint ValueSwizzleWidget::get_connect_point_of_channel(bool from, size_t index) @@ -323,30 +344,12 @@ SwizzleMap ValueSwizzleWidget::get_map_from_connectors() const return map; } -QString ValueSwizzleWidget::get_label_text(size_t index) const +void ValueSwizzleWidget::hint_changed(const NodeInput &input) { - switch (labels_) { - case kNumberLabels: - break; - case kXYZWLabels: - switch (index) { - case 0: return tr("X"); - case 1: return tr("Y"); - case 2: return tr("Z"); - case 3: return tr("W"); - } - break; - case kRGBALabels: - switch (index) { - case 0: return tr("R"); - case 1: return tr("G"); - case 2: return tr("B"); - case 3: return tr("A"); - } - break; + if (input_ == input) { + Node::ValueHint vh = input.node()->GetValueHintForInput(input.input(), input.element()); + set_map(vh.swizzle()); } - - return QString::number(index); } SwizzleConnectorItem::SwizzleConnectorItem(QGraphicsItem *parent) : diff --git a/app/widget/nodevaluetree/valueswizzlewidget.h b/app/widget/nodevaluetree/valueswizzlewidget.h index eb9ed46226..a025693745 100644 --- a/app/widget/nodevaluetree/valueswizzlewidget.h +++ b/app/widget/nodevaluetree/valueswizzlewidget.h @@ -26,6 +26,7 @@ #include #include +#include "node/globals.h" #include "node/swizzlemap.h" #include "widget/nodeview/curvedconnectoritem.h" @@ -51,23 +52,11 @@ class ValueSwizzleWidget : public QGraphicsView { Q_OBJECT public: - enum Labels - { - kNumberLabels, - kXYZWLabels, - kRGBALabels, - }; - explicit ValueSwizzleWidget(QWidget *parent = nullptr); bool delete_selected(); - void set(const SwizzleMap &map); - void set_channels(size_t from, size_t to); - - void set_label_type(Labels l) { labels_ = l; } - - void set_type(type_t t); + void set(const ValueParams &g, const NodeInput &input); protected: virtual void drawBackground(QPainter *p, const QRectF &r) override; @@ -82,9 +71,7 @@ class ValueSwizzleWidget : public QGraphicsView void value_changed(const SwizzleMap &map); private: - QString get_label_text(size_t index) const; - - void draw_channel(QPainter *p, size_t i, int x); + void draw_channel(QPainter *p, size_t i, int x, const QString &name); inline bool channel_is_from(int x) const { return x < get_left_channel_bound(); } inline bool channel_is_to(int x) const { return x >= get_right_channel_bound(); } inline bool is_inside_bounds(int x) const { return channel_is_from(x) || channel_is_to(x); } @@ -92,6 +79,11 @@ class ValueSwizzleWidget : public QGraphicsView inline int get_right_channel_bound() const { return viewport()->width() - channel_width_ - 1; } inline size_t get_channel_index_from_y(int y) const { return y / channel_height_; } + void set_map(const SwizzleMap &map); + + size_t from_count() const { return from_.size(); } + size_t to_count() const; + QPoint get_connect_point_of_channel(bool from, size_t index); void adjust_all(); @@ -102,19 +94,23 @@ class ValueSwizzleWidget : public QGraphicsView QGraphicsScene *scene_; int channel_width_; int channel_height_; - size_t from_count_; - size_t to_count_; - Labels labels_; bool drag_from_; size_t drag_index_; bool new_item_connected_; + NodeInput input_; + std::vector outputs_; + std::vector from_; + SwizzleMap cached_map_; SwizzleConnectorItem *new_item_; QPoint drag_start_; std::vector connectors_; +private slots: + void hint_changed(const NodeInput &input); + }; } diff --git a/app/widget/standardcombos/channellayoutcombobox.h b/app/widget/standardcombos/channellayoutcombobox.h index 272664c9c4..e23c69aad0 100644 --- a/app/widget/standardcombos/channellayoutcombobox.h +++ b/app/widget/standardcombos/channellayoutcombobox.h @@ -35,21 +35,20 @@ class ChannelLayoutComboBox : public QComboBox ChannelLayoutComboBox(QWidget* parent = nullptr) : QComboBox(parent) { - foreach (const uint64_t& ch_layout, AudioParams::kSupportedChannelLayouts) { - this->addItem(HumanStrings::ChannelLayoutToString(ch_layout), - QVariant::fromValue(ch_layout)); + foreach (const AudioChannelLayout& ch_layout, AudioParams::kSupportedChannelLayouts) { + this->addItem(ch_layout.toHumanString(), QVariant::fromValue(ch_layout)); } } - uint64_t GetChannelLayout() const + AudioChannelLayout GetChannelLayout() const { - return this->currentData().toULongLong(); + return this->currentData().value(); } - void SetChannelLayout(uint64_t ch) + void SetChannelLayout(const AudioChannelLayout &ch) { for (int i=0; icount(); i++) { - if (this->itemData(i).toULongLong() == ch) { + if (this->itemData(i).value() == ch) { this->setCurrentIndex(i); break; } diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 58629ef6bc..b3c2730ad9 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -515,7 +515,7 @@ void ViewerWidget::UpdateAudioProcessor() ap.set_format(ViewerOutput::kDefaultSampleFormat); AudioParams packed(OLIVE_CONFIG("AudioOutputSampleRate").toInt(), - OLIVE_CONFIG("AudioOutputChannelLayout").toInt(), + AudioChannelLayout::fromString(OLIVE_CONFIG("AudioOutputChannelLayout").toString()), SampleFormat::from_string(OLIVE_CONFIG("AudioOutputSampleFormat").toString())); audio_processor_.Open(ap, packed, (playback_speed_ == 0) ? 1 : std::abs(playback_speed_)); @@ -1582,7 +1582,7 @@ void ViewerWidget::Play(bool in_to_out_only) ); AudioParams ap(OLIVE_CONFIG("AudioRecordingSampleRate").toInt(), - OLIVE_CONFIG("AudioRecordingChannelLayout").toInt(), + AudioChannelLayout::fromString(OLIVE_CONFIG("AudioRecordingChannelLayout").toString()), SampleFormat::from_string(OLIVE_CONFIG("AudioRecordingSampleFormat").toString())); EncodingParams encode_param; diff --git a/lib/olive/include/render/audiochannellayout.h b/lib/olive/include/render/audiochannellayout.h new file mode 100644 index 0000000000..247c91973e --- /dev/null +++ b/lib/olive/include/render/audiochannellayout.h @@ -0,0 +1,138 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#ifndef AUDIOCHANNELLAYOUT_H +#define AUDIOCHANNELLAYOUT_H + +extern "C" { +#include +} + +#include +#include + +#include "node/type.h" + +namespace olive { + +class AudioChannelLayout +{ +public: + static const AudioChannelLayout STEREO; + + AudioChannelLayout() + { + internal_ = {}; + } + + AudioChannelLayout(const AVChannelLayout &other) : + AudioChannelLayout() + { + av_channel_layout_copy(&internal_, &other); + } + + ~AudioChannelLayout() + { + av_channel_layout_uninit(&internal_); + } + + AudioChannelLayout(const AudioChannelLayout& other) : + AudioChannelLayout() + { + av_channel_layout_copy(&internal_, &other.internal_); + } + + AudioChannelLayout& operator=(const AudioChannelLayout& other) + { + av_channel_layout_copy(&internal_, &other.internal_); + return *this; + } + + bool operator==(const AVChannelLayout &c) const + { + return !av_channel_layout_compare(&internal_, &c); + } + + bool operator==(const AudioChannelLayout &c) const + { + return *this == c.internal_; + } + + bool operator!=(const AudioChannelLayout &c) const { return !(*this == c); } + bool operator!=(const AVChannelLayout &c) const { return !(*this == c); } + + bool isNull() const + { + return !av_channel_layout_check(&internal_); + } + + QString toString() const + { + char buf[100]; + av_channel_layout_describe(&internal_, buf, sizeof(buf)); + return buf; + } + + QString toHumanString() const + { + // TODO: Provide more usable strings than FFmpeg's descriptions + return toString(); + } + + static AudioChannelLayout fromString(const QString &s) + { + AudioChannelLayout layout; + av_channel_layout_from_string(&layout.internal_, s.toUtf8().constData()); + return layout; + } + + static AudioChannelLayout fromMask(const uint64_t &i) + { + AudioChannelLayout layout; + av_channel_layout_from_mask(&layout.internal_, i); + return layout; + } + + int count() const { return internal_.nb_channels; } + + void exportTo(AVChannelLayout &out) const + { + return exportTo(&out); + } + + void exportTo(AVChannelLayout *out) const + { + av_channel_layout_copy(out, &internal_); + } + + std::vector getChannelNames() const; + + const AVChannelLayout *ref() const { return &internal_; } + +private: + AVChannelLayout internal_; + +}; + +uint qHash(const AudioChannelLayout &l, uint seed = 0); + +} + +#endif // AUDIOCHANNELLAYOUT_H diff --git a/lib/olive/include/render/audioparams.h b/lib/olive/include/render/audioparams.h index 4a5ef4cd66..65e03afa3c 100644 --- a/lib/olive/include/render/audioparams.h +++ b/lib/olive/include/render/audioparams.h @@ -29,6 +29,7 @@ extern "C" { #include #include +#include "audiochannellayout.h" #include "sampleformat.h" #include "util/rational.h" @@ -38,25 +39,18 @@ class AudioParams { public: AudioParams() : sample_rate_(0), - channel_layout_(0), format_(SampleFormat::INVALID) { set_default_footage_parameters(); - - // Cache channel count - calculate_channel_count(); } - AudioParams(const int& sample_rate, const uint64_t& channel_layout, const SampleFormat& format) : + AudioParams(const int& sample_rate, const AudioChannelLayout& channel_layout, const SampleFormat& format) : sample_rate_(sample_rate), channel_layout_(channel_layout), format_(format) { set_default_footage_parameters(); timebase_ = sample_rate_as_time_base(); - - // Cache channel count - calculate_channel_count(); } int sample_rate() const @@ -69,15 +63,14 @@ class AudioParams { sample_rate_ = sample_rate; } - uint64_t channel_layout() const + const AudioChannelLayout &channel_layout() const { return channel_layout_; } - void set_channel_layout(uint64_t channel_layout) + void set_channel_layout(const AudioChannelLayout &channel_layout) { channel_layout_ = channel_layout; - calculate_channel_count(); } rational time_base() const @@ -158,7 +151,7 @@ class AudioParams { bool operator==(const AudioParams& other) const; bool operator!=(const AudioParams& other) const; - static const std::vector kSupportedChannelLayouts; + static const std::vector kSupportedChannelLayouts; static const std::vector kSupportedSampleRates; private: @@ -169,13 +162,9 @@ class AudioParams { duration_ = 0; } - void calculate_channel_count(); - int sample_rate_; - uint64_t channel_layout_; - - int channel_count_; + AudioChannelLayout channel_layout_; SampleFormat format_; diff --git a/lib/olive/src/render/CMakeLists.txt b/lib/olive/src/render/CMakeLists.txt index aeafd8e8e2..1c72352150 100644 --- a/lib/olive/src/render/CMakeLists.txt +++ b/lib/olive/src/render/CMakeLists.txt @@ -16,6 +16,7 @@ set(OLIVECORE_SOURCES ${OLIVECORE_SOURCES} + src/render/audiochannellayout.cpp src/render/audioparams.cpp src/render/samplebuffer.cpp PARENT_SCOPE diff --git a/lib/olive/src/render/audiochannellayout.cpp b/lib/olive/src/render/audiochannellayout.cpp new file mode 100644 index 0000000000..59c89cfe19 --- /dev/null +++ b/lib/olive/src/render/audiochannellayout.cpp @@ -0,0 +1,61 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2023 Olive Studios LLC + + 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 . + +***/ + +#include "render/audiochannellayout.h" + +namespace olive { + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +AudioChannelLayout const AudioChannelLayout::STEREO = AVChannelLayout(AV_CHANNEL_LAYOUT_STEREO); +#pragma GCC diagnostic pop + +uint qHash(const AudioChannelLayout &l, uint seed) +{ + const AVChannelLayout *r = l.ref(); + + uint h = ::qHash(r->order, seed); + + h ^= ::qHash(r->nb_channels, seed); + + if (r->order == AV_CHANNEL_ORDER_NATIVE || r->order == AV_CHANNEL_ORDER_AMBISONIC) { + h ^= ::qHash(r->u.mask, seed); + } else { + h ^= ::qHashBits(r->u.map, sizeof(AVChannelCustom) * r->nb_channels, seed); + } + + return h; +} + +std::vector AudioChannelLayout::getChannelNames() const +{ + std::vector channels(internal_.nb_channels); + + char buf[8]; + for (int i = 0; i < internal_.nb_channels; i++) { + AVChannel channel = av_channel_layout_channel_from_index(&internal_, i); + av_channel_name(buf, sizeof(buf), channel); + channels[i] = buf; + } + + return channels; +} + +} diff --git a/lib/olive/src/render/audioparams.cpp b/lib/olive/src/render/audioparams.cpp index eeed783f1d..1d81dfd3d0 100644 --- a/lib/olive/src/render/audioparams.cpp +++ b/lib/olive/src/render/audioparams.cpp @@ -39,12 +39,12 @@ const std::vector AudioParams::kSupportedSampleRates = { 96000 // 96000 Hz }; -const std::vector AudioParams::kSupportedChannelLayouts = { - AV_CH_LAYOUT_MONO, - AV_CH_LAYOUT_STEREO, - AV_CH_LAYOUT_2_1, - AV_CH_LAYOUT_5POINT1, - AV_CH_LAYOUT_7POINT1 +const std::vector AudioParams::kSupportedChannelLayouts = { + AVChannelLayout(AV_CHANNEL_LAYOUT_MONO), + AVChannelLayout(AV_CHANNEL_LAYOUT_STEREO), + AVChannelLayout(AV_CHANNEL_LAYOUT_2_1), + AVChannelLayout(AV_CHANNEL_LAYOUT_5POINT1), + AVChannelLayout(AV_CHANNEL_LAYOUT_7POINT1) }; bool AudioParams::operator==(const AudioParams &other) const @@ -136,7 +136,7 @@ rational AudioParams::bytes_per_channel_to_time(const int64_t &bytes) const int AudioParams::channel_count() const { - return channel_count_; + return channel_layout_.count(); } int AudioParams::bytes_per_sample_per_channel() const @@ -152,7 +152,7 @@ int AudioParams::bits_per_sample() const bool AudioParams::is_valid() const { return (!time_base().isNull() - && channel_layout() > 0 + && !channel_layout().isNull() && format_ > SampleFormat::INVALID && format_ < SampleFormat::COUNT); } @@ -163,7 +163,7 @@ void AudioParams::load(QXmlStreamReader *reader) if (reader->name() == QStringLiteral("samplerate")) { set_sample_rate(reader->readElementText().toInt()); } else if (reader->name() == QStringLiteral("channellayout")) { - set_channel_layout(reader->readElementText().toULongLong()); + set_channel_layout(AudioChannelLayout::fromString(reader->readElementText())); } else if (reader->name() == QStringLiteral("format")) { set_format(SampleFormat::from_string(reader->readElementText())); } else if (reader->name() == QStringLiteral("enabled")) { @@ -183,7 +183,7 @@ void AudioParams::load(QXmlStreamReader *reader) void AudioParams::save(QXmlStreamWriter *writer) const { writer->writeTextElement(QStringLiteral("samplerate"), QString::number(sample_rate())); - writer->writeTextElement(QStringLiteral("channellayout"), QString::number(channel_layout())); + writer->writeTextElement(QStringLiteral("channellayout"), channel_layout().toString()); writer->writeTextElement(QStringLiteral("format"), format().to_string()); writer->writeTextElement(QStringLiteral("enabled"), QString::number(enabled())); writer->writeTextElement(QStringLiteral("streamindex"), QString::number(stream_index())); @@ -191,9 +191,4 @@ void AudioParams::save(QXmlStreamWriter *writer) const writer->writeTextElement(QStringLiteral("timebase"), time_base().toString()); } -void AudioParams::calculate_channel_count() -{ - channel_count_ = av_get_channel_layout_nb_channels(channel_layout()); -} - } diff --git a/lib/olive/src/render/samplebuffer.cpp b/lib/olive/src/render/samplebuffer.cpp index f419334043..56272baaef 100644 --- a/lib/olive/src/render/samplebuffer.cpp +++ b/lib/olive/src/render/samplebuffer.cpp @@ -52,7 +52,7 @@ SampleBuffer::SampleBuffer(const AudioParams &audio_params, size_t samples_per_c SampleBuffer SampleBuffer::rip_channel(int channel) const { AudioParams p = this->audio_params_; - p.set_channel_layout(AV_CH_LAYOUT_MONO); + p.set_channel_layout(AVChannelLayout(AV_CHANNEL_LAYOUT_MONO)); SampleBuffer b(p, this->sample_count_per_channel_); b.fast_set(*this, 0, channel); diff --git a/lib/olive/src/util/color.cpp b/lib/olive/src/util/color.cpp index 88fa24836a..96a826ee19 100644 --- a/lib/olive/src/util/color.cpp +++ b/lib/olive/src/util/color.cpp @@ -244,6 +244,25 @@ Color Color::fromData(const char *in, const PixelFormat &format, unsigned int nb } } + switch (nb_channels) { + case 1: + // Convert grayscale channel to RGBA + c.set_alpha(1.0); + c.set_green(c.red()); + c.set_blue(c.red()); + break; + case 2: + // Map grayscale and alpha to RGBA + c.set_alpha(c.green()); + c.set_green(c.red()); + c.set_blue(c.red()); + break; + case 3: + // Map RGB to RGBA + c.set_alpha(1.0); + break; + } + return c; } From 30233a85fde90f888a912a4c0563a4311ce02fbb Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 5 Jul 2023 16:58:18 -0700 Subject: [PATCH 67/71] address swizzling between multiple nodes --- app/node/block/clip/clip.cpp | 2 + .../transform/transformdistortnode.cpp | 2 +- app/node/node.cpp | 158 ++++++-- app/node/node.h | 17 +- app/node/nodeundo.cpp | 7 +- app/node/nodeundo.h | 3 +- app/node/output/viewer/viewer.cpp | 3 +- app/node/param.cpp | 8 + app/node/param.h | 3 + app/node/swizzlemap.h | 66 +++- app/node/value.h | 2 + .../nodeparamviewconnectedlabel.cpp | 9 +- .../nodeparamviewconnectedlabel.h | 5 +- app/widget/nodevaluetree/nodevaluetree.cpp | 4 +- .../nodevaluetree/valueswizzlewidget.cpp | 348 ++++++++++++++---- app/widget/nodevaluetree/valueswizzlewidget.h | 55 ++- app/widget/nodeview/nodeview.cpp | 75 ++-- app/widget/nodeview/nodeview.h | 2 + 18 files changed, 588 insertions(+), 181 deletions(-) diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index 308fd1c39c..57b655aaa3 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -60,6 +60,8 @@ ClipBlock::ClipBlock() : AddInput(kAutoCacheInput, TYPE_BOOL, false, kInputFlagNotConnectable | kInputFlagNotKeyframable); PrependInput(kBufferIn, kInputFlagNotKeyframable); + AddAcceptableTypeForInput(kBufferIn, TYPE_TEXTURE); + AddAcceptableTypeForInput(kBufferIn, TYPE_SAMPLES); SetEffectInput(kBufferIn); diff --git a/app/node/distort/transform/transformdistortnode.cpp b/app/node/distort/transform/transformdistortnode.cpp index f3fe44358b..3d1d3026e4 100644 --- a/app/node/distort/transform/transformdistortnode.cpp +++ b/app/node/distort/transform/transformdistortnode.cpp @@ -68,7 +68,7 @@ TransformDistortNode::TransformDistortNode() SetFlag(kVideoEffect); SetEffectInput(kTextureInput); - AddOutput(kMatrixOutput); + AddOutput(kMatrixOutput, TYPE_MATRIX); // Undo MatrixGenerator deprecation flag for derivative SetFlag(kDontShowInCreateMenu, false); diff --git a/app/node/node.cpp b/app/node/node.cpp index 39bf7f1ac3..1a368ad950 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -186,6 +186,17 @@ QBrush Node::brush(qreal top, qreal bottom) const } } +bool Node::ConnectionExistsNodeOnly(const Node *node, const NodeInput &input) +{ + for (auto it = node->output_connections_.cbegin(); it != node->output_connections_.cend(); it++) { + if (it->first.node() == node && it->second == input) { + return true; + } + } + + return false; +} + bool Node::ConnectionExists(const NodeOutput &output, const NodeInput &input) { for (auto it = output.node()->output_connections_.cbegin(); it != output.node()->output_connections_.cend(); it++) { @@ -197,7 +208,7 @@ bool Node::ConnectionExists(const NodeOutput &output, const NodeInput &input) return false; } -void Node::ConnectEdge(const NodeOutput &output, const NodeInput &input) +void Node::ConnectEdge(const NodeOutput &output, const NodeInput &input, int64_t index) { // Ensure graph is the same Q_ASSERT(input.node()->parent() == output.node()->parent()); @@ -207,7 +218,12 @@ void Node::ConnectEdge(const NodeOutput &output, const NodeInput &input) // Insert connection on both sides auto conn = std::pair({output, input}); - input.node()->input_connections_.push_back(conn); + Connections &ic = input.node()->input_connections_; + if (index == -1) { + ic.push_back(conn); + } else { + ic.insert(ic.begin() + index, conn); + } output.node()->output_connections_.push_back(conn); // Call internal events @@ -273,6 +289,16 @@ QString Node::GetInputName(const QString &id) const } } +QString Node::GetOutputName(const QString &id) const +{ + for (auto it = outputs_.constBegin(); it != outputs_.constEnd(); it++) { + if (it->id == id) { + return it->name; + } + } + return QString(); +} + bool Node::IsInputHidden(const QString &input) const { return (GetInputFlags(input) & kInputFlagHidden); @@ -365,7 +391,11 @@ type_t Node::GetInputDataType(const QString &id) const const Input* i = GetInternalInputData(id); if (i) { - return i->type; + if (i->types.empty()) { + return TYPE_NONE; + } else { + return i->types.front(); + } } else { ReportInvalidInput("get data type of", id, -1); return TYPE_NONE; @@ -438,7 +468,7 @@ value_t Node::GetValueAtTime(const QString &input, const rational &time, int ele return value_t(); } - value_t v(in->type, in->channel_count); + value_t v(in->types.empty() ? TYPE_NONE : in->types.front(), in->channel_count); for (size_t i = 0; i < v.data().size(); i++) { value_t::component_t &c = v.data()[i]; @@ -738,7 +768,7 @@ value_t Node::GetStandardValue(const QString &id, int element) const NodeInputImmediate* imm = GetImmediate(id, element); if (imm) { - return value_t(input->type, imm->get_standard_value()); + return value_t(input->types.empty() ? TYPE_NONE : input->types.front(), imm->get_standard_value()); } else { ReportInvalidInput("get standard value of", id, element); return value_t(); @@ -882,9 +912,58 @@ int Node::InputArraySize(const QString &id) const value_t Node::GetInputValue(const ValueParams &g, const QString &input, int element, bool autoconversion) const { if (!g.is_cancelled()) { - NodeOutput output = GetConnectedOutput2(input, element); - if (output.IsValid()) { - return GetFakeConnectedValue(g, output, input, element, autoconversion); + std::vector outputs = GetConnectedOutputs(input, element); + if (!outputs.empty()) { + Node::ValueHint vh = this->GetValueHintForInput(input, element); + + // Perform swizzle if requested + const SwizzleMap &swizzle = vh.swizzle(); + value_t v; + + if (swizzle.empty()) { + // Pass along first value + v = GetFakeConnectedValue(g, outputs.front(), input, element, autoconversion); + } else { + // Swizzle various + std::vector vals(outputs.size()); + + // Retrieve used values + for (auto it = swizzle.cbegin(); it != swizzle.cend(); it++) { + size_t output_index = it->second.output(); + + if (output_index < outputs.size()) { + value_t &vi = vals[output_index]; + + if (!vi.isValid()) { + vi = GetFakeConnectedValue(g, outputs.at(output_index), input, element, autoconversion); + } + } + } + + // Perform swizzle + for (auto it = swizzle.cbegin(); it != swizzle.cend(); it++) { + const SwizzleMap::From &from = it->second; + size_t to = it->first; + + if (from.output() < vals.size()) { + const value_t &vi = vals.at(from.output()); + + if (from.element() < vi.size()) { + if (to >= v.size()) { + v.resize(to + 1); + } + + if (v.type() == TYPE_NONE) { + v.set_type(vi.type()); + } + + v[to] = vi[from.element()]; + } + } + } + } + + return v; } else { TimeRange adjusted_time = InputTimeAdjustment(input, element, g.time(), true); @@ -929,7 +1008,6 @@ value_t Node::GetFakeConnectedValue(const ValueParams &g, NodeOutput output, con while (output.IsValid()) { if (output.node()->is_enabled()) { - Node::ValueHint vh = this->GetValueHintForInput(input, element); ValueParams connp = g.time_transformed(adjusted_time).output_edited(output.output()); // Find cached value with this @@ -939,25 +1017,6 @@ value_t Node::GetFakeConnectedValue(const ValueParams &g, NodeOutput output, con g.insert_cached_value(output.node(), connp, v); } - // Perform swizzle if requested - const SwizzleMap &swizzle = vh.swizzle(); - if (!swizzle.empty()) { - value_t swiz(v.type(), v.size()); - for (auto it = swizzle.cbegin(); it != swizzle.cend(); it++) { - size_t from = it->second; - size_t to = it->first; - - if (from < v.size()) { - if (to >= swiz.size()) { - swiz.resize(to + 1); - } - - swiz[to] = v[from]; - } - } - v = swiz; - } - // Perform conversion if necessary type_t expected_type = this->GetInputDataType(input); if (autoconversion && expected_type != TYPE_NONE && expected_type != v.type() && !(this->GetInputFlags(input) & kInputFlagDontAutoConvert)) { @@ -1011,6 +1070,17 @@ const NodeKeyframeTrack &Node::GetTrackFromKeyframe(NodeKeyframe *key) const return GetImmediate(key->input(), key->element())->keyframe_tracks().at(key->track()); } +int64_t Node::GetInputConnectionIndex(const NodeOutput &output, const NodeInput &in) const +{ + for (size_t i = 0; i < input_connections_.size(); i++) { + if (input_connections_.at(i).first == output) { + return i; + } + } + + return -1; +} + NodeInputImmediate *Node::GetImmediate(const QString &input, int element) const { if (element == -1) { @@ -1824,7 +1894,11 @@ void Node::SetInputDataTypeInternal(Node::Input *i, const QString &id, type_t ty type = ResolveSpecialType(type, channel_count, subtype, id_map); - i->type = type; + i->types.clear(); + if (type != TYPE_NONE) { + i->types.push_back(type); + } + i->channel_count = channel_count; i->id_map = id_map; @@ -1891,7 +1965,7 @@ void Node::RemoveInput(const QString &id) emit InputRemoved(id); } -void Node::AddOutput(const QString &id) +void Node::AddOutput(const QString &id, const type_t &type) { for (auto it = outputs_.constBegin(); it != outputs_.constEnd(); it++) { if (it->id == id) { @@ -1902,6 +1976,7 @@ void Node::AddOutput(const QString &id) Output o; o.id = id; + o.type = type; outputs_.append(o); emit OutputAdded(id); @@ -1919,6 +1994,29 @@ void Node::RemoveOutput(const QString &id) emit OutputRemoved(id); } +void Node::AddAcceptableTypeForInput(const QString &id, type_t type) +{ + Input* i = GetInternalInputData(id); + + if (i) { + i->types.push_back(type); + } else { + ReportInvalidInput("add acceptable type for", id, -1); + } +} + +std::vector Node::GetAcceptableTypesForInput(const QString &id) const +{ + const Input* i = GetInternalInputData(id); + + if (i) { + return i->types; + } else { + ReportInvalidInput("get acceptable types for", id, -1); + return std::vector(); + } +} + void Node::ReportInvalidInput(const char *attempted_action, const QString& id, int element) const { qWarning() << "Failed to" << attempted_action << "parameter" << id << "element" << element diff --git a/app/node/node.h b/app/node/node.h index 7c8a5ca56e..75db996be0 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -216,6 +216,7 @@ class Node : public QObject { QString id; QString name; + type_t type; }; const QVector& outputs() const { return outputs_; } @@ -337,9 +338,11 @@ class Node : public QObject } } + static bool ConnectionExistsNodeOnly(const Node *node, const NodeInput& input); + static bool ConnectionExists(const NodeOutput &output, const NodeInput& input); - static void ConnectEdge(const NodeOutput &output, const NodeInput& input); + static void ConnectEdge(const NodeOutput &output, const NodeInput& input, int64_t index = -1); static void DisconnectEdge(const NodeOutput &output, const NodeInput& input); @@ -350,6 +353,8 @@ class Node : public QObject virtual QString GetInputName(const QString& id) const; + QString GetOutputName(const QString &id) const; + void SetInputName(const QString& id, const QString& name); void SetOutputName(const QString &id, const QString &name); @@ -624,6 +629,8 @@ class Node : public QObject return input_connections_; } + int64_t GetInputConnectionIndex(const NodeOutput &output, const NodeInput &i) const; + /** * @brief Return list of output connections * @@ -849,6 +856,8 @@ class Node : public QObject static QString GetConnectCommandString(const NodeOutput &output, const NodeInput &input); static QString GetDisconnectCommandString(const NodeOutput &output, const NodeInput &input); + std::vector GetAcceptableTypesForInput(const QString &id) const; + static const QString kEnabledInput; protected: @@ -900,7 +909,7 @@ class Node : public QObject void RemoveInput(const QString& id); - void AddOutput(const QString &id); + void AddOutput(const QString &id, const type_t &type = TYPE_NONE); void RemoveOutput(const QString &id); @@ -909,6 +918,8 @@ class Node : public QObject SetInputProperty(id, QStringLiteral("combo_str"), value_t("strl", strings)); } + void AddAcceptableTypeForInput(const QString &id, type_t type); + void SendInvalidateCache(const TimeRange &range, const InvalidateCacheOptions &options); enum GizmoScaleHandles { @@ -1064,7 +1075,7 @@ protected slots: private: struct Input { - type_t type; + std::vector types; InputFlag flags; value_t default_value; QHash properties; diff --git a/app/node/nodeundo.cpp b/app/node/nodeundo.cpp index ce0d60cd2e..cd49bdfd92 100644 --- a/app/node/nodeundo.cpp +++ b/app/node/nodeundo.cpp @@ -113,15 +113,16 @@ void NodeSetPositionAndDependenciesRecursivelyCommand::move_recursively(Node *no } } -NodeEdgeAddCommand::NodeEdgeAddCommand(const NodeOutput &output, const NodeInput &input) : +NodeEdgeAddCommand::NodeEdgeAddCommand(const NodeOutput &output, const NodeInput &input, int64_t index) : output_(output), - input_(input) + input_(input), + index_(index) { } void NodeEdgeAddCommand::redo() { - Node::ConnectEdge(output_, input_); + Node::ConnectEdge(output_, input_, index_); } void NodeEdgeAddCommand::undo() diff --git a/app/node/nodeundo.h b/app/node/nodeundo.h index f09f42a404..79bf27973f 100644 --- a/app/node/nodeundo.h +++ b/app/node/nodeundo.h @@ -306,7 +306,7 @@ class NodeEdgeRemoveCommand : public UndoCommand { */ class NodeEdgeAddCommand : public UndoCommand { public: - NodeEdgeAddCommand(const NodeOutput &output, const NodeInput& input); + NodeEdgeAddCommand(const NodeOutput &output, const NodeInput& input, int64_t index = -1); virtual Project* GetRelevantProject() const override; @@ -317,6 +317,7 @@ class NodeEdgeAddCommand : public UndoCommand { private: NodeOutput output_; NodeInput input_; + int64_t index_; }; diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index c118f36103..4c577492c4 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -600,9 +600,10 @@ void ViewerOutput::ArraySizeChanged(const QString &id, int old_size, int new_siz { if (id == kVideoParamsInput || id == kAudioParamsInput) { Track::Type type = (id == kVideoParamsInput) ? Track::kVideo : Track::kAudio; + type_t data_type = (id == kVideoParamsInput) ? TYPE_TEXTURE : TYPE_SAMPLES; for (int i = old_size; i < new_size; i++) { - AddOutput(Track::Reference(type, i).ToString()); + AddOutput(Track::Reference(type, i).ToString(), data_type); } for (int i = new_size; i < old_size; i++) { diff --git a/app/node/param.cpp b/app/node/param.cpp index e6d8f4c133..5a14b5ca49 100644 --- a/app/node/param.cpp +++ b/app/node/param.cpp @@ -210,4 +210,12 @@ uint qHash(const NodeInputPair &i) return qHash(i.node) & qHash(i.input); } +QString NodeOutput::GetName() const +{ + if (node_) { + return node_->GetOutputName(output_); + } + return QString(); +} + } diff --git a/app/node/param.h b/app/node/param.h index ffb01003f2..9896a2adec 100644 --- a/app/node/param.h +++ b/app/node/param.h @@ -129,6 +129,8 @@ class NodeOutput return output_ < rhs.output_; } + QString GetName() const; + Node *node() const { return node_; } const QString &output() const { return output_; } void set_node(Node *n) { node_ = n; } @@ -349,6 +351,7 @@ uint qHash(const NodeKeyframeTrackReference& i); } Q_DECLARE_METATYPE(olive::NodeInput) +Q_DECLARE_METATYPE(olive::NodeOutput) Q_DECLARE_METATYPE(olive::NodeKeyframeTrackReference) #endif // NODEPARAM_H diff --git a/app/node/swizzlemap.h b/app/node/swizzlemap.h index 047e7990f5..4c4b425d7b 100644 --- a/app/node/swizzlemap.h +++ b/app/node/swizzlemap.h @@ -33,13 +33,56 @@ namespace olive { class SwizzleMap { public: + class From + { + public: + From() = default; + + From(size_t output, size_t element) + { + output_ = output; + element_ = element; + } + + const size_t &output() const { return output_; } + const size_t &element() const { return element_; } + + bool operator==(const From &f) const { return output_ == f.output_ && element_ == f.element_; } + + private: + size_t output_; + size_t element_; + + }; + + class To + { + public: + To() = default; + + To(size_t index, type_t tag) + { + index_ = index; + tag_ = tag; + } + + const size_t &index() const { return index_; } + const type_t &tag() const { return tag_; } + + bool operator==(const To &to) const { return index_ == to.index_ && tag_ == to.tag_; } + + private: + size_t index_; + type_t tag_; + }; + SwizzleMap() = default; bool empty() const { return map_.empty(); } void clear() { map_.clear(); } - void insert(size_t to, size_t from) + void insert(size_t to, const From &from) { map_[to] = from; } @@ -53,12 +96,15 @@ class SwizzleMap { while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("entry")) { - bool got_from = false, got_to = false; - size_t from, to; + bool got_output = false, got_from = false, got_to = false; + size_t from, to, output; while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("from")) { from = reader->readElementText().toULongLong(); got_from = true; + } else if (reader->name() == QStringLiteral("output")) { + output = reader->readElementText().toULongLong(); + got_output = true; } else if (reader->name() == QStringLiteral("to")) { to = reader->readElementText().toULongLong(); got_to = true; @@ -67,8 +113,8 @@ class SwizzleMap } } - if (got_from && got_to) { - insert(to, from); + if (got_output && got_from && got_to) { + insert(to, From(output, from)); } } else { reader->skipCurrentElement(); @@ -81,7 +127,8 @@ class SwizzleMap for (auto it = map_.cbegin(); it != map_.cend(); it++) { writer->writeStartElement(QStringLiteral("entry")); writer->writeTextElement(QStringLiteral("to"), QString::number(it->first)); - writer->writeTextElement(QStringLiteral("from"), QString::number(it->second)); + writer->writeTextElement(QStringLiteral("output"), QString::number(it->second.output())); + writer->writeTextElement(QStringLiteral("from"), QString::number(it->second.element())); writer->writeEndElement(); // entry } } @@ -89,13 +136,14 @@ class SwizzleMap bool operator==(const SwizzleMap &m) const { return map_ == m.map_; } bool operator!=(const SwizzleMap &m) const { return map_ != m.map_; } - std::map::const_iterator cbegin() const { return map_.cbegin(); } - std::map::const_iterator cend() const { return map_.cend(); } + std::map::const_iterator cbegin() const { return map_.cbegin(); } + std::map::const_iterator cend() const { return map_.cend(); } size_t size() const { return map_.size(); } private: - std::map map_; + + std::map map_; }; diff --git a/app/node/value.h b/app/node/value.h index 15790946f1..256d50fef3 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -253,6 +253,8 @@ class value_t return type_; } + void set_type(const type_t &type) { type_ = type; } + component_t at(size_t channel) const { if (channel < data_.size()) { diff --git a/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp b/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp index 6424a411c5..55249f587e 100644 --- a/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp +++ b/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp @@ -88,13 +88,13 @@ NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(const NodeInput &input, void NodeParamViewConnectedLabel::SetViewerNode(ViewerOutput *viewer) { if (viewer_) { - disconnect(viewer_, &ViewerOutput::PlayheadChanged, this, &NodeParamViewConnectedLabel::UpdateValueTree); + //disconnect(viewer_, &ViewerOutput::PlayheadChanged, this, &NodeParamViewConnectedLabel::UpdateValueTree); } viewer_ = viewer; if (viewer_) { - connect(viewer_, &ViewerOutput::PlayheadChanged, this, &NodeParamViewConnectedLabel::UpdateValueTree); + //connect(viewer_, &ViewerOutput::PlayheadChanged, this, &NodeParamViewConnectedLabel::UpdateValueTree); UpdateValueTree(); } } @@ -111,7 +111,7 @@ bool NodeParamViewConnectedLabel::DeleteSelected() void NodeParamViewConnectedLabel::CreateTree() { // Set up table area - value_tree_ = new NodeValueTree(this); + value_tree_ = new ValueSwizzleWidget(this); layout()->addWidget(value_tree_); } @@ -175,7 +175,8 @@ void NodeParamViewConnectedLabel::UpdateLabel() void NodeParamViewConnectedLabel::UpdateValueTree() { if (value_tree_ && viewer_ && value_tree_->isVisible()) { - value_tree_->SetNode(input_); + ValueParams vp(viewer_->GetVideoParams(), viewer_->GetAudioParams(), 0, QString(), LoopMode::kLoopModeOff, nullptr, nullptr); + value_tree_->set(vp, input_); } } diff --git a/app/widget/nodeparamview/nodeparamviewconnectedlabel.h b/app/widget/nodeparamview/nodeparamviewconnectedlabel.h index a9ce593fd9..97071d6243 100644 --- a/app/widget/nodeparamview/nodeparamviewconnectedlabel.h +++ b/app/widget/nodeparamview/nodeparamviewconnectedlabel.h @@ -22,9 +22,10 @@ #define NODEPARAMVIEWCONNECTEDLABEL_H #include "node/param.h" +#include "node/output/viewer/viewer.h" #include "widget/clickablelabel/clickablelabel.h" #include "widget/collapsebutton/collapsebutton.h" -#include "widget/nodevaluetree/nodevaluetree.h" +#include "widget/nodevaluetree/valueswizzlewidget.h" namespace olive { @@ -66,7 +67,7 @@ private slots: NodeOutput output_; - NodeValueTree *value_tree_; + ValueSwizzleWidget *value_tree_; ViewerOutput *viewer_; diff --git a/app/widget/nodevaluetree/nodevaluetree.cpp b/app/widget/nodevaluetree/nodevaluetree.cpp index a4fc35bcf1..774dd3319b 100644 --- a/app/widget/nodevaluetree/nodevaluetree.cpp +++ b/app/widget/nodevaluetree/nodevaluetree.cpp @@ -73,7 +73,7 @@ void NodeValueTree::SetNode(const NodeInput &input) ValueSwizzleWidget *b = new ValueSwizzleWidget(); b->set(ValueParams(), input_); - connect(b, &ValueSwizzleWidget::value_changed, this, &NodeValueTree::SwizzleChanged); + //connect(b, &ValueSwizzleWidget::value_changed, this, &NodeValueTree::SwizzleChanged); this->setItemWidget(swizzler_items, 1, b); } } @@ -83,7 +83,7 @@ bool NodeValueTree::DeleteSelected() { for (int i = 0; i < topLevelItemCount(); i++) { ValueSwizzleWidget *w = GetSwizzleWidgetFromTopLevelItem(i); - if (w->delete_selected()) { + if (w->DeleteSelected()) { return true; } } diff --git a/app/widget/nodevaluetree/valueswizzlewidget.cpp b/app/widget/nodevaluetree/valueswizzlewidget.cpp index f5d6ec7ff9..74949c3a59 100644 --- a/app/widget/nodevaluetree/valueswizzlewidget.cpp +++ b/app/widget/nodevaluetree/valueswizzlewidget.cpp @@ -25,7 +25,10 @@ #include #include "config/config.h" +#include "core.h" #include "node/node.h" +#include "node/nodeundo.h" +#include "widget/menu/menu.h" namespace olive { @@ -53,7 +56,7 @@ ValueSwizzleWidget::ValueSwizzleWidget(QWidget *parent) : setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } -bool ValueSwizzleWidget::delete_selected() +bool ValueSwizzleWidget::DeleteSelected() { auto sel = scene_->selectedItems(); if (sel.isEmpty()) { @@ -68,7 +71,7 @@ bool ValueSwizzleWidget::delete_selected() } set_map(map); - emit value_changed(map); + modify_input(map); return true; } @@ -77,110 +80,157 @@ void ValueSwizzleWidget::set(const ValueParams &g, const NodeInput &input) { if (input_.IsValid()) { disconnect(input_.node(), &Node::InputValueHintChanged, this, &ValueSwizzleWidget::hint_changed); + disconnect(input_.node(), &Node::InputConnected, this, &ValueSwizzleWidget::connection_changed); + disconnect(input_.node(), &Node::InputDisconnected, this, &ValueSwizzleWidget::connection_changed); } input_ = input; from_.clear(); outputs_.clear(); + value_params_ = g; if (input_.IsValid()) { Node *n = input_.node(); - outputs_ = n->GetConnectedOutputs(input_); - from_.resize(outputs_.size()); - for (size_t i = 0; i < from_.size(); i++) { - from_[i] = n->GetFakeConnectedValue(g, outputs_[i], input_.input(), input_.element()); - } + refresh_outputs(); set_map(n->GetValueHintForInput(input.input(), input.element()).swizzle()); connect(input_.node(), &Node::InputValueHintChanged, this, &ValueSwizzleWidget::hint_changed); + connect(input_.node(), &Node::InputConnected, this, &ValueSwizzleWidget::connection_changed); + connect(input_.node(), &Node::InputDisconnected, this, &ValueSwizzleWidget::connection_changed); } +} - setFixedHeight(std::max(from_count(), to_count()) * channel_height_); +void ValueSwizzleWidget::drawBackground(QPainter *gp, const QRectF &r) +{ + gp->drawPixmap(0, 0, pixmap_); } -void ValueSwizzleWidget::drawBackground(QPainter *p, const QRectF &r) +void ValueSwizzleWidget::mousePressEvent(QMouseEvent *e) { - if (!input_.IsValid()) { - return; - } + QPoint p = mapToScene(e->pos()).toPoint(); - QFontMetrics fm = p->fontMetrics(); - p->drawText(0, 0, viewport()->width(), fm.height(), Qt::AlignLeft | Qt::AlignTop, ); + bool found = false; + for (auto it = output_rects_.cbegin(); it != output_rects_.cend(); it++) { + const OutputRect &orr = *it; + if (orr.rect.contains(p)) { + const NodeOutput &o = orr.output; - for (size_t i = 0; i < from_count(); i++) { - draw_channel(p, i, 0, from_.at(i).id().toString()); - } + o.node()->Retranslate(); - for (size_t i = 0; i < to_count(); i++) { - // FIXME: Get naming scheme for this... - draw_channel(p, i, width() - channel_width_ - 1, QString()); - } -} + Menu m(this); -void ValueSwizzleWidget::mousePressEvent(QMouseEvent *e) -{ - if (is_inside_bounds(e->x())) { - drag_from_ = channel_is_from(e->x()); - drag_index_ = get_channel_index_from_y(e->y()); - drag_start_ = get_connect_point_of_channel(drag_from_, drag_index_); - new_item_connected_ = false; + m.setProperty("oldout", QVariant::fromValue(o)); + + for (auto jt = o.node()->outputs().cbegin(); jt != o.node()->outputs().cend(); jt++) { + m.AddActionWithData(jt->name, jt->id, o.output()); + } - if (!drag_from_) { - // "to"s can only have one connection, so if there's already a connection here, we'll remove it - for (auto it = connectors_.cbegin(); it != connectors_.cend(); it++) { - SwizzleConnectorItem *s = *it; - if (s->to() == drag_index_) { - // Grab this item - new_item_ = s; + connect(&m, &Menu::triggered, this, &ValueSwizzleWidget::output_menu_selection); - // Flip so we're dragging the connector from its old position + m.exec(QCursor::pos()); + + found = true; + break; + } + } + + if (!found) { + for (size_t i = 0; i < from_rects_.size(); i++) { + const std::vector &l = from_rects_.at(i); + for (size_t j = 0; j < l.size(); j++) { + if (l.at(j).contains(p)) { drag_from_ = true; - drag_index_ = new_item_->from(); - drag_start_ = get_connect_point_of_channel(true, drag_index_); - new_item_connected_ = true; + drag_source_.from = SwizzleMap::From(i, j); + drag_start_ = get_connect_point_of_from(drag_source_.from); + new_item_connected_ = false; - // Remove from list because we'll delete this later - connectors_.erase(it); + found = true; break; } } } - if (!new_item_) { - new_item_ = new SwizzleConnectorItem(); - if (drag_from_) { - new_item_->set_from(drag_index_); - } else { - new_item_->set_to(drag_index_); + if (!found) { + for (size_t j = 0; j < to_rects_.size(); j++) { + const QRect &r = to_rects_.at(j); + + if (r.contains(p)) { + drag_from_ = false; + drag_source_.to = j; + drag_start_ = get_connect_point_of_to(j); + new_item_connected_ = true; + + // "to"s can only have one connection, so if there's already a connection here, we'll remove it + for (auto it = connectors_.cbegin(); it != connectors_.cend(); it++) { + SwizzleConnectorItem *s = *it; + if (s->to() == drag_source_.to) { + // Grab this item + new_item_ = s; + + // Flip so we're dragging the connector from its old position + drag_from_ = true; + drag_source_.from = new_item_->from(); + drag_start_ = get_connect_point_of_from(drag_source_.from); + new_item_connected_ = true; + + // Remove from list because we'll delete this later + connectors_.erase(it); + break; + } + } + } + } + } + + if (found) { + if (!new_item_) { + new_item_ = new SwizzleConnectorItem(); + if (drag_from_) { + new_item_->set_from(drag_source_.from); + } else { + new_item_->set_to(drag_source_.to); + } + scene_->addItem(new_item_); } - scene_->addItem(new_item_); + } else { + super::mousePressEvent(e); } - } else { - super::mousePressEvent(e); } } void ValueSwizzleWidget::mouseMoveEvent(QMouseEvent *e) { if (new_item_) { + QPoint p = mapToScene(e->pos()).toPoint(); QPoint end_point; new_item_connected_ = false; - if (is_inside_bounds(e->x())) { - bool is_from = channel_is_from(e->x()); - if (drag_from_ != is_from) { - size_t index = get_channel_index_from_y(e->y()); - if ((is_from && index < from_count()) || (!is_from && index < to_count())) { - end_point = get_connect_point_of_channel(is_from, index); + if (drag_from_) { + // Only register with "to" rects + for (size_t j = 0; j < to_rects_.size(); j++) { + const QRect &r = to_rects_.at(j); + if (r.contains(p)) { + end_point = get_connect_point_of_to(j); new_item_connected_ = true; - if (drag_from_) { - new_item_->set_to(index); - } else { - new_item_->set_from(index); + new_item_->set_to(j); + break; + } + } + } else { + // Only register with "from" rects + for (size_t i = 0; i < from_rects_.size(); i++) { + const std::vector &l = from_rects_.at(i); + for (size_t j = 0; j < l.size(); j++) { + const QRect &r = l.at(j); + if (r.contains(p)) { + end_point = get_connect_point_of_from(SwizzleMap::From(i, j)); + new_item_connected_ = true; + new_item_->set_from(SwizzleMap::From(i, j)); + break; } } } @@ -210,7 +260,7 @@ void ValueSwizzleWidget::mouseReleaseEvent(QMouseEvent *e) new_item_ = nullptr; set_map(map); - emit value_changed(map); + modify_input(map); } else { super::mouseReleaseEvent(e); } @@ -220,12 +270,12 @@ void ValueSwizzleWidget::resizeEvent(QResizeEvent *e) { this->setSceneRect(this->viewport()->rect()); - adjust_all(); + refresh_pixmap(); super::resizeEvent(e); } -void ValueSwizzleWidget::draw_channel(QPainter *p, size_t i, int x, const QString &name) +QRect ValueSwizzleWidget::draw_channel(QPainter *p, size_t i, int x, int y, const type_t &name) { static const size_t kChannelColorCount = 4; static const QColor kDefaultColor = QColor(16, 16, 16); @@ -243,12 +293,24 @@ void ValueSwizzleWidget::draw_channel(QPainter *p, size_t i, int x, const QStrin QColor(32, 32, 32), }; - QRect r(x, i * channel_height_, channel_width_, channel_height_); + QRect r(x, y, channel_width_, channel_height_); //const QColor *kChannelColors = type_ == TYPE_TEXTURE || type_ == TYPE_COLOR ? kRGBAChannelColors : kGrayChannelColors; const QColor *kChannelColors = kGrayChannelColors; - const QColor &main_col = i < kChannelColorCount ? kChannelColors[i] : kDefaultColor; + QColor main_col; + if (name == "r") { + main_col = kRGBAChannelColors[0]; + } else if (name == "g") { + main_col = kRGBAChannelColors[1]; + } else if (name == "b") { + main_col = kRGBAChannelColors[2]; + } else if (name == "a") { + main_col = kRGBAChannelColors[3]; + } else { + main_col = i < kChannelColorCount ? kChannelColors[i] : kDefaultColor; + } + if (OLIVE_CONFIG("UseGradients").toBool()) { QLinearGradient lg(r.topLeft(), r.bottomLeft()); @@ -265,8 +327,10 @@ void ValueSwizzleWidget::draw_channel(QPainter *p, size_t i, int x, const QStrin p->setPen(Qt::white); - QString t = (!name.isEmpty()) ? name : QString::number(i); + QString t = (name == type_t()) ? QString::number(i) : name.toString(); p->drawText(r, Qt::AlignCenter, t); + + return r; } void ValueSwizzleWidget::set_map(const SwizzleMap &map) @@ -279,7 +343,7 @@ void ValueSwizzleWidget::set_map(const SwizzleMap &map) // Connect everything directly for empty maps size_t lim = std::min(from_count(), to_count()); for (size_t i = 0; i < lim; i++) { - make_item(i, i); + make_item(SwizzleMap::From(0, i), i); } } else { for (auto it = cached_map_.cbegin(); it != cached_map_.cend(); it++) { @@ -292,33 +356,41 @@ void ValueSwizzleWidget::set_map(const SwizzleMap &map) size_t ValueSwizzleWidget::to_count() const { - return 4; - //return std::max(input_.GetChannelCount(), cached_map_.size()); + return cached_map_.empty() ? from_count() : cached_map_.size(); } -QPoint ValueSwizzleWidget::get_connect_point_of_channel(bool from, size_t index) +QPoint ValueSwizzleWidget::get_connect_point_of_from(const SwizzleMap::From &from) { - QPoint p; + if (from.output() < from_rects_.size()) { + const std::vector &l = from_rects_.at(from.output()); + if (from.element() < l.size()) { + const QRect &r = l.at(from.element()); + return QPoint(r.right(), r.center().y()); + } + } - if (from) { - p.setX(channel_width_); - } else { - p.setX(get_right_channel_bound()); + return QPoint(); +} + +QPoint ValueSwizzleWidget::get_connect_point_of_to(size_t index) +{ + if (index < to_rects_.size()) { + const QRect &r = to_rects_.at(index); + return QPoint(r.left(), r.center().y()); } - p.setY(index * channel_height_ + channel_height_ / 2); - return p; + return QPoint(); } void ValueSwizzleWidget::adjust_all() { for (auto it = connectors_.cbegin(); it != connectors_.cend(); it++) { SwizzleConnectorItem *s = *it; - s->SetPoints(get_connect_point_of_channel(true, s->from()), get_connect_point_of_channel(false, s->to())); + s->SetPoints(get_connect_point_of_from(s->from()), get_connect_point_of_to(s->to())); } } -void ValueSwizzleWidget::make_item(size_t from, size_t to) +void ValueSwizzleWidget::make_item(const SwizzleMap::From &from, size_t to) { SwizzleConnectorItem *s = new SwizzleConnectorItem(); s->set_from(from); @@ -344,6 +416,95 @@ SwizzleMap ValueSwizzleWidget::get_map_from_connectors() const return map; } +void ValueSwizzleWidget::refresh_outputs() +{ + if (input_.IsValid()) { + Node *n = input_.node(); + + outputs_ = n->GetConnectedOutputs(input_); + + from_.resize(outputs_.size()); + for (size_t i = 0; i < from_.size(); i++) { + from_[i] = n->GetFakeConnectedValue(value_params_, outputs_[i], input_.input(), input_.element()); + } + + refresh_pixmap(); + } +} + +void ValueSwizzleWidget::refresh_pixmap() +{ + if (outputs_.empty()) { + return; + } + + QFontMetrics fm = fontMetrics(); + int to_height = channel_height_ * to_count(); + int from_height = 0; + + for (size_t i = 0; i < outputs_.size(); i++) { + from_height += fm.height() + channel_height_ * from_.at(i).size(); + } + + int required_height = std::max(from_height, to_height); + setFixedHeight(required_height); + + pixmap_ = QPixmap(viewport()->width(), required_height); + pixmap_.fill(Qt::transparent); + + QPainter p(&pixmap_); + + int from_y = 0; + + from_rects_.resize(outputs_.size()); + output_rects_.resize(outputs_.size()); + + for (size_t i = 0; i < outputs_.size(); i++) { + const NodeOutput &output = outputs_.at(i); + + output.node()->Retranslate(); + + QString lbl = QStringLiteral("%1 - %2 %3").arg(output.node()->GetLabelAndName(), output.node()->GetOutputName(output.output()), QChar(0x25BE)); + QRect lbl_rect(0, from_y, fm.horizontalAdvance(lbl), fm.height()); + + const value_t &from = from_.at(i); + + output_rects_[i] = {output, lbl_rect}; + + p.setPen(palette().text().color()); + p.drawText(lbl_rect, Qt::AlignLeft | Qt::AlignTop, lbl); + + from_y += fm.height(); + + from_rects_[i].resize(from.size()); + + for (size_t j = 0; j < from.size(); j++) { + QRect rct = draw_channel(&p, j, 0, from_y, from.at(j).id()); + from_rects_[i][j] = rct; + from_y += rct.height(); + } + } + + int to_y; + if (outputs_.size() == 1) { + to_y = fm.height(); + } else { + to_y = std::max(0, viewport()->height()/2 - (channel_height_ * int(to_count()))/2); + } + + to_rects_.resize(to_count()); + for (size_t j = 0; j < to_count(); j++) { + // FIXME: Get naming scheme for this... + QRect rct = draw_channel(&p, j, width() - channel_width_ - 1, to_y, type_t()); + to_rects_[j] = rct; + to_y += rct.height(); + } + + adjust_all(); + + viewport()->update(); +} + void ValueSwizzleWidget::hint_changed(const NodeInput &input) { if (input_ == input) { @@ -352,6 +513,33 @@ void ValueSwizzleWidget::hint_changed(const NodeInput &input) } } +void ValueSwizzleWidget::connection_changed(const NodeOutput &output, const NodeInput &input) +{ + if (input_ == input) { + refresh_outputs(); + } +} + +void ValueSwizzleWidget::output_menu_selection(QAction *action) +{ + NodeOutput old_out = static_cast(sender())->property("oldout").value(); + NodeOutput new_out(old_out.node(), action->data().toString()); + + MultiUndoCommand *c = new MultiUndoCommand(); + + c->add_child(new NodeEdgeRemoveCommand(old_out, input_)); + c->add_child(new NodeEdgeAddCommand(new_out, input_, old_out.node()->GetInputConnectionIndex(old_out, input_))); + + Core::instance()->undo_stack()->push(c, tr("Switched connection output from %1 to %2").arg(old_out.GetName(), new_out.GetName())); +} + +void ValueSwizzleWidget::modify_input(const SwizzleMap &map) +{ + Node::ValueHint hint = input_.node()->GetValueHintForInput(input_.input(), input_.element()); + hint.set_swizzle(map); + Core::instance()->undo_stack()->push(new NodeSetValueHintCommand(input_, hint), tr("Edited Channel Swizzle For Input")); +} + SwizzleConnectorItem::SwizzleConnectorItem(QGraphicsItem *parent) : CurvedConnectorItem(parent) { diff --git a/app/widget/nodevaluetree/valueswizzlewidget.h b/app/widget/nodevaluetree/valueswizzlewidget.h index a025693745..17b8a58317 100644 --- a/app/widget/nodevaluetree/valueswizzlewidget.h +++ b/app/widget/nodevaluetree/valueswizzlewidget.h @@ -37,13 +37,13 @@ class SwizzleConnectorItem : public CurvedConnectorItem public: SwizzleConnectorItem(QGraphicsItem *parent = nullptr); - size_t from() const { return from_index_; } + const SwizzleMap::From &from() const { return from_; } size_t to() const { return to_index_; } - void set_from(size_t f) { from_index_ = f; } + void set_from(const SwizzleMap::From &f) { from_ = f; } void set_to(size_t t) { to_index_ = t; } private: - size_t from_index_; + SwizzleMap::From from_; size_t to_index_; }; @@ -54,7 +54,7 @@ class ValueSwizzleWidget : public QGraphicsView public: explicit ValueSwizzleWidget(QWidget *parent = nullptr); - bool delete_selected(); + bool DeleteSelected(); void set(const ValueParams &g, const NodeInput &input); @@ -67,40 +67,51 @@ class ValueSwizzleWidget : public QGraphicsView virtual void resizeEvent(QResizeEvent *e) override; -signals: - void value_changed(const SwizzleMap &map); - private: - void draw_channel(QPainter *p, size_t i, int x, const QString &name); - inline bool channel_is_from(int x) const { return x < get_left_channel_bound(); } - inline bool channel_is_to(int x) const { return x >= get_right_channel_bound(); } - inline bool is_inside_bounds(int x) const { return channel_is_from(x) || channel_is_to(x); } - inline int get_left_channel_bound() const { return channel_width_; } - inline int get_right_channel_bound() const { return viewport()->width() - channel_width_ - 1; } - inline size_t get_channel_index_from_y(int y) const { return y / channel_height_; } + QRect draw_channel(QPainter *p, size_t i, int x, int y, const type_t &name); void set_map(const SwizzleMap &map); - size_t from_count() const { return from_.size(); } + size_t from_count() const { return from_.empty() ? 0 : from_.at(0).size(); } size_t to_count() const; - QPoint get_connect_point_of_channel(bool from, size_t index); + QPoint get_connect_point_of_from(const SwizzleMap::From &from); + QPoint get_connect_point_of_to(size_t index); void adjust_all(); - void make_item(size_t from, size_t to); + void make_item(const SwizzleMap::From &from, size_t to); void clear_all(); SwizzleMap get_map_from_connectors() const; + void refresh_outputs(); + void refresh_pixmap(); + QGraphicsScene *scene_; int channel_width_; int channel_height_; bool drag_from_; - size_t drag_index_; + union DragSource + { + SwizzleMap::From from; + size_t to; + }; + DragSource drag_source_; bool new_item_connected_; NodeInput input_; std::vector outputs_; std::vector from_; + std::vector< std::vector< QRect > > from_rects_; + std::vector to_rects_; + ValueParams value_params_; + + struct OutputRect + { + NodeOutput output; + QRect rect; + }; + + std::vector output_rects_; SwizzleMap cached_map_; SwizzleConnectorItem *new_item_; @@ -108,9 +119,17 @@ class ValueSwizzleWidget : public QGraphicsView std::vector connectors_; + QPixmap pixmap_; + private slots: void hint_changed(const NodeInput &input); + void connection_changed(const NodeOutput &output, const NodeInput &input); + + void output_menu_selection(QAction *action); + + void modify_input(const SwizzleMap &map); + }; } diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index 711c5fbe17..437e8df538 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -1133,7 +1133,7 @@ QVector NodeView::ProcessDroppingAttachedNodes(MultiUndoCommand *command, // Place new edges drop_edge_command->add_child(new NodeEdgeAddCommand(drop_edge_->output(), drop_input_)); - drop_edge_command->add_child(new NodeEdgeAddCommand(NodeOutput(dropping_node), drop_edge_->input())); + drop_edge_command->add_child(new NodeEdgeAddCommand(GuessOutput(dropping_node, drop_edge_->input()), drop_edge_->input())); } drop_edge_ = nullptr; @@ -1631,48 +1631,32 @@ void NodeView::EndEdgeDrag(bool cancel) if (creating_input.IsValid()) { // Make connection if (!reconnected_to_itself) { - Node *creating_output = create_edge_output_item_->GetNode(); + Node *creating_output_node = create_edge_output_item_->GetNode(); - while (NodeGroup *output_group = dynamic_cast(creating_output)) { - creating_output = output_group->GetOutputPassthrough(); + while (NodeGroup *output_group = dynamic_cast(creating_output_node)) { + creating_output_node = output_group->GetOutputPassthrough(); } + NodeOutput creating_output = GuessOutput(creating_output_node, creating_input); + while (NodeGroup *input_group = dynamic_cast(creating_input.node())) { creating_input = input_group->GetInputFromID(creating_input.input()); } - if (Node::ConnectionExists(NodeOutput(creating_output), creating_input)) { + if (Node::ConnectionExists(creating_output, creating_input)) { cancel = true; } - /*if (creating_input.IsConnected()) { - Node::OutputConnection existing_edge_to_remove = {creating_input.GetConnectedOutput(), creating_input}; - - Node *already_connected_output = creating_input.GetConnectedOutput(); - NodeViewContext *ctx = GetContextItemFromNodeItem(create_edge_input_item_); - if (ctx && !ctx->GetItemFromMap(already_connected_output)) { - if (QMessageBox::warning(this, QString(), tr("Input \"%1\" is currently connected to node \"%2\", which is not visible in this context. " - "By connecting this, that connection will be removed. Do you wish to continue?").arg(creating_input.name(), already_connected_output->GetLabelAndName()), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) { - cancel = true; - } - } - - if (!cancel) { - command->add_child(new NodeEdgeRemoveCommand(existing_edge_to_remove.first, existing_edge_to_remove.second)); - } - }*/ - if (!cancel) { - command->add_child(new NodeEdgeAddCommand(NodeOutput(creating_output), creating_input)); + command->add_child(new NodeEdgeAddCommand(creating_output, creating_input)); - command_name = Node::GetConnectCommandString(NodeOutput(creating_output), creating_input); + command_name = Node::GetConnectCommandString(creating_output, creating_input); // If the output is not in the input's context, add it now. We check the item rather than // the node itself, because sometimes a node may not be in the context but another node // representing it will be (e.g. groups) - if (!scene_.context_map().value(create_edge_input_item_->GetContext())->GetItemFromMap(creating_output)) { - command->add_child(new NodeSetPositionCommand(creating_output, create_edge_input_item_->GetContext(), scene_.context_map().value(create_edge_input_item_->GetContext())->MapScenePosToNodePosInContext(create_edge_output_item_->scenePos()))); + if (!scene_.context_map().value(create_edge_input_item_->GetContext())->GetItemFromMap(creating_output_node)) { + command->add_child(new NodeSetPositionCommand(creating_output_node, create_edge_input_item_->GetContext(), scene_.context_map().value(create_edge_input_item_->GetContext())->MapScenePosToNodePosInContext(create_edge_output_item_->scenePos()))); } } } @@ -1740,6 +1724,43 @@ void NodeView::ResizeOverlay() overlay_view_->resize(this->size()); } +NodeOutput NodeView::GuessOutput(Node *output, const NodeInput &input) +{ + std::vector types = input.node()->GetAcceptableTypesForInput(input.input()); + QString out_id; + size_t confidence = SIZE_MAX; + + qDebug() << "guessing output for" << types; + + if (!types.empty()) { + for (auto it = output->outputs().constBegin(); it != output->outputs().constEnd(); it++) { + type_t output_type = it->type; + + qDebug() << " got type" << output_type; + + for (size_t i = 0; i < types.size(); i++) { + if (output_type == types.at(i)) { + size_t this_confidence = i; + + if (this_confidence < confidence) { + qDebug() << " is acceptable, confidence" << this_confidence; + + out_id = it->id; + confidence = this_confidence; + + if (confidence == 0) { + // LITERALLY CAN'T GET ANY MORE CONFIDENT! + break; + } + } + } + } + } + } + + return NodeOutput(output, out_id); +} + NodeViewContext *NodeView::GetContextItemFromNodeItem(NodeViewItem *item) { QGraphicsItem *i = item; diff --git a/app/widget/nodeview/nodeview.h b/app/widget/nodeview/nodeview.h index fd497f338b..27c44b40bf 100644 --- a/app/widget/nodeview/nodeview.h +++ b/app/widget/nodeview/nodeview.h @@ -192,6 +192,8 @@ public slots: void ResizeOverlay(); + static NodeOutput GuessOutput(Node *output, const NodeInput &input); + NodeViewMiniMap *minimap_; NodeViewContext *GetContextItemFromNodeItem(NodeViewItem *item); From 95609595c5fdcb3ec86f2b74ba11da10e4976876 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 5 Jul 2023 17:27:48 -0700 Subject: [PATCH 68/71] math: add modulo function --- app/node/math/math/math.cpp | 7 +++++++ app/node/math/math/math.h | 1 + app/node/math/math/mathfunctions.cpp | 9 +++++++++ app/node/math/math/mathfunctions.h | 1 + 4 files changed, 18 insertions(+) diff --git a/app/node/math/math/math.cpp b/app/node/math/math/math.cpp index 5c2f967c75..08a7190687 100644 --- a/app/node/math/math/math.cpp +++ b/app/node/math/math/math.cpp @@ -105,6 +105,7 @@ void MathNode::Retranslate() GetOperationName(kOpSubtract), GetOperationName(kOpMultiply), GetOperationName(kOpDivide), + GetOperationName(kOpModulo), QString(), GetOperationName(kOpPower), QString(), @@ -264,6 +265,7 @@ void MathNode::OperateSampleNumber(Operation operation, const float *input, floa case kOpCeil: case kOpRound: case kOpAbs: + case kOpModulo: break; } } @@ -362,6 +364,7 @@ void MathNode::OperateSampleSample(Operation operation, const float *input, floa case kOpCeil: case kOpRound: case kOpAbs: + case kOpModulo: break; } } @@ -390,6 +393,7 @@ int MathNode::GetNumberOfOperands(Operation o) case kOpPower: case kOpMin: case kOpMax: + case kOpModulo: return 2; case kOpClamp: return 3; @@ -495,6 +499,7 @@ ShaderCode MathNode::GetShaderCode(const QString &id) case kOpSubtract: line = QStringLiteral("%1 - %2"); break; case kOpMultiply: line = QStringLiteral("%1 * %2"); break; case kOpDivide: line = QStringLiteral("%1 / %2"); break; + case kOpModulo: line = QStringLiteral("mod(%1, %2)"); break; case kOpPower: case kOpMin: case kOpMax: @@ -631,6 +636,7 @@ QString MathNode::GetOperationName(Operation o) case kOpSubtract: return tr("Subtract"); case kOpMultiply: return tr("Multiply"); case kOpDivide: return tr("Divide"); + case kOpModulo: return tr("Modulo"); case kOpPower: return tr("Power"); case kOpSine: return tr("Sine"); case kOpCosine: return tr("Cosine"); @@ -659,6 +665,7 @@ void MathNode::PopulateOperations() operations_[kOpSubtract][TYPE_DOUBLE][TYPE_DOUBLE][TYPE_NONE] = Math::SubtractDoubleDouble; operations_[kOpMultiply][TYPE_DOUBLE][TYPE_DOUBLE][TYPE_NONE] = Math::MultiplyDoubleDouble; operations_[kOpDivide][TYPE_DOUBLE][TYPE_DOUBLE][TYPE_NONE] = Math::DivideDoubleDouble; + operations_[kOpModulo][TYPE_DOUBLE][TYPE_DOUBLE][TYPE_NONE] = Math::ModuloDoubleDouble; operations_[kOpPower][TYPE_DOUBLE][TYPE_DOUBLE][TYPE_NONE] = Math::PowerDoubleDouble; operations_[kOpSine][TYPE_DOUBLE][TYPE_NONE][TYPE_NONE] = Math::SineDouble; diff --git a/app/node/math/math/math.h b/app/node/math/math/math.h index 7f4f4b1c17..49d7c1f6bc 100644 --- a/app/node/math/math/math.h +++ b/app/node/math/math/math.h @@ -36,6 +36,7 @@ class MathNode : public Node kOpSubtract, kOpMultiply, kOpDivide, + kOpModulo, kOpPower, diff --git a/app/node/math/math/mathfunctions.cpp b/app/node/math/math/mathfunctions.cpp index 149f0cc54b..eb6421049b 100644 --- a/app/node/math/math/mathfunctions.cpp +++ b/app/node/math/math/mathfunctions.cpp @@ -58,6 +58,15 @@ value_t Math::DivideDoubleDouble(const value_t &a, const value_t &b, const value return v; } +value_t Math::ModuloDoubleDouble(const value_t &a, const value_t &b, const value_t &c) +{ + value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::fmod(a.value(i), b.value(i)); + } + return v; +} + value_t Math::PowerDoubleDouble(const value_t &a, const value_t &b, const value_t &c) { value_t v(TYPE_DOUBLE, std::max(a.size(), b.size())); diff --git a/app/node/math/math/mathfunctions.h b/app/node/math/math/mathfunctions.h index 99d619cb0f..958533a6e9 100644 --- a/app/node/math/math/mathfunctions.h +++ b/app/node/math/math/mathfunctions.h @@ -32,6 +32,7 @@ class Math static value_t SubtractDoubleDouble(const value_t &a, const value_t &b, const value_t &c); static value_t MultiplyDoubleDouble(const value_t &a, const value_t &b, const value_t &c); static value_t DivideDoubleDouble(const value_t &a, const value_t &b, const value_t &c); + static value_t ModuloDoubleDouble(const value_t &a, const value_t &b, const value_t &c); static value_t PowerDoubleDouble(const value_t &a, const value_t &b, const value_t &c); static value_t SineDouble(const value_t &a, const value_t &b, const value_t &c); From 58aaab2812ef49316744b490e57a5bfc66da8e86 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 5 Jul 2023 17:28:02 -0700 Subject: [PATCH 69/71] remove integer from value node We encourage doubles in almost all cases --- app/node/input/value/valuenode.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/app/node/input/value/valuenode.cpp b/app/node/input/value/valuenode.cpp index f8e752e13b..b12a461b59 100644 --- a/app/node/input/value/valuenode.cpp +++ b/app/node/input/value/valuenode.cpp @@ -29,7 +29,6 @@ const QString ValueNode::kValueInput = QStringLiteral("value_in"); const QVector ValueNode::kSupportedTypes = { TYPE_DOUBLE, - TYPE_INTEGER, TYPE_RATIONAL, TYPE_VEC2, TYPE_VEC3, From c43936ca4c39769d6c997ec073504e73565fad90 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 5 Jul 2023 17:28:14 -0700 Subject: [PATCH 70/71] pick up on old bool strings for int conversion --- app/node/value.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/node/value.cpp b/app/node/value.cpp index 9a9f7fbfa8..c4a0882ad4 100644 --- a/app/node/value.cpp +++ b/app/node/value.cpp @@ -297,7 +297,13 @@ value_t::component_t converter_IntegerToString(const value_t::component_t &v) value_t::component_t converter_StringToInteger(const value_t::component_t &v) { - return int64_t(v.value().toLongLong()); + if (!v.value().compare(QStringLiteral("false"))) { + return int64_t(0); + } else if (!v.value().compare(QStringLiteral("true"))) { + return int64_t(1); + } else { + return int64_t(v.value().toLongLong()); + } } value_t::component_t converter_DoubleToString(const value_t::component_t &v) From 442038ca6e247977ca2b50f2e580666be1547ec4 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Wed, 5 Jul 2023 17:28:22 -0700 Subject: [PATCH 71/71] cmake: bump ffmpeg version requirement --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a7cb2c74e..04ccc1e0bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -168,7 +168,7 @@ list(APPEND OLIVE_LIBRARIES ) # Link FFmpeg -find_package(FFMPEG 3.0 REQUIRED +find_package(FFMPEG 5.0 REQUIRED COMPONENTS avutil avcodec