From 0e8810109ab2cef111b1f81ee186ec193be9a0cf Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 2 Sep 2018 16:57:50 +1000 Subject: [PATCH] ported ALL effects to opengl --- effects/effect.cpp | 87 ++++++++- effects/effect.h | 36 +++- effects/video/boxblureffect.cpp | 4 +- effects/video/boxblureffect.h | 3 +- effects/video/chromakeyeffect.cpp | 4 +- effects/video/chromakeyeffect.h | 2 +- effects/video/cropeffect.cpp | 4 +- effects/video/cropeffect.h | 2 +- effects/video/flipeffect.cpp | 4 +- effects/video/flipeffect.h | 2 +- effects/video/gaussianblureffect.cpp | 4 +- effects/video/gaussianblureffect.h | 2 +- effects/video/inverteffect.cpp | 4 +- effects/video/inverteffect.h | 2 +- effects/video/shakeeffect.cpp | 4 +- effects/video/shakeeffect.h | 2 +- effects/video/solideffect.cpp | 112 +++++++++++- effects/video/solideffect.h | 8 +- effects/video/texteffect.cpp | 256 +++++++++++++++++++-------- effects/video/texteffect.h | 6 +- effects/video/transformeffect.cpp | 4 +- effects/video/transformeffect.h | 2 +- io/exportthread.cpp | 4 +- mainwindow.cpp | 2 +- panels/project.cpp | 2 +- playback/cacher.cpp | 9 - playback/playback.cpp | 17 +- project/clip.h | 4 +- ui/viewerwidget.cpp | 57 +++--- ui/viewerwidget.h | 4 +- 30 files changed, 485 insertions(+), 168 deletions(-) diff --git a/effects/effect.cpp b/effects/effect.cpp index 99f9a1853..501375677 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -95,8 +95,9 @@ Effect::Effect(Clip* c, int t, int i) : parent_clip(c), type(t), id(i), - enable_image(false), - enable_opengl(false), + enable_shader(false), + enable_coords(false), + enable_superimpose(false), iterations(1), isOpen(false), glslProgram(NULL), @@ -355,10 +356,88 @@ Effect* Effect::copy(Clip* c) { return copy; } -void Effect::process_image(double, uint8_t*, int, int) {} -void Effect::process_gl(double, GLTextureCoords&) {} +void Effect::process_shader(double) {} +void Effect::process_coords(double, GLTextureCoords&) {} +const GLuint Effect::process_superimpose(double) {return 0;} void Effect::process_audio(double, double, quint8*, int, int) {} +SuperimposeEffect::SuperimposeEffect(Clip* c, int t, int i) : Effect(c, t, i), texture(NULL) { + enable_superimpose = true; +} + +void SuperimposeEffect::open() { + Effect::open(); + texture = new QOpenGLTexture(QOpenGLTexture::Target2D); +} + +void SuperimposeEffect::close() { + Effect::close(); + deleteTexture(); +} + +const GLuint SuperimposeEffect::process_superimpose(double timecode) { + bool recreate_texture = false; + int width = parent_clip->getWidth(); + int height = parent_clip->getHeight(); + + if (width != img.width() || height != img.height()) { + img = QImage(width, height, QImage::Format_RGBA8888); + recreate_texture = true; + } + + if (valueHasChanged(timecode) || recreate_texture) { + redraw(timecode); + } + + if (texture != NULL) { + if (recreate_texture || texture->width() != img.width() || texture->height() != img.height()) { + deleteTexture(); + texture = new QOpenGLTexture(QOpenGLTexture::Target2D); + texture->setData(img); + } else { + texture->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, img.constBits()); + } + return texture->textureId(); + } + return 0; + +} + +void SuperimposeEffect::redraw(double) {} + +void SuperimposeEffect::deleteTexture() { + delete texture; + texture = NULL; +} + +bool SuperimposeEffect::valueHasChanged(double timecode) { + if (cachedValues.size() == 0) { + for (int i=0;ifieldCount();j++) { + cachedValues.append(crow->field(j)->get_current_data()); + } + } + return true; + } else { + bool changed = false; + int index = 0; + for (int i=0;ifieldCount();j++) { + EffectField* field = crow->field(j); + field->validate_keyframe_data(timecode); + if (cachedValues.at(index) != field->get_current_data()) { + changed = true; + } + cachedValues[index] = field->get_current_data(); + index++; + } + } + return changed; + } +} + /* Effect Row Definitions */ EffectRow::EffectRow(Effect *parent, QGridLayout *uilayout, const QString &n, int row) : diff --git a/effects/effect.h b/effects/effect.h index cf4e6bb8c..9964cead9 100644 --- a/effects/effect.h +++ b/effects/effect.h @@ -5,7 +5,9 @@ #include #include #include +#include #include +#include class QLabel; class QWidget; class CollapsibleWidget; @@ -193,21 +195,23 @@ public: void save(QXmlStreamWriter& stream); // glsl handling - void open(); - void close(); - void startEffect(); - void endEffect(); + virtual void open(); + virtual void close(); + virtual void startEffect(); + virtual void endEffect(); - bool enable_image; - bool enable_opengl; + bool enable_shader; + bool enable_coords; + bool enable_superimpose; int getIterations(); void setIterations(int i); const char* ffmpeg_filter; - virtual void process_image(double timecode, uint8_t* data, int width, int height); - virtual void process_gl(double timecode, GLTextureCoords& coords); + virtual void process_shader(double timecode); + virtual void process_coords(double timecode, GLTextureCoords& coords); + virtual const GLuint process_superimpose(double timecode); virtual void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count); public slots: void field_changed(); @@ -224,4 +228,20 @@ private: bool bound; }; +class SuperimposeEffect : public Effect { +public: + SuperimposeEffect(Clip* c, int t, int i); + virtual void open(); + virtual void close(); + virtual const GLuint process_superimpose(double timecode); + virtual void redraw(double timecode); +protected: + QImage img; + QOpenGLTexture* texture; + void deleteTexture(); + bool valueHasChanged(double timecode); +private: + QVector cachedValues; +}; + #endif // EFFECT_H diff --git a/effects/video/boxblureffect.cpp b/effects/video/boxblureffect.cpp index 0fd82406c..4f8ccbd6c 100644 --- a/effects/video/boxblureffect.cpp +++ b/effects/video/boxblureffect.cpp @@ -3,7 +3,7 @@ #include "project/clip.h" BoxBlurEffect::BoxBlurEffect(Clip *c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_BOXBLUR_EFFECT) { - enable_opengl = true; + enable_shader = true; radius_val = add_row("Radius:")->add_field(EFFECT_FIELD_DOUBLE); // iteration_val = add_row("Iterations:")->add_field(EFFECT_FIELD_DOUBLE); @@ -33,7 +33,7 @@ BoxBlurEffect::BoxBlurEffect(Clip *c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_BOXBL connect(vert_val, SIGNAL(changed()), this, SLOT(field_changed())); } -void BoxBlurEffect::process_gl(double timecode, GLTextureCoords&) { +void BoxBlurEffect::process_shader(double timecode) { glslProgram->setUniformValue("resolution", parent_clip->getWidth(), parent_clip->getHeight()); glslProgram->setUniformValue("radius", (GLfloat) radius_val->get_double_value(timecode)); glslProgram->setUniformValue("horiz_blur", horiz_val->get_bool_value(timecode)); diff --git a/effects/video/boxblureffect.h b/effects/video/boxblureffect.h index 261964918..008018a91 100644 --- a/effects/video/boxblureffect.h +++ b/effects/video/boxblureffect.h @@ -7,8 +7,7 @@ class BoxBlurEffect : public Effect { public: BoxBlurEffect(Clip* c); - - void process_gl(double timecode, GLTextureCoords &coords); + void process_shader(double timecode); private: EffectField* radius_val; EffectField* iteration_val; diff --git a/effects/video/chromakeyeffect.cpp b/effects/video/chromakeyeffect.cpp index 4a67b89fc..029ce7397 100644 --- a/effects/video/chromakeyeffect.cpp +++ b/effects/video/chromakeyeffect.cpp @@ -1,7 +1,7 @@ #include "chromakeyeffect.h" ChromaKeyEffect::ChromaKeyEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_CHROMAKEY_EFFECT) { - enable_opengl = true; + enable_shader = true; color_field = add_row("Color:")->add_field(EFFECT_FIELD_COLOR); tolerance_field = add_row("Tolerance:")->add_field(EFFECT_FIELD_DOUBLE); @@ -19,7 +19,7 @@ ChromaKeyEffect::ChromaKeyEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_C connect(tolerance_field, SIGNAL(changed()), this, SLOT(field_changed())); } -void ChromaKeyEffect::process_gl(double timecode, GLTextureCoords&) { +void ChromaKeyEffect::process_shader(double timecode) { glslProgram->setUniformValue("keyColor", color_field->get_color_value(timecode)); glslProgram->setUniformValue("threshold", (GLfloat) (tolerance_field->get_double_value(timecode)*0.01)); } diff --git a/effects/video/chromakeyeffect.h b/effects/video/chromakeyeffect.h index 6cc619185..a7498cdc1 100644 --- a/effects/video/chromakeyeffect.h +++ b/effects/video/chromakeyeffect.h @@ -6,7 +6,7 @@ class ChromaKeyEffect : public Effect { public: ChromaKeyEffect(Clip* c); - void process_gl(double timecode, GLTextureCoords &coords); + void process_shader(double timecode); private: EffectField* color_field; EffectField* tolerance_field; diff --git a/effects/video/cropeffect.cpp b/effects/video/cropeffect.cpp index 19675cb1c..db4dbf99c 100644 --- a/effects/video/cropeffect.cpp +++ b/effects/video/cropeffect.cpp @@ -1,7 +1,7 @@ #include "cropeffect.h" CropEffect::CropEffect(Clip *c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_CROP_EFFECT) { - enable_opengl = true; + enable_coords = true; left_field = add_row("Left:")->add_field(EFFECT_FIELD_DOUBLE); top_field = add_row("Top:")->add_field(EFFECT_FIELD_DOUBLE); @@ -23,7 +23,7 @@ CropEffect::CropEffect(Clip *c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_CROP_EFFECT connect(bottom_field, SIGNAL(changed()), this, SLOT(field_changed())); } -void CropEffect::process_gl(double timecode, GLTextureCoords &coords) { +void CropEffect::process_coords(double timecode, GLTextureCoords &coords) { // store initial coord data int left = coords.vertexTopLeftX; int top = coords.vertexTopLeftY; diff --git a/effects/video/cropeffect.h b/effects/video/cropeffect.h index 212d8f473..49d0ca2ff 100644 --- a/effects/video/cropeffect.h +++ b/effects/video/cropeffect.h @@ -7,7 +7,7 @@ class CropEffect : public Effect { public: CropEffect(Clip* c); - void process_gl(double timecode, GLTextureCoords &coords); + void process_coords(double timecode, GLTextureCoords &coords); private: EffectField* left_field; EffectField* top_field; diff --git a/effects/video/flipeffect.cpp b/effects/video/flipeffect.cpp index d338a5f76..143405168 100644 --- a/effects/video/flipeffect.cpp +++ b/effects/video/flipeffect.cpp @@ -1,7 +1,7 @@ #include "flipeffect.h" FlipEffect::FlipEffect(Clip *c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_FLIP_EFFECT) { - enable_opengl = true; + enable_coords = true; horizontal_field = add_row("Horizontal:")->add_field(EFFECT_FIELD_BOOL); vertical_field = add_row("Vertical:")->add_field(EFFECT_FIELD_BOOL); @@ -10,7 +10,7 @@ FlipEffect::FlipEffect(Clip *c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_FLIP_EFFECT connect(vertical_field, SIGNAL(changed()), this, SLOT(field_changed())); } -void FlipEffect::process_gl(double timecode, GLTextureCoords &coords) { +void FlipEffect::process_coords(double timecode, GLTextureCoords &coords) { if (horizontal_field->get_bool_value(timecode)) { double tlX = coords.textureTopLeftX; double blX = coords.textureBottomLeftX; diff --git a/effects/video/flipeffect.h b/effects/video/flipeffect.h index 4b20ae675..68ae07350 100644 --- a/effects/video/flipeffect.h +++ b/effects/video/flipeffect.h @@ -7,7 +7,7 @@ class FlipEffect : public Effect { public: FlipEffect(Clip* c); - void process_gl(double timecode, GLTextureCoords &coords); + void process_coords(double timecode, GLTextureCoords &coords); private: EffectField* horizontal_field; EffectField* vertical_field; diff --git a/effects/video/gaussianblureffect.cpp b/effects/video/gaussianblureffect.cpp index 62765e9ab..7bb4dcf9e 100644 --- a/effects/video/gaussianblureffect.cpp +++ b/effects/video/gaussianblureffect.cpp @@ -5,7 +5,7 @@ #include GaussianBlurEffect::GaussianBlurEffect(Clip *c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_GAUSSIANBLUR_EFFECT) { - enable_opengl = true; + enable_shader = true; radius_val = add_row("Radius:")->add_field(EFFECT_FIELD_DOUBLE); sigma_val = add_row("Sigma:")->add_field(EFFECT_FIELD_DOUBLE); @@ -30,7 +30,7 @@ GaussianBlurEffect::GaussianBlurEffect(Clip *c) : Effect(c, EFFECT_TYPE_VIDEO, V connect(vert_val, SIGNAL(changed()), this, SLOT(field_changed())); } -void GaussianBlurEffect::process_gl(double timecode, GLTextureCoords &coords) { +void GaussianBlurEffect::process_shader(double timecode) { glslProgram->setUniformValue("resolution", parent_clip->getWidth(), parent_clip->getHeight()); glslProgram->setUniformValue("radius", (GLfloat) radius_val->get_double_value(timecode)); glslProgram->setUniformValue("sigma", (GLfloat) sigma_val->get_double_value(timecode)); diff --git a/effects/video/gaussianblureffect.h b/effects/video/gaussianblureffect.h index 0be35631c..fdf5fea01 100644 --- a/effects/video/gaussianblureffect.h +++ b/effects/video/gaussianblureffect.h @@ -7,7 +7,7 @@ class GaussianBlurEffect : public Effect { public: GaussianBlurEffect(Clip* c); - void process_gl(double timecode, GLTextureCoords &coords); + void process_shader(double timecode); private: EffectField* radius_val; EffectField* sigma_val; diff --git a/effects/video/inverteffect.cpp b/effects/video/inverteffect.cpp index 8995c1d8c..73947accb 100644 --- a/effects/video/inverteffect.cpp +++ b/effects/video/inverteffect.cpp @@ -10,7 +10,7 @@ #include InvertEffect::InvertEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_INVERT_EFFECT) { - enable_opengl = true; + enable_shader = true; EffectRow* amount_row = add_row("Amount:"); amount_val = amount_row->add_field(EFFECT_FIELD_DOUBLE); @@ -26,6 +26,6 @@ InvertEffect::InvertEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_INVERT_ fragPath = ":/shaders/inverteffect.frag"; } -void InvertEffect::process_gl(double timecode, GLTextureCoords&) { +void InvertEffect::process_shader(double timecode) { glslProgram->setUniformValue("amount_val", (GLfloat) (amount_val->get_double_value(timecode)*0.01)); } diff --git a/effects/video/inverteffect.h b/effects/video/inverteffect.h index 1d3aa3ff7..80c10a743 100644 --- a/effects/video/inverteffect.h +++ b/effects/video/inverteffect.h @@ -7,7 +7,7 @@ class InvertEffect : public Effect { Q_OBJECT public: InvertEffect(Clip* c); - void process_gl(double timecode, GLTextureCoords& coords); + void process_shader(double timecode); private: EffectField* amount_val; }; diff --git a/effects/video/shakeeffect.cpp b/effects/video/shakeeffect.cpp index 85ac7c418..ac1c13759 100644 --- a/effects/video/shakeeffect.cpp +++ b/effects/video/shakeeffect.cpp @@ -13,7 +13,7 @@ #include "panels/timeline.h" ShakeEffect::ShakeEffect(Clip *c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_SHAKE_EFFECT), inside(false) { - enable_opengl = true; + enable_coords = true; EffectRow* intensity_row = add_row("Intensity:"); intensity_val = intensity_row->add_field(EFFECT_FIELD_DOUBLE); @@ -49,7 +49,7 @@ void ShakeEffect::refresh() { } } -void ShakeEffect::process_gl(double timecode, GLTextureCoords&) { +void ShakeEffect::process_coords(double timecode, GLTextureCoords&) { if (shake_progress > shake_limit) { double ival = intensity_val->get_double_value(timecode); if ((int)ival > 0) { diff --git a/effects/video/shakeeffect.h b/effects/video/shakeeffect.h index cad853ac5..dd9f47e5e 100644 --- a/effects/video/shakeeffect.h +++ b/effects/video/shakeeffect.h @@ -7,7 +7,7 @@ class ShakeEffect : public Effect { Q_OBJECT public: ShakeEffect(Clip* c); - void process_gl(double timecode, GLTextureCoords& coords); + void process_coords(double timecode, GLTextureCoords& coords); EffectField* intensity_val; EffectField* rotation_val; diff --git a/effects/video/solideffect.cpp b/effects/video/solideffect.cpp index 96fe19e43..98380aa23 100644 --- a/effects/video/solideffect.cpp +++ b/effects/video/solideffect.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "project/clip.h" #include "project/sequence.h" @@ -15,9 +16,7 @@ #define SMPTE_STRIP_COUNT 3 #define SMPTE_LOWER_BARS 4 -SolidEffect::SolidEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_SOLID_EFFECT) { - enable_opengl = true; - +SolidEffect::SolidEffect(Clip* c) : SuperimposeEffect(c, EFFECT_TYPE_VIDEO, VIDEO_SOLID_EFFECT) { solid_type = add_row("Type:")->add_field(EFFECT_FIELD_COMBO); solid_type->add_combo_item("Solid Color", SOLID_TYPE_COLOR); solid_type->add_combo_item("SMPTE Bars", SOLID_TYPE_BARS); @@ -38,14 +37,113 @@ SolidEffect::SolidEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_SOLID_EFF fragPath = ":/shaders/solideffect.frag"; } -void SolidEffect::process_gl(double timecode, GLTextureCoords&) { - solid_color_field->set_enabled(solid_type->get_combo_data(timecode) == SOLID_TYPE_COLOR); +/*void SolidEffect::process_gl(double timecode, GLTextureCoords&) { + glslProgram->setUniformValue("solidColor", solid_color_field->get_color_value(timecode)); glslProgram->setUniformValue("amount_val", (GLfloat) (opacity_field->get_double_value(timecode)*0.01)); +}*/ + +void SolidEffect::redraw(double timecode) { + int w = img.width(); + int h = img.height(); + int alpha = (opacity_field->get_double_value(timecode)*2.55); + switch (solid_type->get_combo_data(timecode).toInt()) { + case SOLID_TYPE_COLOR: + { + QColor solidColor = solid_color_field->get_color_value(timecode); + solidColor.setAlpha(alpha); + img.fill(solidColor); + } + break; + case SOLID_TYPE_BARS: + { + // draw smpte bars + QPainter p(&img); + img.fill(Qt::transparent); + int bar_width = qCeil((double) w / 7.0); + int first_bar_height = qCeil((double) h / 3.0 * 2.0); + int second_bar_height = qCeil((double) h / 12.5); + int third_bar_y = first_bar_height + second_bar_height; + int third_bar_height = h - third_bar_y; + int third_bar_width = 0; + int bar_x, strip_width; + QColor first_color, second_color, third_color; + for (int i=0;i + +class SolidEffect : public SuperimposeEffect { Q_OBJECT public: SolidEffect(Clip* c); EffectField* solid_type; EffectField* solid_color_field; EffectField* opacity_field; - void process_gl(double timecode, GLTextureCoords& coords); - void process_image(long p, uint8_t* data, int width, int height); + void redraw(double timecode); }; #endif // SOLIDEFFECT_H diff --git a/effects/video/texteffect.cpp b/effects/video/texteffect.cpp index ed0d8ffe8..d67f6cb36 100644 --- a/effects/video/texteffect.cpp +++ b/effects/video/texteffect.cpp @@ -21,9 +21,9 @@ #include "ui/fontcombobox.h" TextEffect::TextEffect(Clip *c) : - Effect(c, EFFECT_TYPE_VIDEO, VIDEO_TEXT_EFFECT) + SuperimposeEffect(c, EFFECT_TYPE_VIDEO, VIDEO_TEXT_EFFECT) { - enable_image = true; + enable_superimpose = true; text_val = add_row("Text:")->add_field(EFFECT_FIELD_STRING, 2); @@ -98,6 +98,186 @@ TextEffect::TextEffect(Clip *c) : connect(outline_bool, SIGNAL(toggled(bool)), this, SLOT(outline_enable(bool))); } +void blurred2(QImage& result, const QRect& rect, int radius, bool alphaOnly = false) { + int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 }; + int alpha = (radius < 1) ? 16 : (radius > 17) ? 1 : tab[radius-1]; + + int r1 = rect.top(); + int r2 = rect.bottom(); + int c1 = rect.left(); + int c2 = rect.right(); + + int bpl = result.bytesPerLine(); + int rgba[4]; + unsigned char* p; + + int i1 = 0; + int i2 = 3; + + if (alphaOnly) + i1 = i2 = (QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3); + + for (int col = c1; col <= c2; col++) { + p = result.scanLine(r1) + col * 4; + for (int i = i1; i <= i2; i++) + rgba[i] = p[i] << 4; + + p += bpl; + for (int j = r1; j < r2; j++, p += bpl) + for (int i = i1; i <= i2; i++) + p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; + } + + for (int row = r1; row <= r2; row++) { + p = result.scanLine(row) + c1 * 4; + for (int i = i1; i <= i2; i++) + rgba[i] = p[i] << 4; + + p += 4; + for (int j = c1; j < c2; j++, p += 4) + for (int i = i1; i <= i2; i++) + p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; + } + + for (int col = c1; col <= c2; col++) { + p = result.scanLine(r2) + col * 4; + for (int i = i1; i <= i2; i++) + rgba[i] = p[i] << 4; + + p -= bpl; + for (int j = r1; j < r2; j++, p -= bpl) + for (int i = i1; i <= i2; i++) + p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; + } + + for (int row = r1; row <= r2; row++) { + p = result.scanLine(row) + c2 * 4; + for (int i = i1; i <= i2; i++) + rgba[i] = p[i] << 4; + + p -= 4; + for (int j = c1; j < c2; j++, p -= 4) + for (int i = i1; i <= i2; i++) + p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; + } +} + +void TextEffect::redraw(double timecode) { + img.fill(Qt::transparent); + + QPainter p(&img); + p.setRenderHint(QPainter::Antialiasing); + int width = img.width(); + int height = img.height(); + + // set font + font.setStyleHint(QFont::Helvetica, QFont::PreferAntialias); + font.setFamily(set_font_combobox->get_font_name(timecode)); + font.setPointSize(size_val->get_double_value(timecode)); + p.setFont(font); + QFontMetrics fm(font); + + QStringList lines = text_val->get_string_value(timecode).split('\n'); + + // word wrap function + if (word_wrap_field->get_bool_value(timecode)) { + for (int i=0;i width) { + int last_space_index = 0; + for (int j=0;j width) { + break; + } else { + last_space_index = j; + } + } + } + if (last_space_index > 0) { + lines.insert(i+1, s.mid(last_space_index + 1)); + lines[i] = s.left(last_space_index); + } + } + } + } + + QPainterPath path; + + int text_height = fm.height()*lines.size(); + + for (int i=0;iget_combo_data(timecode).toInt()) { + case Qt::AlignLeft: text_x = 0; break; + case Qt::AlignHCenter: text_x = (width/2) - (fm.width(lines.at(i))/2); break; + case Qt::AlignRight: text_x = width - fm.width(lines.at(i)); break; + case Qt::AlignJustify: + // add spaces until the string is too big + text_x = 0; + while (fm.width(lines.at(i) < width)) { + bool space = false; + QString spaced(lines.at(i)); + for (int i=0;i width || !space) { + break; + } else { + lines[i] = spaced; + } + } + break; + } + + switch (valign_field->get_combo_data(timecode).toInt()) { + case Qt::AlignTop: text_y = (fm.height()*i)+fm.ascent(); break; + case Qt::AlignVCenter: text_y = ((height/2) - (text_height/2) - fm.descent()) + (fm.height()*(i+1)); break; + case Qt::AlignBottom: text_y = (height - text_height - fm.descent()) + (fm.height()*(i+1)); break; + } + + path.addText(text_x, text_y, font, lines.at(i)); + } + + p.setPen(Qt::NoPen); + + // draw shadow + if (shadow_bool->get_bool_value(timecode)) { + int shadow_offset = shadow_distance->get_double_value(timecode); + + QPainterPath shadow_path(path); + shadow_path.translate(shadow_offset, shadow_offset); + + QColor col = shadow_color->get_color_value(timecode); + col.setAlphaF(shadow_opacity->get_double_value(timecode)*0.01); + p.setBrush(col); + p.drawPath(shadow_path); + + int blurSoftness = shadow_softness->get_double_value(timecode); + if (blurSoftness > 0) blurred2(img, img.rect(), blurSoftness, false); + } + + // draw outline + int outline_width_val = outline_width->get_double_value(timecode); + if (outline_bool->get_bool_value(timecode) && outline_width_val > 0) { + QPen outline(outline_color->get_color_value(timecode)); + outline.setWidth(outline_width_val); + p.setPen(outline); + } + + // draw "master" text + p.setBrush(set_color_button->get_color_value(timecode)); + p.drawPath(path); +} + void TextEffect::shadow_enable(bool e) { shadow_color->set_enabled(e); shadow_distance->set_enabled(e); @@ -110,7 +290,7 @@ void TextEffect::outline_enable(bool e) { outline_width->set_enabled(e); } -QImage blurred(const QImage& image, const QRect& rect, int radius, bool alphaOnly = false) +/*QImage blurred(const QImage& image, const QRect& rect, int radius, bool alphaOnly = false) { int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 }; int alpha = (radius < 1) ? 16 : (radius > 17) ? 1 : tab[radius-1]; @@ -176,73 +356,9 @@ QImage blurred(const QImage& image, const QRect& rect, int radius, bool alphaOnl } return result; -} +}*/ -void blurred2(QImage& result, const QRect& rect, int radius, bool alphaOnly = false) { - int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 }; - int alpha = (radius < 1) ? 16 : (radius > 17) ? 1 : tab[radius-1]; - - int r1 = rect.top(); - int r2 = rect.bottom(); - int c1 = rect.left(); - int c2 = rect.right(); - - int bpl = result.bytesPerLine(); - int rgba[4]; - unsigned char* p; - - int i1 = 0; - int i2 = 3; - - if (alphaOnly) - i1 = i2 = (QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3); - - for (int col = c1; col <= c2; col++) { - p = result.scanLine(r1) + col * 4; - for (int i = i1; i <= i2; i++) - rgba[i] = p[i] << 4; - - p += bpl; - for (int j = r1; j < r2; j++, p += bpl) - for (int i = i1; i <= i2; i++) - p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; - } - - for (int row = r1; row <= r2; row++) { - p = result.scanLine(row) + c1 * 4; - for (int i = i1; i <= i2; i++) - rgba[i] = p[i] << 4; - - p += 4; - for (int j = c1; j < c2; j++, p += 4) - for (int i = i1; i <= i2; i++) - p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; - } - - for (int col = c1; col <= c2; col++) { - p = result.scanLine(r2) + col * 4; - for (int i = i1; i <= i2; i++) - rgba[i] = p[i] << 4; - - p -= bpl; - for (int j = r1; j < r2; j++, p -= bpl) - for (int i = i1; i <= i2; i++) - p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; - } - - for (int row = r1; row <= r2; row++) { - p = result.scanLine(row) + c2 * 4; - for (int i = i1; i <= i2; i++) - rgba[i] = p[i] << 4; - - p -= 4; - for (int j = c1; j < c2; j++, p -= 4) - for (int i = i1; i <= i2; i++) - p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4; - } -} - -void TextEffect::process_image(double timecode, uint8_t* data, int w, int h) { +/*void TextEffect::process_image(double timecode, uint8_t* data, int w, int h) { QImage img(data, w, h, QImage::Format_RGBA8888); QPainter p(&img); p.setRenderHint(QPainter::Antialiasing); @@ -357,4 +473,4 @@ void TextEffect::process_image(double timecode, uint8_t* data, int w, int h) { // draw "master" text p.setBrush(set_color_button->get_color_value(timecode)); p.drawPath(path); -} +}*/ diff --git a/effects/video/texteffect.h b/effects/video/texteffect.h index 0a0587f6d..81e6c541c 100644 --- a/effects/video/texteffect.h +++ b/effects/video/texteffect.h @@ -4,12 +4,14 @@ #include "../effect.h" #include +#include +class QOpenGLTexture; -class TextEffect : public Effect { +class TextEffect : public SuperimposeEffect { Q_OBJECT public: TextEffect(Clip* c); - void process_image(double timecode, uint8_t* data, int width, int height); + void redraw(double timecode); EffectField* text_val; EffectField* size_val; diff --git a/effects/video/transformeffect.cpp b/effects/video/transformeffect.cpp index 8db50431f..0b27cd2ec 100644 --- a/effects/video/transformeffect.cpp +++ b/effects/video/transformeffect.cpp @@ -23,7 +23,7 @@ #define BLEND_MODE_OVERLAY 3 TransformEffect::TransformEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_TRANSFORM_EFFECT) { - enable_opengl = true; + enable_coords = true; EffectRow* position_row = add_row("Position:"); position_x = position_row->add_field(EFFECT_FIELD_DOUBLE); // position X @@ -104,7 +104,7 @@ void TransformEffect::toggle_uniform_scale(bool enabled) { scale_y->set_enabled(!enabled); } -void TransformEffect::process_gl(double timecode, GLTextureCoords& coords) { +void TransformEffect::process_coords(double timecode, GLTextureCoords& coords) { // position glTranslatef(position_x->get_double_value(timecode)-(parent_clip->sequence->width/2), position_y->get_double_value(timecode)-(parent_clip->sequence->height/2), 0); diff --git a/effects/video/transformeffect.h b/effects/video/transformeffect.h index dc885676a..9cc211b25 100644 --- a/effects/video/transformeffect.h +++ b/effects/video/transformeffect.h @@ -8,7 +8,7 @@ class TransformEffect : public Effect { public: TransformEffect(Clip* c); void refresh(); - void process_gl(double timecode, GLTextureCoords& coords); + void process_coords(double timecode, GLTextureCoords& coords); EffectField* position_x; EffectField* position_y; diff --git a/io/exportthread.cpp b/io/exportthread.cpp index 248b98da7..f46b81245 100644 --- a/io/exportthread.cpp +++ b/io/exportthread.cpp @@ -321,7 +321,7 @@ void ExportThread::run() { fbo.bind(); panel_viewer->viewer_widget->rendering = true; - panel_viewer->viewer_widget->fbo = &fbo; + panel_viewer->viewer_widget->default_fbo = &fbo; long file_audio_samples = 0; @@ -371,7 +371,7 @@ void ExportThread::run() { panel_timeline->playhead++; } - panel_viewer->viewer_widget->fbo = NULL; + panel_viewer->viewer_widget->default_fbo = NULL; panel_viewer->viewer_widget->rendering = false; fbo.release(); diff --git a/mainwindow.cpp b/mainwindow.cpp index c5d62588f..83b6ff07d 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -85,7 +85,7 @@ MainWindow::MainWindow(QWidget *parent) : updateTitle(""); setWindowState(Qt::WindowMaximized); - statusBar()->showMessage("Welcome to Olive"); + statusBar()->showMessage("Welcome to " + appName); setDockNestingEnabled(true); diff --git a/panels/project.cpp b/panels/project.cpp index 6390be8b4..09c784a56 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -1281,7 +1281,7 @@ void MediaThrobber::stop(int icon_type, bool replace) { } // redraw clips -// panel_timeline->redraw_all_clips(replace); + panel_timeline->redraw_all_clips(replace); panel_project->source_table->viewport()->update(); deleteLater(); diff --git a/playback/cacher.cpp b/playback/cacher.cpp index bef01ac21..d47eae22f 100644 --- a/playback/cacher.cpp +++ b/playback/cacher.cpp @@ -347,9 +347,6 @@ void open_clip_worker(Clip* clip) { } clip->cache_B.frames[i]->linesize[0] = dstW*4; } - - clip->comp_frame_size = dstW * dstH * 4; - clip->comp_frame = new uchar[clip->comp_frame_size]; } else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { // if FFmpeg can't pick up the channel layout (usually WAV), assume // based on channel count (doesn't support surround sound sources yet) @@ -418,16 +415,10 @@ void cache_clip_worker(Clip* clip, long playhead, bool write_A, bool write_B, bo } void close_clip_worker(Clip* clip) { - for (int i=0;ieffects.size();i++) { - clip->effects.at(i)->close(); - } - // closes ffmpeg file handle and frees any memory used for caching MediaStream* ms = static_cast(clip->media)->get_stream_from_file_index(clip->track < 0, clip->media_stream); if (clip->track < 0) { sws_freeContext(clip->sws_ctx); - - delete [] clip->comp_frame; } else { swr_free(&clip->swr_ctx); } diff --git a/playback/playback.cpp b/playback/playback.cpp index a9ca87cb4..053cb8615 100644 --- a/playback/playback.cpp +++ b/playback/playback.cpp @@ -55,8 +55,14 @@ void close_clip(Clip* clip) { clip->texture = NULL; } + for (int i=0;ieffects.size();i++) { + clip->effects.at(i)->close(); + } + if (clip->fbo != NULL) { - delete clip->fbo; + delete clip->fbo[0]; + delete clip->fbo[1]; + delete [] clip->fbo; clip->fbo = NULL; } @@ -178,14 +184,7 @@ bool get_clip_frame(Clip* c, long playhead) { c->texture->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8); } - memcpy(c->comp_frame, current_frame->data[0], c->comp_frame_size); - for (int i=0;ieffects.size();i++) { - if (c->effects.at(i)->is_enabled() && c->effects.at(i)->enable_image) { - c->effects.at(i)->process_image(sequence_clip_time, c->comp_frame, current_frame->width, current_frame->height); - } - } - - c->texture->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, c->comp_frame); + c->texture->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, current_frame->data[0]); c->texture_frame = clip_time; return true; diff --git a/project/clip.h b/project/clip.h index 4e7193ed4..3a3ce2e30 100644 --- a/project/clip.h +++ b/project/clip.h @@ -81,8 +81,6 @@ struct Clip AVCodecContext* codecCtx; AVPacket* pkt; AVFrame* frame; - uchar* comp_frame; - int comp_frame_size; bool pkt_written; bool reached_end; @@ -102,7 +100,7 @@ struct Clip // video playback variables SwsContext* sws_ctx; - QOpenGLFramebufferObject* fbo; + QOpenGLFramebufferObject** fbo; QOpenGLTexture* texture; long texture_frame; diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 05def23ea..1566b4b4b 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -28,7 +28,7 @@ extern "C" { ViewerWidget::ViewerWidget(QWidget *parent) : QOpenGLWidget(parent), rendering(false), - fbo(NULL) + default_fbo(NULL) { QSurfaceFormat format; format.setDepthBufferSize(24); @@ -67,12 +67,12 @@ void ViewerWidget::paintEvent(QPaintEvent *e) { } } -GLuint ViewerWidget::draw_clip(Clip* clip, GLuint texture) { +GLuint ViewerWidget::draw_clip(QOpenGLFramebufferObject* fbo, GLuint texture) { glPushMatrix(); glLoadIdentity(); glOrtho(0, 1, 0, 1, -1, 1); - clip->fbo->bind(); + fbo->bind(); glBindTexture(GL_TEXTURE_2D, texture); glBegin(GL_QUADS); @@ -87,11 +87,11 @@ GLuint ViewerWidget::draw_clip(Clip* clip, GLuint texture) { glEnd(); glBindTexture(GL_TEXTURE_2D, NULL); - clip->fbo->release(); - if (fbo != NULL) fbo->bind(); + fbo->release(); + if (default_fbo != NULL) default_fbo->bind(); glPopMatrix(); - return clip->fbo->takeTexture(); + return fbo->texture(); } GLuint ViewerWidget::compose_sequence(Clip* nest, bool render_audio) { @@ -198,12 +198,15 @@ GLuint ViewerWidget::compose_sequence(Clip* nest, bool render_audio) { } else if (playhead >= c->timeline_in) { // start preparing cache if (c->fbo == NULL) { - c->fbo = new QOpenGLFramebufferObject(video_width, video_height); + c->fbo = new QOpenGLFramebufferObject* [2]; + c->fbo[0] = new QOpenGLFramebufferObject(video_width, video_height); + c->fbo[1] = new QOpenGLFramebufferObject(video_width, video_height); } glViewport(0, 0, video_width, video_height); - GLuint composite_texture = draw_clip(c, textureID); + GLuint composite_texture = draw_clip(c->fbo[0], textureID); + bool fbo_switcher = true; GLTextureCoords coords; coords.vertexTopLeftX = coords.vertexBottomLeftX = -video_width/2; @@ -215,11 +218,23 @@ GLuint ViewerWidget::compose_sequence(Clip* nest, bool render_audio) { // EFFECT CODE START for (int j=0;jeffects.size();j++) { - if (c->effects.at(j)->enable_opengl && c->effects.at(j)->is_enabled()) { - c->effects.at(j)->startEffect(); - for (int k=0;keffects.at(j)->getIterations();k++) { - c->effects.at(j)->process_gl(((double)(panel_timeline->playhead-c->timeline_in+c->clip_in)/(double)sequence->frame_rate), coords); - composite_texture = draw_clip(c, composite_texture); + Effect* e = c->effects.at(j); + if (e->is_enabled()) { + double timecode = ((double)(panel_timeline->playhead-c->timeline_in+c->clip_in)/(double)sequence->frame_rate); + if (e->enable_coords) { + e->process_coords(timecode, coords); + } + if (e->enable_shader || e->enable_superimpose) { + e->startEffect(); + for (int k=0;kgetIterations();k++) { + e->process_shader(timecode); + composite_texture = draw_clip(c->fbo[fbo_switcher], composite_texture); + if (e->enable_superimpose) { + GLuint superimpose_texture = e->process_superimpose(timecode); + if (superimpose_texture != 0) draw_clip(c->fbo[fbo_switcher], superimpose_texture); + } + fbo_switcher = !fbo_switcher; + } } } } @@ -237,17 +252,17 @@ GLuint ViewerWidget::compose_sequence(Clip* nest, bool render_audio) { c->closing_transition->process_transition((double)transition_progress/(double)c->closing_transition->length); } } - // EFFECT CODE END for (int j=0;jeffects.size();j++) { - if (c->effects.at(j)->enable_opengl && c->effects.at(j)->is_enabled()) { + if ((c->effects.at(j)->enable_shader || c->effects.at(j)->enable_superimpose) && c->effects.at(j)->is_enabled()) { c->effects.at(j)->endEffect(); } } + // EFFECT CODE END if (nest != NULL) { - if (nest->fbo == NULL) nest->fbo = new QOpenGLFramebufferObject(s->width, s->height); - nest->fbo->bind(); +// if (nest->fbo == NULL) nest->fbo = new QOpenGLFramebufferObject(s->width, s->height); + nest->fbo[0]->bind(); glViewport(0, 0, s->width, s->height); } else if (rendering) { glViewport(0, 0, s->width, s->height); @@ -271,11 +286,9 @@ GLuint ViewerWidget::compose_sequence(Clip* nest, bool render_audio) { glVertex2f(coords.vertexBottomLeftX, coords.vertexBottomLeftY); // bottom left glEnd(); - glDeleteTextures(1, &composite_texture); - if (nest != NULL) { - nest->fbo->release(); - if (fbo != NULL) fbo->bind(); + nest->fbo[0]->release(); + if (default_fbo != NULL) default_fbo->bind(); } } } else { @@ -292,7 +305,7 @@ GLuint ViewerWidget::compose_sequence(Clip* nest, bool render_audio) { } } - return (nest != NULL && nest->fbo != NULL) ? nest->fbo->takeTexture() : 0; + return (nest != NULL && nest->fbo != NULL) ? nest->fbo[0]->texture() : 0; } void ViewerWidget::paintGL() { diff --git a/ui/viewerwidget.h b/ui/viewerwidget.h index 677de4b3d..17d9dff64 100644 --- a/ui/viewerwidget.h +++ b/ui/viewerwidget.h @@ -23,7 +23,7 @@ public: void paintGL(); void initializeGL(); - QOpenGLFramebufferObject* fbo; + QOpenGLFramebufferObject* default_fbo; protected: void paintEvent(QPaintEvent *e); // void resizeGL(int w, int h); @@ -33,7 +33,7 @@ private slots: void retry(); void deleteFunction(); GLuint compose_sequence(Clip *nest, bool render_audio); - GLuint draw_clip(Clip *clip, GLuint texture); + GLuint draw_clip(QOpenGLFramebufferObject *clip, GLuint texture); }; #endif // VIEWERWIDGET_H