Merge branch 'master' into pr/1913
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef ALPHAASSOC_H
|
||||
#define ALPHAASSOC_H
|
||||
|
||||
namespace olive {
|
||||
|
||||
enum AlphaAssociated {
|
||||
kAlphaNone,
|
||||
kAlphaUnassociated,
|
||||
kAlphaAssociated
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // ALPHAASSOC_H
|
||||
@@ -186,6 +186,26 @@ public:
|
||||
duration_ = duration;
|
||||
}
|
||||
|
||||
static bool FormatIsPacked(Format f)
|
||||
{
|
||||
return f >= kPackedStart && f < kPackedEnd;
|
||||
}
|
||||
|
||||
bool FormatIsPacked() const
|
||||
{
|
||||
return FormatIsPacked(format_);
|
||||
}
|
||||
|
||||
static bool FormatIsPlanar(Format f)
|
||||
{
|
||||
return f >= kPlanarStart && f < kPlanarEnd;
|
||||
}
|
||||
|
||||
bool FormatIsPlanar() const
|
||||
{
|
||||
return FormatIsPlanar(format_);
|
||||
}
|
||||
|
||||
qint64 time_to_bytes(const double& time) const;
|
||||
qint64 time_to_bytes(const rational& time) const;
|
||||
qint64 time_to_bytes_per_channel(const double& time) const;
|
||||
|
||||
@@ -58,7 +58,7 @@ void AudioPlaybackCache::SetParameters(const AudioParams ¶ms)
|
||||
emit ParametersChanged();
|
||||
}
|
||||
|
||||
void AudioPlaybackCache::WritePCM(const TimeRange &range, const TimeRangeList &valid_ranges, SampleBufferPtr samples)
|
||||
void AudioPlaybackCache::WritePCM(const TimeRange &range, const TimeRangeList &valid_ranges, const SampleBuffer &samples)
|
||||
{
|
||||
// Ensure if we have enough segments to write this data, creating more if not
|
||||
qint64 length_diff = params_.time_to_bytes_per_channel(range.out()) - playlist_.GetLength();
|
||||
@@ -72,7 +72,7 @@ void AudioPlaybackCache::WritePCM(const TimeRange &range, const TimeRangeList &v
|
||||
TimeRangeList ranges_we_validated;
|
||||
|
||||
// Calculate buffer size per channel
|
||||
qint64 buffer_size_per_channel = samples ? samples->sample_count() * params_.bytes_per_sample_per_channel() : 0;
|
||||
qint64 buffer_size_per_channel = samples.sample_count() * params_.bytes_per_sample_per_channel();
|
||||
|
||||
// Write each valid range to the segments
|
||||
foreach (const TimeRange& r, valid_ranges) {
|
||||
@@ -114,7 +114,7 @@ void AudioPlaybackCache::WritePCM(const TimeRange &range, const TimeRangeList &v
|
||||
if (possible_write_length > 0) {
|
||||
// Assume `samples` is valid if we're here, or else `buffer_size_per_channel` and
|
||||
// therefore `possible_write_length` will be 0.
|
||||
seg_file.write(reinterpret_cast<const char*>(samples->data(i)) + src_offset, possible_write_length);
|
||||
seg_file.write(reinterpret_cast<const char*>(samples.data(i)) + src_offset, possible_write_length);
|
||||
}
|
||||
|
||||
if (possible_write_length < total_write_length) {
|
||||
@@ -167,7 +167,7 @@ void AudioPlaybackCache::WriteSilence(const TimeRange &range)
|
||||
{
|
||||
// WritePCM will automatically fill non-existent bytes with silence, so we just have to send
|
||||
// it an empty sample buffer
|
||||
WritePCM(range, {range}, nullptr);
|
||||
WritePCM(range, {range}, SampleBuffer());
|
||||
}
|
||||
|
||||
void AudioPlaybackCache::ShiftEvent(const rational &from_in_time, const rational &to_in_time)
|
||||
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
|
||||
void SetParameters(const AudioParams& params);
|
||||
|
||||
void WritePCM(const TimeRange &range, const TimeRangeList &valid_ranges, SampleBufferPtr samples);
|
||||
void WritePCM(const TimeRange &range, const TimeRangeList &valid_ranges, const SampleBuffer &samples);
|
||||
|
||||
void WriteWaveform(const TimeRange &range, const TimeRangeList &valid_ranges, const AudioVisualWaveform *waveform);
|
||||
|
||||
|
||||
+47
-57
@@ -27,12 +27,12 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
Color Color::fromHsv(const float &h, const float &s, const float &v)
|
||||
Color Color::fromHsv(const DataType &h, const DataType &s, const DataType &v)
|
||||
{
|
||||
float C = s * v;
|
||||
float X = C * (1.0 - abs(fmod(h / 60.0, 2.0) - 1.0));
|
||||
float m = v - C;
|
||||
float Rs, Gs, Bs;
|
||||
DataType C = s * v;
|
||||
DataType X = C * (1.0 - abs(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;
|
||||
@@ -81,11 +81,11 @@ Color::Color(const QColor &c)
|
||||
set_alpha(c.alphaF());
|
||||
}
|
||||
|
||||
void Color::toHsv(float *hue, float *sat, float *val) const
|
||||
void Color::toHsv(DataType *hue, DataType *sat, DataType *val) const
|
||||
{
|
||||
float fCMax = qMax(qMax(red(), green()), blue());
|
||||
float fCMin = qMin(qMin(red(), green()), blue());
|
||||
float fDelta = fCMax - fCMin;
|
||||
DataType fCMax = qMax(qMax(red(), green()), blue());
|
||||
DataType fCMin = qMin(qMin(red(), green()), blue());
|
||||
DataType fDelta = fCMax - fCMin;
|
||||
|
||||
if(fDelta > 0) {
|
||||
if(fCMax == red()) {
|
||||
@@ -114,31 +114,31 @@ void Color::toHsv(float *hue, float *sat, float *val) const
|
||||
}
|
||||
}
|
||||
|
||||
float Color::hsv_hue() const
|
||||
Color::DataType Color::hsv_hue() const
|
||||
{
|
||||
float h, s, v;
|
||||
DataType h, s, v;
|
||||
toHsv(&h, &s, &v);
|
||||
return h;
|
||||
}
|
||||
|
||||
float Color::hsv_saturation() const
|
||||
Color::DataType Color::hsv_saturation() const
|
||||
{
|
||||
float h, s, v;
|
||||
DataType h, s, v;
|
||||
toHsv(&h, &s, &v);
|
||||
return s;
|
||||
}
|
||||
|
||||
float Color::value() const
|
||||
Color::DataType Color::value() const
|
||||
{
|
||||
float h, s, v;
|
||||
DataType h, s, v;
|
||||
toHsv(&h, &s, &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
void Color::toHsl(float *hue, float *sat, float *lightness) const
|
||||
void Color::toHsl(DataType *hue, DataType *sat, DataType *lightness) const
|
||||
{
|
||||
float fCMin = qMin(red(), qMin(green(), blue()));
|
||||
float fCMax = qMax(red(), qMax(green(), blue()));
|
||||
DataType fCMin = qMin(red(), qMin(green(), blue()));
|
||||
DataType fCMax = qMax(red(), qMax(green(), blue()));
|
||||
|
||||
*lightness = 0.5 * (fCMin + fCMax);
|
||||
|
||||
@@ -176,23 +176,23 @@ void Color::toHsl(float *hue, float *sat, float *lightness) const
|
||||
}
|
||||
}
|
||||
|
||||
float Color::hsl_hue() const
|
||||
Color::DataType Color::hsl_hue() const
|
||||
{
|
||||
float h, s, l;
|
||||
DataType h, s, l;
|
||||
toHsl(&h, &s, &l);
|
||||
return h;
|
||||
}
|
||||
|
||||
float Color::hsl_saturation() const
|
||||
Color::DataType Color::hsl_saturation() const
|
||||
{
|
||||
float h, s, l;
|
||||
DataType h, s, l;
|
||||
toHsl(&h, &s, &l);
|
||||
return s;
|
||||
}
|
||||
|
||||
float Color::lightness() const
|
||||
Color::DataType Color::lightness() const
|
||||
{
|
||||
float h, s, l;
|
||||
DataType h, s, l;
|
||||
toHsl(&h, &s, &l);
|
||||
return l;
|
||||
}
|
||||
@@ -232,12 +232,12 @@ QColor Color::toQColor() const
|
||||
return c;
|
||||
}
|
||||
|
||||
float Color::GetRoughLuminance() const
|
||||
Color::DataType Color::GetRoughLuminance() const
|
||||
{
|
||||
return (2*red()+blue()+3*green())/6.0;
|
||||
}
|
||||
|
||||
const Color &Color::operator+=(const Color &rhs)
|
||||
Color &Color::operator+=(const Color &rhs)
|
||||
{
|
||||
for (int i=0;i<VideoParams::kRGBAChannelCount;i++) {
|
||||
data_[i] += rhs.data_[i];
|
||||
@@ -246,7 +246,7 @@ const Color &Color::operator+=(const Color &rhs)
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Color &Color::operator-=(const Color &rhs)
|
||||
Color &Color::operator-=(const Color &rhs)
|
||||
{
|
||||
for (int i=0;i<VideoParams::kRGBAChannelCount;i++) {
|
||||
data_[i] -= rhs.data_[i];
|
||||
@@ -255,7 +255,25 @@ const Color &Color::operator-=(const Color &rhs)
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Color &Color::operator*=(const float &rhs)
|
||||
Color &Color::operator+=(const DataType &rhs)
|
||||
{
|
||||
for (int i=0;i<VideoParams::kRGBAChannelCount;i++) {
|
||||
data_[i] += rhs;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Color &Color::operator-=(const DataType &rhs)
|
||||
{
|
||||
for (int i=0;i<VideoParams::kRGBAChannelCount;i++) {
|
||||
data_[i] -= rhs;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Color &Color::operator*=(const DataType &rhs)
|
||||
{
|
||||
for (int i=0;i<VideoParams::kRGBAChannelCount;i++) {
|
||||
data_[i] *= rhs;
|
||||
@@ -264,7 +282,7 @@ const Color &Color::operator*=(const float &rhs)
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Color &Color::operator/=(const float &rhs)
|
||||
Color &Color::operator/=(const DataType &rhs)
|
||||
{
|
||||
for (int i=0;i<VideoParams::kRGBAChannelCount;i++) {
|
||||
data_[i] /= rhs;
|
||||
@@ -273,34 +291,6 @@ const Color &Color::operator/=(const float &rhs)
|
||||
return *this;
|
||||
}
|
||||
|
||||
Color Color::operator+(const Color &rhs) const
|
||||
{
|
||||
Color c(*this);
|
||||
c += rhs;
|
||||
return c;
|
||||
}
|
||||
|
||||
Color Color::operator-(const Color &rhs) const
|
||||
{
|
||||
Color c(*this);
|
||||
c -= rhs;
|
||||
return c;
|
||||
}
|
||||
|
||||
Color Color::operator*(const float &rhs) const
|
||||
{
|
||||
Color c(*this);
|
||||
c *= rhs;
|
||||
return c;
|
||||
}
|
||||
|
||||
Color Color::operator/(const float &rhs) const
|
||||
{
|
||||
Color c(*this);
|
||||
c /= rhs;
|
||||
return c;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const olive::Color &r)
|
||||
|
||||
+72
-31
@@ -30,11 +30,13 @@
|
||||
namespace olive {
|
||||
|
||||
/**
|
||||
* @brief High precision 32-bit float based RGBA color value
|
||||
* @brief High precision 32-bit DataType based RGBA color value
|
||||
*/
|
||||
class Color
|
||||
{
|
||||
public:
|
||||
using DataType = float;
|
||||
|
||||
Color()
|
||||
{
|
||||
for (int i=0;i<VideoParams::kRGBAChannelCount;i++) {
|
||||
@@ -42,7 +44,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
Color(const float& r, const float& g, const float& b, const float& a = 1.0f)
|
||||
Color(const DataType& r, const DataType& g, const DataType& b, const DataType& a = 1.0f)
|
||||
{
|
||||
data_[0] = r;
|
||||
data_[1] = g;
|
||||
@@ -59,30 +61,30 @@ public:
|
||||
*
|
||||
* Hue expects a value between 0.0 and 360.0. Saturation and Value expect a value between 0.0 and 1.0.
|
||||
*/
|
||||
static Color fromHsv(const float& h, const float& s, const float &v);
|
||||
static Color fromHsv(const DataType& h, const DataType& s, const DataType &v);
|
||||
|
||||
const float& red() const {return data_[0];}
|
||||
const float& green() const {return data_[1];}
|
||||
const float& blue() const {return data_[2];}
|
||||
const float& alpha() const {return data_[3];}
|
||||
const DataType& red() const {return data_[0];}
|
||||
const DataType& green() const {return data_[1];}
|
||||
const DataType& blue() const {return data_[2];}
|
||||
const DataType& alpha() const {return data_[3];}
|
||||
|
||||
void toHsv(float* hue, float* sat, float* val) const;
|
||||
float hsv_hue() const;
|
||||
float hsv_saturation() const;
|
||||
float value() const;
|
||||
void toHsv(DataType* hue, DataType* sat, DataType* val) const;
|
||||
DataType hsv_hue() const;
|
||||
DataType hsv_saturation() const;
|
||||
DataType value() const;
|
||||
|
||||
void toHsl(float* hue, float* sat, float* lightness) const;
|
||||
float hsl_hue() const;
|
||||
float hsl_saturation() const;
|
||||
float lightness() const;
|
||||
void toHsl(DataType* hue, DataType* sat, DataType* lightness) const;
|
||||
DataType hsl_hue() const;
|
||||
DataType hsl_saturation() const;
|
||||
DataType lightness() const;
|
||||
|
||||
void set_red(const float& red) {data_[0] = red;}
|
||||
void set_green(const float& green) {data_[1] = green;}
|
||||
void set_blue(const float& blue) {data_[2] = blue;}
|
||||
void set_alpha(const float& alpha) {data_[3] = alpha;}
|
||||
void set_red(const DataType& red) {data_[0] = red;}
|
||||
void set_green(const DataType& green) {data_[1] = green;}
|
||||
void set_blue(const DataType& blue) {data_[2] = blue;}
|
||||
void set_alpha(const DataType& alpha) {data_[3] = alpha;}
|
||||
|
||||
float* data() {return data_;}
|
||||
const float* data() const {return data_;}
|
||||
DataType* data() {return data_;}
|
||||
const DataType* data() const {return data_;}
|
||||
|
||||
void toData(char* data, const VideoParams::Format& format, int ch_layout) const;
|
||||
|
||||
@@ -92,22 +94,61 @@ public:
|
||||
|
||||
// Suuuuper rough luminance value mostly used for UI (determining whether to overlay with black
|
||||
// or white text)
|
||||
float GetRoughLuminance() const;
|
||||
DataType GetRoughLuminance() const;
|
||||
|
||||
// Assignment math operators
|
||||
const Color& operator+=(const Color& rhs);
|
||||
const Color& operator-=(const Color& rhs);
|
||||
const Color& operator*=(const float& rhs);
|
||||
const Color& operator/=(const float& rhs);
|
||||
Color& operator+=(const Color& rhs);
|
||||
Color& operator-=(const Color& rhs);
|
||||
Color& operator+=(const DataType& rhs);
|
||||
Color& operator-=(const DataType& rhs);
|
||||
Color& operator*=(const DataType& rhs);
|
||||
Color& operator/=(const DataType& rhs);
|
||||
|
||||
// Binary math operators
|
||||
Color operator+(const Color& rhs) const;
|
||||
Color operator-(const Color& rhs) const;
|
||||
Color operator*(const float& rhs) const;
|
||||
Color operator/(const float& rhs) const;
|
||||
Color operator+(const Color& rhs) const
|
||||
{
|
||||
Color c(*this);
|
||||
c += rhs;
|
||||
return c;
|
||||
}
|
||||
|
||||
Color operator-(const Color& rhs) const
|
||||
{
|
||||
Color c(*this);
|
||||
c -= rhs;
|
||||
return c;
|
||||
}
|
||||
|
||||
Color operator+(const DataType& rhs) const
|
||||
{
|
||||
Color c(*this);
|
||||
c += rhs;
|
||||
return c;
|
||||
}
|
||||
|
||||
Color operator-(const DataType& rhs) const
|
||||
{
|
||||
Color c(*this);
|
||||
c -= rhs;
|
||||
return c;
|
||||
}
|
||||
|
||||
Color operator*(const DataType& rhs) const
|
||||
{
|
||||
Color c(*this);
|
||||
c *= rhs;
|
||||
return c;
|
||||
}
|
||||
|
||||
Color operator/(const DataType& rhs) const
|
||||
{
|
||||
Color c(*this);
|
||||
c /= rhs;
|
||||
return c;
|
||||
}
|
||||
|
||||
private:
|
||||
float data_[VideoParams::kRGBAChannelCount];
|
||||
DataType data_[VideoParams::kRGBAChannelCount];
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
ColorProcessor::ColorProcessor(ColorManager *config, const QString &input, const ColorTransform &transform)
|
||||
ColorProcessor::ColorProcessor(ColorManager *config, const QString &input, const ColorTransform &transform, Direction direction)
|
||||
{
|
||||
QMutexLocker locker(config->mutex());
|
||||
|
||||
@@ -41,6 +41,7 @@ ColorProcessor::ColorProcessor(ColorManager *config, const QString &input, const
|
||||
display_transform->setSrc(input.toUtf8());
|
||||
display_transform->setDisplay(output.toUtf8());
|
||||
display_transform->setView(view.toUtf8());
|
||||
display_transform->setDirection(direction == kNormal ? OCIO::TRANSFORM_DIR_FORWARD : OCIO::TRANSFORM_DIR_INVERSE);
|
||||
|
||||
OCIO_SET_C_LOCALE_FOR_SCOPE;
|
||||
|
||||
@@ -69,13 +70,25 @@ ColorProcessor::ColorProcessor(ColorManager *config, const QString &input, const
|
||||
} else {
|
||||
|
||||
OCIO_SET_C_LOCALE_FOR_SCOPE;
|
||||
processor_ = config->GetConfig()->getProcessor(input.toUtf8(),
|
||||
output.toUtf8());
|
||||
try {
|
||||
if (direction == kNormal) {
|
||||
processor_ = config->GetConfig()->getProcessor(input.toUtf8(), output.toUtf8());
|
||||
} else {
|
||||
processor_ = config->GetConfig()->getProcessor(output.toUtf8(), input.toUtf8());
|
||||
}
|
||||
} catch (OCIO::Exception &e) {
|
||||
qWarning() << "ColorProcessor exception:" << e.what();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cpu_processor_ = processor_->getDefaultCPUProcessor();
|
||||
id_ = GenerateID(config, input, transform);
|
||||
}
|
||||
|
||||
ColorProcessor::ColorProcessor(OCIO::ConstProcessorRcPtr processor)
|
||||
{
|
||||
processor_ = processor;
|
||||
cpu_processor_ = processor_->getDefaultCPUProcessor();
|
||||
}
|
||||
|
||||
void ColorProcessor::ConvertFrame(Frame *f)
|
||||
@@ -109,18 +122,14 @@ Color ColorProcessor::ConvertColor(const Color& in)
|
||||
return Color(c[0], c[1], c[2], c[3]);
|
||||
}
|
||||
|
||||
QString ColorProcessor::GenerateID(ColorManager *config, const QString &input, const ColorTransform &transform)
|
||||
ColorProcessorPtr ColorProcessor::Create(ColorManager *config, const QString& input, const ColorTransform &transform, Direction direction)
|
||||
{
|
||||
return QStringLiteral("%1:%2:%3:%4:%5").arg(config->GetConfigFilename(),
|
||||
input,
|
||||
transform.display(),
|
||||
transform.view(),
|
||||
transform.look());
|
||||
return std::make_shared<ColorProcessor>(config, input, transform, direction);
|
||||
}
|
||||
|
||||
ColorProcessorPtr ColorProcessor::Create(ColorManager *config, const QString& input, const ColorTransform &transform)
|
||||
ColorProcessorPtr ColorProcessor::Create(OCIO::ConstProcessorRcPtr processor)
|
||||
{
|
||||
return std::make_shared<ColorProcessor>(config, input, transform);
|
||||
return std::make_shared<ColorProcessor>(processor);
|
||||
}
|
||||
|
||||
OCIO::ConstProcessorRcPtr ColorProcessor::GetProcessor()
|
||||
|
||||
@@ -41,11 +41,13 @@ public:
|
||||
kInverse
|
||||
};
|
||||
|
||||
ColorProcessor(ColorManager* config, const QString& input, const ColorTransform& dest_space);
|
||||
ColorProcessor(ColorManager* config, const QString& input, const ColorTransform& dest_space, Direction direction = kNormal);
|
||||
ColorProcessor(OCIO::ConstProcessorRcPtr processor);
|
||||
|
||||
DISABLE_COPY_MOVE(ColorProcessor)
|
||||
|
||||
static ColorProcessorPtr Create(ColorManager* config, const QString& input, const ColorTransform& dest_space);
|
||||
static ColorProcessorPtr Create(ColorManager* config, const QString& input, const ColorTransform& dest_space, Direction direction = kNormal);
|
||||
static ColorProcessorPtr Create(OCIO::ConstProcessorRcPtr processor);
|
||||
|
||||
OCIO::ConstProcessorRcPtr GetProcessor();
|
||||
|
||||
@@ -54,20 +56,16 @@ public:
|
||||
|
||||
Color ConvertColor(const Color &in);
|
||||
|
||||
const QString& id() const
|
||||
const char *id() const
|
||||
{
|
||||
return id_;
|
||||
return processor_->getCacheID();
|
||||
}
|
||||
|
||||
static QString GenerateID(ColorManager* config, const QString& input, const ColorTransform& dest_space);
|
||||
|
||||
private:
|
||||
OCIO::ConstProcessorRcPtr processor_;
|
||||
|
||||
OCIO::ConstCPUProcessorRcPtr cpu_processor_;
|
||||
|
||||
QString id_;
|
||||
|
||||
};
|
||||
|
||||
using ColorProcessorChain = QVector<ColorProcessorPtr>;
|
||||
|
||||
@@ -196,7 +196,7 @@ DiskCacheFolder::DiskCacheFolder(const QString &path, QObject *parent) :
|
||||
{
|
||||
SetPath(path);
|
||||
|
||||
save_timer_.setInterval(Config::Current()[QStringLiteral("DiskCacheSaveInterval")].toInt());
|
||||
save_timer_.setInterval(OLIVE_CONFIG("DiskCacheSaveInterval").toInt());
|
||||
connect(&save_timer_, &QTimer::timeout, this, &DiskCacheFolder::SaveDiskCacheIndex);
|
||||
save_timer_.start();
|
||||
}
|
||||
|
||||
@@ -30,22 +30,22 @@ class AcceleratedJob {
|
||||
public:
|
||||
AcceleratedJob() = default;
|
||||
|
||||
NodeValue GetValue(const QString& input) const
|
||||
NodeValue Get(const QString& input) const
|
||||
{
|
||||
return value_map_.value(input);
|
||||
}
|
||||
|
||||
void InsertValue(const QString &input, const NodeValueRow &row)
|
||||
void Insert(const QString &input, const NodeValueRow &row)
|
||||
{
|
||||
value_map_.insert(input, row.value(input));
|
||||
}
|
||||
|
||||
void InsertValue(const QString& input, const NodeValue& value)
|
||||
void Insert(const QString& input, const NodeValue& value)
|
||||
{
|
||||
value_map_.insert(input, value);
|
||||
}
|
||||
|
||||
void InsertValue(const NodeValueRow &row)
|
||||
void Insert(const NodeValueRow &row)
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||
value_map_.insert(row);
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef COLORTRANSFORMJOB_H
|
||||
#define COLORTRANSFORMJOB_H
|
||||
|
||||
#include <QMatrix4x4>
|
||||
#include <QString>
|
||||
|
||||
#include "render/job/generatejob.h"
|
||||
#include "render/alphaassoc.h"
|
||||
#include "render/colorprocessor.h"
|
||||
#include "render/texture.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class Node;
|
||||
|
||||
class ColorTransformJob : public GenerateJob
|
||||
{
|
||||
public:
|
||||
ColorTransformJob()
|
||||
{
|
||||
processor_ = nullptr;
|
||||
input_texture_ = nullptr;
|
||||
custom_shader_src_ = nullptr;
|
||||
input_alpha_association_ = kAlphaNone;
|
||||
clear_destination_ = true;
|
||||
}
|
||||
|
||||
QString id() const
|
||||
{
|
||||
if (id_.isEmpty()) {
|
||||
return processor_->id();
|
||||
} else {
|
||||
return id_;
|
||||
}
|
||||
}
|
||||
|
||||
void SetOverrideID(const QString &id) { id_ = id; }
|
||||
|
||||
TexturePtr GetInputTexture() const { return input_texture_; }
|
||||
void SetInputTexture(TexturePtr tex) { input_texture_ = tex; }
|
||||
|
||||
ColorProcessorPtr GetColorProcessor() const { return processor_; }
|
||||
void SetColorProcessor(ColorProcessorPtr p) { processor_ = p; }
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool IsClearDestinationEnabled() const { return clear_destination_; }
|
||||
void SetClearDestinationEnabled(bool e) { clear_destination_ = e; }
|
||||
|
||||
const QMatrix4x4 &GetTransformMatrix() const { return matrix_; }
|
||||
void SetTransformMatrix(const QMatrix4x4 &m) { matrix_ = m; }
|
||||
|
||||
const QMatrix4x4 &GetCropMatrix() const { return crop_matrix_; }
|
||||
void SetCropMatrix(const QMatrix4x4 &m) { crop_matrix_ = m; }
|
||||
|
||||
const QString &GetFunctionName() const { return function_name_; }
|
||||
void SetFunctionName(const QString &function_name = QString()) { function_name_ = function_name; };
|
||||
|
||||
private:
|
||||
ColorProcessorPtr processor_;
|
||||
QString id_;
|
||||
|
||||
TexturePtr input_texture_;
|
||||
|
||||
const Node *custom_shader_src_;
|
||||
QString custom_shader_id_;
|
||||
|
||||
AlphaAssociated input_alpha_association_;
|
||||
|
||||
bool clear_destination_;
|
||||
|
||||
QMatrix4x4 matrix_;
|
||||
|
||||
QMatrix4x4 crop_matrix_;
|
||||
|
||||
QString function_name_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(olive::ColorTransformJob)
|
||||
|
||||
#endif // COLORTRANSFORMJOB_H
|
||||
@@ -30,31 +30,30 @@ class SampleJob : public AcceleratedJob {
|
||||
public:
|
||||
SampleJob()
|
||||
{
|
||||
samples_ = nullptr;
|
||||
}
|
||||
|
||||
SampleJob(const NodeValue& value)
|
||||
{
|
||||
samples_ = value.data().value<SampleBufferPtr>();
|
||||
samples_ = value.toSamples();
|
||||
}
|
||||
|
||||
SampleJob(const QString& from, const NodeValueRow& row)
|
||||
{
|
||||
samples_ = row[from].data().value<SampleBufferPtr>();
|
||||
samples_ = row[from].toSamples();
|
||||
}
|
||||
|
||||
SampleBufferPtr samples() const
|
||||
const SampleBuffer &samples() const
|
||||
{
|
||||
return samples_;
|
||||
}
|
||||
|
||||
bool HasSamples() const
|
||||
{
|
||||
return samples_ && samples_->is_allocated();
|
||||
return samples_.is_allocated();
|
||||
}
|
||||
|
||||
private:
|
||||
SampleBufferPtr samples_;
|
||||
SampleBuffer samples_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <QVector>
|
||||
|
||||
#include "generatejob.h"
|
||||
#include "render/colorprocessor.h"
|
||||
#include "render/texture.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -449,48 +449,48 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
|
||||
case NodeValue::kInt:
|
||||
// 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.data().toInt());
|
||||
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.data().toFloat());
|
||||
functions_->glUniform1f(variable_location, value.toDouble());
|
||||
break;
|
||||
case NodeValue::kVec2:
|
||||
{
|
||||
QVector2D v = value.data().value<QVector2D>();
|
||||
QVector2D v = value.toVec2();
|
||||
functions_->glUniform2fv(variable_location, 1, reinterpret_cast<const GLfloat*>(&v));
|
||||
break;
|
||||
}
|
||||
case NodeValue::kVec3:
|
||||
{
|
||||
QVector3D v = value.data().value<QVector3D>();
|
||||
QVector3D v = value.toVec3();
|
||||
functions_->glUniform3fv(variable_location, 1, reinterpret_cast<const GLfloat*>(&v));
|
||||
break;
|
||||
}
|
||||
case NodeValue::kVec4:
|
||||
{
|
||||
QVector4D v = value.data().value<QVector4D>();
|
||||
QVector4D v = value.toVec4();
|
||||
functions_->glUniform4fv(variable_location, 1, reinterpret_cast<const GLfloat*>(&v));
|
||||
break;
|
||||
}
|
||||
case NodeValue::kMatrix:
|
||||
functions_->glUniformMatrix4fv(variable_location, 1, false, value.data().value<QMatrix4x4>().constData());
|
||||
functions_->glUniformMatrix4fv(variable_location, 1, false, value.toMatrix().constData());
|
||||
break;
|
||||
case NodeValue::kCombo:
|
||||
functions_->glUniform1i(variable_location, value.data().value<int>());
|
||||
functions_->glUniform1i(variable_location, value.toInt());
|
||||
break;
|
||||
case NodeValue::kColor:
|
||||
{
|
||||
Color color = value.data().value<Color>();
|
||||
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.data().toBool());
|
||||
functions_->glUniform1i(variable_location, value.toBool());
|
||||
break;
|
||||
case NodeValue::kTexture:
|
||||
{
|
||||
TexturePtr texture = value.data().value<TexturePtr>();
|
||||
TexturePtr texture = value.toTexture();
|
||||
|
||||
// Set value to bound texture
|
||||
functions_->glUniform1i(variable_location, textures_to_bind.size());
|
||||
@@ -552,7 +552,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
|
||||
// Ensure matrix is set, at least to identity
|
||||
GLint mvpmat_location = functions_->glGetUniformLocation(shader, "ove_mvpmat");
|
||||
if (mvpmat_location > -1) {
|
||||
functions_->glUniformMatrix4fv(mvpmat_location, 1, false, job.GetValue(QStringLiteral("ove_mvpmat")).data().value<QMatrix4x4>().constData());
|
||||
functions_->glUniformMatrix4fv(mvpmat_location, 1, false, job.Get(QStringLiteral("ove_mvpmat")).toMatrix().constData());
|
||||
}
|
||||
|
||||
// Set the viewport to the "physical" resolution of the destination
|
||||
|
||||
@@ -48,7 +48,7 @@ PreviewAutoCacher::PreviewAutoCacher() :
|
||||
SetPlayhead(0);
|
||||
|
||||
// Wait a certain amount of time before requeuing when we receive an invalidate signal
|
||||
delayed_requeue_timer_.setInterval(Config::Current()[QStringLiteral("AutoCacheDelay")].toInt());
|
||||
delayed_requeue_timer_.setInterval(OLIVE_CONFIG("AutoCacheDelay").toInt());
|
||||
delayed_requeue_timer_.setSingleShot(true);
|
||||
connect(&delayed_requeue_timer_, &QTimer::timeout, this, &PreviewAutoCacher::RequeueFrames);
|
||||
|
||||
@@ -207,7 +207,7 @@ void PreviewAutoCacher::AudioRendered()
|
||||
// WritePCM is tolerant to its buffer being null, it will just write silence instead
|
||||
viewer_node_->audio_playback_cache()->WritePCM(range,
|
||||
valid_ranges,
|
||||
watcher->Get().value<SampleBufferPtr>());
|
||||
watcher->Get().value<SampleBuffer>());
|
||||
}
|
||||
|
||||
viewer_node_->audio_playback_cache()->WriteWaveform(range, valid_ranges, &waveform);
|
||||
@@ -221,39 +221,39 @@ void PreviewAutoCacher::AudioRendered()
|
||||
// Wait for conform
|
||||
audio_needing_conform_.insert(range);
|
||||
}
|
||||
}
|
||||
} else{
|
||||
// Retrieve visual waveforms
|
||||
QVector<RenderProcessor::RenderedWaveform> waveform_list = watcher->GetTicket()->property("waveforms").value< QVector<RenderProcessor::RenderedWaveform> >();
|
||||
foreach (const RenderProcessor::RenderedWaveform& waveform_info, waveform_list) {
|
||||
// Find original track
|
||||
ClipBlock* block = nullptr;
|
||||
|
||||
// Retrieve visual waveforms
|
||||
QVector<RenderProcessor::RenderedWaveform> waveform_list = watcher->GetTicket()->property("waveforms").value< QVector<RenderProcessor::RenderedWaveform> >();
|
||||
foreach (const RenderProcessor::RenderedWaveform& waveform_info, waveform_list) {
|
||||
// Find original track
|
||||
ClipBlock* block = nullptr;
|
||||
|
||||
for (auto it=copy_map_.cbegin(); it!=copy_map_.cend(); it++) {
|
||||
if (it.value() == waveform_info.block) {
|
||||
block = static_cast<ClipBlock*>(it.key());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (block && !valid_ranges.isEmpty()) {
|
||||
// Generate visual waveform in this background thread
|
||||
block->waveform().set_channel_count(viewer_node_->GetAudioParams().channel_count());
|
||||
|
||||
// Determine which of the waveform ranges we got intersects with the valid ranges
|
||||
TimeRangeList intersections = valid_ranges.Intersects(waveform_info.range + block->in());
|
||||
foreach (TimeRange r, intersections) {
|
||||
// For each range, adjust it relative to the block and write it
|
||||
r -= block->in();
|
||||
|
||||
if (waveform_info.silence) {
|
||||
block->waveform().OverwriteSilence(r.in(), r.length());
|
||||
} else {
|
||||
block->waveform().OverwriteSums(waveform_info.waveform, r.in(), r.in() - waveform_info.range.in(), r.length());
|
||||
for (auto it=copy_map_.cbegin(); it!=copy_map_.cend(); it++) {
|
||||
if (it.value() == waveform_info.block) {
|
||||
block = static_cast<ClipBlock*>(it.key());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
emit block->PreviewChanged();
|
||||
if (block && !valid_ranges.isEmpty()) {
|
||||
// Generate visual waveform in this background thread
|
||||
block->waveform().set_channel_count(viewer_node_->GetAudioParams().channel_count());
|
||||
|
||||
// Determine which of the waveform ranges we got intersects with the valid ranges
|
||||
TimeRangeList intersections = valid_ranges.Intersects(waveform_info.range + block->in());
|
||||
foreach (TimeRange r, intersections) {
|
||||
// For each range, adjust it relative to the block and write it
|
||||
r -= block->in();
|
||||
|
||||
if (waveform_info.silence) {
|
||||
block->waveform().OverwriteSilence(r.in(), r.length());
|
||||
} else {
|
||||
block->waveform().OverwriteSums(waveform_info.waveform, r.in(), r.in() - waveform_info.range.in(), r.length());
|
||||
}
|
||||
}
|
||||
|
||||
emit block->PreviewChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -502,8 +502,8 @@ void PreviewAutoCacher::StartCachingAudioRange(const TimeRange &range)
|
||||
|
||||
void PreviewAutoCacher::SetPlayhead(const rational &playhead)
|
||||
{
|
||||
cache_range_ = TimeRange(playhead - Config::Current()[QStringLiteral("DiskCacheBehind")].value<rational>(),
|
||||
playhead + Config::Current()[QStringLiteral("DiskCacheAhead")].value<rational>());
|
||||
cache_range_ = TimeRange(playhead - OLIVE_CONFIG("DiskCacheBehind").value<rational>(),
|
||||
playhead + OLIVE_CONFIG("DiskCacheAhead").value<rational>());
|
||||
|
||||
RequeueFrames();
|
||||
}
|
||||
|
||||
+39
-41
@@ -52,16 +52,6 @@ TexturePtr Renderer::CreateTexture(const VideoParams ¶ms, const void *data,
|
||||
return CreateTexture(params, Texture::k2D, data, linesize);
|
||||
}
|
||||
|
||||
void Renderer::BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, AlphaAssociated source_alpha_association, Texture *destination, bool clear_destination, const QMatrix4x4 &matrix, const QMatrix4x4 &crop_matrix)
|
||||
{
|
||||
BlitColorManagedInternal(color_processor, source, source_alpha_association, destination, destination->params(), clear_destination, matrix, crop_matrix);
|
||||
}
|
||||
|
||||
void Renderer::BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, AlphaAssociated source_alpha_association, VideoParams params, bool clear_destination, const QMatrix4x4& matrix, const QMatrix4x4 &crop_matrix)
|
||||
{
|
||||
BlitColorManagedInternal(color_processor, source, source_alpha_association, nullptr, params, clear_destination, matrix, crop_matrix);
|
||||
}
|
||||
|
||||
TexturePtr Renderer::InterlaceTexture(TexturePtr top, TexturePtr bottom, const VideoParams ¶ms)
|
||||
{
|
||||
color_cache_mutex_.lock();
|
||||
@@ -71,9 +61,9 @@ TexturePtr Renderer::InterlaceTexture(TexturePtr top, TexturePtr bottom, const V
|
||||
color_cache_mutex_.unlock();
|
||||
|
||||
ShaderJob job;
|
||||
job.InsertValue(QStringLiteral("top_tex_in"), NodeValue(NodeValue::kTexture, QVariant::fromValue(top)));
|
||||
job.InsertValue(QStringLiteral("bottom_tex_in"), NodeValue(NodeValue::kTexture, QVariant::fromValue(bottom)));
|
||||
job.InsertValue(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, QVector2D(params.effective_width(), params.effective_height())));
|
||||
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())));
|
||||
|
||||
TexturePtr output = CreateTexture(params);
|
||||
|
||||
@@ -103,35 +93,45 @@ TexturePtr Renderer::CreateTextureFromNativeHandle(const QVariant &v, const Vide
|
||||
return std::make_shared<Texture>(this, v, params, type);
|
||||
}
|
||||
|
||||
bool Renderer::GetColorContext(ColorProcessorPtr color_processor, Renderer::ColorContext *ctx)
|
||||
bool Renderer::GetColorContext(const ColorTransformJob &color_job, Renderer::ColorContext *ctx)
|
||||
{
|
||||
QMutexLocker locker(&color_cache_mutex_);
|
||||
|
||||
ColorContext& color_ctx = *ctx;
|
||||
|
||||
if (color_cache_.contains(color_processor->id())) {
|
||||
color_ctx = color_cache_.value(color_processor->id());
|
||||
QString proc_id = color_job.id();
|
||||
|
||||
if (color_cache_.contains(proc_id)) {
|
||||
color_ctx = color_cache_.value(proc_id);
|
||||
return true;
|
||||
} else {
|
||||
// Create shader description
|
||||
const char* ocio_func_name = "OCIODisplay";
|
||||
QString ocio_func_name;
|
||||
if (color_job.GetFunctionName().isEmpty()) {
|
||||
ocio_func_name = "OCIODisplay";
|
||||
} else {
|
||||
ocio_func_name = color_job.GetFunctionName();
|
||||
}
|
||||
auto shader_desc = OCIO::GpuShaderDesc::CreateShaderDesc();
|
||||
shader_desc->setLanguage(OCIO::GPU_LANGUAGE_GLSL_ES_3_0);
|
||||
shader_desc->setFunctionName(ocio_func_name);
|
||||
shader_desc->setFunctionName(ocio_func_name.toUtf8());
|
||||
shader_desc->setResourcePrefix("ocio_");
|
||||
|
||||
// Generate shader
|
||||
color_processor->GetProcessor()->getDefaultGPUProcessor()->extractGpuShaderInfo(shader_desc);
|
||||
color_job.GetColorProcessor()->GetProcessor()->getDefaultGPUProcessor()->extractGpuShaderInfo(shader_desc);
|
||||
|
||||
// Generate shader code using OCIO stub and our auto-generated name
|
||||
QString shader_frag = FileFunctions::ReadFileAsString(QStringLiteral(":shaders/colormanage.frag")).arg(
|
||||
shader_desc->getShaderText(),
|
||||
ocio_func_name
|
||||
);
|
||||
ShaderCode code;
|
||||
if (const Node *shader_src = color_job.CustomShaderSource()) {
|
||||
// Use shader code from associated node
|
||||
code = shader_src->GetShaderCode({color_job.CustomShaderID(), shader_desc->getShaderText()});
|
||||
} else {
|
||||
// Generate shader code using OCIO stub and our auto-generated name
|
||||
code = FileFunctions::ReadFileAsString(QStringLiteral(":shaders/colormanage.frag"));
|
||||
code.set_frag_code(code.frag_code().arg(shader_desc->getShaderText()));
|
||||
}
|
||||
|
||||
// Try to compile shader
|
||||
color_ctx.compiled_shader = CreateNativeShader(ShaderCode(shader_frag,
|
||||
FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/default.vert"))));
|
||||
color_ctx.compiled_shader = CreateNativeShader(code);
|
||||
|
||||
if (color_ctx.compiled_shader.isNull()) {
|
||||
return false;
|
||||
@@ -199,42 +199,40 @@ bool Renderer::GetColorContext(ColorProcessorPtr color_processor, Renderer::Colo
|
||||
color_ctx.lut1d_textures[i].interpolation = (interpolation == OCIO::INTERP_NEAREST) ? Texture::kNearest : Texture::kLinear;
|
||||
}
|
||||
|
||||
color_cache_.insert(color_processor->id(), color_ctx);
|
||||
color_cache_.insert(proc_id, color_ctx);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::BlitColorManagedInternal(ColorProcessorPtr color_processor, TexturePtr source,
|
||||
AlphaAssociated source_alpha_association, Texture *destination,
|
||||
VideoParams params, bool clear_destination, const QMatrix4x4& matrix,
|
||||
const QMatrix4x4& crop_matrix)
|
||||
void Renderer::BlitColorManaged(const ColorTransformJob &color_job, Texture *destination, const VideoParams ¶ms)
|
||||
{
|
||||
ColorContext color_ctx;
|
||||
if (!GetColorContext(color_processor, &color_ctx)) {
|
||||
if (!GetColorContext(color_job, &color_ctx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ShaderJob job;
|
||||
|
||||
job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(source)));
|
||||
job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, matrix));
|
||||
job.InsertValue(QStringLiteral("ove_cropmatrix"), NodeValue(NodeValue::kMatrix, crop_matrix.inverted()));
|
||||
job.InsertValue(QStringLiteral("ove_maintex_alpha"), NodeValue(NodeValue::kInt, source_alpha_association));
|
||||
job.Insert(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(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(color_job.GetValues());
|
||||
job.SetAlphaChannelRequired(color_job.GetAlphaChannelRequired());
|
||||
|
||||
foreach (const ColorContext::LUT& l, color_ctx.lut3d_textures) {
|
||||
job.InsertValue(l.name, NodeValue(NodeValue::kTexture, QVariant::fromValue(l.texture)));
|
||||
job.Insert(l.name, NodeValue(NodeValue::kTexture, QVariant::fromValue(l.texture)));
|
||||
job.SetInterpolation(l.name, l.interpolation);
|
||||
}
|
||||
foreach (const ColorContext::LUT& l, color_ctx.lut1d_textures) {
|
||||
job.InsertValue(l.name, NodeValue(NodeValue::kTexture, QVariant::fromValue(l.texture)));
|
||||
job.Insert(l.name, NodeValue(NodeValue::kTexture, QVariant::fromValue(l.texture)));
|
||||
job.SetInterpolation(l.name, l.interpolation);
|
||||
}
|
||||
|
||||
if (destination) {
|
||||
BlitToTexture(color_ctx.compiled_shader, job, destination, clear_destination);
|
||||
BlitToTexture(color_ctx.compiled_shader, job, destination, color_job.IsClearDestinationEnabled());
|
||||
} else {
|
||||
Blit(color_ctx.compiled_shader, job, params, clear_destination);
|
||||
Blit(color_ctx.compiled_shader, job, params, color_job.IsClearDestinationEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-14
@@ -29,6 +29,7 @@
|
||||
#include "common/timerange.h"
|
||||
#include "node/node.h"
|
||||
#include "render/colorprocessor.h"
|
||||
#include "render/job/colortransformjob.h"
|
||||
#include "render/videoparams.h"
|
||||
#include "texture.h"
|
||||
|
||||
@@ -63,14 +64,15 @@ public:
|
||||
Blit(shader, job, nullptr, params, clear_destination);
|
||||
}
|
||||
|
||||
enum AlphaAssociated {
|
||||
kAlphaNone,
|
||||
kAlphaUnassociated,
|
||||
kAlphaAssociated
|
||||
};
|
||||
|
||||
void BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, AlphaAssociated source_alpha_association, Texture* destination, bool clear_destination = true, const QMatrix4x4& matrix = QMatrix4x4(), const QMatrix4x4 &crop_matrix = QMatrix4x4());
|
||||
void BlitColorManaged(ColorProcessorPtr color_processor, TexturePtr source, AlphaAssociated source_alpha_association, VideoParams params, bool clear_destination = true, const QMatrix4x4& matrix = QMatrix4x4(), const QMatrix4x4 &crop_matrix = QMatrix4x4());
|
||||
void BlitColorManaged(const ColorTransformJob &color_job, Texture* destination, const VideoParams ¶ms);
|
||||
void BlitColorManaged(const ColorTransformJob &job, Texture* destination)
|
||||
{
|
||||
BlitColorManaged(job, destination, destination->params());
|
||||
}
|
||||
void BlitColorManaged(const ColorTransformJob &job, const VideoParams ¶ms)
|
||||
{
|
||||
BlitColorManaged(job, nullptr, params);
|
||||
}
|
||||
|
||||
TexturePtr InterlaceTexture(TexturePtr top, TexturePtr bottom, const VideoParams ¶ms);
|
||||
|
||||
@@ -126,12 +128,7 @@ private:
|
||||
|
||||
};
|
||||
|
||||
bool GetColorContext(ColorProcessorPtr color_processor, ColorContext* ctx);
|
||||
|
||||
void BlitColorManagedInternal(ColorProcessorPtr color_processor, TexturePtr source,
|
||||
AlphaAssociated source_alpha_association,
|
||||
Texture* destination, VideoParams params, bool clear_destination,
|
||||
const QMatrix4x4 &matrix, const QMatrix4x4 &crop_matrix);
|
||||
bool GetColorContext(const ColorTransformJob &color_job, ColorContext* ctx);
|
||||
|
||||
QHash<QString, ColorContext> color_cache_;
|
||||
|
||||
|
||||
@@ -25,9 +25,7 @@
|
||||
#include <QVector3D>
|
||||
#include <QVector4D>
|
||||
|
||||
#include "audio/packedprocessor.h"
|
||||
#include "audio/planarprocessor.h"
|
||||
#include "audio/tempoprocessor.h"
|
||||
#include "audio/audioprocessor.h"
|
||||
#include "node/block/clip/clip.h"
|
||||
#include "node/block/transition/transition.h"
|
||||
#include "node/project/project.h"
|
||||
@@ -57,11 +55,11 @@ TexturePtr RenderProcessor::GenerateTexture(const rational &time, const rational
|
||||
table = GenerateTable(texture_output, viewer->GetValueHintForInput(ViewerOutput::kTextureInput), range);
|
||||
}
|
||||
|
||||
NodeValue tex_val = table.GetWithMeta(NodeValue::kTexture);
|
||||
NodeValue tex_val = table.Get(NodeValue::kTexture);
|
||||
|
||||
ResolveJobs(tex_val, range);
|
||||
|
||||
return tex_val.data().value<TexturePtr>();
|
||||
return tex_val.toTexture();
|
||||
}
|
||||
|
||||
FramePtr RenderProcessor::GenerateFrame(TexturePtr texture, const rational& time)
|
||||
@@ -105,14 +103,19 @@ FramePtr RenderProcessor::GenerateFrame(TexturePtr texture, const rational& time
|
||||
|
||||
if (output_color_transform) {
|
||||
// Yes color transform, blit color managed
|
||||
render_ctx_->BlitColorManaged(output_color_transform, texture,
|
||||
Config::Current()[QStringLiteral("ReassocLinToNonLin")].toBool() ? Renderer::kAlphaAssociated : Renderer::kAlphaNone,
|
||||
blit_tex.get(), true, matrix);
|
||||
ColorTransformJob job;
|
||||
|
||||
job.SetColorProcessor(output_color_transform);
|
||||
job.SetInputTexture(texture);
|
||||
job.SetInputAlphaAssociation(OLIVE_CONFIG("ReassocLinToNonLin").toBool() ? kAlphaAssociated : kAlphaNone);
|
||||
job.SetTransformMatrix(matrix);
|
||||
|
||||
render_ctx_->BlitColorManaged(job, blit_tex.get());
|
||||
} else {
|
||||
// No color transform, just blit
|
||||
ShaderJob job;
|
||||
job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture)));
|
||||
job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, matrix));
|
||||
job.Insert(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture)));
|
||||
job.Insert(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, matrix));
|
||||
|
||||
render_ctx_->BlitToTexture(default_shader_, job, blit_tex.get());
|
||||
}
|
||||
@@ -193,19 +196,22 @@ void RenderProcessor::Run()
|
||||
table = GenerateTable(texture_output, viewer->GetValueHintForInput(ViewerOutput::kSamplesInput),time);
|
||||
}
|
||||
|
||||
QVariant sample_variant = table.Get(NodeValue::kSamples);
|
||||
SampleBufferPtr samples = sample_variant.value<SampleBufferPtr>();
|
||||
if (samples && ticket_->property("enablewaveforms").toBool()) {
|
||||
NodeValue sample_val = table.Get(NodeValue::kSamples);
|
||||
|
||||
ResolveJobs(sample_val, time);
|
||||
|
||||
SampleBuffer samples = sample_val.toSamples();
|
||||
if (samples.is_allocated() && ticket_->property("enablewaveforms").toBool()) {
|
||||
AudioVisualWaveform vis;
|
||||
vis.set_channel_count(samples->audio_params().channel_count());
|
||||
vis.OverwriteSamples(samples, samples->audio_params().sample_rate());
|
||||
vis.set_channel_count(samples.audio_params().channel_count());
|
||||
vis.OverwriteSamples(samples, samples.audio_params().sample_rate());
|
||||
ticket_->setProperty("waveform", QVariant::fromValue(vis));
|
||||
}
|
||||
|
||||
if (ticket_->IsCancelled()) {
|
||||
ticket_->Finish();
|
||||
} else {
|
||||
ticket_->Finish(sample_variant);
|
||||
ticket_->Finish(QVariant::fromValue(samples));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -269,9 +275,8 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim
|
||||
QVector<Block*> active_blocks = track->BlocksAtTimeRange(range);
|
||||
|
||||
// All these blocks will need to output to a buffer so we create one here
|
||||
SampleBufferPtr block_range_buffer = SampleBuffer::CreateAllocated(audio_params,
|
||||
audio_params.time_to_samples(range.length()));
|
||||
block_range_buffer->silence();
|
||||
SampleBuffer block_range_buffer(audio_params, range.length());
|
||||
block_range_buffer.silence();
|
||||
|
||||
NodeValueTable merged_table;
|
||||
|
||||
@@ -286,10 +291,10 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim
|
||||
|
||||
// Destination buffer
|
||||
NodeValueTable table = GenerateTable(b, track->GetValueHintForInput(Track::kBlockInput, track->GetArrayIndexFromBlock(b)),Track::TransformRangeForBlock(b, range_for_block));
|
||||
SampleBufferPtr samples_from_this_block = table.Take(NodeValue::kSamples).value<SampleBufferPtr>();
|
||||
SampleBuffer samples_from_this_block = table.Take(NodeValue::kSamples).toSamples();
|
||||
ClipBlock *clip_cast = dynamic_cast<ClipBlock*>(b);
|
||||
|
||||
if (samples_from_this_block) {
|
||||
if (samples_from_this_block.is_allocated()) {
|
||||
// If this is a clip, we might have extra speed/reverse information
|
||||
if (clip_cast) {
|
||||
double speed_value = clip_cast->speed();
|
||||
@@ -297,17 +302,13 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim
|
||||
|
||||
if (qIsNull(speed_value)) {
|
||||
// Just silence, don't think there's any other practical application of 0 speed audio
|
||||
samples_from_this_block->silence();
|
||||
samples_from_this_block.silence();
|
||||
} else if (!qFuzzyCompare(speed_value, 1.0)) {
|
||||
if (clip_cast->maintain_audio_pitch()) {
|
||||
PackedProcessor packer;
|
||||
packer.Open(samples_from_this_block->audio_params());
|
||||
AudioProcessor processor;
|
||||
|
||||
QByteArray packed = packer.Convert(samples_from_this_block);
|
||||
|
||||
if (!packed.isEmpty()) {
|
||||
TempoProcessor tp;
|
||||
tp.Open(samples_from_this_block->audio_params(), speed_value);
|
||||
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
|
||||
@@ -315,33 +316,46 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim
|
||||
// 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.
|
||||
tp.Push(packed);
|
||||
tp.Flush();
|
||||
packed = tp.Pull();
|
||||
tp.Close();
|
||||
int r = processor.Convert(samples_from_this_block.to_raw_ptrs().data(), samples_from_this_block.sample_count(), nullptr);
|
||||
|
||||
if (!packed.isEmpty()) {
|
||||
PlanarProcessor planar;
|
||||
planar.Open(samples_from_this_block->audio_params());
|
||||
samples_from_this_block = planar.Convert(packed);
|
||||
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<out.size(); i++) {
|
||||
memcpy(new_samples.data(i), out[i].data(), out[i].size());
|
||||
}
|
||||
|
||||
samples_from_this_block = new_samples;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Multiply time
|
||||
samples_from_this_block->speed(speed_value);
|
||||
samples_from_this_block.speed(speed_value);
|
||||
}
|
||||
}
|
||||
|
||||
if (reversed) {
|
||||
samples_from_this_block->reverse();
|
||||
samples_from_this_block.reverse();
|
||||
}
|
||||
}
|
||||
|
||||
int copy_length = qMin(max_dest_sz, samples_from_this_block->sample_count());
|
||||
int copy_length = qMin(max_dest_sz, samples_from_this_block.sample_count());
|
||||
|
||||
// Copy samples into destination buffer
|
||||
for (int i=0; i<samples_from_this_block->audio_params().channel_count(); i++) {
|
||||
block_range_buffer->set(i, samples_from_this_block->data(i), destination_offset, copy_length);
|
||||
for (int i=0; i<samples_from_this_block.audio_params().channel_count(); i++) {
|
||||
block_range_buffer.set(i, samples_from_this_block.data(i), destination_offset, copy_length);
|
||||
}
|
||||
|
||||
NodeValueTable::Merge({merged_table, table});
|
||||
@@ -354,7 +368,7 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim
|
||||
waveform_info.block = clip_cast;
|
||||
waveform_info.range = range_for_block - b->in();
|
||||
|
||||
if (!(waveform_info.silence = !samples_from_this_block.get())) {
|
||||
if (!(waveform_info.silence = !samples_from_this_block.is_allocated())) {
|
||||
// Generate a visual waveform from the samples acquired from this block
|
||||
AudioVisualWaveform visual_waveform;
|
||||
visual_waveform.set_channel_count(audio_params.channel_count());
|
||||
@@ -388,19 +402,10 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJ
|
||||
// Check the still frame cache. On large frames such as high resolution still images, uploading
|
||||
// and color managing them for every frame is a waste of time, so we implement a small cache here
|
||||
// to optimize such a situation
|
||||
const VideoParams& render_params = GetCacheVideoParams();
|
||||
VideoParams stream_data = stream.video_params();
|
||||
|
||||
ColorManager* color_manager = Node::ValueToPtr<ColorManager>(ticket_->property("colormanager"));
|
||||
|
||||
// See if we can make this divider larger (i.e. if the fooage is smaller)
|
||||
int footage_divider = render_params.divider();
|
||||
while (footage_divider > 1
|
||||
&& VideoParams::GetScaledDimension(stream_data.width(), footage_divider-1) < render_params.effective_width()
|
||||
&& VideoParams::GetScaledDimension(stream_data.height(), footage_divider-1) < render_params.effective_height()) {
|
||||
footage_divider--;
|
||||
}
|
||||
|
||||
QString using_colorspace = stream_data.colorspace();
|
||||
|
||||
if (using_colorspace.isEmpty()) {
|
||||
@@ -435,7 +440,7 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJ
|
||||
|
||||
if (decoder) {
|
||||
Decoder::RetrieveVideoParams p;
|
||||
p.divider = footage_divider;
|
||||
p.divider = stream.video_params().divider();
|
||||
p.src_interlacing = stream_data.interlacing();
|
||||
p.dst_interlacing = GetCacheVideoParams().interlacing();
|
||||
|
||||
@@ -454,25 +459,27 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJ
|
||||
using_colorspace,
|
||||
color_manager->GetReferenceColorSpace());
|
||||
|
||||
Renderer::AlphaAssociated alpha_assoc;
|
||||
ColorTransformJob job;
|
||||
|
||||
job.SetColorProcessor(processor);
|
||||
job.SetInputTexture(unmanaged_texture);
|
||||
|
||||
if (stream_data.channel_count() != VideoParams::kRGBAChannelCount
|
||||
|| stream_data.colorspace() == color_manager->GetReferenceColorSpace()) {
|
||||
alpha_assoc = Renderer::kAlphaNone;
|
||||
job.SetInputAlphaAssociation(kAlphaNone);
|
||||
} else if (stream_data.premultiplied_alpha()) {
|
||||
alpha_assoc = Renderer::kAlphaAssociated;
|
||||
job.SetInputAlphaAssociation(kAlphaAssociated);
|
||||
} else {
|
||||
alpha_assoc = Renderer::kAlphaUnassociated;
|
||||
job.SetInputAlphaAssociation(kAlphaUnassociated);
|
||||
}
|
||||
|
||||
render_ctx_->BlitColorManaged(processor, unmanaged_texture,
|
||||
alpha_assoc,
|
||||
destination.get());
|
||||
render_ctx_->BlitColorManaged(job, destination.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RenderProcessor::ProcessAudioFootage(SampleBufferPtr destination, const FootageJob &stream, const TimeRange &input_time)
|
||||
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()));
|
||||
|
||||
@@ -515,9 +522,9 @@ void RenderProcessor::ProcessShader(TexturePtr destination, const Node *node, co
|
||||
render_ctx_->BlitToTexture(shader, job, destination.get());
|
||||
}
|
||||
|
||||
void RenderProcessor::ProcessSamples(SampleBufferPtr destination, const Node *node, const TimeRange &range, const SampleJob &job)
|
||||
void RenderProcessor::ProcessSamples(SampleBuffer &destination, const Node *node, const TimeRange &range, const SampleJob &job)
|
||||
{
|
||||
if (!job.samples() || !job.samples()->is_allocated()) {
|
||||
if (!job.samples().is_allocated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -525,7 +532,7 @@ void RenderProcessor::ProcessSamples(SampleBufferPtr destination, const Node *no
|
||||
|
||||
const AudioParams& audio_params = GetCacheAudioParams();
|
||||
|
||||
for (int i=0;i<job.samples()->sample_count();i++) {
|
||||
for (int i=0;i<job.samples().sample_count();i++) {
|
||||
// Calculate the exact rational time at this sample
|
||||
double sample_to_second = static_cast<double>(i) / static_cast<double>(audio_params.sample_rate());
|
||||
|
||||
@@ -545,6 +552,11 @@ void RenderProcessor::ProcessSamples(SampleBufferPtr destination, const Node *no
|
||||
}
|
||||
}
|
||||
|
||||
void RenderProcessor::ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob &job)
|
||||
{
|
||||
render_ctx_->BlitColorManaged(job, destination.get());
|
||||
}
|
||||
|
||||
void RenderProcessor::ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob &job)
|
||||
{
|
||||
FramePtr frame = Frame::Create();
|
||||
@@ -566,7 +578,14 @@ void RenderProcessor::ConvertToReferenceSpace(TexturePtr destination, TexturePtr
|
||||
{
|
||||
ColorManager* color_manager = Node::ValueToPtr<ColorManager>(ticket_->property("colormanager"));
|
||||
ColorProcessorPtr cp = ColorProcessor::Create(color_manager, input_cs, color_manager->GetReferenceColorSpace());
|
||||
render_ctx_->BlitColorManaged(cp, source, Renderer::kAlphaAssociated, destination.get());
|
||||
|
||||
ColorTransformJob ctj;
|
||||
|
||||
ctj.SetColorProcessor(cp);
|
||||
ctj.SetInputTexture(source);
|
||||
ctj.SetInputAlphaAssociation(kAlphaAssociated);
|
||||
|
||||
render_ctx_->BlitColorManaged(ctj, destination.get());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -46,11 +46,13 @@ protected:
|
||||
|
||||
virtual void ProcessVideoFootage(TexturePtr destination, const FootageJob &stream, const rational &input_time) override;
|
||||
|
||||
virtual void ProcessAudioFootage(SampleBufferPtr destination, const FootageJob &stream, const TimeRange &input_time) override;
|
||||
virtual void ProcessAudioFootage(SampleBuffer &destination, const FootageJob &stream, const TimeRange &input_time) override;
|
||||
|
||||
virtual void ProcessShader(TexturePtr destination, const Node *node, const TimeRange &range, const ShaderJob& job) override;
|
||||
|
||||
virtual void ProcessSamples(SampleBufferPtr destination, const Node *node, const TimeRange &range, const SampleJob &job) override;
|
||||
virtual void ProcessSamples(SampleBuffer &destination, const Node *node, const TimeRange &range, const SampleJob &job) override;
|
||||
|
||||
virtual void ProcessColorTransform(TexturePtr destination, const Node *node, const ColorTransformJob& job) override;
|
||||
|
||||
virtual void ProcessFrameGeneration(TexturePtr destination, const Node *node, const GenerateJob& job) override;
|
||||
|
||||
@@ -61,9 +63,9 @@ protected:
|
||||
return render_ctx_->CreateTexture(p);
|
||||
}
|
||||
|
||||
virtual SampleBufferPtr CreateSampleBuffer(const AudioParams ¶ms, int sample_count) override
|
||||
virtual SampleBuffer CreateSampleBuffer(const AudioParams ¶ms, int sample_count) override
|
||||
{
|
||||
return SampleBuffer::CreateAllocated(params, sample_count);
|
||||
return SampleBuffer(params, sample_count);
|
||||
}
|
||||
|
||||
virtual void ConvertToReferenceSpace(TexturePtr destination, TexturePtr source, const QString &input_cs) override;
|
||||
|
||||
@@ -33,15 +33,11 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
const QString& frag_code() const
|
||||
{
|
||||
return frag_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_;
|
||||
}
|
||||
const QString& vert_code() const { return vert_code_; }
|
||||
void set_vert_code(const QString &v) { vert_code_ = v; }
|
||||
|
||||
private:
|
||||
QString frag_code_;
|
||||
|
||||
Reference in New Issue
Block a user