change: change code style to Linux style except indent.
This commit is contained in:
@@ -23,15 +23,12 @@
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class AutoScroll {
|
||||
public:
|
||||
enum Method {
|
||||
kNone,
|
||||
kPage,
|
||||
kSmooth
|
||||
};
|
||||
enum Method { kNone, kPage, kSmooth };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -24,36 +24,38 @@
|
||||
#include "common/define.h"
|
||||
#include "render/cancelatom.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class CancelableObject {
|
||||
public:
|
||||
CancelableObject()
|
||||
{
|
||||
}
|
||||
CancelableObject()
|
||||
{
|
||||
}
|
||||
|
||||
void Cancel()
|
||||
{
|
||||
cancel_.Cancel();
|
||||
CancelEvent();
|
||||
}
|
||||
void Cancel()
|
||||
{
|
||||
cancel_.Cancel();
|
||||
CancelEvent();
|
||||
}
|
||||
|
||||
CancelAtom *GetCancelAtom()
|
||||
{
|
||||
return &cancel_;
|
||||
}
|
||||
CancelAtom *GetCancelAtom()
|
||||
{
|
||||
return &cancel_;
|
||||
}
|
||||
|
||||
bool IsCancelled()
|
||||
{
|
||||
return cancel_.IsCancelled();
|
||||
}
|
||||
bool IsCancelled()
|
||||
{
|
||||
return cancel_.IsCancelled();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void CancelEvent(){}
|
||||
virtual void CancelEvent()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
CancelAtom cancel_;
|
||||
|
||||
CancelAtom cancel_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -25,146 +25,154 @@
|
||||
|
||||
CommandLineParser::~CommandLineParser()
|
||||
{
|
||||
foreach (const KnownOption& o, options_) {
|
||||
delete o.option;
|
||||
}
|
||||
foreach (const KnownOption &o, options_) {
|
||||
delete o.option;
|
||||
}
|
||||
|
||||
foreach (const KnownPositionalArgument& a, positional_args_) {
|
||||
delete a.option;
|
||||
}
|
||||
foreach (const KnownPositionalArgument &a, positional_args_) {
|
||||
delete a.option;
|
||||
}
|
||||
}
|
||||
|
||||
const CommandLineParser::Option *CommandLineParser::AddOption(const QStringList &strings, const QString &description, bool takes_arg, const QString &arg_placeholder, bool hidden)
|
||||
const CommandLineParser::Option *
|
||||
CommandLineParser::AddOption(const QStringList &strings,
|
||||
const QString &description, bool takes_arg,
|
||||
const QString &arg_placeholder, bool hidden)
|
||||
{
|
||||
Option* o = new Option();
|
||||
Option *o = new Option();
|
||||
|
||||
options_.append({strings, description, o, takes_arg, arg_placeholder, hidden});
|
||||
options_.append(
|
||||
{ strings, description, o, takes_arg, arg_placeholder, hidden });
|
||||
|
||||
return o;
|
||||
return o;
|
||||
}
|
||||
|
||||
const CommandLineParser::PositionalArgument *CommandLineParser::AddPositionalArgument(const QString &name, const QString &description, bool required)
|
||||
const CommandLineParser::PositionalArgument *
|
||||
CommandLineParser::AddPositionalArgument(const QString &name,
|
||||
const QString &description,
|
||||
bool required)
|
||||
{
|
||||
PositionalArgument* a = new PositionalArgument();
|
||||
PositionalArgument *a = new PositionalArgument();
|
||||
|
||||
positional_args_.append({name, description, a, required});
|
||||
positional_args_.append({ name, description, a, required });
|
||||
|
||||
return a;
|
||||
return a;
|
||||
}
|
||||
|
||||
void CommandLineParser::Process(const QVector<QString> &argv)
|
||||
{
|
||||
int positional_index = 0;
|
||||
int positional_index = 0;
|
||||
|
||||
for (int i=1; i<argv.size(); i++) {
|
||||
if (argv[i][0] == '-') {
|
||||
// Must be an option
|
||||
for (int i = 1; i < argv.size(); i++) {
|
||||
if (argv[i][0] == '-') {
|
||||
// Must be an option
|
||||
|
||||
// Skip past first dashes
|
||||
QString arg_basename = argv[i].mid(1);
|
||||
// Skip past first dashes
|
||||
QString arg_basename = argv[i].mid(1);
|
||||
|
||||
bool matched_known = false;
|
||||
bool matched_known = false;
|
||||
|
||||
for (int j=0; j<options_.size(); j++) {
|
||||
KnownOption& o = options_[j];
|
||||
for (int j = 0; j < options_.size(); j++) {
|
||||
KnownOption &o = options_[j];
|
||||
|
||||
foreach (const QString& s, o.args) {
|
||||
if (!s.compare(arg_basename, Qt::CaseInsensitive)) {
|
||||
// Flag discovered!
|
||||
o.option->Set();
|
||||
foreach (const QString &s, o.args) {
|
||||
if (!s.compare(arg_basename, Qt::CaseInsensitive)) {
|
||||
// Flag discovered!
|
||||
o.option->Set();
|
||||
|
||||
if (o.takes_arg && i+1 < argv.size()) {
|
||||
o.option->SetSetting(argv[i+1]);
|
||||
i++;
|
||||
}
|
||||
if (o.takes_arg && i + 1 < argv.size()) {
|
||||
o.option->SetSetting(argv[i + 1]);
|
||||
i++;
|
||||
}
|
||||
|
||||
matched_known = true;
|
||||
goto found_flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
matched_known = true;
|
||||
goto found_flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
found_flag:
|
||||
if (!matched_known) {
|
||||
qWarning() << "Unknown parameter:" << argv[i];
|
||||
}
|
||||
if (!matched_known) {
|
||||
qWarning() << "Unknown parameter:" << argv[i];
|
||||
}
|
||||
|
||||
} else {
|
||||
// Must be a positional flag
|
||||
if (positional_index < positional_args_.size()) {
|
||||
positional_args_[positional_index].option->SetSetting(argv[i]);
|
||||
positional_index++;
|
||||
} else {
|
||||
qWarning() << "Unknown parameter:" << argv[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Must be a positional flag
|
||||
if (positional_index < positional_args_.size()) {
|
||||
positional_args_[positional_index].option->SetSetting(argv[i]);
|
||||
positional_index++;
|
||||
} else {
|
||||
qWarning() << "Unknown parameter:" << argv[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CommandLineParser::PrintHelp(const char* filename)
|
||||
void CommandLineParser::PrintHelp(const char *filename)
|
||||
{
|
||||
printf("%s %s\n",
|
||||
QCoreApplication::applicationName().toUtf8().constData(),
|
||||
QCoreApplication::applicationVersion().toUtf8().constData());
|
||||
printf("%s %s\n", QCoreApplication::applicationName().toUtf8().constData(),
|
||||
QCoreApplication::applicationVersion().toUtf8().constData());
|
||||
|
||||
printf("Copyright (C) 2018-2022 Olive Team\n");
|
||||
printf("Copyright (C) 2018-2022 Olive Team\n");
|
||||
|
||||
QString positional_args;
|
||||
for (int i=0; i<positional_args_.size(); i++) {
|
||||
if (i > 0) {
|
||||
positional_args.append(' ');
|
||||
}
|
||||
QString positional_args;
|
||||
for (int i = 0; i < positional_args_.size(); i++) {
|
||||
if (i > 0) {
|
||||
positional_args.append(' ');
|
||||
}
|
||||
|
||||
positional_args.append('[');
|
||||
positional_args.append(positional_args_.at(i).name);
|
||||
positional_args.append(']');
|
||||
}
|
||||
positional_args.append('[');
|
||||
positional_args.append(positional_args_.at(i).name);
|
||||
positional_args.append(']');
|
||||
}
|
||||
|
||||
const char* basename;
|
||||
const char *basename;
|
||||
#ifdef Q_OS_WINDOWS
|
||||
basename = strrchr(filename, '\\');
|
||||
if (!basename) {
|
||||
basename = strrchr(filename, '/');
|
||||
}
|
||||
basename = strrchr(filename, '\\');
|
||||
if (!basename) {
|
||||
basename = strrchr(filename, '/');
|
||||
}
|
||||
#else
|
||||
basename = strrchr(filename, '/');
|
||||
basename = strrchr(filename, '/');
|
||||
#endif
|
||||
|
||||
if (basename) {
|
||||
// Slash found, increment pointer to avoid showing the slash itself
|
||||
basename++;
|
||||
} else {
|
||||
// If no slashes are found, assume string is already a basename
|
||||
basename = filename;
|
||||
}
|
||||
if (basename) {
|
||||
// Slash found, increment pointer to avoid showing the slash itself
|
||||
basename++;
|
||||
} else {
|
||||
// If no slashes are found, assume string is already a basename
|
||||
basename = filename;
|
||||
}
|
||||
|
||||
printf("Usage: %s [options] %s\n\n", basename, positional_args.toUtf8().constData());
|
||||
foreach (const KnownOption& o, options_) {
|
||||
if (o.hidden) {
|
||||
continue;
|
||||
}
|
||||
printf("Usage: %s [options] %s\n\n", basename,
|
||||
positional_args.toUtf8().constData());
|
||||
foreach (const KnownOption &o, options_) {
|
||||
if (o.hidden) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString all_args;
|
||||
QString all_args;
|
||||
|
||||
for (int i=0; i<o.args.size(); i++) {
|
||||
if (i > 0) {
|
||||
all_args.append(QStringLiteral(", "));
|
||||
}
|
||||
for (int i = 0; i < o.args.size(); i++) {
|
||||
if (i > 0) {
|
||||
all_args.append(QStringLiteral(", "));
|
||||
}
|
||||
|
||||
const QString& this_arg = o.args.at(i);
|
||||
const QString &this_arg = o.args.at(i);
|
||||
|
||||
all_args.append('-');
|
||||
all_args.append(this_arg);
|
||||
}
|
||||
all_args.append('-');
|
||||
all_args.append(this_arg);
|
||||
}
|
||||
|
||||
if (o.arg_placeholder.isEmpty()) {
|
||||
printf(" %s\n", all_args.toUtf8().constData());
|
||||
} else {
|
||||
printf(" %s <%s>\n", all_args.toUtf8().constData(), o.arg_placeholder.toUtf8().constData());
|
||||
}
|
||||
if (o.arg_placeholder.isEmpty()) {
|
||||
printf(" %s\n", all_args.toUtf8().constData());
|
||||
} else {
|
||||
printf(" %s <%s>\n", all_args.toUtf8().constData(),
|
||||
o.arg_placeholder.toUtf8().constData());
|
||||
}
|
||||
|
||||
printf(" %s\n\n", o.description.toUtf8().constData());
|
||||
}
|
||||
printf(" %s\n\n", o.description.toUtf8().constData());
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
@@ -36,86 +36,86 @@
|
||||
* the user as a command line argument. Therefore we needed a custom implementation that could
|
||||
* parse arguments without the need for a Q(Core)Application to be present already.
|
||||
*/
|
||||
class CommandLineParser
|
||||
{
|
||||
class CommandLineParser {
|
||||
public:
|
||||
~CommandLineParser();
|
||||
~CommandLineParser();
|
||||
|
||||
DISABLE_COPY_MOVE(CommandLineParser)
|
||||
DISABLE_COPY_MOVE(CommandLineParser)
|
||||
|
||||
class PositionalArgument
|
||||
{
|
||||
public:
|
||||
PositionalArgument() = default;
|
||||
class PositionalArgument {
|
||||
public:
|
||||
PositionalArgument() = default;
|
||||
|
||||
const QString& GetSetting() const
|
||||
{
|
||||
return setting_;
|
||||
}
|
||||
const QString &GetSetting() const
|
||||
{
|
||||
return setting_;
|
||||
}
|
||||
|
||||
void SetSetting(const QString& s)
|
||||
{
|
||||
setting_ = s;
|
||||
}
|
||||
void SetSetting(const QString &s)
|
||||
{
|
||||
setting_ = s;
|
||||
}
|
||||
|
||||
private:
|
||||
QString setting_;
|
||||
private:
|
||||
QString setting_;
|
||||
};
|
||||
|
||||
};
|
||||
class Option : public PositionalArgument {
|
||||
public:
|
||||
Option()
|
||||
{
|
||||
is_set_ = false;
|
||||
}
|
||||
|
||||
class Option : public PositionalArgument {
|
||||
public:
|
||||
Option()
|
||||
{
|
||||
is_set_ = false;
|
||||
}
|
||||
bool IsSet() const
|
||||
{
|
||||
return is_set_;
|
||||
}
|
||||
|
||||
bool IsSet() const
|
||||
{
|
||||
return is_set_;
|
||||
}
|
||||
void Set()
|
||||
{
|
||||
is_set_ = true;
|
||||
}
|
||||
|
||||
void Set()
|
||||
{
|
||||
is_set_ = true;
|
||||
}
|
||||
private:
|
||||
bool is_set_;
|
||||
};
|
||||
|
||||
private:
|
||||
bool is_set_;
|
||||
CommandLineParser() = default;
|
||||
|
||||
};
|
||||
const Option *AddOption(const QStringList &strings,
|
||||
const QString &description, bool takes_arg = false,
|
||||
const QString &arg_placeholder = QString(),
|
||||
bool hidden = false);
|
||||
|
||||
CommandLineParser() = default;
|
||||
const PositionalArgument *AddPositionalArgument(const QString &name,
|
||||
const QString &description,
|
||||
bool required = false);
|
||||
|
||||
const Option* AddOption(const QStringList& strings, const QString& description, bool takes_arg = false, const QString& arg_placeholder = QString(), bool hidden = false);
|
||||
void Process(const QVector<QString> &argv);
|
||||
|
||||
const PositionalArgument* AddPositionalArgument(const QString& name, const QString& description, bool required = false);
|
||||
|
||||
void Process(const QVector<QString> &argv);
|
||||
|
||||
void PrintHelp(const char* filename);
|
||||
void PrintHelp(const char *filename);
|
||||
|
||||
private:
|
||||
struct KnownOption {
|
||||
QStringList args;
|
||||
QString description;
|
||||
Option* option;
|
||||
bool takes_arg;
|
||||
QString arg_placeholder;
|
||||
bool hidden;
|
||||
};
|
||||
struct KnownOption {
|
||||
QStringList args;
|
||||
QString description;
|
||||
Option *option;
|
||||
bool takes_arg;
|
||||
QString arg_placeholder;
|
||||
bool hidden;
|
||||
};
|
||||
|
||||
struct KnownPositionalArgument {
|
||||
QString name;
|
||||
QString description;
|
||||
PositionalArgument* option;
|
||||
bool required;
|
||||
};
|
||||
struct KnownPositionalArgument {
|
||||
QString name;
|
||||
QString description;
|
||||
PositionalArgument *option;
|
||||
bool required;
|
||||
};
|
||||
|
||||
QVector<KnownOption> options_;
|
||||
|
||||
QVector<KnownPositionalArgument> positional_args_;
|
||||
QVector<KnownOption> options_;
|
||||
|
||||
QVector<KnownPositionalArgument> positional_args_;
|
||||
};
|
||||
|
||||
#endif // COMMANDLINEPARSER_H
|
||||
|
||||
@@ -40,52 +40,63 @@ crashpad::CrashpadClient *client;
|
||||
|
||||
bool InitializeCrashpad()
|
||||
{
|
||||
QString report_path = QDir(olive::FileFunctions::GetTempFilePath()).filePath(QStringLiteral("reports"));
|
||||
QString report_path = QDir(olive::FileFunctions::GetTempFilePath())
|
||||
.filePath(QStringLiteral("reports"));
|
||||
|
||||
QString handler_fn = olive::FileFunctions::GetFormattedExecutableForPlatform(QStringLiteral("crashpad_handler"));
|
||||
QString handler_fn =
|
||||
olive::FileFunctions::GetFormattedExecutableForPlatform(
|
||||
QStringLiteral("crashpad_handler"));
|
||||
|
||||
// Generate absolute path
|
||||
QString handler_abs_path = QDir(QCoreApplication::applicationDirPath()).filePath(handler_fn);
|
||||
// Generate absolute path
|
||||
QString handler_abs_path =
|
||||
QDir(QCoreApplication::applicationDirPath()).filePath(handler_fn);
|
||||
|
||||
bool status = false;
|
||||
bool status = false;
|
||||
|
||||
if (QFileInfo::exists(handler_abs_path)) {
|
||||
base::FilePath handler(QSTRING_TO_BASE_STRING(handler_abs_path));
|
||||
if (QFileInfo::exists(handler_abs_path)) {
|
||||
base::FilePath handler(QSTRING_TO_BASE_STRING(handler_abs_path));
|
||||
|
||||
base::FilePath reports_dir(QSTRING_TO_BASE_STRING(report_path));
|
||||
base::FilePath reports_dir(QSTRING_TO_BASE_STRING(report_path));
|
||||
|
||||
base::FilePath metrics_dir(QSTRING_TO_BASE_STRING(QDir(olive::FileFunctions::GetTempFilePath()).filePath(QStringLiteral("metrics"))));
|
||||
base::FilePath metrics_dir(
|
||||
QSTRING_TO_BASE_STRING(QDir(olive::FileFunctions::GetTempFilePath())
|
||||
.filePath(QStringLiteral("metrics"))));
|
||||
|
||||
// Metadata that will be posted to the server with the crash report map
|
||||
std::map<std::string, std::string> annotations;
|
||||
// Metadata that will be posted to the server with the crash report map
|
||||
std::map<std::string, std::string> annotations;
|
||||
|
||||
// Disable crashpad rate limiting so that all crashes have dmp files
|
||||
std::vector<std::string> arguments;
|
||||
arguments.push_back("--no-rate-limit");
|
||||
arguments.push_back("--no-upload-gzip");
|
||||
// Disable crashpad rate limiting so that all crashes have dmp files
|
||||
std::vector<std::string> arguments;
|
||||
arguments.push_back("--no-rate-limit");
|
||||
arguments.push_back("--no-upload-gzip");
|
||||
|
||||
// Initialize Crashpad database
|
||||
std::unique_ptr<crashpad::CrashReportDatabase> database = crashpad::CrashReportDatabase::Initialize(reports_dir);
|
||||
if (database == NULL) return false;
|
||||
// Initialize Crashpad database
|
||||
std::unique_ptr<crashpad::CrashReportDatabase> database =
|
||||
crashpad::CrashReportDatabase::Initialize(reports_dir);
|
||||
if (database == NULL)
|
||||
return false;
|
||||
|
||||
// Disable automated crash uploads
|
||||
crashpad::Settings *settings = database->GetSettings();
|
||||
if (settings == NULL) return false;
|
||||
settings->SetUploadsEnabled(false);
|
||||
// Disable automated crash uploads
|
||||
crashpad::Settings *settings = database->GetSettings();
|
||||
if (settings == NULL)
|
||||
return false;
|
||||
settings->SetUploadsEnabled(false);
|
||||
|
||||
// Start crash handler
|
||||
client = new crashpad::CrashpadClient();
|
||||
status = client->StartHandler(handler, reports_dir, metrics_dir,
|
||||
"https://olivevideoeditor.org/crashpad/report.php",
|
||||
annotations, arguments, true, true);
|
||||
}
|
||||
// Start crash handler
|
||||
client = new crashpad::CrashpadClient();
|
||||
status = client->StartHandler(
|
||||
handler, reports_dir, metrics_dir,
|
||||
"https://olivevideoeditor.org/crashpad/report.php", annotations,
|
||||
arguments, true, true);
|
||||
}
|
||||
|
||||
// Override Crashpad exception filter with our own
|
||||
if (!status) {
|
||||
qWarning() << "Failed to start Crashpad, automatic crash reporting will be disabled";
|
||||
}
|
||||
// Override Crashpad exception filter with our own
|
||||
if (!status) {
|
||||
qWarning()
|
||||
<< "Failed to start Crashpad, automatic crash reporting will be disabled";
|
||||
}
|
||||
|
||||
return status;
|
||||
return status;
|
||||
}
|
||||
|
||||
#endif // USE_CRASHPAD
|
||||
|
||||
@@ -35,6 +35,6 @@
|
||||
// arrays encoded in UTF-16.
|
||||
#define QSTRING_TO_BASE_STRING(x) x.toStdWString()
|
||||
#define BASE_STRING_TO_QSTRING(x) QString::fromStdWString(x)
|
||||
#endif // BUILDFLAG(IS_WIN)
|
||||
#endif // BUILDFLAG(IS_WIN)
|
||||
|
||||
#endif // CRASHPADUTILS_H
|
||||
|
||||
+29
-27
@@ -20,38 +20,40 @@
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
void DebugHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||
namespace olive
|
||||
{
|
||||
QByteArray localMsg = msg.toLocal8Bit();
|
||||
|
||||
const char* msg_type = "UNKNOWN";
|
||||
switch (type) {
|
||||
case QtDebugMsg:
|
||||
msg_type = "DEBUG";
|
||||
break;
|
||||
case QtInfoMsg:
|
||||
msg_type = "INFO";
|
||||
break;
|
||||
case QtWarningMsg:
|
||||
msg_type = "WARNING";
|
||||
break;
|
||||
case QtCriticalMsg:
|
||||
msg_type = "ERROR";
|
||||
break;
|
||||
case QtFatalMsg:
|
||||
msg_type = "FATAL";
|
||||
break;
|
||||
}
|
||||
void DebugHandler(QtMsgType type, const QMessageLogContext &context,
|
||||
const QString &msg)
|
||||
{
|
||||
QByteArray localMsg = msg.toLocal8Bit();
|
||||
|
||||
//fprintf(stderr, "[%s] %s (%s:%u)\n", msg_type, localMsg.constData(), context.function, context.line);
|
||||
fprintf(stderr, "[%s] %s\n", msg_type, localMsg.constData());
|
||||
const char *msg_type = "UNKNOWN";
|
||||
switch (type) {
|
||||
case QtDebugMsg:
|
||||
msg_type = "DEBUG";
|
||||
break;
|
||||
case QtInfoMsg:
|
||||
msg_type = "INFO";
|
||||
break;
|
||||
case QtWarningMsg:
|
||||
msg_type = "WARNING";
|
||||
break;
|
||||
case QtCriticalMsg:
|
||||
msg_type = "ERROR";
|
||||
break;
|
||||
case QtFatalMsg:
|
||||
msg_type = "FATAL";
|
||||
break;
|
||||
}
|
||||
|
||||
//fprintf(stderr, "[%s] %s (%s:%u)\n", msg_type, localMsg.constData(), context.function, context.line);
|
||||
fprintf(stderr, "[%s] %s\n", msg_type, localMsg.constData());
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
// Windows still seems to buffer stderr and we want to see debug messages immediately, so here we make sure each line
|
||||
// is flushed
|
||||
fflush(stderr);
|
||||
// Windows still seems to buffer stderr and we want to see debug messages immediately, so here we make sure each line
|
||||
// is flushed
|
||||
fflush(stderr);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -25,9 +25,11 @@
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
void DebugHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
|
||||
void DebugHandler(QtMsgType type, const QMessageLogContext &context,
|
||||
const QString &msg);
|
||||
|
||||
}
|
||||
|
||||
|
||||
+54
-55
@@ -26,77 +26,76 @@
|
||||
|
||||
//#define ALLOW_RETURNING_INFINITY
|
||||
|
||||
namespace olive {
|
||||
|
||||
class Decibel
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class Decibel {
|
||||
public:
|
||||
// In basically all circumstances, this should calculate to 0.0 linear
|
||||
static constexpr double MINIMUM = -200.0;
|
||||
// In basically all circumstances, this should calculate to 0.0 linear
|
||||
static constexpr double MINIMUM = -200.0;
|
||||
|
||||
static double fromLinear(double linear)
|
||||
{
|
||||
double v = double(20.0) * std::log10(linear);
|
||||
static double fromLinear(double linear)
|
||||
{
|
||||
double v = double(20.0) * std::log10(linear);
|
||||
#ifndef ALLOW_RETURNING_INFINITY
|
||||
if (std::isinf(v)) {
|
||||
return MINIMUM;
|
||||
}
|
||||
if (std::isinf(v)) {
|
||||
return MINIMUM;
|
||||
}
|
||||
#endif
|
||||
return v;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
static double toLinear(double decibel)
|
||||
{
|
||||
double to_linear = std::pow(double(10.0), decibel / double(20.0));
|
||||
static double toLinear(double decibel)
|
||||
{
|
||||
double to_linear = std::pow(double(10.0), decibel / double(20.0));
|
||||
|
||||
// Minimum threshold that we figure is close enough to 0 that we may as well just return 0
|
||||
if (to_linear < 0.000001) {
|
||||
return 0;
|
||||
} else {
|
||||
return to_linear;
|
||||
}
|
||||
}
|
||||
// Minimum threshold that we figure is close enough to 0 that we may as well just return 0
|
||||
if (to_linear < 0.000001) {
|
||||
return 0;
|
||||
} else {
|
||||
return to_linear;
|
||||
}
|
||||
}
|
||||
|
||||
static double fromLogarithmic(double logarithmic)
|
||||
{
|
||||
if (logarithmic < 0.001)
|
||||
static double fromLogarithmic(double logarithmic)
|
||||
{
|
||||
if (logarithmic < 0.001)
|
||||
#ifdef ALLOW_RETURNING_INFINITY
|
||||
return std::numeric_limits<double>::infinity();
|
||||
return std::numeric_limits<double>::infinity();
|
||||
#else
|
||||
return MINIMUM;
|
||||
return MINIMUM;
|
||||
#endif
|
||||
else if (logarithmic > 0.99)
|
||||
return 0;
|
||||
else
|
||||
return 20.0 * std::log10(-std::log(1 - logarithmic) / LOG100);
|
||||
}
|
||||
else if (logarithmic > 0.99)
|
||||
return 0;
|
||||
else
|
||||
return 20.0 * std::log10(-std::log(1 - logarithmic) / LOG100);
|
||||
}
|
||||
|
||||
static double toLogarithmic(double decibel)
|
||||
{
|
||||
if (qFuzzyIsNull(decibel)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 1 - std::exp(-std::pow(10.0, decibel / 20.0) * LOG100);
|
||||
}
|
||||
}
|
||||
static double toLogarithmic(double decibel)
|
||||
{
|
||||
if (qFuzzyIsNull(decibel)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 1 - std::exp(-std::pow(10.0, decibel / 20.0) * LOG100);
|
||||
}
|
||||
}
|
||||
|
||||
static double LinearToLogarithmic(double linear)
|
||||
{
|
||||
return 1 - std::exp(-linear * LOG100);
|
||||
}
|
||||
static double LinearToLogarithmic(double linear)
|
||||
{
|
||||
return 1 - std::exp(-linear * LOG100);
|
||||
}
|
||||
|
||||
static double LogarithmicToLinear(double logarithmic)
|
||||
{
|
||||
if (logarithmic > 0.99) {
|
||||
return 1;
|
||||
} else {
|
||||
return -std::log(1 - logarithmic) / LOG100;
|
||||
}
|
||||
}
|
||||
static double LogarithmicToLinear(double logarithmic)
|
||||
{
|
||||
if (logarithmic > 0.99) {
|
||||
return 1;
|
||||
} else {
|
||||
return -std::log(1 - logarithmic) / LOG100;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr double LOG100 = 4.60517018599;
|
||||
|
||||
static constexpr double LOG100 = 4.60517018599;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+16
-12
@@ -21,7 +21,8 @@
|
||||
#ifndef OLIVECOMMONDEFINE_H
|
||||
#define OLIVECOMMONDEFINE_H
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/// The minimum size an icon in ProjectExplorer can be
|
||||
const int kProjectIconSizeMinimum = 16;
|
||||
@@ -39,25 +40,28 @@ const int kBytesInGigabyte = 1073741824;
|
||||
#define MACRO_NAME_AS_STR(s) #s
|
||||
#define MACRO_VAL_AS_STR(s) MACRO_NAME_AS_STR(s)
|
||||
|
||||
#define OLIVE_NS_CONST_ARG(x, y) QArgument<const olive::x>("const " MACRO_VAL_AS_STR(olive) "::" #x, y)
|
||||
#define OLIVE_NS_ARG(x, y) QArgument<olive::x>(MACRO_VAL_AS_STR(olive) "::" #x, y)
|
||||
#define OLIVE_NS_RETURN_ARG(x, y) QReturnArgument<olive::x>(MACRO_VAL_AS_STR(olive) "::" #x, y)
|
||||
#define OLIVE_NS_CONST_ARG(x, y) \
|
||||
QArgument<const olive::x>("const " MACRO_VAL_AS_STR(olive) "::" #x, y)
|
||||
#define OLIVE_NS_ARG(x, y) \
|
||||
QArgument<olive::x>(MACRO_VAL_AS_STR(olive) "::" #x, y)
|
||||
#define OLIVE_NS_RETURN_ARG(x, y) \
|
||||
QReturnArgument<olive::x>(MACRO_VAL_AS_STR(olive) "::" #x, y)
|
||||
|
||||
/**
|
||||
* Copy/move deleters. Similar to Q_DISABLE_COPY_MOVE, et al. but those functions are not present in Qt < 5.13 so we
|
||||
* use our own functions for portability.
|
||||
*/
|
||||
|
||||
#define DISABLE_COPY(Class) \
|
||||
Class(const Class &) = delete;\
|
||||
Class &operator=(const Class &) = delete;
|
||||
#define DISABLE_COPY(Class) \
|
||||
Class(const Class &) = delete; \
|
||||
Class &operator=(const Class &) = delete;
|
||||
|
||||
#define DISABLE_MOVE(Class) \
|
||||
Class(Class &&) = delete; \
|
||||
Class &operator=(Class &&) = delete;
|
||||
#define DISABLE_MOVE(Class) \
|
||||
Class(Class &&) = delete; \
|
||||
Class &operator=(Class &&) = delete;
|
||||
|
||||
#define DISABLE_COPY_MOVE(Class) \
|
||||
DISABLE_COPY(Class) \
|
||||
DISABLE_MOVE(Class)
|
||||
DISABLE_COPY(Class) \
|
||||
DISABLE_MOVE(Class)
|
||||
|
||||
#endif // OLIVECOMMONDEFINE_H
|
||||
|
||||
+10
-9
@@ -23,21 +23,22 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
inline int64_t GetDigitCount(int64_t input)
|
||||
{
|
||||
input = std::abs(input);
|
||||
input = std::abs(input);
|
||||
|
||||
int64_t lim = 10;
|
||||
int64_t digit = 1;
|
||||
int64_t lim = 10;
|
||||
int64_t digit = 1;
|
||||
|
||||
while (input >= lim) {
|
||||
lim *= 10;
|
||||
digit++;
|
||||
}
|
||||
while (input >= lim) {
|
||||
lim *= 10;
|
||||
digit++;
|
||||
}
|
||||
|
||||
return digit;
|
||||
return digit;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+148
-141
@@ -20,180 +20,187 @@
|
||||
|
||||
#include "common/ffmpegutils.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
AVPixelFormat FFmpegUtils::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt, PixelFormat maximum)
|
||||
namespace olive
|
||||
{
|
||||
AVPixelFormat possible_pix_fmts[3];
|
||||
|
||||
possible_pix_fmts[0] = AV_PIX_FMT_RGBA;
|
||||
AVPixelFormat
|
||||
FFmpegUtils::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt,
|
||||
PixelFormat maximum)
|
||||
{
|
||||
AVPixelFormat possible_pix_fmts[3];
|
||||
|
||||
if (maximum == PixelFormat::U8) {
|
||||
possible_pix_fmts[1] = AV_PIX_FMT_NONE;
|
||||
} else {
|
||||
possible_pix_fmts[1] = AV_PIX_FMT_RGBA64;
|
||||
possible_pix_fmts[2] = AV_PIX_FMT_NONE;
|
||||
}
|
||||
possible_pix_fmts[0] = AV_PIX_FMT_RGBA;
|
||||
|
||||
return avcodec_find_best_pix_fmt_of_list(possible_pix_fmts,
|
||||
pix_fmt,
|
||||
1,
|
||||
nullptr);
|
||||
if (maximum == PixelFormat::U8) {
|
||||
possible_pix_fmts[1] = AV_PIX_FMT_NONE;
|
||||
} else {
|
||||
possible_pix_fmts[1] = AV_PIX_FMT_RGBA64;
|
||||
possible_pix_fmts[2] = AV_PIX_FMT_NONE;
|
||||
}
|
||||
|
||||
return avcodec_find_best_pix_fmt_of_list(possible_pix_fmts, pix_fmt, 1,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
SampleFormat FFmpegUtils::GetNativeSampleFormat(const AVSampleFormat &smp_fmt)
|
||||
{
|
||||
switch (smp_fmt) {
|
||||
case AV_SAMPLE_FMT_U8:
|
||||
return SampleFormat::U8;
|
||||
case AV_SAMPLE_FMT_S16:
|
||||
return SampleFormat::S16;
|
||||
case AV_SAMPLE_FMT_S32:
|
||||
return SampleFormat::S32;
|
||||
case AV_SAMPLE_FMT_S64:
|
||||
return SampleFormat::S64;
|
||||
case AV_SAMPLE_FMT_FLT:
|
||||
return SampleFormat::F32;
|
||||
case AV_SAMPLE_FMT_DBL:
|
||||
return SampleFormat::F64;
|
||||
case AV_SAMPLE_FMT_U8P :
|
||||
return SampleFormat::U8P;
|
||||
case AV_SAMPLE_FMT_S16P:
|
||||
return SampleFormat::S16P;
|
||||
case AV_SAMPLE_FMT_S32P:
|
||||
return SampleFormat::S32P;
|
||||
case AV_SAMPLE_FMT_S64P:
|
||||
return SampleFormat::S64P;
|
||||
case AV_SAMPLE_FMT_FLTP:
|
||||
return SampleFormat::F32P;
|
||||
case AV_SAMPLE_FMT_DBLP:
|
||||
return SampleFormat::F64P;
|
||||
case AV_SAMPLE_FMT_NONE:
|
||||
case AV_SAMPLE_FMT_NB:
|
||||
break;
|
||||
}
|
||||
switch (smp_fmt) {
|
||||
case AV_SAMPLE_FMT_U8:
|
||||
return SampleFormat::U8;
|
||||
case AV_SAMPLE_FMT_S16:
|
||||
return SampleFormat::S16;
|
||||
case AV_SAMPLE_FMT_S32:
|
||||
return SampleFormat::S32;
|
||||
case AV_SAMPLE_FMT_S64:
|
||||
return SampleFormat::S64;
|
||||
case AV_SAMPLE_FMT_FLT:
|
||||
return SampleFormat::F32;
|
||||
case AV_SAMPLE_FMT_DBL:
|
||||
return SampleFormat::F64;
|
||||
case AV_SAMPLE_FMT_U8P:
|
||||
return SampleFormat::U8P;
|
||||
case AV_SAMPLE_FMT_S16P:
|
||||
return SampleFormat::S16P;
|
||||
case AV_SAMPLE_FMT_S32P:
|
||||
return SampleFormat::S32P;
|
||||
case AV_SAMPLE_FMT_S64P:
|
||||
return SampleFormat::S64P;
|
||||
case AV_SAMPLE_FMT_FLTP:
|
||||
return SampleFormat::F32P;
|
||||
case AV_SAMPLE_FMT_DBLP:
|
||||
return SampleFormat::F64P;
|
||||
case AV_SAMPLE_FMT_NONE:
|
||||
case AV_SAMPLE_FMT_NB:
|
||||
break;
|
||||
}
|
||||
|
||||
return SampleFormat::INVALID;
|
||||
return SampleFormat::INVALID;
|
||||
}
|
||||
|
||||
AVSampleFormat FFmpegUtils::GetFFmpegSampleFormat(const SampleFormat &smp_fmt)
|
||||
{
|
||||
switch (smp_fmt) {
|
||||
case SampleFormat::U8:
|
||||
return AV_SAMPLE_FMT_U8;
|
||||
case SampleFormat::S16:
|
||||
return AV_SAMPLE_FMT_S16;
|
||||
case SampleFormat::S32:
|
||||
return AV_SAMPLE_FMT_S32;
|
||||
case SampleFormat::S64:
|
||||
return AV_SAMPLE_FMT_S64;
|
||||
case SampleFormat::F32:
|
||||
return AV_SAMPLE_FMT_FLT;
|
||||
case SampleFormat::F64:
|
||||
return AV_SAMPLE_FMT_DBL;
|
||||
case SampleFormat::U8P:
|
||||
return AV_SAMPLE_FMT_U8P;
|
||||
case SampleFormat::S16P:
|
||||
return AV_SAMPLE_FMT_S16P;
|
||||
case SampleFormat::S32P:
|
||||
return AV_SAMPLE_FMT_S32P;
|
||||
case SampleFormat::S64P:
|
||||
return AV_SAMPLE_FMT_S64P;
|
||||
case SampleFormat::F32P:
|
||||
return AV_SAMPLE_FMT_FLTP;
|
||||
case SampleFormat::F64P:
|
||||
return AV_SAMPLE_FMT_DBLP;
|
||||
case SampleFormat::INVALID:
|
||||
case SampleFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
switch (smp_fmt) {
|
||||
case SampleFormat::U8:
|
||||
return AV_SAMPLE_FMT_U8;
|
||||
case SampleFormat::S16:
|
||||
return AV_SAMPLE_FMT_S16;
|
||||
case SampleFormat::S32:
|
||||
return AV_SAMPLE_FMT_S32;
|
||||
case SampleFormat::S64:
|
||||
return AV_SAMPLE_FMT_S64;
|
||||
case SampleFormat::F32:
|
||||
return AV_SAMPLE_FMT_FLT;
|
||||
case SampleFormat::F64:
|
||||
return AV_SAMPLE_FMT_DBL;
|
||||
case SampleFormat::U8P:
|
||||
return AV_SAMPLE_FMT_U8P;
|
||||
case SampleFormat::S16P:
|
||||
return AV_SAMPLE_FMT_S16P;
|
||||
case SampleFormat::S32P:
|
||||
return AV_SAMPLE_FMT_S32P;
|
||||
case SampleFormat::S64P:
|
||||
return AV_SAMPLE_FMT_S64P;
|
||||
case SampleFormat::F32P:
|
||||
return AV_SAMPLE_FMT_FLTP;
|
||||
case SampleFormat::F64P:
|
||||
return AV_SAMPLE_FMT_DBLP;
|
||||
case SampleFormat::INVALID:
|
||||
case SampleFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
|
||||
return AV_SAMPLE_FMT_NONE;
|
||||
return AV_SAMPLE_FMT_NONE;
|
||||
}
|
||||
|
||||
int FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVColorSpace cs)
|
||||
{
|
||||
switch (cs) {
|
||||
case AVCOL_SPC_BT709:
|
||||
return SWS_CS_ITU709;
|
||||
case AVCOL_SPC_FCC:
|
||||
return SWS_CS_FCC;
|
||||
case AVCOL_SPC_BT470BG:
|
||||
return SWS_CS_ITU624;
|
||||
case AVCOL_SPC_SMPTE170M:
|
||||
return SWS_CS_SMPTE170M;
|
||||
case AVCOL_SPC_SMPTE240M:
|
||||
return SWS_CS_SMPTE240M;
|
||||
case AVCOL_SPC_BT2020_NCL:
|
||||
return SWS_CS_BT2020;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (cs) {
|
||||
case AVCOL_SPC_BT709:
|
||||
return SWS_CS_ITU709;
|
||||
case AVCOL_SPC_FCC:
|
||||
return SWS_CS_FCC;
|
||||
case AVCOL_SPC_BT470BG:
|
||||
return SWS_CS_ITU624;
|
||||
case AVCOL_SPC_SMPTE170M:
|
||||
return SWS_CS_SMPTE170M;
|
||||
case AVCOL_SPC_SMPTE240M:
|
||||
return SWS_CS_SMPTE240M;
|
||||
case AVCOL_SPC_BT2020_NCL:
|
||||
return SWS_CS_BT2020;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return SWS_CS_DEFAULT;
|
||||
return SWS_CS_DEFAULT;
|
||||
}
|
||||
|
||||
AVPixelFormat FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AVPixelFormat f)
|
||||
{
|
||||
switch (f) {
|
||||
case AV_PIX_FMT_YUVJ420P: return AV_PIX_FMT_YUV420P;
|
||||
case AV_PIX_FMT_YUVJ422P: return AV_PIX_FMT_YUV422P;
|
||||
case AV_PIX_FMT_YUVJ444P: return AV_PIX_FMT_YUV444P;
|
||||
case AV_PIX_FMT_YUVJ440P: return AV_PIX_FMT_YUV440P;
|
||||
case AV_PIX_FMT_YUVJ411P: return AV_PIX_FMT_YUV411P;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (f) {
|
||||
case AV_PIX_FMT_YUVJ420P:
|
||||
return AV_PIX_FMT_YUV420P;
|
||||
case AV_PIX_FMT_YUVJ422P:
|
||||
return AV_PIX_FMT_YUV422P;
|
||||
case AV_PIX_FMT_YUVJ444P:
|
||||
return AV_PIX_FMT_YUV444P;
|
||||
case AV_PIX_FMT_YUVJ440P:
|
||||
return AV_PIX_FMT_YUV440P;
|
||||
case AV_PIX_FMT_YUVJ411P:
|
||||
return AV_PIX_FMT_YUV411P;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return f;
|
||||
return f;
|
||||
}
|
||||
|
||||
AVPixelFormat FFmpegUtils::GetFFmpegPixelFormat(const PixelFormat &pix_fmt, int channel_layout)
|
||||
AVPixelFormat FFmpegUtils::GetFFmpegPixelFormat(const PixelFormat &pix_fmt,
|
||||
int channel_layout)
|
||||
{
|
||||
if (channel_layout == VideoParams::kRGBChannelCount) {
|
||||
switch (pix_fmt) {
|
||||
case PixelFormat::U8:
|
||||
return AV_PIX_FMT_RGB24;
|
||||
case PixelFormat::U16:
|
||||
return AV_PIX_FMT_RGB48;
|
||||
case PixelFormat::F16:
|
||||
case PixelFormat::F32:
|
||||
case PixelFormat::INVALID:
|
||||
case PixelFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
} else if (channel_layout == VideoParams::kRGBAChannelCount) {
|
||||
switch (pix_fmt) {
|
||||
case PixelFormat::U8:
|
||||
return AV_PIX_FMT_RGBA;
|
||||
case PixelFormat::U16:
|
||||
return AV_PIX_FMT_RGBA64;
|
||||
case PixelFormat::F16:
|
||||
case PixelFormat::F32:
|
||||
case PixelFormat::INVALID:
|
||||
case PixelFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (channel_layout == VideoParams::kRGBChannelCount) {
|
||||
switch (pix_fmt) {
|
||||
case PixelFormat::U8:
|
||||
return AV_PIX_FMT_RGB24;
|
||||
case PixelFormat::U16:
|
||||
return AV_PIX_FMT_RGB48;
|
||||
case PixelFormat::F16:
|
||||
case PixelFormat::F32:
|
||||
case PixelFormat::INVALID:
|
||||
case PixelFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
} else if (channel_layout == VideoParams::kRGBAChannelCount) {
|
||||
switch (pix_fmt) {
|
||||
case PixelFormat::U8:
|
||||
return AV_PIX_FMT_RGBA;
|
||||
case PixelFormat::U16:
|
||||
return AV_PIX_FMT_RGBA64;
|
||||
case PixelFormat::F16:
|
||||
case PixelFormat::F32:
|
||||
case PixelFormat::INVALID:
|
||||
case PixelFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return AV_PIX_FMT_NONE;
|
||||
return AV_PIX_FMT_NONE;
|
||||
}
|
||||
|
||||
PixelFormat FFmpegUtils::GetCompatiblePixelFormat(const PixelFormat &pix_fmt)
|
||||
{
|
||||
switch (pix_fmt) {
|
||||
case PixelFormat::U8:
|
||||
return PixelFormat::U8;
|
||||
case PixelFormat::U16:
|
||||
case PixelFormat::F16:
|
||||
case PixelFormat::F32:
|
||||
return PixelFormat::U16;
|
||||
case PixelFormat::INVALID:
|
||||
case PixelFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
switch (pix_fmt) {
|
||||
case PixelFormat::U8:
|
||||
return PixelFormat::U8;
|
||||
case PixelFormat::U16:
|
||||
case PixelFormat::F16:
|
||||
case PixelFormat::F32:
|
||||
return PixelFormat::U16;
|
||||
case PixelFormat::INVALID:
|
||||
case PixelFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
|
||||
return PixelFormat::INVALID;
|
||||
return PixelFormat::INVALID;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+21
-17
@@ -31,63 +31,67 @@ extern "C" {
|
||||
|
||||
#include "render/videoparams.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
using namespace core;
|
||||
|
||||
class FFmpegUtils {
|
||||
public:
|
||||
/**
|
||||
/**
|
||||
* @brief Returns an AVPixelFormat that can be used to convert a frame to a data type Olive supports with minimal data loss
|
||||
*/
|
||||
static AVPixelFormat GetCompatiblePixelFormat(const AVPixelFormat& pix_fmt, PixelFormat maximum = PixelFormat::INVALID);
|
||||
static AVPixelFormat
|
||||
GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt,
|
||||
PixelFormat maximum = PixelFormat::INVALID);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns a native pixel format that can be used to convert from a native frame to an AVFrame with minimal data loss
|
||||
*/
|
||||
static PixelFormat GetCompatiblePixelFormat(const PixelFormat& pix_fmt);
|
||||
static PixelFormat GetCompatiblePixelFormat(const PixelFormat &pix_fmt);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns an FFmpeg pixel format for a given native pixel format
|
||||
*/
|
||||
static AVPixelFormat GetFFmpegPixelFormat(const PixelFormat& pix_fmt, int channel_layout);
|
||||
static AVPixelFormat GetFFmpegPixelFormat(const PixelFormat &pix_fmt,
|
||||
int channel_layout);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns a native sample format type for a given AVSampleFormat
|
||||
*/
|
||||
static SampleFormat GetNativeSampleFormat(const AVSampleFormat& smp_fmt);
|
||||
static SampleFormat GetNativeSampleFormat(const AVSampleFormat &smp_fmt);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns an FFmpeg sample format type for a given native type
|
||||
*/
|
||||
static AVSampleFormat GetFFmpegSampleFormat(const SampleFormat &smp_fmt);
|
||||
static AVSampleFormat GetFFmpegSampleFormat(const SampleFormat &smp_fmt);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns an SWS_CS_* macro from an AVColorSpace enum member
|
||||
*
|
||||
* Why aren't these the same thing anyway? And for that matter, why doesn't FFmpeg provide a
|
||||
* convenience function to do this conversion for us? Who knows, but here we are.
|
||||
*/
|
||||
static int GetSwsColorspaceFromAVColorSpace(AVColorSpace cs);
|
||||
static int GetSwsColorspaceFromAVColorSpace(AVColorSpace cs);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Convert "JPEG"/full-range colorspace to its regular counterpart
|
||||
*
|
||||
* "JPEG "spaces are deprecated in favor of the regular space and setting `color_range`. For the
|
||||
* time being, FFmpeg still uses these JPEG spaces, so for simplicity (since we *are* color_range
|
||||
* aware), we use this function.
|
||||
*/
|
||||
static AVPixelFormat ConvertJPEGSpaceToRegularSpace(AVPixelFormat f);
|
||||
static AVPixelFormat ConvertJPEGSpaceToRegularSpace(AVPixelFormat f);
|
||||
};
|
||||
|
||||
using AVFramePtr = std::shared_ptr<AVFrame>;
|
||||
inline AVFramePtr CreateAVFramePtr(AVFrame *f)
|
||||
{
|
||||
return std::shared_ptr<AVFrame>(f, [](AVFrame *g){ av_frame_free(&g); });
|
||||
return std::shared_ptr<AVFrame>(f, [](AVFrame *g) { av_frame_free(&g); });
|
||||
}
|
||||
inline AVFramePtr CreateAVFramePtr()
|
||||
{
|
||||
return CreateAVFramePtr(av_frame_alloc());
|
||||
return CreateAVFramePtr(av_frame_alloc());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+142
-125
@@ -29,206 +29,223 @@
|
||||
|
||||
#include "config/config.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
QString FileFunctions::GetUniqueFileIdentifier(const QString &filename)
|
||||
{
|
||||
QFileInfo info(filename);
|
||||
QFileInfo info(filename);
|
||||
|
||||
if (!info.exists()) {
|
||||
return QString();
|
||||
}
|
||||
if (!info.exists()) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
QCryptographicHash hash(QCryptographicHash::Sha1);
|
||||
QCryptographicHash hash(QCryptographicHash::Sha1);
|
||||
|
||||
hash.addData(info.absoluteFilePath().toUtf8());
|
||||
hash.addData(info.absoluteFilePath().toUtf8());
|
||||
|
||||
hash.addData(QString::number(info.lastModified().toMSecsSinceEpoch()).toUtf8());
|
||||
hash.addData(
|
||||
QString::number(info.lastModified().toMSecsSinceEpoch()).toUtf8());
|
||||
|
||||
QByteArray result = hash.result();
|
||||
QByteArray result = hash.result();
|
||||
|
||||
return QString(result.toHex());
|
||||
return QString(result.toHex());
|
||||
}
|
||||
|
||||
QString FileFunctions::GetConfigurationLocation()
|
||||
{
|
||||
if (IsPortable()) {
|
||||
return GetApplicationPath();
|
||||
} else {
|
||||
QString s = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||
QDir(s).mkpath(".");
|
||||
return s;
|
||||
}
|
||||
if (IsPortable()) {
|
||||
return GetApplicationPath();
|
||||
} else {
|
||||
QString s =
|
||||
QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||
QDir(s).mkpath(".");
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileFunctions::IsPortable()
|
||||
{
|
||||
return QFileInfo::exists(QDir(GetApplicationPath()).filePath("portable"));
|
||||
return QFileInfo::exists(QDir(GetApplicationPath()).filePath("portable"));
|
||||
}
|
||||
|
||||
QString FileFunctions::GetApplicationPath()
|
||||
{
|
||||
return QCoreApplication::applicationDirPath();
|
||||
return QCoreApplication::applicationDirPath();
|
||||
}
|
||||
|
||||
QString FileFunctions::GetTempFilePath()
|
||||
{
|
||||
QString temp_path = QDir(QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation))
|
||||
.filePath(QCoreApplication::organizationName()))
|
||||
.filePath(QCoreApplication::applicationName());
|
||||
QString temp_path =
|
||||
QDir(
|
||||
QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation))
|
||||
.filePath(QCoreApplication::organizationName()))
|
||||
.filePath(QCoreApplication::applicationName());
|
||||
|
||||
// Ensure it exists
|
||||
QDir(temp_path).mkpath(".");
|
||||
// Ensure it exists
|
||||
QDir(temp_path).mkpath(".");
|
||||
|
||||
return temp_path;
|
||||
return temp_path;
|
||||
}
|
||||
|
||||
bool FileFunctions::CanCopyDirectoryWithoutOverwriting(const QString& source, const QString& dest)
|
||||
bool FileFunctions::CanCopyDirectoryWithoutOverwriting(const QString &source,
|
||||
const QString &dest)
|
||||
{
|
||||
QFileInfoList info_list = QDir(source).entryInfoList();
|
||||
QFileInfoList info_list = QDir(source).entryInfoList();
|
||||
|
||||
foreach (const QFileInfo& info, info_list) {
|
||||
// QDir::NoDotAndDotDot continues to not work, so we have to check manually
|
||||
if (info.fileName() == QStringLiteral(".") || info.fileName() == QStringLiteral("..")) {
|
||||
continue;
|
||||
}
|
||||
foreach (const QFileInfo &info, info_list) {
|
||||
// QDir::NoDotAndDotDot continues to not work, so we have to check manually
|
||||
if (info.fileName() == QStringLiteral(".") ||
|
||||
info.fileName() == QStringLiteral("..")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString dest_equivalent = QDir(dest).filePath(info.fileName());
|
||||
QString dest_equivalent = QDir(dest).filePath(info.fileName());
|
||||
|
||||
if (info.isDir()) {
|
||||
if (!CanCopyDirectoryWithoutOverwriting(info.absoluteFilePath(), dest_equivalent)) {
|
||||
return false;
|
||||
}
|
||||
} else if (QFileInfo::exists(dest_equivalent)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (info.isDir()) {
|
||||
if (!CanCopyDirectoryWithoutOverwriting(info.absoluteFilePath(),
|
||||
dest_equivalent)) {
|
||||
return false;
|
||||
}
|
||||
} else if (QFileInfo::exists(dest_equivalent)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FileFunctions::CopyDirectory(const QString &source, const QString &dest, bool overwrite)
|
||||
void FileFunctions::CopyDirectory(const QString &source, const QString &dest,
|
||||
bool overwrite)
|
||||
{
|
||||
QDir d(source);
|
||||
QDir d(source);
|
||||
|
||||
if (!d.exists()) {
|
||||
qCritical() << "Failed to copy directory, source" << source << "didn't exist";
|
||||
return;
|
||||
}
|
||||
if (!d.exists()) {
|
||||
qCritical()
|
||||
<< "Failed to copy directory, source" << source << "didn't exist";
|
||||
return;
|
||||
}
|
||||
|
||||
QDir dest_dir(dest);
|
||||
QDir dest_dir(dest);
|
||||
|
||||
if (!dest_dir.mkpath(QStringLiteral("."))) {
|
||||
qCritical() << "Failed to create destination directory" << dest;
|
||||
return;
|
||||
}
|
||||
if (!dest_dir.mkpath(QStringLiteral("."))) {
|
||||
qCritical() << "Failed to create destination directory" << dest;
|
||||
return;
|
||||
}
|
||||
|
||||
QFileInfoList l = d.entryInfoList();
|
||||
QFileInfoList l = d.entryInfoList();
|
||||
|
||||
foreach (const QFileInfo& info, l) {
|
||||
// QDir::NoDotAndDotDot continues to not work, so we have to check manually
|
||||
if (info.fileName() == QStringLiteral(".") || info.fileName() == QStringLiteral("..")) {
|
||||
continue;
|
||||
}
|
||||
foreach (const QFileInfo &info, l) {
|
||||
// QDir::NoDotAndDotDot continues to not work, so we have to check manually
|
||||
if (info.fileName() == QStringLiteral(".") ||
|
||||
info.fileName() == QStringLiteral("..")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString dest_file_path = dest_dir.filePath(info.fileName());
|
||||
QString dest_file_path = dest_dir.filePath(info.fileName());
|
||||
|
||||
if (info.isDir()) {
|
||||
// Copy dir
|
||||
CopyDirectory(info.absoluteFilePath(), dest_file_path, overwrite);
|
||||
} else {
|
||||
// Copy file
|
||||
if (overwrite && QFile::exists(dest_file_path)) {
|
||||
QFile file(dest_file_path);
|
||||
file.setPermissions(file.permissions() | QFileDevice::WriteOwner | QFileDevice::WriteUser |
|
||||
QFileDevice::WriteGroup | QFileDevice::WriteOther);
|
||||
file.remove();
|
||||
}
|
||||
if (info.isDir()) {
|
||||
// Copy dir
|
||||
CopyDirectory(info.absoluteFilePath(), dest_file_path, overwrite);
|
||||
} else {
|
||||
// Copy file
|
||||
if (overwrite && QFile::exists(dest_file_path)) {
|
||||
QFile file(dest_file_path);
|
||||
file.setPermissions(
|
||||
file.permissions() | QFileDevice::WriteOwner |
|
||||
QFileDevice::WriteUser | QFileDevice::WriteGroup |
|
||||
QFileDevice::WriteOther);
|
||||
file.remove();
|
||||
}
|
||||
|
||||
QFile::copy(info.absoluteFilePath(), dest_file_path);
|
||||
}
|
||||
}
|
||||
QFile::copy(info.absoluteFilePath(), dest_file_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FileFunctions::DirectoryIsValid(const QDir &d, bool try_to_create_if_not_exists)
|
||||
bool FileFunctions::DirectoryIsValid(const QDir &d,
|
||||
bool try_to_create_if_not_exists)
|
||||
{
|
||||
// Return whether the directory exists, or whether it could be created if it doesn't
|
||||
return d.exists() || d.mkpath(QStringLiteral("."));
|
||||
// Return whether the directory exists, or whether it could be created if it doesn't
|
||||
return d.exists() || d.mkpath(QStringLiteral("."));
|
||||
}
|
||||
|
||||
QString FileFunctions::EnsureFilenameExtension(QString fn, const QString &extension)
|
||||
QString FileFunctions::EnsureFilenameExtension(QString fn,
|
||||
const QString &extension)
|
||||
{
|
||||
// No-op if either input is empty
|
||||
if (!fn.isEmpty() && !extension.isEmpty()) {
|
||||
QString extension_with_dot;
|
||||
// No-op if either input is empty
|
||||
if (!fn.isEmpty() && !extension.isEmpty()) {
|
||||
QString extension_with_dot;
|
||||
|
||||
extension_with_dot.append('.');
|
||||
extension_with_dot.append(extension);
|
||||
extension_with_dot.append('.');
|
||||
extension_with_dot.append(extension);
|
||||
|
||||
if (!fn.endsWith(extension_with_dot, Qt::CaseInsensitive)) {
|
||||
fn.append(extension_with_dot);
|
||||
}
|
||||
}
|
||||
if (!fn.endsWith(extension_with_dot, Qt::CaseInsensitive)) {
|
||||
fn.append(extension_with_dot);
|
||||
}
|
||||
}
|
||||
|
||||
return fn;
|
||||
return fn;
|
||||
}
|
||||
|
||||
QString FileFunctions::ReadFileAsString(const QString &filename)
|
||||
{
|
||||
QFile f(filename);
|
||||
QString file_data;
|
||||
if (f.open(QFile::ReadOnly | QFile::Text)) {
|
||||
QTextStream text_stream(&f);
|
||||
file_data = text_stream.readAll();
|
||||
f.close();
|
||||
}
|
||||
return file_data;
|
||||
QFile f(filename);
|
||||
QString file_data;
|
||||
if (f.open(QFile::ReadOnly | QFile::Text)) {
|
||||
QTextStream text_stream(&f);
|
||||
file_data = text_stream.readAll();
|
||||
f.close();
|
||||
}
|
||||
return file_data;
|
||||
}
|
||||
|
||||
QString FileFunctions::GetSafeTemporaryFilename(const QString &original)
|
||||
{
|
||||
int counter = 0;
|
||||
int counter = 0;
|
||||
|
||||
QFileInfo original_info(original);
|
||||
QString basename = original_info.baseName();
|
||||
QString complete_suffix = original_info.completeSuffix();
|
||||
QFileInfo original_info(original);
|
||||
QString basename = original_info.baseName();
|
||||
QString complete_suffix = original_info.completeSuffix();
|
||||
|
||||
// If we have a complete suffix, make sure there's a period in it
|
||||
if (!complete_suffix.isEmpty()) {
|
||||
complete_suffix.prepend('.');
|
||||
}
|
||||
// If we have a complete suffix, make sure there's a period in it
|
||||
if (!complete_suffix.isEmpty()) {
|
||||
complete_suffix.prepend('.');
|
||||
}
|
||||
|
||||
QString temp_abs_path;
|
||||
do {
|
||||
temp_abs_path = original_info.dir().filePath(
|
||||
QStringLiteral("%1.tmp%2%3").arg(basename,
|
||||
QString::number(counter),
|
||||
complete_suffix));
|
||||
counter++;
|
||||
} while (QFileInfo::exists(temp_abs_path));
|
||||
QString temp_abs_path;
|
||||
do {
|
||||
temp_abs_path = original_info.dir().filePath(
|
||||
QStringLiteral("%1.tmp%2%3")
|
||||
.arg(basename, QString::number(counter), complete_suffix));
|
||||
counter++;
|
||||
} while (QFileInfo::exists(temp_abs_path));
|
||||
|
||||
return temp_abs_path;
|
||||
return temp_abs_path;
|
||||
}
|
||||
|
||||
bool FileFunctions::RenameFileAllowOverwrite(const QString &from, const QString &to)
|
||||
bool FileFunctions::RenameFileAllowOverwrite(const QString &from,
|
||||
const QString &to)
|
||||
{
|
||||
if (QFileInfo::exists(to) && !QFile::remove(to)) {
|
||||
qCritical() << "Couldn't remove existing file" << to << "for overwrite";
|
||||
return false;
|
||||
}
|
||||
if (QFileInfo::exists(to) && !QFile::remove(to)) {
|
||||
qCritical() << "Couldn't remove existing file" << to << "for overwrite";
|
||||
return false;
|
||||
}
|
||||
|
||||
// By this point, we can assume `to` either never existed or has now been deleted
|
||||
if (!QFile::rename(from, to)) {
|
||||
qCritical() << "Failed to rename file" << from << "to" << to;
|
||||
return false;
|
||||
}
|
||||
// By this point, we can assume `to` either never existed or has now been deleted
|
||||
if (!QFile::rename(from, to)) {
|
||||
qCritical() << "Failed to rename file" << from << "to" << to;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
QString FileFunctions::GetAutoRecoveryRoot()
|
||||
{
|
||||
return QDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath(QStringLiteral("autorecovery"));
|
||||
return QDir(QStandardPaths::writableLocation(
|
||||
QStandardPaths::AppLocalDataLocation))
|
||||
.filePath(QStringLiteral("autorecovery"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+29
-26
@@ -26,36 +26,40 @@
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A collection of static file and directory functions
|
||||
*/
|
||||
class FileFunctions {
|
||||
public:
|
||||
/**
|
||||
/**
|
||||
* @brief Returns true if the application is running in portable mode
|
||||
*
|
||||
* In portable mode, any persistent configuration files should be made in a path relative to the application rather
|
||||
* than in the user's home folder.
|
||||
*/
|
||||
static bool IsPortable();
|
||||
static bool IsPortable();
|
||||
|
||||
static QString GetUniqueFileIdentifier(const QString& filename);
|
||||
static QString GetUniqueFileIdentifier(const QString &filename);
|
||||
|
||||
static QString GetConfigurationLocation();
|
||||
static QString GetConfigurationLocation();
|
||||
|
||||
static QString GetApplicationPath();
|
||||
static QString GetApplicationPath();
|
||||
|
||||
static QString GetTempFilePath();
|
||||
static QString GetTempFilePath();
|
||||
|
||||
static bool CanCopyDirectoryWithoutOverwriting(const QString& source, const QString& dest);
|
||||
static bool CanCopyDirectoryWithoutOverwriting(const QString &source,
|
||||
const QString &dest);
|
||||
|
||||
static void CopyDirectory(const QString& source, const QString& dest, bool overwrite = false);
|
||||
static void CopyDirectory(const QString &source, const QString &dest,
|
||||
bool overwrite = false);
|
||||
|
||||
static bool DirectoryIsValid(const QDir& dir, bool try_to_create_if_not_exists = true);
|
||||
static bool DirectoryIsValid(const QDir &dir,
|
||||
bool try_to_create_if_not_exists = true);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Ensures a given filename has a certain extension
|
||||
*
|
||||
* Checks if the filename has the extension provided and appends it if not. The extension is
|
||||
@@ -64,11 +68,12 @@ public:
|
||||
|
||||
* @return The filename provided either untouched or with the extension appended to it.
|
||||
*/
|
||||
static QString EnsureFilenameExtension(QString fn, const QString& extension);
|
||||
static QString EnsureFilenameExtension(QString fn,
|
||||
const QString &extension);
|
||||
|
||||
static QString ReadFileAsString(const QString& filename);
|
||||
static QString ReadFileAsString(const QString &filename);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns a temporary filename that can be used while writing rather than the original
|
||||
*
|
||||
* If overwriting a file, it's safest to write to a new file first and then only replace it at
|
||||
@@ -78,28 +83,26 @@ public:
|
||||
* This function returns a slight variant of the filename provided that's guaranteed to not exist
|
||||
* and therefore won't overwrite anything important.
|
||||
*/
|
||||
static QString GetSafeTemporaryFilename(const QString& original);
|
||||
static QString GetSafeTemporaryFilename(const QString &original);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Renames a file from `from` to `to`, deleting `to` if such a file already exists first
|
||||
*/
|
||||
static bool RenameFileAllowOverwrite(const QString& from, const QString& to);
|
||||
static bool RenameFileAllowOverwrite(const QString &from,
|
||||
const QString &to);
|
||||
|
||||
inline static QString GetFormattedExecutableForPlatform(QString unformatted)
|
||||
{
|
||||
inline static QString GetFormattedExecutableForPlatform(QString unformatted)
|
||||
{
|
||||
#ifdef Q_OS_WINDOWS
|
||||
unformatted.append(QStringLiteral(".exe"));
|
||||
unformatted.append(QStringLiteral(".exe"));
|
||||
#endif
|
||||
|
||||
return unformatted;
|
||||
}
|
||||
|
||||
static QString GetAutoRecoveryRoot();
|
||||
return unformatted;
|
||||
}
|
||||
|
||||
static QString GetAutoRecoveryRoot();
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // FILEFUNCTIONS_H
|
||||
|
||||
+349
-323
@@ -5,446 +5,472 @@
|
||||
|
||||
#include "xmlutils.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const QVector<QString> Html::kBlockTags = {
|
||||
QStringLiteral("p"),
|
||||
QStringLiteral("div")
|
||||
};
|
||||
const QVector<QString> Html::kBlockTags = { QStringLiteral("p"),
|
||||
QStringLiteral("div") };
|
||||
|
||||
inline bool StrEquals(const QStringView &a, const QStringView &b)
|
||||
{
|
||||
return !a.compare(b, Qt::CaseInsensitive);
|
||||
return !a.compare(b, Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
QString Html::DocToHtml(const QTextDocument *doc)
|
||||
{
|
||||
QString html;
|
||||
QXmlStreamWriter writer(&html);
|
||||
QString html;
|
||||
QXmlStreamWriter writer(&html);
|
||||
|
||||
//writer.setAutoFormatting(true);
|
||||
//writer.setAutoFormatting(true);
|
||||
|
||||
for (auto it=doc->begin(); it!=doc->end(); it=it.next()) {
|
||||
WriteBlock(&writer, it);
|
||||
}
|
||||
for (auto it = doc->begin(); it != doc->end(); it = it.next()) {
|
||||
WriteBlock(&writer, it);
|
||||
}
|
||||
|
||||
return html;
|
||||
return html;
|
||||
}
|
||||
|
||||
struct HtmlNode {
|
||||
QString tag;
|
||||
QTextCharFormat format;
|
||||
QString tag;
|
||||
QTextCharFormat format;
|
||||
};
|
||||
|
||||
QTextCharFormat MergeHtmlFormats(const QVector<HtmlNode> &stack)
|
||||
{
|
||||
QTextCharFormat f;
|
||||
QTextCharFormat f;
|
||||
|
||||
for (int i=0; i<stack.size(); i++) {
|
||||
f.merge(stack.at(i).format);
|
||||
}
|
||||
for (int i = 0; i < stack.size(); i++) {
|
||||
f.merge(stack.at(i).format);
|
||||
}
|
||||
|
||||
return f;
|
||||
return f;
|
||||
}
|
||||
|
||||
void Html::HtmlToDoc(QTextDocument *doc, const QString &html)
|
||||
{
|
||||
// Empty doc
|
||||
doc->clear();
|
||||
bool inside_block = true;
|
||||
// Empty doc
|
||||
doc->clear();
|
||||
bool inside_block = true;
|
||||
|
||||
// Create cursor, which appears to be Qt's official way of inserting blocks and fragments
|
||||
QTextCursor c(doc);
|
||||
// Create cursor, which appears to be Qt's official way of inserting blocks and fragments
|
||||
QTextCursor c(doc);
|
||||
|
||||
QString wrapped = QStringLiteral("<html>").append(html).append("</html>");
|
||||
QXmlStreamReader reader(wrapped);
|
||||
QString wrapped = QStringLiteral("<html>").append(html).append("</html>");
|
||||
QXmlStreamReader reader(wrapped);
|
||||
|
||||
QVector<HtmlNode> fmt_stack;
|
||||
QVector<HtmlNode> fmt_stack;
|
||||
|
||||
QTextCharFormat default_fmt;
|
||||
default_fmt.setFontWeight(QFont::Normal);
|
||||
fmt_stack.append({QStringLiteral("html"), default_fmt});
|
||||
QTextCharFormat default_fmt;
|
||||
default_fmt.setFontWeight(QFont::Normal);
|
||||
fmt_stack.append({ QStringLiteral("html"), default_fmt });
|
||||
|
||||
QTextCharFormat current_fmt;
|
||||
QTextCharFormat current_fmt;
|
||||
|
||||
while (!reader.atEnd()) {
|
||||
reader.readNext();
|
||||
while (!reader.atEnd()) {
|
||||
reader.readNext();
|
||||
|
||||
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
||||
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
||||
QString tag = reader.name().toString().toLower();
|
||||
|
||||
QString tag = reader.name().toString().toLower();
|
||||
fmt_stack.append({ tag, ReadCharFormat(reader.attributes()) });
|
||||
current_fmt = MergeHtmlFormats(fmt_stack);
|
||||
|
||||
fmt_stack.append({tag, ReadCharFormat(reader.attributes())});
|
||||
current_fmt = MergeHtmlFormats(fmt_stack);
|
||||
if (kBlockTags.contains(tag)) {
|
||||
QTextBlockFormat block_fmt =
|
||||
ReadBlockFormat(reader.attributes());
|
||||
if (inside_block) {
|
||||
c.setBlockFormat(block_fmt);
|
||||
c.setBlockCharFormat(current_fmt);
|
||||
} else {
|
||||
c.insertBlock(block_fmt, current_fmt);
|
||||
inside_block = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (kBlockTags.contains(tag)) {
|
||||
QTextBlockFormat block_fmt = ReadBlockFormat(reader.attributes());
|
||||
if (inside_block) {
|
||||
c.setBlockFormat(block_fmt);
|
||||
c.setBlockCharFormat(current_fmt);
|
||||
} else {
|
||||
c.insertBlock(block_fmt, current_fmt);
|
||||
inside_block = true;
|
||||
}
|
||||
}
|
||||
} else if (reader.tokenType() == QXmlStreamReader::Characters) {
|
||||
QString characters = reader.text().toString();
|
||||
c.insertText(characters, current_fmt);
|
||||
|
||||
} else if (reader.tokenType() == QXmlStreamReader::Characters) {
|
||||
} else if (reader.tokenType() == QXmlStreamReader::EndElement) {
|
||||
QString tag = reader.name().toString().toLower();
|
||||
|
||||
QString characters = reader.text().toString();
|
||||
c.insertText(characters, current_fmt);
|
||||
for (int i = fmt_stack.size() - 1; i >= 0; i--) {
|
||||
if (fmt_stack.at(i).tag == tag) {
|
||||
fmt_stack.removeAt(i);
|
||||
current_fmt = MergeHtmlFormats(fmt_stack);
|
||||
|
||||
} else if (reader.tokenType() == QXmlStreamReader::EndElement) {
|
||||
if (kBlockTags.contains(tag)) {
|
||||
inside_block = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString tag = reader.name().toString().toLower();
|
||||
|
||||
for (int i=fmt_stack.size()-1; i>=0; i--) {
|
||||
if (fmt_stack.at(i).tag == tag) {
|
||||
fmt_stack.removeAt(i);
|
||||
current_fmt = MergeHtmlFormats(fmt_stack);
|
||||
|
||||
if (kBlockTags.contains(tag)) {
|
||||
inside_block = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (reader.error()) {
|
||||
qCritical() << "Failed to parse HTML:" << reader.errorString();
|
||||
}
|
||||
if (reader.error()) {
|
||||
qCritical() << "Failed to parse HTML:" << reader.errorString();
|
||||
}
|
||||
}
|
||||
|
||||
void Html::WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block)
|
||||
{
|
||||
writer->writeStartElement(QStringLiteral("p"));
|
||||
writer->writeStartElement(QStringLiteral("p"));
|
||||
|
||||
const QTextBlockFormat &fmt = block.blockFormat();
|
||||
const QTextBlockFormat &fmt = block.blockFormat();
|
||||
|
||||
// Write block alignment
|
||||
if (!(fmt.alignment() & Qt::AlignLeft)) {
|
||||
if (fmt.alignment() & Qt::AlignRight) {
|
||||
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("right"));
|
||||
} else if (fmt.alignment() & Qt::AlignHCenter) {
|
||||
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("center"));
|
||||
} else if (fmt.alignment() & Qt::AlignJustify) {
|
||||
writer->writeAttribute(QStringLiteral("align"), QStringLiteral("justify"));
|
||||
}
|
||||
}
|
||||
// Write block alignment
|
||||
if (!(fmt.alignment() & Qt::AlignLeft)) {
|
||||
if (fmt.alignment() & Qt::AlignRight) {
|
||||
writer->writeAttribute(QStringLiteral("align"),
|
||||
QStringLiteral("right"));
|
||||
} else if (fmt.alignment() & Qt::AlignHCenter) {
|
||||
writer->writeAttribute(QStringLiteral("align"),
|
||||
QStringLiteral("center"));
|
||||
} else if (fmt.alignment() & Qt::AlignJustify) {
|
||||
writer->writeAttribute(QStringLiteral("align"),
|
||||
QStringLiteral("justify"));
|
||||
}
|
||||
}
|
||||
|
||||
// RTL support
|
||||
if (block.textDirection() == Qt::RightToLeft) {
|
||||
writer->writeAttribute(QStringLiteral("dir"), QStringLiteral("rtl"));
|
||||
}
|
||||
// RTL support
|
||||
if (block.textDirection() == Qt::RightToLeft) {
|
||||
writer->writeAttribute(QStringLiteral("dir"), QStringLiteral("rtl"));
|
||||
}
|
||||
|
||||
// Write CSS attributes
|
||||
QString style;
|
||||
// Write CSS attributes
|
||||
QString style;
|
||||
|
||||
if (fmt.lineHeightType() != QTextBlockFormat::SingleHeight) {
|
||||
WriteCSSProperty(&style, QStringLiteral("line-height"), QStringLiteral("%1%").arg(fmt.lineHeight()));
|
||||
}
|
||||
if (fmt.lineHeightType() != QTextBlockFormat::SingleHeight) {
|
||||
WriteCSSProperty(&style, QStringLiteral("line-height"),
|
||||
QStringLiteral("%1%").arg(fmt.lineHeight()));
|
||||
}
|
||||
|
||||
WriteCharFormat(&style, block.charFormat());
|
||||
WriteCharFormat(&style, block.charFormat());
|
||||
|
||||
if (!style.isEmpty()) {
|
||||
writer->writeAttribute(QStringLiteral("style"), style);
|
||||
}
|
||||
if (!style.isEmpty()) {
|
||||
writer->writeAttribute(QStringLiteral("style"), style);
|
||||
}
|
||||
|
||||
auto it = block.begin();
|
||||
auto it = block.begin();
|
||||
|
||||
if (it != block.end()) {
|
||||
for (; it!=block.end(); it++) {
|
||||
WriteFragment(writer, it.fragment());
|
||||
}
|
||||
}
|
||||
if (it != block.end()) {
|
||||
for (; it != block.end(); it++) {
|
||||
WriteFragment(writer, it.fragment());
|
||||
}
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // p
|
||||
writer->writeEndElement(); // p
|
||||
}
|
||||
|
||||
void Html::WriteFragment(QXmlStreamWriter *writer, const QTextFragment &fragment)
|
||||
void Html::WriteFragment(QXmlStreamWriter *writer,
|
||||
const QTextFragment &fragment)
|
||||
{
|
||||
const QTextCharFormat &fmt = fragment.charFormat();
|
||||
const QTextCharFormat &fmt = fragment.charFormat();
|
||||
|
||||
writer->writeStartElement(QStringLiteral("span"));
|
||||
writer->writeStartElement(QStringLiteral("span"));
|
||||
|
||||
// Write CSS attributes
|
||||
QString style;
|
||||
// Write CSS attributes
|
||||
QString style;
|
||||
|
||||
WriteCharFormat(&style, fmt);
|
||||
WriteCharFormat(&style, fmt);
|
||||
|
||||
if (!style.isEmpty()) {
|
||||
writer->writeAttribute(QStringLiteral("style"), style);
|
||||
}
|
||||
if (!style.isEmpty()) {
|
||||
writer->writeAttribute(QStringLiteral("style"), style);
|
||||
}
|
||||
|
||||
QStringList lines = fragment.text().split(QChar::LineSeparator);
|
||||
bool first_line = true;
|
||||
foreach (const QString &l, lines) {
|
||||
if (first_line) {
|
||||
first_line = false;
|
||||
} else {
|
||||
writer->writeEmptyElement(QStringLiteral("br"));
|
||||
}
|
||||
writer->writeCharacters(l);
|
||||
}
|
||||
QStringList lines = fragment.text().split(QChar::LineSeparator);
|
||||
bool first_line = true;
|
||||
foreach (const QString &l, lines) {
|
||||
if (first_line) {
|
||||
first_line = false;
|
||||
} else {
|
||||
writer->writeEmptyElement(QStringLiteral("br"));
|
||||
}
|
||||
writer->writeCharacters(l);
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // span
|
||||
writer->writeEndElement(); // span
|
||||
}
|
||||
|
||||
void Html::WriteCSSProperty(QString *style, const QString &key, const QStringList &values)
|
||||
void Html::WriteCSSProperty(QString *style, const QString &key,
|
||||
const QStringList &values)
|
||||
{
|
||||
QString value;
|
||||
foreach (QString v, values) {
|
||||
if (v.contains(' ')) {
|
||||
v = QStringLiteral("'%1'").arg(v);
|
||||
}
|
||||
QString value;
|
||||
foreach (QString v, values) {
|
||||
if (v.contains(' ')) {
|
||||
v = QStringLiteral("'%1'").arg(v);
|
||||
}
|
||||
|
||||
AppendStringAutoSpace(&value, v);
|
||||
}
|
||||
AppendStringAutoSpace(&value, v);
|
||||
}
|
||||
|
||||
AppendStringAutoSpace(style, QStringLiteral("%1: %2;").arg(key, value));
|
||||
AppendStringAutoSpace(style, QStringLiteral("%1: %2;").arg(key, value));
|
||||
}
|
||||
|
||||
void Html::WriteCharFormat(QString *style, const QTextCharFormat &fmt)
|
||||
{
|
||||
QStringList families = fmt.fontFamilies().toStringList();
|
||||
if (!families.isEmpty()) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-family"), families.first());
|
||||
}
|
||||
QStringList families = fmt.fontFamilies().toStringList();
|
||||
if (!families.isEmpty()) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-family"),
|
||||
families.first());
|
||||
}
|
||||
|
||||
if (fmt.hasProperty(QTextFormat::FontPointSize)) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-size"), QStringLiteral("%1pt").arg(QString::number(fmt.fontPointSize())));
|
||||
}
|
||||
if (fmt.hasProperty(QTextFormat::FontPointSize)) {
|
||||
WriteCSSProperty(
|
||||
style, QStringLiteral("font-size"),
|
||||
QStringLiteral("%1pt").arg(QString::number(fmt.fontPointSize())));
|
||||
}
|
||||
|
||||
if (fmt.hasProperty(QTextFormat::FontWeight)) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-weight"), QString::number(fmt.fontWeight() * 8));
|
||||
}
|
||||
if (fmt.hasProperty(QTextFormat::FontWeight)) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-weight"),
|
||||
QString::number(fmt.fontWeight() * 8));
|
||||
}
|
||||
|
||||
if (fmt.hasProperty(QTextFormat::FontItalic)) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-style"), fmt.fontItalic() ? QStringLiteral("italic") : QStringLiteral("normal"));
|
||||
}
|
||||
if (fmt.hasProperty(QTextFormat::FontItalic)) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-style"),
|
||||
fmt.fontItalic() ? QStringLiteral("italic") :
|
||||
QStringLiteral("normal"));
|
||||
}
|
||||
|
||||
if (fmt.hasProperty(QTextFormat::FontStyleName)) {
|
||||
WriteCSSProperty(style, QStringLiteral("-ove-font-style"), fmt.fontStyleName().toString());
|
||||
}
|
||||
if (fmt.hasProperty(QTextFormat::FontStyleName)) {
|
||||
WriteCSSProperty(style, QStringLiteral("-ove-font-style"),
|
||||
fmt.fontStyleName().toString());
|
||||
}
|
||||
|
||||
QStringList deco;
|
||||
QStringList deco;
|
||||
|
||||
if (fmt.fontUnderline()) {
|
||||
deco.append(QStringLiteral("underline"));
|
||||
}
|
||||
if (fmt.fontUnderline()) {
|
||||
deco.append(QStringLiteral("underline"));
|
||||
}
|
||||
|
||||
if (fmt.fontStrikeOut()) {
|
||||
deco.append(QStringLiteral("line-through"));
|
||||
}
|
||||
if (fmt.fontStrikeOut()) {
|
||||
deco.append(QStringLiteral("line-through"));
|
||||
}
|
||||
|
||||
if (fmt.fontOverline()) {
|
||||
deco.append(QStringLiteral("overline"));
|
||||
}
|
||||
if (fmt.fontOverline()) {
|
||||
deco.append(QStringLiteral("overline"));
|
||||
}
|
||||
|
||||
if (!deco.isEmpty()) {
|
||||
WriteCSSProperty(style, QStringLiteral("text-decoration"), deco);
|
||||
}
|
||||
if (!deco.isEmpty()) {
|
||||
WriteCSSProperty(style, QStringLiteral("text-decoration"), deco);
|
||||
}
|
||||
|
||||
if (fmt.foreground().style() != Qt::NoBrush) {
|
||||
const QColor &color = fmt.foreground().color();
|
||||
QString cs;
|
||||
if (fmt.foreground().style() != Qt::NoBrush) {
|
||||
const QColor &color = fmt.foreground().color();
|
||||
QString cs;
|
||||
|
||||
if (color.alpha() == 255) {
|
||||
cs = color.name();
|
||||
} else if (color.alpha()) {
|
||||
cs = QStringLiteral("rgba(%1, %2, %3, %4)").arg(QString::number(color.red()), QString::number(color.green()), QString::number(color.blue()), QString::number(color.alphaF()));
|
||||
}
|
||||
if (color.alpha() == 255) {
|
||||
cs = color.name();
|
||||
} else if (color.alpha()) {
|
||||
cs = QStringLiteral("rgba(%1, %2, %3, %4)")
|
||||
.arg(QString::number(color.red()),
|
||||
QString::number(color.green()),
|
||||
QString::number(color.blue()),
|
||||
QString::number(color.alphaF()));
|
||||
}
|
||||
|
||||
WriteCSSProperty(style, QStringLiteral("color"), cs);
|
||||
}
|
||||
WriteCSSProperty(style, QStringLiteral("color"), cs);
|
||||
}
|
||||
|
||||
if (fmt.fontCapitalization() != QFont::MixedCase) {
|
||||
if (fmt.fontCapitalization() == QFont::SmallCaps) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-variant"), QStringLiteral("small-caps"));
|
||||
// TODO: Add others
|
||||
}
|
||||
}
|
||||
if (fmt.fontCapitalization() != QFont::MixedCase) {
|
||||
if (fmt.fontCapitalization() == QFont::SmallCaps) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-variant"),
|
||||
QStringLiteral("small-caps"));
|
||||
// TODO: Add others
|
||||
}
|
||||
}
|
||||
|
||||
if (fmt.fontLetterSpacing() != 0.0) {
|
||||
WriteCSSProperty(style, QStringLiteral("letter-spacing"), QStringLiteral("%1%").arg(QString::number(fmt.fontLetterSpacing())));
|
||||
}
|
||||
if (fmt.fontLetterSpacing() != 0.0) {
|
||||
WriteCSSProperty(style, QStringLiteral("letter-spacing"),
|
||||
QStringLiteral("%1%").arg(
|
||||
QString::number(fmt.fontLetterSpacing())));
|
||||
}
|
||||
|
||||
if (fmt.fontStretch() != 0) {
|
||||
WriteCSSProperty(style, QStringLiteral("font-stretch"), QStringLiteral("%1%").arg(QString::number(fmt.fontStretch())));
|
||||
}
|
||||
if (fmt.fontStretch() != 0) {
|
||||
WriteCSSProperty(
|
||||
style, QStringLiteral("font-stretch"),
|
||||
QStringLiteral("%1%").arg(QString::number(fmt.fontStretch())));
|
||||
}
|
||||
}
|
||||
|
||||
QTextCharFormat Html::ReadCharFormat(const QXmlStreamAttributes &attributes)
|
||||
{
|
||||
QTextCharFormat fmt;
|
||||
QTextCharFormat fmt;
|
||||
|
||||
foreach (const QXmlStreamAttribute &attr, attributes) {
|
||||
if (StrEquals(attr.name(), QStringLiteral("style"))) {
|
||||
auto css = GetCSSFromStyle(attr.value().toString());
|
||||
foreach (const QXmlStreamAttribute &attr, attributes) {
|
||||
if (StrEquals(attr.name(), QStringLiteral("style"))) {
|
||||
auto css = GetCSSFromStyle(attr.value().toString());
|
||||
|
||||
for (auto it=css.begin(); it!=css.end(); it++) {
|
||||
const QString &first_val = it.value().first();
|
||||
for (auto it = css.begin(); it != css.end(); it++) {
|
||||
const QString &first_val = it.value().first();
|
||||
|
||||
if (it.key() == QStringLiteral("font-family")) {
|
||||
fmt.setFontFamilies({first_val});
|
||||
} else if (it.key() == QStringLiteral("font-size")) {
|
||||
if (first_val.endsWith(QStringLiteral("pt"), Qt::CaseInsensitive)) {
|
||||
fmt.setFontPointSize(first_val.chopped(2).toDouble());
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("font-weight")) {
|
||||
fmt.setFontWeight(first_val.toInt()/8);
|
||||
} else if (it.key() == QStringLiteral("font-style")) {
|
||||
fmt.setFontItalic(StrEquals(first_val, QStringLiteral("italic")));
|
||||
} else if (it.key() == QStringLiteral("text-decoration")) {
|
||||
foreach (const QString &v, it.value()) {
|
||||
if (StrEquals(v, QStringLiteral("underline"))) {
|
||||
fmt.setFontUnderline(true);
|
||||
} else if (StrEquals(v, QStringLiteral("line-through"))) {
|
||||
fmt.setFontStrikeOut(true);
|
||||
} else if (StrEquals(v, QStringLiteral("overline"))) {
|
||||
fmt.setFontOverline(true);
|
||||
}
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("color")) {
|
||||
if (first_val.startsWith(QStringLiteral("rgba"), Qt::CaseInsensitive)) {
|
||||
QString vals_only = first_val;
|
||||
vals_only.remove(QStringLiteral("rgba"));
|
||||
vals_only.remove(QStringLiteral("("));
|
||||
vals_only.remove(QStringLiteral(")"));
|
||||
QStringList rgba = vals_only.split(',');
|
||||
if (rgba.size() == 4) {
|
||||
QColor c;
|
||||
c.setRedF(rgba.at(0).toDouble());
|
||||
c.setGreenF(rgba.at(1).toDouble());
|
||||
c.setBlueF(rgba.at(2).toDouble());
|
||||
c.setAlphaF(rgba.at(3).toDouble());
|
||||
fmt.setForeground(c);
|
||||
}
|
||||
} else {
|
||||
fmt.setForeground(QColor(first_val));
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("font-variant")) {
|
||||
if (StrEquals(first_val, QStringLiteral("small-caps"))) {
|
||||
fmt.setFontCapitalization(QFont::SmallCaps);
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("letter-spacing")) {
|
||||
if (first_val.contains(QChar('%'))) {
|
||||
fmt.setFontLetterSpacing(first_val.chopped(1).toDouble());
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("font-stretch")) {
|
||||
if (first_val.contains(QChar('%'))) {
|
||||
fmt.setFontStretch(first_val.chopped(1).toInt());
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("-ove-font-style")) {
|
||||
fmt.setFontStyleName(first_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt;
|
||||
if (it.key() == QStringLiteral("font-family")) {
|
||||
fmt.setFontFamilies({ first_val });
|
||||
} else if (it.key() == QStringLiteral("font-size")) {
|
||||
if (first_val.endsWith(QStringLiteral("pt"),
|
||||
Qt::CaseInsensitive)) {
|
||||
fmt.setFontPointSize(first_val.chopped(2).toDouble());
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("font-weight")) {
|
||||
fmt.setFontWeight(first_val.toInt() / 8);
|
||||
} else if (it.key() == QStringLiteral("font-style")) {
|
||||
fmt.setFontItalic(
|
||||
StrEquals(first_val, QStringLiteral("italic")));
|
||||
} else if (it.key() == QStringLiteral("text-decoration")) {
|
||||
foreach (const QString &v, it.value()) {
|
||||
if (StrEquals(v, QStringLiteral("underline"))) {
|
||||
fmt.setFontUnderline(true);
|
||||
} else if (StrEquals(v,
|
||||
QStringLiteral("line-through"))) {
|
||||
fmt.setFontStrikeOut(true);
|
||||
} else if (StrEquals(v, QStringLiteral("overline"))) {
|
||||
fmt.setFontOverline(true);
|
||||
}
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("color")) {
|
||||
if (first_val.startsWith(QStringLiteral("rgba"),
|
||||
Qt::CaseInsensitive)) {
|
||||
QString vals_only = first_val;
|
||||
vals_only.remove(QStringLiteral("rgba"));
|
||||
vals_only.remove(QStringLiteral("("));
|
||||
vals_only.remove(QStringLiteral(")"));
|
||||
QStringList rgba = vals_only.split(',');
|
||||
if (rgba.size() == 4) {
|
||||
QColor c;
|
||||
c.setRedF(rgba.at(0).toDouble());
|
||||
c.setGreenF(rgba.at(1).toDouble());
|
||||
c.setBlueF(rgba.at(2).toDouble());
|
||||
c.setAlphaF(rgba.at(3).toDouble());
|
||||
fmt.setForeground(c);
|
||||
}
|
||||
} else {
|
||||
fmt.setForeground(QColor(first_val));
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("font-variant")) {
|
||||
if (StrEquals(first_val, QStringLiteral("small-caps"))) {
|
||||
fmt.setFontCapitalization(QFont::SmallCaps);
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("letter-spacing")) {
|
||||
if (first_val.contains(QChar('%'))) {
|
||||
fmt.setFontLetterSpacing(
|
||||
first_val.chopped(1).toDouble());
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("font-stretch")) {
|
||||
if (first_val.contains(QChar('%'))) {
|
||||
fmt.setFontStretch(first_val.chopped(1).toInt());
|
||||
}
|
||||
} else if (it.key() == QStringLiteral("-ove-font-style")) {
|
||||
fmt.setFontStyleName(first_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt;
|
||||
}
|
||||
|
||||
QTextBlockFormat Html::ReadBlockFormat(const QXmlStreamAttributes &attributes)
|
||||
{
|
||||
QTextBlockFormat block_fmt;
|
||||
QTextBlockFormat block_fmt;
|
||||
|
||||
foreach (const QXmlStreamAttribute &attr, attributes) {
|
||||
if (StrEquals(attr.name(), QStringLiteral("align"))) {
|
||||
if (StrEquals(attr.value(), QStringLiteral("right"))) {
|
||||
block_fmt.setAlignment(Qt::AlignRight);
|
||||
} else if (StrEquals(attr.value(), QStringLiteral("center"))) {
|
||||
block_fmt.setAlignment(Qt::AlignHCenter);
|
||||
} else if (StrEquals(attr.value(), QStringLiteral("justify"))) {
|
||||
block_fmt.setAlignment(Qt::AlignJustify);
|
||||
}
|
||||
} else if (StrEquals(attr.name(), QStringLiteral("dir"))) {
|
||||
if (StrEquals(attr.value(), QStringLiteral("rtl"))) {
|
||||
block_fmt.setLayoutDirection(Qt::RightToLeft);
|
||||
}
|
||||
} else if (StrEquals(attr.name(), QStringLiteral("style"))) {
|
||||
auto css = GetCSSFromStyle(attr.value().toString());
|
||||
foreach (const QXmlStreamAttribute &attr, attributes) {
|
||||
if (StrEquals(attr.name(), QStringLiteral("align"))) {
|
||||
if (StrEquals(attr.value(), QStringLiteral("right"))) {
|
||||
block_fmt.setAlignment(Qt::AlignRight);
|
||||
} else if (StrEquals(attr.value(), QStringLiteral("center"))) {
|
||||
block_fmt.setAlignment(Qt::AlignHCenter);
|
||||
} else if (StrEquals(attr.value(), QStringLiteral("justify"))) {
|
||||
block_fmt.setAlignment(Qt::AlignJustify);
|
||||
}
|
||||
} else if (StrEquals(attr.name(), QStringLiteral("dir"))) {
|
||||
if (StrEquals(attr.value(), QStringLiteral("rtl"))) {
|
||||
block_fmt.setLayoutDirection(Qt::RightToLeft);
|
||||
}
|
||||
} else if (StrEquals(attr.name(), QStringLiteral("style"))) {
|
||||
auto css = GetCSSFromStyle(attr.value().toString());
|
||||
|
||||
for (auto it=css.begin(); it!=css.end(); it++) {
|
||||
if (it.key() == QStringLiteral("line-height")) {
|
||||
const QString &first_val = it.value().constFirst();
|
||||
if (first_val.contains(QChar('%'))) {
|
||||
block_fmt.setLineHeight(first_val.chopped(1).toDouble(), QTextBlockFormat::ProportionalHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto it = css.begin(); it != css.end(); it++) {
|
||||
if (it.key() == QStringLiteral("line-height")) {
|
||||
const QString &first_val = it.value().constFirst();
|
||||
if (first_val.contains(QChar('%'))) {
|
||||
block_fmt.setLineHeight(
|
||||
first_val.chopped(1).toDouble(),
|
||||
QTextBlockFormat::ProportionalHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return block_fmt;
|
||||
return block_fmt;
|
||||
}
|
||||
|
||||
void Html::AppendStringAutoSpace(QString *s, const QString &append)
|
||||
{
|
||||
if (!s->isEmpty()) {
|
||||
s->append(QChar(' '));
|
||||
}
|
||||
if (!s->isEmpty()) {
|
||||
s->append(QChar(' '));
|
||||
}
|
||||
|
||||
s->append(append);
|
||||
s->append(append);
|
||||
}
|
||||
|
||||
QMap<QString, QStringList> Html::GetCSSFromStyle(const QString &s)
|
||||
{
|
||||
QMap<QString, QStringList> map;
|
||||
QMap<QString, QStringList> map;
|
||||
|
||||
QStringList list = s.split(QChar(';'));
|
||||
QStringList list = s.split(QChar(';'));
|
||||
|
||||
foreach (const QString &a, list) {
|
||||
QStringList kv = a.split(QChar(':'));
|
||||
foreach (const QString &a, list) {
|
||||
QStringList kv = a.split(QChar(':'));
|
||||
|
||||
if (kv.size() != 2) {
|
||||
continue;
|
||||
}
|
||||
if (kv.size() != 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// I'm sure there's regex that could do this, but I couldn't figure it out. It needs to split
|
||||
// by space EXCEPT within quotes OR double-quotes, and said quotes should be EXCLUDED from each
|
||||
// match. Also commas should be filtered out.
|
||||
QStringList values;
|
||||
const QString &val = kv.at(1);
|
||||
QChar in_quote(0);
|
||||
QString current_str;
|
||||
for (int i=0; i<val.size(); i++) {
|
||||
const QChar ¤t_char = val.at(i);
|
||||
// I'm sure there's regex that could do this, but I couldn't figure it out. It needs to split
|
||||
// by space EXCEPT within quotes OR double-quotes, and said quotes should be EXCLUDED from each
|
||||
// match. Also commas should be filtered out.
|
||||
QStringList values;
|
||||
const QString &val = kv.at(1);
|
||||
QChar in_quote(0);
|
||||
QString current_str;
|
||||
for (int i = 0; i < val.size(); i++) {
|
||||
const QChar ¤t_char = val.at(i);
|
||||
|
||||
if (!in_quote.isNull()) {
|
||||
// If inside quotes and character isn't quote, indiscriminately append char
|
||||
if (current_char == in_quote) {
|
||||
in_quote = QChar(0);
|
||||
} else {
|
||||
current_str.append(current_char);
|
||||
}
|
||||
} else if (current_char.isSpace() || current_char == QChar(',')) {
|
||||
// Dump current
|
||||
if (!current_str.isEmpty()) {
|
||||
values.append(current_str);
|
||||
current_str.clear();
|
||||
}
|
||||
} else if (in_quote.isNull() && (current_char == QChar('\'') || current_char == QChar('"'))) {
|
||||
in_quote = current_char;
|
||||
} else {
|
||||
current_str.append(current_char);
|
||||
}
|
||||
}
|
||||
if (!in_quote.isNull()) {
|
||||
// If inside quotes and character isn't quote, indiscriminately append char
|
||||
if (current_char == in_quote) {
|
||||
in_quote = QChar(0);
|
||||
} else {
|
||||
current_str.append(current_char);
|
||||
}
|
||||
} else if (current_char.isSpace() || current_char == QChar(',')) {
|
||||
// Dump current
|
||||
if (!current_str.isEmpty()) {
|
||||
values.append(current_str);
|
||||
current_str.clear();
|
||||
}
|
||||
} else if (in_quote.isNull() && (current_char == QChar('\'') ||
|
||||
current_char == QChar('"'))) {
|
||||
in_quote = current_char;
|
||||
} else {
|
||||
current_str.append(current_char);
|
||||
}
|
||||
}
|
||||
|
||||
if (!current_str.isEmpty()) {
|
||||
values.append(current_str);
|
||||
}
|
||||
if (!current_str.isEmpty()) {
|
||||
values.append(current_str);
|
||||
}
|
||||
|
||||
// Not sure if this will ever happen, but just in case, we will avoid assert failures with this
|
||||
if (values.isEmpty()) {
|
||||
values.append(QString());
|
||||
}
|
||||
// Not sure if this will ever happen, but just in case, we will avoid assert failures with this
|
||||
if (values.isEmpty()) {
|
||||
values.append(QString());
|
||||
}
|
||||
|
||||
map[kv.at(0).trimmed().toLower()] = values;
|
||||
}
|
||||
map[kv.at(0).trimmed().toLower()] = values;
|
||||
}
|
||||
|
||||
return map;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+22
-17
@@ -6,7 +6,8 @@
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Functions for converting HTML to QTextDocument and vice versa
|
||||
@@ -26,33 +27,37 @@ namespace olive {
|
||||
*/
|
||||
class Html {
|
||||
public:
|
||||
static QString DocToHtml(const QTextDocument *doc);
|
||||
static QString DocToHtml(const QTextDocument *doc);
|
||||
|
||||
static void HtmlToDoc(QTextDocument *doc, const QString &html);
|
||||
static void HtmlToDoc(QTextDocument *doc, const QString &html);
|
||||
|
||||
private:
|
||||
static void WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block);
|
||||
static void WriteBlock(QXmlStreamWriter *writer, const QTextBlock &block);
|
||||
|
||||
static void WriteFragment(QXmlStreamWriter *writer, const QTextFragment &fragment);
|
||||
static void WriteFragment(QXmlStreamWriter *writer,
|
||||
const QTextFragment &fragment);
|
||||
|
||||
static void WriteCSSProperty(QString *style, const QString &key, const QStringList &value);
|
||||
static void WriteCSSProperty(QString *style, const QString &key, const QString &value)
|
||||
{
|
||||
WriteCSSProperty(style, key, QStringList({value}));
|
||||
}
|
||||
static void WriteCSSProperty(QString *style, const QString &key,
|
||||
const QStringList &value);
|
||||
static void WriteCSSProperty(QString *style, const QString &key,
|
||||
const QString &value)
|
||||
{
|
||||
WriteCSSProperty(style, key, QStringList({ value }));
|
||||
}
|
||||
|
||||
static void WriteCharFormat(QString *style, const QTextCharFormat &fmt);
|
||||
static void WriteCharFormat(QString *style, const QTextCharFormat &fmt);
|
||||
|
||||
static QTextCharFormat ReadCharFormat(const QXmlStreamAttributes &attributes);
|
||||
static QTextCharFormat
|
||||
ReadCharFormat(const QXmlStreamAttributes &attributes);
|
||||
|
||||
static QTextBlockFormat ReadBlockFormat(const QXmlStreamAttributes &attributes);
|
||||
static QTextBlockFormat
|
||||
ReadBlockFormat(const QXmlStreamAttributes &attributes);
|
||||
|
||||
static void AppendStringAutoSpace(QString *s, const QString &append);
|
||||
static void AppendStringAutoSpace(QString *s, const QString &append);
|
||||
|
||||
static QMap<QString, QStringList> GetCSSFromStyle(const QString &s);
|
||||
|
||||
static const QVector<QString> kBlockTags;
|
||||
static QMap<QString, QStringList> GetCSSFromStyle(const QString &s);
|
||||
|
||||
static const QVector<QString> kBlockTags;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -2,29 +2,30 @@
|
||||
|
||||
#include <QMutex>
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
uint64_t job_time_index = 0;
|
||||
QMutex job_time_mutex;
|
||||
|
||||
JobTime::JobTime()
|
||||
{
|
||||
Acquire();
|
||||
Acquire();
|
||||
}
|
||||
|
||||
void JobTime::Acquire()
|
||||
{
|
||||
job_time_mutex.lock();
|
||||
job_time_mutex.lock();
|
||||
|
||||
value_ = job_time_index;
|
||||
job_time_index++;
|
||||
value_ = job_time_index;
|
||||
job_time_index++;
|
||||
|
||||
job_time_mutex.unlock();
|
||||
job_time_mutex.unlock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const olive::JobTime& r)
|
||||
QDebug operator<<(QDebug debug, const olive::JobTime &r)
|
||||
{
|
||||
return debug.space() << r.value();
|
||||
return debug.space() << r.value();
|
||||
}
|
||||
|
||||
+35
-36
@@ -4,58 +4,57 @@
|
||||
#include <QDebug>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace olive {
|
||||
|
||||
class JobTime
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class JobTime {
|
||||
public:
|
||||
JobTime();
|
||||
JobTime();
|
||||
|
||||
void Acquire();
|
||||
void Acquire();
|
||||
|
||||
uint64_t value() const
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
uint64_t value() const
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
|
||||
bool operator==(const JobTime &rhs) const
|
||||
{
|
||||
return value_ == rhs.value_;
|
||||
}
|
||||
bool operator==(const JobTime &rhs) const
|
||||
{
|
||||
return value_ == rhs.value_;
|
||||
}
|
||||
|
||||
bool operator!=(const JobTime &rhs) const
|
||||
{
|
||||
return value_ != rhs.value_;
|
||||
}
|
||||
bool operator!=(const JobTime &rhs) const
|
||||
{
|
||||
return value_ != rhs.value_;
|
||||
}
|
||||
|
||||
bool operator<(const JobTime &rhs) const
|
||||
{
|
||||
return value_ < rhs.value_;
|
||||
}
|
||||
bool operator<(const JobTime &rhs) const
|
||||
{
|
||||
return value_ < rhs.value_;
|
||||
}
|
||||
|
||||
bool operator>(const JobTime &rhs) const
|
||||
{
|
||||
return value_ > rhs.value_;
|
||||
}
|
||||
bool operator>(const JobTime &rhs) const
|
||||
{
|
||||
return value_ > rhs.value_;
|
||||
}
|
||||
|
||||
bool operator<=(const JobTime &rhs) const
|
||||
{
|
||||
return value_ <= rhs.value_;
|
||||
}
|
||||
bool operator<=(const JobTime &rhs) const
|
||||
{
|
||||
return value_ <= rhs.value_;
|
||||
}
|
||||
|
||||
bool operator>=(const JobTime &rhs) const
|
||||
{
|
||||
return value_ >= rhs.value_;
|
||||
}
|
||||
bool operator>=(const JobTime &rhs) const
|
||||
{
|
||||
return value_ >= rhs.value_;
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t value_;
|
||||
|
||||
uint64_t value_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const olive::JobTime& r);
|
||||
QDebug operator<<(QDebug debug, const olive::JobTime &r);
|
||||
|
||||
Q_DECLARE_METATYPE(olive::JobTime)
|
||||
|
||||
|
||||
+7
-6
@@ -21,20 +21,21 @@
|
||||
#ifndef LERP_H
|
||||
#define LERP_H
|
||||
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
/**
|
||||
* @brief Linearly interpolate a value between a and b using t
|
||||
*
|
||||
* t should be a number between 0.0 and 1.0. 0.0 will return a, 1.0 will return b, and between will return a value
|
||||
* in between a and b at that point linearly.
|
||||
*/
|
||||
T lerp(T a, T b, double t) {
|
||||
return (a * (1.0 - t)) + (b * t);
|
||||
T lerp(T a, T b, double t)
|
||||
{
|
||||
return (a * (1.0 - t)) + (b * t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T lerp(T a, T b, float t) {
|
||||
return (a * (1.0f - t)) + (b * t);
|
||||
template <typename T> T lerp(T a, T b, float t)
|
||||
{
|
||||
return (a * (1.0f - t)) + (b * t);
|
||||
}
|
||||
|
||||
#endif // LERP_H
|
||||
|
||||
+249
-245
@@ -31,7 +31,8 @@
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief MemoryPool base class
|
||||
@@ -46,386 +47,389 @@ namespace olive {
|
||||
* `Get()` will return an ElementPtr. The original desired data can be accessed through ElementPtr::data(). This data
|
||||
* will belong to the caller until ElementPtr goes out of scope and the memory is freed back into the pool.
|
||||
*/
|
||||
class MemoryPool : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
class MemoryPool : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param element_count
|
||||
*
|
||||
* Number of elements per arena
|
||||
*/
|
||||
MemoryPool(int element_count)
|
||||
{
|
||||
element_count_ = element_count;
|
||||
MemoryPool(int element_count)
|
||||
{
|
||||
element_count_ = element_count;
|
||||
|
||||
clear_timer_ = new QTimer();
|
||||
clear_timer_->setInterval(kMaxEmptyArenaLife);
|
||||
clear_timer_->moveToThread(qApp->thread());
|
||||
connect(clear_timer_, &QTimer::timeout, this, &MemoryPool::ClearEmptyArenas, Qt::DirectConnection);
|
||||
QMetaObject::invokeMethod(clear_timer_, "start", Qt::QueuedConnection);
|
||||
}
|
||||
clear_timer_ = new QTimer();
|
||||
clear_timer_->setInterval(kMaxEmptyArenaLife);
|
||||
clear_timer_->moveToThread(qApp->thread());
|
||||
connect(clear_timer_, &QTimer::timeout, this,
|
||||
&MemoryPool::ClearEmptyArenas, Qt::DirectConnection);
|
||||
QMetaObject::invokeMethod(clear_timer_, "start", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Destructor
|
||||
*
|
||||
* Deletes all arenas.
|
||||
*/
|
||||
virtual ~MemoryPool()
|
||||
{
|
||||
Clear();
|
||||
clear_timer_->deleteLater();
|
||||
}
|
||||
virtual ~MemoryPool()
|
||||
{
|
||||
Clear();
|
||||
clear_timer_->deleteLater();
|
||||
}
|
||||
|
||||
DISABLE_COPY_MOVE(MemoryPool)
|
||||
DISABLE_COPY_MOVE(MemoryPool)
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Clears all arenas, freeing all of their memory
|
||||
*
|
||||
* Note that this function is not safe, any elements that are still out there will be invalid
|
||||
* and accessing them will cause a crash. You'll need to make sure all elements are already
|
||||
* relinquished before then.
|
||||
*/
|
||||
void Clear()
|
||||
{
|
||||
qDeleteAll(arenas_);
|
||||
arenas_.clear();
|
||||
}
|
||||
void Clear()
|
||||
{
|
||||
qDeleteAll(arenas_);
|
||||
arenas_.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns whether any arenas are successfully allocated
|
||||
*/
|
||||
inline bool IsAllocated() const
|
||||
{
|
||||
return !arenas_.empty();
|
||||
}
|
||||
inline bool IsAllocated() const
|
||||
{
|
||||
return !arenas_.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns current number of allocated arenas
|
||||
*/
|
||||
inline int GetArenaCount() const
|
||||
{
|
||||
return arenas_.size();
|
||||
}
|
||||
inline int GetArenaCount() const
|
||||
{
|
||||
return arenas_.size();
|
||||
}
|
||||
|
||||
class Arena;
|
||||
class Arena;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief A handle for a chunk of memory in an arena
|
||||
*
|
||||
* Calling Get() on the pool or arena will return a shared pointer to an element which will contain a pointer to
|
||||
* the desired object/data in data(). When Element is destroyed (i.e. when ElementPtr goes out of scope), the memory
|
||||
* is released back into the pool so it can be used by another class.
|
||||
*/
|
||||
class Element {
|
||||
public:
|
||||
/**
|
||||
class Element {
|
||||
public:
|
||||
/**
|
||||
* @brief Element Constructor
|
||||
*
|
||||
* There is no need to use this outside of the memory pool's internal functions.
|
||||
*/
|
||||
Element(Arena* parent, uint8_t* data)
|
||||
{
|
||||
parent_ = parent;
|
||||
data_ = data;
|
||||
accessed_ = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
Element(Arena *parent, uint8_t *data)
|
||||
{
|
||||
parent_ = parent;
|
||||
data_ = data;
|
||||
accessed_ = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Element Destructor
|
||||
*
|
||||
* Automatically releases this element's memory back to the arena it was retrieved from.
|
||||
*/
|
||||
~Element()
|
||||
{
|
||||
release();
|
||||
}
|
||||
~Element()
|
||||
{
|
||||
release();
|
||||
}
|
||||
|
||||
DISABLE_COPY_MOVE(Element)
|
||||
DISABLE_COPY_MOVE(Element)
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Access data represented in the pool
|
||||
*/
|
||||
inline uint8_t* data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
inline uint8_t *data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
inline const int64_t& timestamp() const
|
||||
{
|
||||
return timestamp_;
|
||||
}
|
||||
inline const int64_t ×tamp() const
|
||||
{
|
||||
return timestamp_;
|
||||
}
|
||||
|
||||
inline void set_timestamp(const int64_t& timestamp)
|
||||
{
|
||||
timestamp_ = timestamp;
|
||||
}
|
||||
inline void set_timestamp(const int64_t ×tamp)
|
||||
{
|
||||
timestamp_ = timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Register that this element has been accessed
|
||||
*
|
||||
* \see last_accessed()
|
||||
*/
|
||||
inline void access()
|
||||
{
|
||||
accessed_ = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
inline void access()
|
||||
{
|
||||
accessed_ = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the last time `access()` was called on this function
|
||||
*
|
||||
* Useful for determining the relative age of an element (i.e. if it hasn't been accessed for a certain amount of
|
||||
* time, it can probably be freed back into the pool). This requires all usages to call `access()`.
|
||||
*/
|
||||
inline const int64_t& last_accessed() const
|
||||
{
|
||||
return accessed_;
|
||||
}
|
||||
inline const int64_t &last_accessed() const
|
||||
{
|
||||
return accessed_;
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
if (data_) {
|
||||
parent_->Release(this);
|
||||
data_ = nullptr;
|
||||
}
|
||||
}
|
||||
void release()
|
||||
{
|
||||
if (data_) {
|
||||
parent_->Release(this);
|
||||
data_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Arena* parent_;
|
||||
private:
|
||||
Arena *parent_;
|
||||
|
||||
uint8_t* data_;
|
||||
uint8_t *data_;
|
||||
|
||||
int64_t timestamp_;
|
||||
int64_t timestamp_;
|
||||
|
||||
int64_t accessed_;
|
||||
int64_t accessed_;
|
||||
};
|
||||
|
||||
};
|
||||
using ElementPtr = std::shared_ptr<Element>;
|
||||
|
||||
using ElementPtr = std::shared_ptr<Element>;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief A memory pool arena - a subsection of memory
|
||||
*
|
||||
* The pool itself does not store memory, it stores "arenas". This is so that the pool can handle the situation of
|
||||
* an arena becoming full with no more memory to lend. A pool can automatically allocate another arena and continue
|
||||
* providing memory (and freeing arenas when they're no longer in use).
|
||||
*/
|
||||
class Arena {
|
||||
public:
|
||||
Arena(MemoryPool* parent)
|
||||
{
|
||||
parent_ = parent;
|
||||
data_ = nullptr;
|
||||
allocated_sz_ = 0;
|
||||
empty_time_ = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
class Arena {
|
||||
public:
|
||||
Arena(MemoryPool *parent)
|
||||
{
|
||||
parent_ = parent;
|
||||
data_ = nullptr;
|
||||
allocated_sz_ = 0;
|
||||
empty_time_ = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
~Arena()
|
||||
{
|
||||
std::list<Element*> copy = lent_elements_;
|
||||
foreach (Element* e, copy) {
|
||||
e->release();
|
||||
}
|
||||
~Arena()
|
||||
{
|
||||
std::list<Element *> copy = lent_elements_;
|
||||
foreach (Element *e, copy) {
|
||||
e->release();
|
||||
}
|
||||
|
||||
delete [] data_;
|
||||
}
|
||||
delete[] data_;
|
||||
}
|
||||
|
||||
DISABLE_COPY_MOVE(Arena)
|
||||
DISABLE_COPY_MOVE(Arena)
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns an element if there is free memory to do so
|
||||
*/
|
||||
ElementPtr Get()
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
ElementPtr Get()
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
|
||||
for (int i=0;i<available_.size();i++) {
|
||||
if (available_.at(i)) {
|
||||
// This buffer is available
|
||||
available_.replace(i, false);
|
||||
for (int i = 0; i < available_.size(); i++) {
|
||||
if (available_.at(i)) {
|
||||
// This buffer is available
|
||||
available_.replace(i, false);
|
||||
|
||||
ElementPtr e = std::make_shared<Element>(this,
|
||||
reinterpret_cast<uint8_t*>(data_ + i * element_sz_));
|
||||
lent_elements_.push_back(e.get());
|
||||
ElementPtr e = std::make_shared<Element>(
|
||||
this,
|
||||
reinterpret_cast<uint8_t *>(data_ + i * element_sz_));
|
||||
lent_elements_.push_back(e.get());
|
||||
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Releases an element back into the pool for use elsewhere
|
||||
*/
|
||||
void Release(Element* e)
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
quintptr diff = reinterpret_cast<quintptr>(e->data()) - reinterpret_cast<quintptr>(data_);
|
||||
void Release(Element *e)
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
quintptr diff = reinterpret_cast<quintptr>(e->data()) -
|
||||
reinterpret_cast<quintptr>(data_);
|
||||
|
||||
int index = diff / element_sz_;
|
||||
int index = diff / element_sz_;
|
||||
|
||||
available_.replace(index, true);
|
||||
available_.replace(index, true);
|
||||
|
||||
lent_elements_.remove(e);
|
||||
lent_elements_.remove(e);
|
||||
|
||||
if (lent_elements_.empty()) {
|
||||
empty_time_ = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
}
|
||||
if (lent_elements_.empty()) {
|
||||
empty_time_ = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
}
|
||||
|
||||
int GetUsageCount()
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
return lent_elements_.size();
|
||||
}
|
||||
int GetUsageCount()
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
return lent_elements_.size();
|
||||
}
|
||||
|
||||
bool Allocate(size_t ele_sz, size_t nb_elements)
|
||||
{
|
||||
if (IsAllocated()) {
|
||||
return true;
|
||||
}
|
||||
bool Allocate(size_t ele_sz, size_t nb_elements)
|
||||
{
|
||||
if (IsAllocated()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
element_sz_ = ele_sz;
|
||||
element_sz_ = ele_sz;
|
||||
|
||||
allocated_sz_ = element_sz_ * nb_elements;
|
||||
allocated_sz_ = element_sz_ * nb_elements;
|
||||
|
||||
if ((data_ = new uint8_t[allocated_sz_])) {
|
||||
available_.resize(nb_elements);
|
||||
available_.fill(true);
|
||||
if ((data_ = new uint8_t[allocated_sz_])) {
|
||||
available_.resize(nb_elements);
|
||||
available_.fill(true);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
available_.clear();
|
||||
return true;
|
||||
} else {
|
||||
available_.clear();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline int GetElementCount() const
|
||||
{
|
||||
return available_.size();
|
||||
}
|
||||
inline int GetElementCount() const
|
||||
{
|
||||
return available_.size();
|
||||
}
|
||||
|
||||
inline bool IsAllocated() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
inline bool IsAllocated() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
inline qint64 GetTimeArenaWasMadeEmpty()
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
return empty_time_;
|
||||
}
|
||||
inline qint64 GetTimeArenaWasMadeEmpty()
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
return empty_time_;
|
||||
}
|
||||
|
||||
private:
|
||||
MemoryPool* parent_;
|
||||
private:
|
||||
MemoryPool *parent_;
|
||||
|
||||
uint8_t* data_;
|
||||
uint8_t *data_;
|
||||
|
||||
size_t allocated_sz_;
|
||||
size_t allocated_sz_;
|
||||
|
||||
QVector<bool> available_;
|
||||
QVector<bool> available_;
|
||||
|
||||
QMutex lock_;
|
||||
QMutex lock_;
|
||||
|
||||
size_t element_sz_;
|
||||
size_t element_sz_;
|
||||
|
||||
std::list<Element*> lent_elements_;
|
||||
std::list<Element *> lent_elements_;
|
||||
|
||||
qint64 empty_time_;
|
||||
qint64 empty_time_;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Retrieves an element from an available arena
|
||||
*/
|
||||
ElementPtr Get()
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
ElementPtr Get()
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
|
||||
// Attempt to get an element from an arena
|
||||
foreach (Arena* a, arenas_) {
|
||||
ElementPtr e = a->Get();
|
||||
// Attempt to get an element from an arena
|
||||
foreach (Arena *a, arenas_) {
|
||||
ElementPtr e = a->Get();
|
||||
|
||||
if (e) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
if (e) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
// All arenas were empty, we'll need to create a new one
|
||||
if (arenas_.empty()) {
|
||||
qDebug() << "No arenas, creating new...";
|
||||
} else {
|
||||
qDebug() << "All arenas are full, creating new...";
|
||||
}
|
||||
// All arenas were empty, we'll need to create a new one
|
||||
if (arenas_.empty()) {
|
||||
qDebug() << "No arenas, creating new...";
|
||||
} else {
|
||||
qDebug() << "All arenas are full, creating new...";
|
||||
}
|
||||
|
||||
size_t ele_sz = GetElementSize();
|
||||
size_t ele_sz = GetElementSize();
|
||||
|
||||
if (!ele_sz) {
|
||||
qCritical() << "Failed to create arena, element size was 0";
|
||||
return nullptr;
|
||||
}
|
||||
if (!ele_sz) {
|
||||
qCritical() << "Failed to create arena, element size was 0";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (element_count_ <= 0) {
|
||||
qCritical() << "Failed to create arena, element count was invalid:" << element_count_;
|
||||
return nullptr;
|
||||
}
|
||||
if (element_count_ <= 0) {
|
||||
qCritical() << "Failed to create arena, element count was invalid:"
|
||||
<< element_count_;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Arena* a = new Arena(this);
|
||||
if (!a->Allocate(ele_sz, element_count_)) {
|
||||
qCritical() << "Failed to create arena, allocation failed. Out of memory?";
|
||||
delete a;
|
||||
return nullptr;
|
||||
}
|
||||
Arena *a = new Arena(this);
|
||||
if (!a->Allocate(ele_sz, element_count_)) {
|
||||
qCritical()
|
||||
<< "Failed to create arena, allocation failed. Out of memory?";
|
||||
delete a;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
arenas_.push_back(a);
|
||||
return a->Get();
|
||||
}
|
||||
arenas_.push_back(a);
|
||||
return a->Get();
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
/**
|
||||
* @brief The size of each element
|
||||
*
|
||||
* Override this to use a custom size (e.g. a char array where T = char but the element size is > 1)
|
||||
*/
|
||||
virtual size_t GetElementSize()
|
||||
{
|
||||
return sizeof(uint8_t);
|
||||
}
|
||||
virtual size_t GetElementSize()
|
||||
{
|
||||
return sizeof(uint8_t);
|
||||
}
|
||||
|
||||
private:
|
||||
int element_count_;
|
||||
int element_count_;
|
||||
|
||||
std::list<Arena*> arenas_;
|
||||
std::list<Arena *> arenas_;
|
||||
|
||||
QMutex lock_;
|
||||
QMutex lock_;
|
||||
|
||||
QTimer *clear_timer_;
|
||||
QTimer *clear_timer_;
|
||||
|
||||
static const qint64 kMaxEmptyArenaLife = 5000;
|
||||
static const qint64 kMaxEmptyArenaLife = 5000;
|
||||
|
||||
private slots:
|
||||
void ClearEmptyArenas()
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
void ClearEmptyArenas()
|
||||
{
|
||||
QMutexLocker locker(&lock_);
|
||||
|
||||
const qint64 min_time = QDateTime::currentMSecsSinceEpoch() - kMaxEmptyArenaLife;
|
||||
const qint64 min_time =
|
||||
QDateTime::currentMSecsSinceEpoch() - kMaxEmptyArenaLife;
|
||||
|
||||
for (auto it=arenas_.begin(); it!=arenas_.end(); ) {
|
||||
Arena* arena = (*it);
|
||||
|
||||
if (arena->GetUsageCount() == 0 && arena->GetTimeArenaWasMadeEmpty() <= min_time) {
|
||||
qDebug() << "Removing an empty arena";
|
||||
delete arena;
|
||||
it = arenas_.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto it = arenas_.begin(); it != arenas_.end();) {
|
||||
Arena *arena = (*it);
|
||||
|
||||
if (arena->GetUsageCount() == 0 &&
|
||||
arena->GetTimeArenaWasMadeEmpty() <= min_time) {
|
||||
qDebug() << "Removing an empty arena";
|
||||
delete arena;
|
||||
it = arenas_.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+19
-18
@@ -20,28 +20,29 @@
|
||||
|
||||
#include "ocioutils.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
OCIO::BitDepth OCIOUtils::GetOCIOBitDepthFromPixelFormat(PixelFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case PixelFormat::U8:
|
||||
return OCIO::BIT_DEPTH_UINT8;
|
||||
case PixelFormat::U16:
|
||||
return OCIO::BIT_DEPTH_UINT16;
|
||||
break;
|
||||
case PixelFormat::F16:
|
||||
return OCIO::BIT_DEPTH_F16;
|
||||
break;
|
||||
case PixelFormat::F32:
|
||||
return OCIO::BIT_DEPTH_F32;
|
||||
break;
|
||||
case PixelFormat::INVALID:
|
||||
case PixelFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
switch (format) {
|
||||
case PixelFormat::U8:
|
||||
return OCIO::BIT_DEPTH_UINT8;
|
||||
case PixelFormat::U16:
|
||||
return OCIO::BIT_DEPTH_UINT16;
|
||||
break;
|
||||
case PixelFormat::F16:
|
||||
return OCIO::BIT_DEPTH_F16;
|
||||
break;
|
||||
case PixelFormat::F32:
|
||||
return OCIO::BIT_DEPTH_F32;
|
||||
break;
|
||||
case PixelFormat::INVALID:
|
||||
case PixelFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
|
||||
return OCIO::BIT_DEPTH_UNKNOWN;
|
||||
return OCIO::BIT_DEPTH_UNKNOWN;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,12 +26,12 @@ namespace OCIO = OCIO_NAMESPACE;
|
||||
|
||||
#include "render/videoparams.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class OCIOUtils
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class OCIOUtils {
|
||||
public:
|
||||
static OCIO::BitDepth GetOCIOBitDepthFromPixelFormat(PixelFormat format);
|
||||
static OCIO::BitDepth GetOCIOBitDepthFromPixelFormat(PixelFormat format);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+37
-41
@@ -22,62 +22,58 @@
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
namespace olive {
|
||||
|
||||
void OIIOUtils::FrameToBuffer(const Frame* frame, OIIO::ImageBuf *buf)
|
||||
namespace olive
|
||||
{
|
||||
buf->set_pixels(OIIO::ROI(),
|
||||
buf->spec().format,
|
||||
frame->const_data(),
|
||||
OIIO::AutoStride,
|
||||
frame->linesize_bytes());
|
||||
|
||||
void OIIOUtils::FrameToBuffer(const Frame *frame, OIIO::ImageBuf *buf)
|
||||
{
|
||||
buf->set_pixels(OIIO::ROI(), buf->spec().format, frame->const_data(),
|
||||
OIIO::AutoStride, frame->linesize_bytes());
|
||||
}
|
||||
|
||||
void OIIOUtils::BufferToFrame(OIIO::ImageBuf *buf, Frame* frame)
|
||||
void OIIOUtils::BufferToFrame(OIIO::ImageBuf *buf, Frame *frame)
|
||||
{
|
||||
buf->get_pixels(OIIO::ROI(),
|
||||
buf->spec().format,
|
||||
frame->data(),
|
||||
OIIO::AutoStride,
|
||||
frame->linesize_bytes());
|
||||
buf->get_pixels(OIIO::ROI(), buf->spec().format, frame->data(),
|
||||
OIIO::AutoStride, frame->linesize_bytes());
|
||||
}
|
||||
|
||||
rational OIIOUtils::GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec &spec)
|
||||
{
|
||||
return rational::fromDouble(spec.get_float_attribute("PixelAspectRatio", 1));
|
||||
return rational::fromDouble(
|
||||
spec.get_float_attribute("PixelAspectRatio", 1));
|
||||
}
|
||||
|
||||
PixelFormat OIIOUtils::GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type)
|
||||
{
|
||||
switch (type) {
|
||||
case OIIO::TypeDesc::UNKNOWN:
|
||||
case OIIO::TypeDesc::NONE:
|
||||
break;
|
||||
switch (type) {
|
||||
case OIIO::TypeDesc::UNKNOWN:
|
||||
case OIIO::TypeDesc::NONE:
|
||||
break;
|
||||
|
||||
case OIIO::TypeDesc::INT8:
|
||||
case OIIO::TypeDesc::INT16:
|
||||
case OIIO::TypeDesc::INT32:
|
||||
case OIIO::TypeDesc::UINT32:
|
||||
case OIIO::TypeDesc::INT64:
|
||||
case OIIO::TypeDesc::UINT64:
|
||||
case OIIO::TypeDesc::STRING:
|
||||
case OIIO::TypeDesc::PTR:
|
||||
case OIIO::TypeDesc::LASTBASE:
|
||||
case OIIO::TypeDesc::DOUBLE:
|
||||
qDebug() << "Tried to use unknown OIIO base type";
|
||||
break;
|
||||
case OIIO::TypeDesc::INT8:
|
||||
case OIIO::TypeDesc::INT16:
|
||||
case OIIO::TypeDesc::INT32:
|
||||
case OIIO::TypeDesc::UINT32:
|
||||
case OIIO::TypeDesc::INT64:
|
||||
case OIIO::TypeDesc::UINT64:
|
||||
case OIIO::TypeDesc::STRING:
|
||||
case OIIO::TypeDesc::PTR:
|
||||
case OIIO::TypeDesc::LASTBASE:
|
||||
case OIIO::TypeDesc::DOUBLE:
|
||||
qDebug() << "Tried to use unknown OIIO base type";
|
||||
break;
|
||||
|
||||
case OIIO::TypeDesc::UINT8:
|
||||
return PixelFormat::U8;
|
||||
case OIIO::TypeDesc::UINT16:
|
||||
return PixelFormat::U16;
|
||||
case OIIO::TypeDesc::HALF:
|
||||
return PixelFormat::F16;
|
||||
case OIIO::TypeDesc::FLOAT:
|
||||
return PixelFormat::F32;
|
||||
}
|
||||
case OIIO::TypeDesc::UINT8:
|
||||
return PixelFormat::U8;
|
||||
case OIIO::TypeDesc::UINT16:
|
||||
return PixelFormat::U16;
|
||||
case OIIO::TypeDesc::HALF:
|
||||
return PixelFormat::F16;
|
||||
case OIIO::TypeDesc::FLOAT:
|
||||
return PixelFormat::F32;
|
||||
}
|
||||
|
||||
return PixelFormat::INVALID;
|
||||
return PixelFormat::INVALID;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+24
-23
@@ -27,37 +27,38 @@
|
||||
#include "codec/frame.h"
|
||||
#include "render/videoparams.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class OIIOUtils {
|
||||
public:
|
||||
static OIIO::TypeDesc::BASETYPE GetOIIOBaseTypeFromFormat(PixelFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case PixelFormat::U8:
|
||||
return OIIO::TypeDesc::UINT8;
|
||||
case PixelFormat::U16:
|
||||
return OIIO::TypeDesc::UINT16;
|
||||
case PixelFormat::F16:
|
||||
return OIIO::TypeDesc::HALF;
|
||||
case PixelFormat::F32:
|
||||
return OIIO::TypeDesc::FLOAT;
|
||||
case PixelFormat::INVALID:
|
||||
case PixelFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
static OIIO::TypeDesc::BASETYPE
|
||||
GetOIIOBaseTypeFromFormat(PixelFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case PixelFormat::U8:
|
||||
return OIIO::TypeDesc::UINT8;
|
||||
case PixelFormat::U16:
|
||||
return OIIO::TypeDesc::UINT16;
|
||||
case PixelFormat::F16:
|
||||
return OIIO::TypeDesc::HALF;
|
||||
case PixelFormat::F32:
|
||||
return OIIO::TypeDesc::FLOAT;
|
||||
case PixelFormat::INVALID:
|
||||
case PixelFormat::COUNT:
|
||||
break;
|
||||
}
|
||||
|
||||
return OIIO::TypeDesc::UNKNOWN;
|
||||
}
|
||||
return OIIO::TypeDesc::UNKNOWN;
|
||||
}
|
||||
|
||||
static void FrameToBuffer(const Frame *frame, OIIO::ImageBuf* buf);
|
||||
static void FrameToBuffer(const Frame *frame, OIIO::ImageBuf *buf);
|
||||
|
||||
static void BufferToFrame(OIIO::ImageBuf* buf, Frame* frame);
|
||||
static void BufferToFrame(OIIO::ImageBuf *buf, Frame *frame);
|
||||
|
||||
static PixelFormat GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type);
|
||||
|
||||
static rational GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec& spec);
|
||||
static PixelFormat GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type);
|
||||
|
||||
static rational GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec &spec);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+16
-15
@@ -25,30 +25,31 @@
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
uint32_t ceil_to_power_of_2(uint32_t v)
|
||||
{
|
||||
v--;
|
||||
v |= v >> 1;
|
||||
v |= v >> 2;
|
||||
v |= v >> 4;
|
||||
v |= v >> 8;
|
||||
v |= v >> 16;
|
||||
v++;
|
||||
v--;
|
||||
v |= v >> 1;
|
||||
v |= v >> 2;
|
||||
v |= v >> 4;
|
||||
v |= v >> 8;
|
||||
v |= v >> 16;
|
||||
v++;
|
||||
|
||||
return v;
|
||||
return v;
|
||||
}
|
||||
|
||||
uint32_t floor_to_power_of_2(uint32_t x)
|
||||
{
|
||||
x = x | (x >> 1);
|
||||
x = x | (x >> 2);
|
||||
x = x | (x >> 4);
|
||||
x = x | (x >> 8);
|
||||
x = x | (x >> 16);
|
||||
x = x | (x >> 1);
|
||||
x = x | (x >> 2);
|
||||
x = x | (x >> 4);
|
||||
x = x | (x >> 8);
|
||||
x = x | (x >> 16);
|
||||
|
||||
return x - (x >> 1);
|
||||
return x - (x >> 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+125
-114
@@ -22,192 +22,203 @@
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
int QtUtils::QFontMetricsWidth(QFontMetrics fm, const QString& s) {
|
||||
int QtUtils::QFontMetricsWidth(QFontMetrics fm, const QString &s)
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
|
||||
return fm.width(s);
|
||||
return fm.width(s);
|
||||
#else
|
||||
return fm.horizontalAdvance(s);
|
||||
return fm.horizontalAdvance(s);
|
||||
#endif
|
||||
}
|
||||
|
||||
QFrame *QtUtils::CreateHorizontalLine()
|
||||
{
|
||||
QFrame* horizontal_line = new QFrame();
|
||||
horizontal_line->setFrameShape(QFrame::HLine);
|
||||
horizontal_line->setFrameShadow(QFrame::Sunken);
|
||||
return horizontal_line;
|
||||
QFrame *horizontal_line = new QFrame();
|
||||
horizontal_line->setFrameShape(QFrame::HLine);
|
||||
horizontal_line->setFrameShadow(QFrame::Sunken);
|
||||
return horizontal_line;
|
||||
}
|
||||
|
||||
QFrame *QtUtils::CreateVerticalLine()
|
||||
{
|
||||
QFrame *l = CreateHorizontalLine();
|
||||
l->setFrameShape(QFrame::VLine);
|
||||
return l;
|
||||
QFrame *l = CreateHorizontalLine();
|
||||
l->setFrameShape(QFrame::VLine);
|
||||
return l;
|
||||
}
|
||||
|
||||
int QtUtils::MsgBox(QWidget *parent, QMessageBox::Icon icon, const QString &title, const QString &message, QMessageBox::StandardButtons buttons)
|
||||
int QtUtils::MsgBox(QWidget *parent, QMessageBox::Icon icon,
|
||||
const QString &title, const QString &message,
|
||||
QMessageBox::StandardButtons buttons)
|
||||
{
|
||||
QMessageBox b(parent);
|
||||
b.setIcon(icon);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setWindowTitle(title);
|
||||
b.setText(message);
|
||||
QMessageBox b(parent);
|
||||
b.setIcon(icon);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setWindowTitle(title);
|
||||
b.setText(message);
|
||||
|
||||
uint mask = QMessageBox::FirstButton;
|
||||
while (mask <= QMessageBox::LastButton) {
|
||||
uint sb = buttons & mask;
|
||||
if (sb) {
|
||||
b.addButton(static_cast<QMessageBox::StandardButton>(sb));
|
||||
}
|
||||
mask <<= 1;
|
||||
}
|
||||
uint mask = QMessageBox::FirstButton;
|
||||
while (mask <= QMessageBox::LastButton) {
|
||||
uint sb = buttons & mask;
|
||||
if (sb) {
|
||||
b.addButton(static_cast<QMessageBox::StandardButton>(sb));
|
||||
}
|
||||
mask <<= 1;
|
||||
}
|
||||
|
||||
return b.exec();
|
||||
return b.exec();
|
||||
}
|
||||
|
||||
QDateTime QtUtils::GetCreationDate(const QFileInfo &info)
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
|
||||
return info.created();
|
||||
return info.created();
|
||||
#else
|
||||
QDateTime t = info.birthTime();
|
||||
if (!t.isValid()) {
|
||||
t = info.metadataChangeTime();
|
||||
}
|
||||
return t;
|
||||
QDateTime t = info.birthTime();
|
||||
if (!t.isValid()) {
|
||||
t = info.metadataChangeTime();
|
||||
}
|
||||
return t;
|
||||
#endif
|
||||
}
|
||||
|
||||
QString QtUtils::GetFormattedDateTime(const QDateTime &dt)
|
||||
{
|
||||
return dt.toString(Qt::TextDate);
|
||||
return dt.toString(Qt::TextDate);
|
||||
}
|
||||
|
||||
QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm, int bounding_width)
|
||||
QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm,
|
||||
int bounding_width)
|
||||
{
|
||||
QStringList list;
|
||||
QStringList list;
|
||||
|
||||
QStringList lines = s.split('\n');
|
||||
QStringList lines = s.split('\n');
|
||||
|
||||
// Iterate every line
|
||||
for (int i=0; i<lines.size(); i++) {
|
||||
QString this_line = lines.at(i);
|
||||
// Iterate every line
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
QString this_line = lines.at(i);
|
||||
|
||||
while (this_line.size() > 1 && QFontMetricsWidth(fm, this_line) >= bounding_width) {
|
||||
int old_size = this_line.size();
|
||||
int hard_break = -1;
|
||||
while (this_line.size() > 1 &&
|
||||
QFontMetricsWidth(fm, this_line) >= bounding_width) {
|
||||
int old_size = this_line.size();
|
||||
int hard_break = -1;
|
||||
|
||||
for (int j=this_line.size()-1; j>=0; j--) {
|
||||
const QChar &char_test = this_line.at(j);
|
||||
for (int j = this_line.size() - 1; j >= 0; j--) {
|
||||
const QChar &char_test = this_line.at(j);
|
||||
|
||||
if (char_test.isSpace()
|
||||
|| char_test == '-') {
|
||||
if (QFontMetricsWidth(fm, this_line.left(j)) < bounding_width) {
|
||||
if (!char_test.isSpace()) {
|
||||
j++;
|
||||
}
|
||||
if (char_test.isSpace() || char_test == '-') {
|
||||
if (QFontMetricsWidth(fm, this_line.left(j)) <
|
||||
bounding_width) {
|
||||
if (!char_test.isSpace()) {
|
||||
j++;
|
||||
}
|
||||
|
||||
QString chopped = this_line.left(j);
|
||||
QString chopped = this_line.left(j);
|
||||
|
||||
list.append(chopped);
|
||||
list.append(chopped);
|
||||
|
||||
while (j < this_line.size() && this_line.at(j).isSpace()) {
|
||||
j++;
|
||||
}
|
||||
this_line.remove(0, j);
|
||||
break;
|
||||
}
|
||||
} else if (hard_break == -1 && QFontMetricsWidth(fm, this_line.left(j)) < bounding_width) {
|
||||
// In case we can't find a better place to split, split at the earliest time the line
|
||||
// goes under the width limit
|
||||
hard_break = j;
|
||||
}
|
||||
}
|
||||
while (j < this_line.size() &&
|
||||
this_line.at(j).isSpace()) {
|
||||
j++;
|
||||
}
|
||||
this_line.remove(0, j);
|
||||
break;
|
||||
}
|
||||
} else if (hard_break == -1 &&
|
||||
QFontMetricsWidth(fm, this_line.left(j)) <
|
||||
bounding_width) {
|
||||
// In case we can't find a better place to split, split at the earliest time the line
|
||||
// goes under the width limit
|
||||
hard_break = j;
|
||||
}
|
||||
}
|
||||
|
||||
if (old_size == this_line.size()) {
|
||||
if (hard_break != -1) {
|
||||
list.append(this_line.left(hard_break));
|
||||
this_line.remove(0, hard_break);
|
||||
} else {
|
||||
qWarning() << "Failed to find anywhere to wrap. Returning full line.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (old_size == this_line.size()) {
|
||||
if (hard_break != -1) {
|
||||
list.append(this_line.left(hard_break));
|
||||
this_line.remove(0, hard_break);
|
||||
} else {
|
||||
qWarning()
|
||||
<< "Failed to find anywhere to wrap. Returning full line.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this_line.isEmpty()) {
|
||||
list.append(this_line);
|
||||
}
|
||||
}
|
||||
if (!this_line.isEmpty()) {
|
||||
list.append(this_line);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
return list;
|
||||
}
|
||||
|
||||
Qt::KeyboardModifiers QtUtils::FlipControlAndShiftModifiers(Qt::KeyboardModifiers e)
|
||||
Qt::KeyboardModifiers
|
||||
QtUtils::FlipControlAndShiftModifiers(Qt::KeyboardModifiers e)
|
||||
{
|
||||
if (e & Qt::ControlModifier & Qt::ShiftModifier) {
|
||||
return e;
|
||||
}
|
||||
if (e & Qt::ControlModifier & Qt::ShiftModifier) {
|
||||
return e;
|
||||
}
|
||||
|
||||
if (e & Qt::ShiftModifier) {
|
||||
e |= Qt::ControlModifier;
|
||||
e &= ~Qt::ShiftModifier;
|
||||
} else if (e & Qt::ControlModifier) {
|
||||
e |= Qt::ShiftModifier;
|
||||
e &= ~Qt::ControlModifier;
|
||||
}
|
||||
if (e & Qt::ShiftModifier) {
|
||||
e |= Qt::ControlModifier;
|
||||
e &= ~Qt::ShiftModifier;
|
||||
} else if (e & Qt::ControlModifier) {
|
||||
e |= Qt::ShiftModifier;
|
||||
e &= ~Qt::ControlModifier;
|
||||
}
|
||||
|
||||
return e;
|
||||
return e;
|
||||
}
|
||||
|
||||
void QtUtils::SetComboBoxData(QComboBox *cb, int data)
|
||||
{
|
||||
for (int i=0; i<cb->count(); i++) {
|
||||
if (cb->itemData(i).toInt() == data) {
|
||||
cb->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < cb->count(); i++) {
|
||||
if (cb->itemData(i).toInt() == data) {
|
||||
cb->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QtUtils::SetComboBoxData(QComboBox *cb, const QString &data)
|
||||
{
|
||||
for (int i=0; i<cb->count(); i++) {
|
||||
if (cb->itemData(i).toString() == data) {
|
||||
cb->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < cb->count(); i++) {
|
||||
if (cb->itemData(i).toString() == data) {
|
||||
cb->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QColor QtUtils::toQColor(const core::Color &i)
|
||||
{
|
||||
QColor c;
|
||||
QColor c;
|
||||
|
||||
// QColor only supports values from 0.0 to 1.0 and are only used for UI representations
|
||||
c.setRedF(std::clamp(i.red(), 0.0f, 1.0f));
|
||||
c.setGreenF(std::clamp(i.green(), 0.0f, 1.0f));
|
||||
c.setBlueF(std::clamp(i.blue(), 0.0f, 1.0f));
|
||||
c.setAlphaF(std::clamp(i.alpha(), 0.0f, 1.0f));
|
||||
// QColor only supports values from 0.0 to 1.0 and are only used for UI representations
|
||||
c.setRedF(std::clamp(i.red(), 0.0f, 1.0f));
|
||||
c.setGreenF(std::clamp(i.green(), 0.0f, 1.0f));
|
||||
c.setBlueF(std::clamp(i.blue(), 0.0f, 1.0f));
|
||||
c.setAlphaF(std::clamp(i.alpha(), 0.0f, 1.0f));
|
||||
|
||||
return c;
|
||||
return c;
|
||||
}
|
||||
|
||||
namespace core {
|
||||
namespace core
|
||||
{
|
||||
|
||||
uint qHash(const core::rational &r, uint seed)
|
||||
{
|
||||
return ::qHash(r.toDouble(), seed);
|
||||
return ::qHash(r.toDouble(), seed);
|
||||
}
|
||||
|
||||
uint qHash(const core::TimeRange &r, uint seed)
|
||||
{
|
||||
return qHash(r.in(), seed) ^ qHash(r.out(), seed);
|
||||
return qHash(r.in(), seed) ^ qHash(r.out(), seed);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+43
-40
@@ -29,76 +29,79 @@
|
||||
#include <QFrame>
|
||||
#include <QMessageBox>
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class QtUtils {
|
||||
public:
|
||||
/**
|
||||
/**
|
||||
* @brief Retrieves the width of a string according to certain QFontMetrics
|
||||
*
|
||||
* QFontMetrics::width() has been deprecatd in favor of QFontMetrics::horizontalAdvance(), but the
|
||||
* latter was only introduced in 5.11+. This function wraps the latter for 5.11+ and the former for
|
||||
* earlier.
|
||||
*/
|
||||
static int QFontMetricsWidth(QFontMetrics fm, const QString& s);
|
||||
static int QFontMetricsWidth(QFontMetrics fm, const QString &s);
|
||||
|
||||
static QFrame* CreateHorizontalLine();
|
||||
static QFrame *CreateHorizontalLine();
|
||||
|
||||
static QFrame* CreateVerticalLine();
|
||||
static QFrame *CreateVerticalLine();
|
||||
|
||||
static int MsgBox(QWidget *parent, QMessageBox::Icon icon, const QString& title, const QString& message, QMessageBox::StandardButtons buttons = QMessageBox::Ok);
|
||||
static int MsgBox(QWidget *parent, QMessageBox::Icon icon,
|
||||
const QString &title, const QString &message,
|
||||
QMessageBox::StandardButtons buttons = QMessageBox::Ok);
|
||||
|
||||
static QDateTime GetCreationDate(const QFileInfo &info);
|
||||
static QDateTime GetCreationDate(const QFileInfo &info);
|
||||
|
||||
static QString GetFormattedDateTime(const QDateTime &dt);
|
||||
static QString GetFormattedDateTime(const QDateTime &dt);
|
||||
|
||||
static QStringList WordWrapString(const QString &s, const QFontMetrics &fm, int bounding_width);
|
||||
static QStringList WordWrapString(const QString &s, const QFontMetrics &fm,
|
||||
int bounding_width);
|
||||
|
||||
static Qt::KeyboardModifiers FlipControlAndShiftModifiers(Qt::KeyboardModifiers e);
|
||||
static Qt::KeyboardModifiers
|
||||
FlipControlAndShiftModifiers(Qt::KeyboardModifiers e);
|
||||
|
||||
static void SetComboBoxData(QComboBox *cb, int data);
|
||||
static void SetComboBoxData(QComboBox *cb, const QString &data);
|
||||
static void SetComboBoxData(QComboBox *cb, int data);
|
||||
static void SetComboBoxData(QComboBox *cb, const QString &data);
|
||||
|
||||
template <typename T>
|
||||
static T *GetParentOfType(const QObject *child)
|
||||
{
|
||||
QObject *t = child->parent();
|
||||
template <typename T> static T *GetParentOfType(const QObject *child)
|
||||
{
|
||||
QObject *t = child->parent();
|
||||
|
||||
while (t) {
|
||||
if (T *p = dynamic_cast<T*>(t)) {
|
||||
return p;
|
||||
}
|
||||
t = t->parent();
|
||||
}
|
||||
while (t) {
|
||||
if (T *p = dynamic_cast<T *>(t)) {
|
||||
return p;
|
||||
}
|
||||
t = t->parent();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static QColor toQColor(const core::Color &c);
|
||||
static QColor toQColor(const core::Color &c);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Convert a pointer to a value that can be sent between NodeParams
|
||||
*/
|
||||
static QVariant PtrToValue(void* ptr)
|
||||
{
|
||||
return reinterpret_cast<quintptr>(ptr);
|
||||
}
|
||||
static QVariant PtrToValue(void *ptr)
|
||||
{
|
||||
return reinterpret_cast<quintptr>(ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Convert a NodeParam value to a pointer of any kind
|
||||
*/
|
||||
template<class T>
|
||||
static T* ValueToPtr(const QVariant &ptr)
|
||||
{
|
||||
return reinterpret_cast<T*>(ptr.value<quintptr>());
|
||||
}
|
||||
|
||||
template <class T> static T *ValueToPtr(const QVariant &ptr)
|
||||
{
|
||||
return reinterpret_cast<T *>(ptr.value<quintptr>());
|
||||
}
|
||||
};
|
||||
|
||||
namespace core {
|
||||
namespace core
|
||||
{
|
||||
|
||||
uint qHash(const core::rational& r, uint seed = 0);
|
||||
uint qHash(const core::TimeRange& r, uint seed = 0);
|
||||
uint qHash(const core::rational &r, uint seed = 0);
|
||||
uint qHash(const core::TimeRange &r, uint seed = 0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -21,10 +21,9 @@
|
||||
#ifndef RANGE_H
|
||||
#define RANGE_H
|
||||
|
||||
template <typename T>
|
||||
bool InRange(T a, T b, T range)
|
||||
template <typename T> bool InRange(T a, T b, T range)
|
||||
{
|
||||
return (a >= b - range && a <= b + range);
|
||||
return (a >= b - range && a <= b + range);
|
||||
}
|
||||
|
||||
#endif // RANGE_H
|
||||
|
||||
+54
-52
@@ -24,69 +24,71 @@
|
||||
#include <QMessageBox>
|
||||
#include <QRegularExpression>
|
||||
|
||||
namespace olive {
|
||||
|
||||
double GetFloatRatioFromUser(QWidget* parent,
|
||||
const QString& title,
|
||||
bool* ok_in)
|
||||
namespace olive
|
||||
{
|
||||
QString s;
|
||||
|
||||
forever {
|
||||
bool ok;
|
||||
double GetFloatRatioFromUser(QWidget *parent, const QString &title, bool *ok_in)
|
||||
{
|
||||
QString s;
|
||||
|
||||
s = QInputDialog::getText(parent,
|
||||
title,
|
||||
QCoreApplication::translate("RatioDialog", "Enter custom ratio (e.g. \"4:3\", \"16/9\", etc.):"),
|
||||
QLineEdit::Normal,
|
||||
s,
|
||||
&ok);
|
||||
forever
|
||||
{
|
||||
bool ok;
|
||||
|
||||
if (!ok) {
|
||||
// User cancelled dialog, do nothing
|
||||
if (ok_in) {
|
||||
*ok_in = false;
|
||||
}
|
||||
return qSNaN();
|
||||
}
|
||||
s = QInputDialog::getText(
|
||||
parent, title,
|
||||
QCoreApplication::translate(
|
||||
"RatioDialog",
|
||||
"Enter custom ratio (e.g. \"4:3\", \"16/9\", etc.):"),
|
||||
QLineEdit::Normal, s, &ok);
|
||||
|
||||
QStringList ratio_components = s.split(QRegularExpression(QStringLiteral(":|;|\\/")));
|
||||
if (!ok) {
|
||||
// User cancelled dialog, do nothing
|
||||
if (ok_in) {
|
||||
*ok_in = false;
|
||||
}
|
||||
return qSNaN();
|
||||
}
|
||||
|
||||
if (ratio_components.size() == 1) {
|
||||
bool float_ok;
|
||||
QStringList ratio_components =
|
||||
s.split(QRegularExpression(QStringLiteral(":|;|\\/")));
|
||||
|
||||
double flt = ratio_components.at(0).toDouble(&float_ok);
|
||||
if (ratio_components.size() == 1) {
|
||||
bool float_ok;
|
||||
|
||||
if (float_ok && flt > 0) {
|
||||
if (ok_in) {
|
||||
*ok_in = true;
|
||||
}
|
||||
return flt;
|
||||
}
|
||||
} else if (ratio_components.size() == 2) {
|
||||
bool numer_ok, denom_ok;
|
||||
double flt = ratio_components.at(0).toDouble(&float_ok);
|
||||
|
||||
double num = ratio_components.at(0).toDouble(&numer_ok);
|
||||
double den = ratio_components.at(1).toDouble(&denom_ok);
|
||||
if (float_ok && flt > 0) {
|
||||
if (ok_in) {
|
||||
*ok_in = true;
|
||||
}
|
||||
return flt;
|
||||
}
|
||||
} else if (ratio_components.size() == 2) {
|
||||
bool numer_ok, denom_ok;
|
||||
|
||||
if (numer_ok
|
||||
&& denom_ok
|
||||
&& num > 0
|
||||
&& den > 0) {
|
||||
// Exit loop and set this ratio
|
||||
if (ok_in) {
|
||||
*ok_in = true;
|
||||
}
|
||||
return num / den;
|
||||
}
|
||||
}
|
||||
double num = ratio_components.at(0).toDouble(&numer_ok);
|
||||
double den = ratio_components.at(1).toDouble(&denom_ok);
|
||||
|
||||
QMessageBox::warning(parent,
|
||||
QCoreApplication::translate("RatioDialog", "Invalid custom ratio"),
|
||||
QCoreApplication::translate("RatioDialog", "Failed to parse \"%1\" into an aspect ratio. Please format a "
|
||||
"rational fraction with a ':' or a '/' separator.").arg(s),
|
||||
QMessageBox::Ok);
|
||||
}
|
||||
if (numer_ok && denom_ok && num > 0 && den > 0) {
|
||||
// Exit loop and set this ratio
|
||||
if (ok_in) {
|
||||
*ok_in = true;
|
||||
}
|
||||
return num / den;
|
||||
}
|
||||
}
|
||||
|
||||
QMessageBox::warning(
|
||||
parent,
|
||||
QCoreApplication::translate("RatioDialog", "Invalid custom ratio"),
|
||||
QCoreApplication::translate(
|
||||
"RatioDialog",
|
||||
"Failed to parse \"%1\" into an aspect ratio. Please format a "
|
||||
"rational fraction with a ':' or a '/' separator.")
|
||||
.arg(s),
|
||||
QMessageBox::Ok);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,11 +23,11 @@
|
||||
|
||||
#include <QInputDialog>
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
double GetFloatRatioFromUser(QWidget* parent,
|
||||
const QString& title,
|
||||
bool* ok_in);
|
||||
double GetFloatRatioFromUser(QWidget *parent, const QString &title,
|
||||
bool *ok_in);
|
||||
|
||||
}
|
||||
|
||||
|
||||
+10
-13
@@ -4,24 +4,21 @@
|
||||
#include <QMap>
|
||||
#include <QMutex>
|
||||
|
||||
template <typename K, typename V>
|
||||
class ThreadSafeMap
|
||||
{
|
||||
template <typename K, typename V> class ThreadSafeMap {
|
||||
public:
|
||||
ThreadSafeMap() = default;
|
||||
ThreadSafeMap() = default;
|
||||
|
||||
void insert(K key, V value)
|
||||
{
|
||||
mutex_.lock();
|
||||
map_.insert(key, value);
|
||||
mutex_.unlock();
|
||||
}
|
||||
void insert(K key, V value)
|
||||
{
|
||||
mutex_.lock();
|
||||
map_.insert(key, value);
|
||||
mutex_.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
QMutex mutex_;
|
||||
|
||||
QMap<K, V> map_;
|
||||
QMutex mutex_;
|
||||
|
||||
QMap<K, V> map_;
|
||||
};
|
||||
|
||||
#endif // THREADSAFEMAP_H
|
||||
|
||||
+5
-3
@@ -6,10 +6,12 @@
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
inline QString ToHex(quint64 t) {
|
||||
return QStringLiteral("%1").arg(t, 0, 16);
|
||||
inline QString ToHex(quint64 t)
|
||||
{
|
||||
return QStringLiteral("%1").arg(t, 0, 16);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-3
@@ -21,10 +21,9 @@
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
template <typename T>
|
||||
inline T mid(T a, T b)
|
||||
template <typename T> inline T mid(T a, T b)
|
||||
{
|
||||
return (a + b) * 0.5;
|
||||
return (a + b) * 0.5;
|
||||
}
|
||||
|
||||
#endif // UTIL_H
|
||||
|
||||
+13
-12
@@ -23,23 +23,24 @@
|
||||
#include "node/block/block.h"
|
||||
#include "node/factory.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
bool XMLReadNextStartElement(QXmlStreamReader *reader, CancelAtom *cancel_atom)
|
||||
{
|
||||
QXmlStreamReader::TokenType token;
|
||||
QXmlStreamReader::TokenType token;
|
||||
|
||||
while ((token = reader->readNext()) != QXmlStreamReader::Invalid
|
||||
&& token != QXmlStreamReader::EndDocument
|
||||
&& (!cancel_atom || !cancel_atom->IsCancelled())) {
|
||||
if (reader->isEndElement()) {
|
||||
return false;
|
||||
} else if (reader->isStartElement()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
while ((token = reader->readNext()) != QXmlStreamReader::Invalid &&
|
||||
token != QXmlStreamReader::EndDocument &&
|
||||
(!cancel_atom || !cancel_atom->IsCancelled())) {
|
||||
if (reader->isEndElement()) {
|
||||
return false;
|
||||
} else if (reader->isStartElement()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
#include "render/cancelatom.h"
|
||||
#include "undo/undocommand.h"
|
||||
|
||||
namespace olive {
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class Block;
|
||||
class Node;
|
||||
@@ -35,7 +36,7 @@ class NodeInput;
|
||||
class NodeGroup;
|
||||
|
||||
#define XMLAttributeLoop(reader, item) \
|
||||
foreach (const QXmlStreamAttribute& item, reader->attributes())
|
||||
foreach (const QXmlStreamAttribute &item, reader->attributes())
|
||||
|
||||
/**
|
||||
* @brief Workaround for QXmlStreamReader::readNextStartElement not detecting the end of a document
|
||||
@@ -46,7 +47,8 @@ class NodeGroup;
|
||||
*
|
||||
* See also: https://stackoverflow.com/questions/46346450/qt-qxmlstreamreader-always-returns-premature-end-of-document-error
|
||||
*/
|
||||
bool XMLReadNextStartElement(QXmlStreamReader* reader, CancelAtom *cancel_atom = nullptr);
|
||||
bool XMLReadNextStartElement(QXmlStreamReader *reader,
|
||||
CancelAtom *cancel_atom = nullptr);
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user