diff --git a/app/node/color/colormanager/colormanager.cpp b/app/node/color/colormanager/colormanager.cpp index 39472d693..956a8721a 100644 --- a/app/node/color/colormanager/colormanager.cpp +++ b/app/node/color/colormanager/colormanager.cpp @@ -77,7 +77,7 @@ OCIO::ConstConfigRcPtr ColorManager::GetDefaultConfig() void ColorManager::SetUpDefaultConfig() { - if (!qgetenv("OCIO").isEmpty()) { + if (!qEnvironmentVariableIsEmpty("OCIO")) { // Attempt to set config from "OCIO" environment variable try { OCIO_SET_C_LOCALE_FOR_SCOPE; @@ -251,19 +251,6 @@ void ColorManager::GetDefaultLumaCoefs(double *rgb) const config_->getDefaultLumaCoefs(rgb); } -Color ColorManager::GetDefaultLumaCoefs() const -{ - Color c; - - // Just a default value, shouldn't be significant - c.set_alpha(1.0f); - - // The float data in Color lines up with the "rgb" param of this function - GetDefaultLumaCoefs(c.data()); - - return c; -} - void ColorManager::Retranslate() { SetInputName(kConfigFilenameIn, tr("Configuration")); diff --git a/app/node/color/colormanager/colormanager.h b/app/node/color/colormanager/colormanager.h index 3113dd6e5..2dccc7270 100644 --- a/app/node/color/colormanager/colormanager.h +++ b/app/node/color/colormanager/colormanager.h @@ -100,7 +100,6 @@ public: static QStringList ListAvailableColorspaces(OCIO::ConstConfigRcPtr config); void GetDefaultLumaCoefs(double *rgb) const; - Color GetDefaultLumaCoefs() const; class SetLocale { diff --git a/app/node/factory.cpp b/app/node/factory.cpp index f2f5d7d54..f4368d898 100644 --- a/app/node/factory.cpp +++ b/app/node/factory.cpp @@ -35,6 +35,7 @@ #include "generator/polygon/polygon.h" #include "generator/shape/shapenode.h" #include "generator/solid/solid.h" +#include "generator/text/text.h" #include "generator/text/textlegacy.h" #include "filter/blur/blur.h" #include "filter/mosaic/mosaicfilternode.h" @@ -214,8 +215,10 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id) return new MergeNode(); case kStrokeFilter: return new StrokeFilterNode(); - case kTextGenerator: + case kTextGeneratorLegacy: return new TextGeneratorLegacy(); + case kTextGenerator: + return new TextGenerator(); case kCrossDissolveTransition: return new CrossDissolveTransition(); case kDipToColorTransition: diff --git a/app/node/factory.h b/app/node/factory.h index a53a3d6ff..1d8cd72f2 100644 --- a/app/node/factory.h +++ b/app/node/factory.h @@ -48,6 +48,7 @@ public: kSolidGenerator, kMerge, kStrokeFilter, + kTextGeneratorLegacy, kTextGenerator, kCrossDissolveTransition, kDipToColorTransition, diff --git a/app/node/generator/text/CMakeLists.txt b/app/node/generator/text/CMakeLists.txt index c98be672e..889177361 100644 --- a/app/node/generator/text/CMakeLists.txt +++ b/app/node/generator/text/CMakeLists.txt @@ -16,6 +16,8 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} + node/generator/text/text.cpp + node/generator/text/text.h node/generator/text/textlegacy.cpp node/generator/text/textlegacy.h PARENT_SCOPE diff --git a/app/node/generator/text/text.cpp b/app/node/generator/text/text.cpp new file mode 100644 index 000000000..bbd1e4f6b --- /dev/null +++ b/app/node/generator/text/text.cpp @@ -0,0 +1,199 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2021 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "text.h" + +#include +#include +#include + +#if defined(Q_PROCESSOR_X86) +#include +#elif defined(Q_PROCESSOR_ARM) +#include +#endif + +#include "common/functiontimer.h" + +namespace olive { + +#define super ShapeNodeBase + +enum TextVerticalAlign { + kVerticalAlignTop, + kVerticalAlignCenter, + kVerticalAlignBottom, +}; + +const QString TextGenerator::kTextInput = QStringLiteral("text_in"); +const QString TextGenerator::kHtmlInput = QStringLiteral("html_in"); +const QString TextGenerator::kVAlignInput = QStringLiteral("valign_in"); +const QString TextGenerator::kFontInput = QStringLiteral("font_in"); +const QString TextGenerator::kFontSizeInput = QStringLiteral("font_size_in"); + +TextGenerator::TextGenerator() +{ + AddInput(kTextInput, NodeValue::kText, tr("Sample Text")); + + AddInput(kHtmlInput, NodeValue::kBoolean, false); + + AddInput(kVAlignInput, NodeValue::kCombo, kVerticalAlignTop); + + AddInput(kFontInput, NodeValue::kFont); + + AddInput(kFontSizeInput, NodeValue::kFloat, 72.0f); + + SetStandardValue(kColorInput, QVariant::fromValue(Color(1.0f, 1.0f, 1.0))); + SetStandardValue(kSizeInput, QVector2D(400, 300)); +} + +QString TextGenerator::Name() const +{ + return tr("Text"); +} + +QString TextGenerator::id() const +{ + return QStringLiteral("org.olivevideoeditor.Olive.text2"); +} + +QVector TextGenerator::Category() const +{ + return {kCategoryGenerator}; +} + +QString TextGenerator::Description() const +{ + return tr("Generate rich text."); +} + +void TextGenerator::Retranslate() +{ + super::Retranslate(); + + SetInputName(kTextInput, tr("Text")); + SetInputName(kHtmlInput, tr("Enable HTML")); + SetInputName(kFontInput, tr("Font")); + SetInputName(kFontSizeInput, tr("Font Size")); + SetInputName(kVAlignInput, tr("Vertical Align")); + SetComboBoxStrings(kVAlignInput, {tr("Top"), tr("Center"), tr("Bottom")}); +} + +void TextGenerator::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeValueTable *table) const +{ + GenerateJob job; + job.InsertValue(value); + job.SetAlphaChannelRequired(GenerateJob::kAlphaForceOn); + job.SetRequestedFormat(VideoParams::kFormatFloat32); + + if (!job.GetValue(kTextInput).data().toString().isEmpty()) { + table->Push(NodeValue::kGenerateJob, QVariant::fromValue(job), this); + } +} + +void TextGenerator::GenerateFrame(FramePtr frame, const GenerateJob& job) const +{ + // This could probably be more optimized, but for now we use Qt to draw to a QImage. + // QImages only support integer pixels and we use float pixels, so what we do here is draw onto + // a single-channel QImage (alpha only) and then transplant that alpha channel to our float buffer + // with correct float RGB. + QImage img(frame->width(), frame->height(), QImage::Format_Grayscale8); + img.fill(Qt::transparent); + + QTextDocument text_doc; + + // Set default font + QFont default_font; + default_font.setFamily(job.GetValue(kFontInput).data().toString()); + default_font.setPointSizeF(job.GetValue(kFontSizeInput).data().toFloat()); + text_doc.setDefaultFont(default_font); + + QString html = job.GetValue(kTextInput).data().toString(); + if (job.GetValue(kHtmlInput).data().toBool()) { + html.replace('\n', QStringLiteral("
")); + text_doc.setHtml(html); + } else { + text_doc.setPlainText(html); + } + + QVector2D size = job.GetValue(kSizeInput).data().value(); + text_doc.setTextWidth(size.x()); + + // Draw rich text onto image + QPainter p(&img); + p.scale(1.0 / frame->video_params().divider(), 1.0 / frame->video_params().divider()); + + + QVector2D pos = job.GetValue(kPositionInput).data().value(); + p.translate(pos.x() - size.x()/2, pos.y() - size.y()/2); + p.translate(frame->video_params().width()/2, frame->video_params().height()/2); + p.setClipRect(0, 0, size.x(), size.y()); + + TextVerticalAlign valign = static_cast(job.GetValue(kVAlignInput).data().toInt()); + int doc_height = text_doc.size().height(); + + switch (valign) { + case kVerticalAlignTop: + // Do nothing + break; + case kVerticalAlignCenter: + // Center align + p.translate(0, size.y() / 2 - doc_height / 2); + break; + case kVerticalAlignBottom: + p.translate(0, size.y() - doc_height); + break; + } + + QAbstractTextDocumentLayout::PaintContext ctx; + ctx.palette.setColor(QPalette::Text, Qt::white); + + text_doc.documentLayout()->draw(&p, ctx); + + // Transplant alpha channel to frame + Color rgba = job.GetValue(kColorInput).data().value(); +#if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM) + __m128 sse_color = _mm_load_ps(rgba.data()); +#endif + + float *frame_dst = reinterpret_cast(frame->data()); + for (int y=0; yheight(); y++) { + uchar *src_y = img.bits() + img.bytesPerLine() * y; + float *dst_y = frame_dst + y*frame->linesize_pixels()*VideoParams::kRGBAChannelCount; + + for (int x=0; xwidth(); x++) { + float alpha = float(src_y[x]) / 255.0f; + float *dst = dst_y + x*VideoParams::kRGBAChannelCount; + +#if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM) + __m128 sse_alpha = _mm_load1_ps(&alpha); + __m128 sse_res = _mm_mul_ps(sse_color, sse_alpha); + + _mm_store_ps(dst, sse_res); +#else + for (int i=0; i. + +***/ + +#ifndef TEXTGENERATOR_H +#define TEXTGENERATOR_H + +#include "node/generator/shape/shapenodebase.h" + +namespace olive { + +class TextGenerator : public ShapeNodeBase +{ + Q_OBJECT +public: + TextGenerator(); + + NODE_DEFAULT_DESTRUCTOR(TextGenerator) + NODE_COPY_FUNCTION(TextGenerator) + + virtual QString Name() const override; + virtual QString id() const override; + virtual QVector Category() const override; + virtual QString Description() const override; + + virtual void Retranslate() override; + + virtual void Value(const NodeValueRow& value, const NodeGlobals &globals, NodeValueTable *table) const override; + + virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const override; + + static const QString kTextInput; + static const QString kHtmlInput; + static const QString kVAlignInput; + static const QString kFontInput; + static const QString kFontSizeInput; + +}; + +} + +#endif // TEXTGENERATOR_H diff --git a/app/node/generator/text/textlegacy.h b/app/node/generator/text/textlegacy.h index a3d85aaca..296d838e3 100644 --- a/app/node/generator/text/textlegacy.h +++ b/app/node/generator/text/textlegacy.h @@ -18,8 +18,8 @@ ***/ -#ifndef TEXTGENERATOR_H -#define TEXTGENERATOR_H +#ifndef TEXTGENERATORLEGACY_H +#define TEXTGENERATORLEGACY_H #include "node/node.h" @@ -56,4 +56,4 @@ public: } -#endif // TEXTGENERATOR_H +#endif // TEXTGENERATORLEGACY_H diff --git a/app/render/color.cpp b/app/render/color.cpp index 6584ccfef..bbf9a9601 100644 --- a/app/render/color.cpp +++ b/app/render/color.cpp @@ -27,12 +27,12 @@ namespace olive { -Color Color::fromHsv(const double &h, const double &s, const double &v) +Color Color::fromHsv(const float &h, const float &s, const float &v) { - double C = s * v; - double X = C * (1.0 - abs(fmod(h / 60.0, 2.0) - 1.0)); - double m = v - C; - double Rs, Gs, Bs; + float C = s * v; + float X = C * (1.0 - abs(fmod(h / 60.0, 2.0) - 1.0)); + float m = v - C; + float Rs, Gs, Bs; if(h >= 0.0 && h < 60.0) { Rs = C; @@ -81,11 +81,11 @@ Color::Color(const QColor &c) set_alpha(c.alphaF()); } -void Color::toHsv(double *hue, double *sat, double *val) const +void Color::toHsv(float *hue, float *sat, float *val) const { - double fCMax = qMax(qMax(red(), green()), blue()); - double fCMin = qMin(qMin(red(), green()), blue()); - double fDelta = fCMax - fCMin; + float fCMax = qMax(qMax(red(), green()), blue()); + float fCMin = qMin(qMin(red(), green()), blue()); + float fDelta = fCMax - fCMin; if(fDelta > 0) { if(fCMax == red()) { @@ -114,31 +114,31 @@ void Color::toHsv(double *hue, double *sat, double *val) const } } -double Color::hsv_hue() const +float Color::hsv_hue() const { - double h, s, v; + float h, s, v; toHsv(&h, &s, &v); return h; } -double Color::hsv_saturation() const +float Color::hsv_saturation() const { - double h, s, v; + float h, s, v; toHsv(&h, &s, &v); return s; } -double Color::value() const +float Color::value() const { - double h, s, v; + float h, s, v; toHsv(&h, &s, &v); return v; } -void Color::toHsl(double *hue, double *sat, double *lightness) const +void Color::toHsl(float *hue, float *sat, float *lightness) const { - double fCMin = qMin(red(), qMin(green(), blue())); - double fCMax = qMax(red(), qMax(green(), blue())); + float fCMin = qMin(red(), qMin(green(), blue())); + float fCMax = qMax(red(), qMax(green(), blue())); *lightness = 0.5 * (fCMin + fCMax); @@ -176,30 +176,30 @@ void Color::toHsl(double *hue, double *sat, double *lightness) const } } -double Color::hsl_hue() const +float Color::hsl_hue() const { - double h, s, l; + float h, s, l; toHsl(&h, &s, &l); return h; } -double Color::hsl_saturation() const +float Color::hsl_saturation() const { - double h, s, l; + float h, s, l; toHsl(&h, &s, &l); return s; } -double Color::lightness() const +float Color::lightness() const { - double h, s, l; + float h, s, l; toHsl(&h, &s, &l); return l; } void Color::toData(char *data, const VideoParams::Format &format, int ch_layout) const { - OIIO::convert_pixel_values(OIIO::TypeDesc::DOUBLE, + OIIO::convert_pixel_values(OIIO::TypeDesc::FLOAT, data_, OIIOUtils::GetOIIOBaseTypeFromFormat(format), data, @@ -212,7 +212,7 @@ Color Color::fromData(const char *data, const VideoParams::Format &format, int c OIIO::convert_pixel_values(OIIOUtils::GetOIIOBaseTypeFromFormat(format), data, - OIIO::TypeDesc::DOUBLE, + OIIO::TypeDesc::FLOAT, c.data_, ch_layout); @@ -224,15 +224,15 @@ QColor Color::toQColor() const QColor c; // QColor only supports values from 0.0 to 1.0 and are only used for UI representations - c.setRedF(clamp(red(), 0.0, 1.0)); - c.setGreenF(clamp(green(), 0.0, 1.0)); - c.setBlueF(clamp(blue(), 0.0, 1.0)); - c.setAlphaF(clamp(alpha(), 0.0, 1.0)); + c.setRedF(clamp(red(), 0.0f, 1.0f)); + c.setGreenF(clamp(green(), 0.0f, 1.0f)); + c.setBlueF(clamp(blue(), 0.0f, 1.0f)); + c.setAlphaF(clamp(alpha(), 0.0f, 1.0f)); return c; } -double Color::GetRoughLuminance() const +float Color::GetRoughLuminance() const { return (2*red()+blue()+3*green())/6.0; } @@ -255,7 +255,7 @@ const Color &Color::operator-=(const Color &rhs) return *this; } -const Color &Color::operator*=(const double &rhs) +const Color &Color::operator*=(const float &rhs) { for (int i=0;iset_video_params(frame_params); frame->allocate(); diff --git a/app/widget/colorwheel/colorgradientwidget.cpp b/app/widget/colorwheel/colorgradientwidget.cpp index 8fbd9e90c..698fc9bed 100644 --- a/app/widget/colorwheel/colorgradientwidget.cpp +++ b/app/widget/colorwheel/colorgradientwidget.cpp @@ -76,7 +76,7 @@ void ColorGradientWidget::paintEvent(QPaintEvent *e) p.setPen(QPen(GetUISelectorColor(), qMax(1, selector_radius / 2))); p.setBrush(Qt::NoBrush); - double clamped_val = clamp(val_, 0.0, 1.0); + float clamped_val = clamp(val_, 0.0f, 1.0f); if (orientation_ == Qt::Horizontal) { p.drawRect(qRound(width() * (1.0 - clamped_val)) - selector_radius, 0, selector_radius * 2, height() - 1); @@ -87,7 +87,7 @@ void ColorGradientWidget::paintEvent(QPaintEvent *e) void ColorGradientWidget::SelectedColorChangedEvent(const Color &c, bool external) { - double hue, sat; + float hue, sat; c.toHsv(&hue, &sat, &val_); diff --git a/app/widget/colorwheel/colorgradientwidget.h b/app/widget/colorwheel/colorgradientwidget.h index 4fa362ee3..97723a7ab 100644 --- a/app/widget/colorwheel/colorgradientwidget.h +++ b/app/widget/colorwheel/colorgradientwidget.h @@ -50,7 +50,7 @@ private: Color end_; - double val_; + float val_; }; diff --git a/app/widget/colorwheel/colorwheelwidget.cpp b/app/widget/colorwheel/colorwheelwidget.cpp index 905f31d95..5a2caaf63 100644 --- a/app/widget/colorwheel/colorwheelwidget.cpp +++ b/app/widget/colorwheel/colorwheelwidget.cpp @@ -121,7 +121,7 @@ void ColorWheelWidget::SelectedColorChangedEvent(const Color &c, bool external) { if (external) { force_redraw_ = true; - val_ = clamp(c.value(), 0.0, 1.0); + val_ = clamp(c.value(), 0.0f, 1.0f); } } @@ -159,7 +159,7 @@ Color ColorWheelWidget::GetColorFromTriangle(const ColorWheelWidget::Triangle &t QPoint ColorWheelWidget::GetCoordsFromColor(const Color &c) const { - double hue, sat, val; + float hue, sat, val; c.toHsv(&hue, &sat, &val); qreal hypotenuse = sat * GetRadius(); diff --git a/app/widget/timelinewidget/tool/add.cpp b/app/widget/timelinewidget/tool/add.cpp index 42ba41801..07343ccd3 100644 --- a/app/widget/timelinewidget/tool/add.cpp +++ b/app/widget/timelinewidget/tool/add.cpp @@ -24,7 +24,7 @@ #include "node/factory.h" #include "node/generator/shape/shapenode.h" #include "node/generator/solid/solid.h" -#include "node/generator/text/textlegacy.h" +#include "node/generator/text/text.h" #include "widget/timelinewidget/timelinewidget.h" #include "widget/timelinewidget/undo/timelineundopointer.h" @@ -138,7 +138,7 @@ void AddTool::MouseRelease(TimelineViewMouseEvent *event) break; case olive::Tool::kAddableTitle: { - node_to_add = new TextGeneratorLegacy(); + node_to_add = new TextGenerator(); break; } case olive::Tool::kAddableBars: