#include "texteffect.h" #include #include #include #include #include #include #include #include #include #include #include #include "ui/labelslider.h" #include "ui/collapsiblewidget.h" #include "project/clip.h" #include "project/sequence.h" #include "ui/comboboxex.h" #include "ui/colorbutton.h" #include "ui/fontcombobox.h" TextEffect::TextEffect(Clip *c) : SuperimposeEffect(c, EFFECT_TYPE_VIDEO, VIDEO_TEXT_EFFECT) { enable_superimpose = true; text_val = add_row("Text:")->add_field(EFFECT_FIELD_STRING, 2); set_font_combobox = add_row("Font:")->add_field(EFFECT_FIELD_FONT, 2); size_val = add_row("Size:")->add_field(EFFECT_FIELD_DOUBLE, 2); size_val->set_double_minimum_value(0); set_color_button = add_row("Color:")->add_field(EFFECT_FIELD_COLOR, 2); EffectRow* alignment_row = add_row("Alignment:"); halign_field = alignment_row->add_field(EFFECT_FIELD_COMBO); halign_field->add_combo_item("Left", Qt::AlignLeft); halign_field->add_combo_item("Center", Qt::AlignHCenter); halign_field->add_combo_item("Right", Qt::AlignRight); halign_field->add_combo_item("Justify", Qt::AlignJustify); valign_field = alignment_row->add_field(EFFECT_FIELD_COMBO); valign_field->add_combo_item("Top", Qt::AlignTop); valign_field->add_combo_item("Center", Qt::AlignVCenter); valign_field->add_combo_item("Bottom", Qt::AlignBottom); word_wrap_field = add_row("Word Wrap:")->add_field(EFFECT_FIELD_BOOL, 2); outline_bool = add_row("Outline:")->add_field(EFFECT_FIELD_BOOL, 2); outline_color = add_row("Outline Color:")->add_field(EFFECT_FIELD_COLOR, 2); outline_width = add_row("Outline Width:")->add_field(EFFECT_FIELD_DOUBLE, 2); outline_width->set_double_minimum_value(0); shadow_bool = add_row("Shadow:")->add_field(EFFECT_FIELD_BOOL, 2); shadow_color = add_row("Shadow Color:")->add_field(EFFECT_FIELD_COLOR, 2); shadow_distance = add_row("Shadow Distance:")->add_field(EFFECT_FIELD_DOUBLE, 2); shadow_distance->set_double_minimum_value(0); shadow_softness = add_row("Shadow Softness:")->add_field(EFFECT_FIELD_DOUBLE, 2); shadow_softness->set_double_minimum_value(0); shadow_opacity = add_row("Shadow Opacity:")->add_field(EFFECT_FIELD_DOUBLE, 2); shadow_opacity->set_double_minimum_value(0); shadow_opacity->set_double_maximum_value(100); size_val->set_double_default_value(48); text_val->set_string_value("Sample Text"); halign_field->set_combo_index(1); valign_field->set_combo_index(1); word_wrap_field->set_bool_value(true); outline_color->set_color_value(Qt::black); shadow_color->set_color_value(Qt::black); shadow_opacity->set_double_default_value(100); shadow_softness->set_double_default_value(5); shadow_distance->set_double_default_value(5); shadow_opacity->set_double_default_value(80); outline_width->set_double_default_value(2); outline_enable(false); shadow_enable(false); connect(text_val, SIGNAL(changed()), this, SLOT(field_changed())); connect(size_val, SIGNAL(changed()), this, SLOT(field_changed())); connect(set_color_button, SIGNAL(changed()), this, SLOT(field_changed())); connect(set_font_combobox, SIGNAL(changed()), this, SLOT(field_changed())); connect(halign_field, SIGNAL(changed()), this, SLOT(field_changed())); connect(valign_field, SIGNAL(changed()), this, SLOT(field_changed())); connect(word_wrap_field, SIGNAL(changed()), this, SLOT(field_changed())); connect(outline_bool, SIGNAL(changed()), this, SLOT(field_changed())); connect(outline_color, SIGNAL(changed()), this, SLOT(field_changed())); connect(outline_width, SIGNAL(changed()), this, SLOT(field_changed())); connect(shadow_bool, SIGNAL(changed()), this, SLOT(field_changed())); connect(shadow_color, SIGNAL(changed()), this, SLOT(field_changed())); connect(shadow_distance, SIGNAL(changed()), this, SLOT(field_changed())); connect(shadow_softness, SIGNAL(changed()), this, SLOT(field_changed())); connect(shadow_opacity, SIGNAL(changed()), this, SLOT(field_changed())); connect(shadow_bool, SIGNAL(toggled(bool)), this, SLOT(shadow_enable(bool))); 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); shadow_softness->set_enabled(e); shadow_opacity->set_enabled(e); } void TextEffect::outline_enable(bool e) { outline_color->set_enabled(e); outline_width->set_enabled(e); } /*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]; QImage result = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); 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; } return result; }*/ /*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); 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)) { QImage shadow(width, height, QImage::Format_ARGB32_Premultiplied); shadow.fill(Qt::transparent); QPainter spaint(&shadow); int shadow_offset = shadow_distance->get_double_value(timecode); QColor col = shadow_color->get_color_value(timecode); col.setAlphaF(shadow_opacity->get_double_value(timecode)*0.01); spaint.setBrush(col); spaint.drawPath(path); blurred2(shadow, shadow.rect(), shadow_softness->get_double_value(timecode), false); p.drawImage(shadow_offset, shadow_offset, shadow); spaint.end(); } // 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); }*/