subtitles: implemented very basic renderer
This commit is contained in:
@@ -79,4 +79,40 @@ QString QtUtils::GetFormattedDateTime(const QDateTime &dt)
|
||||
return dt.toString(Qt::TextDate);
|
||||
}
|
||||
|
||||
QStringList QtUtils::WordWrapString(const QString &s, const QFontMetrics &fm, int bounding_width)
|
||||
{
|
||||
QStringList list;
|
||||
|
||||
QStringList lines = s.split('\n');
|
||||
|
||||
// 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) {
|
||||
for (int j=this_line.size()-1; j>=0; j--) {
|
||||
if (this_line.at(j).isSpace()) {
|
||||
QString chopped = this_line.left(j);
|
||||
if (QFontMetricsWidth(fm, chopped) < bounding_width) {
|
||||
list.append(chopped);
|
||||
|
||||
int k = j+1;
|
||||
while (k < this_line.size() && this_line.at(k).isSpace()) {
|
||||
k++;
|
||||
}
|
||||
this_line.remove(0, k);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this_line.isEmpty()) {
|
||||
list.append(this_line);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -62,6 +62,8 @@ public:
|
||||
|
||||
static QString GetFormattedDateTime(const QDateTime &dt);
|
||||
|
||||
static QStringList WordWrapString(const QString &s, const QFontMetrics &fm, int bounding_width);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -118,6 +118,15 @@ void Sequence::Retranslate()
|
||||
}
|
||||
}
|
||||
|
||||
void Sequence::InvalidateCache(const TimeRange &range, const QString &from, int element, InvalidateCacheOptions options)
|
||||
{
|
||||
if (from == kTrackInputFormat.arg(Track::kSubtitle)) {
|
||||
emit SubtitlesChanged(range);
|
||||
}
|
||||
|
||||
super::InvalidateCache(range, from, element, options);
|
||||
}
|
||||
|
||||
rational Sequence::VerifyLengthInternal(Track::Type type) const
|
||||
{
|
||||
if (!track_lists_.isEmpty()) {
|
||||
|
||||
@@ -84,6 +84,8 @@ public:
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options) override;
|
||||
|
||||
static const QString kTrackInputFormat;
|
||||
|
||||
virtual bool IsItem() const override
|
||||
@@ -102,6 +104,8 @@ signals:
|
||||
void TrackAdded(Track* track);
|
||||
void TrackRemoved(Track* track);
|
||||
|
||||
void SubtitlesChanged(const TimeRange &range);
|
||||
|
||||
private:
|
||||
QVector<TrackList*> track_lists_;
|
||||
|
||||
|
||||
@@ -275,6 +275,7 @@ void ViewerWidget::DisconnectNodeEvent(ViewerOutput *n)
|
||||
void ViewerWidget::ConnectedNodeChangeEvent(ViewerOutput *n)
|
||||
{
|
||||
auto_cacher_.SetViewerNode(n);
|
||||
display_widget_->SetSubtitleTracks(dynamic_cast<Sequence*>(n));
|
||||
}
|
||||
|
||||
void ViewerWidget::ScaleChangedEvent(const double &s)
|
||||
@@ -1189,6 +1190,13 @@ void ViewerWidget::ShowContextMenu(const QPoint &pos)
|
||||
connect(show_fps_action, &QAction::triggered, display_widget_, &ViewerDisplayWidget::SetShowFPS);
|
||||
}
|
||||
|
||||
if (context_menu_widget_ == display_widget_) {
|
||||
QAction* show_subtitles_action = menu.addAction(tr("Show Subtitles"));
|
||||
show_subtitles_action->setCheckable(true);
|
||||
show_subtitles_action->setChecked(display_widget_->GetShowSubtitles());
|
||||
connect(show_subtitles_action, &QAction::triggered, display_widget_, &ViewerDisplayWidget::SetShowSubtitles);
|
||||
}
|
||||
|
||||
menu.exec(static_cast<QWidget*>(sender())->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
|
||||
@@ -37,8 +37,10 @@
|
||||
#include "common/define.h"
|
||||
#include "common/functiontimer.h"
|
||||
#include "common/html.h"
|
||||
#include "common/qtutils.h"
|
||||
#include "config/config.h"
|
||||
#include "core.h"
|
||||
#include "node/block/subtitle/subtitle.h"
|
||||
#include "node/gizmo/path.h"
|
||||
#include "node/gizmo/point.h"
|
||||
#include "node/gizmo/polygon.h"
|
||||
@@ -59,6 +61,8 @@ ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent) :
|
||||
gizmos_(nullptr),
|
||||
current_gizmo_(nullptr),
|
||||
gizmo_drag_started_(false),
|
||||
show_subtitles_(true),
|
||||
subtitle_tracks_(nullptr),
|
||||
hand_dragging_(false),
|
||||
deinterlace_(false),
|
||||
show_fps_(false),
|
||||
@@ -189,6 +193,21 @@ void ViewerDisplayWidget::SetTime(const rational &time)
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::SetSubtitleTracks(Sequence *list)
|
||||
{
|
||||
if (subtitle_tracks_) {
|
||||
disconnect(subtitle_tracks_, &Sequence::SubtitlesChanged, this, &ViewerDisplayWidget::SubtitlesChanged);
|
||||
}
|
||||
|
||||
subtitle_tracks_ = list;
|
||||
|
||||
if (subtitle_tracks_) {
|
||||
connect(subtitle_tracks_, &Sequence::SubtitlesChanged, this, &ViewerDisplayWidget::SubtitlesChanged);
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
QPointF ViewerDisplayWidget::TransformViewerSpaceToBufferSpace(const QPointF &pos)
|
||||
{
|
||||
/*
|
||||
@@ -582,6 +601,53 @@ void ViewerDisplayWidget::OnPaint()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extraordinarily basic subtitle renderer. Hoping to swap this out with libass at some point.
|
||||
if (show_subtitles_ && subtitle_tracks_) {
|
||||
const QVector<Track*> &subtitle_tracklist = subtitle_tracks_->track_list(Track::kSubtitle)->GetTracks();
|
||||
|
||||
if (!subtitle_tracklist.empty()) {
|
||||
QPainter p(inner_widget());
|
||||
|
||||
QTransform transform = GenerateWorldTransform();
|
||||
QRect bounding_box = transform.mapRect(rect());
|
||||
|
||||
bounding_box.adjust(bounding_box.width()/10, bounding_box.height()/10, -bounding_box.width()/10, -bounding_box.height()/10);
|
||||
|
||||
QFont f = p.font();
|
||||
int font_sz = bounding_box.height() / 18;
|
||||
f.setStyleHint(QFont::SansSerif);
|
||||
f.setFamily(f.defaultFamily());
|
||||
f.setPointSize(font_sz);
|
||||
f.setWeight(QFont::Bold);
|
||||
p.setFont(f);
|
||||
p.setPen(Qt::white);
|
||||
|
||||
QPainterPath path;
|
||||
|
||||
int text_line = 1;
|
||||
|
||||
for (int j=subtitle_tracklist.size()-1; j>=0; j--) {
|
||||
Track *sub_track = subtitle_tracklist.at(j);
|
||||
if (!sub_track->IsMuted()) {
|
||||
if (SubtitleBlock *sub = dynamic_cast<SubtitleBlock*>(sub_track->BlockAtTime(time_))) {
|
||||
// Split into lines
|
||||
QStringList list = QtUtils::WordWrapString(sub->GetText(), p.fontMetrics(), bounding_box.width());
|
||||
|
||||
for (int i=list.size()-1; i>=0; i--) {
|
||||
int w = QtUtils::QFontMetricsWidth(p.fontMetrics(), list.at(i));
|
||||
path.addText(bounding_box.x() + bounding_box.width()/2 - w/2, bounding_box.y() + bounding_box.height() - p.fontMetrics().height() * text_line + p.fontMetrics().ascent(), p.font(), list.at(i));
|
||||
text_line++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p.setPen(QPen(Qt::black, font_sz / 16));
|
||||
p.setBrush(Qt::white);
|
||||
p.drawPath(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::OnDestroy()
|
||||
@@ -618,12 +684,12 @@ QPointF ViewerDisplayWidget::GetTexturePosition(const double &x, const double &y
|
||||
y / gizmo_params_.height());
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::DrawTextWithCrudeShadow(QPainter *painter, const QRect &rect, const QString &text)
|
||||
void ViewerDisplayWidget::DrawTextWithCrudeShadow(QPainter *painter, const QRect &rect, const QString &text, const QTextOption &opt)
|
||||
{
|
||||
painter->setPen(Qt::black);
|
||||
painter->drawText(rect.adjusted(1, 1, 0, 0), text);
|
||||
painter->drawText(rect.adjusted(1, 1, 0, 0), text, opt);
|
||||
painter->setPen(Qt::white);
|
||||
painter->drawText(rect, text);
|
||||
painter->drawText(rect, text, opt);
|
||||
}
|
||||
|
||||
rational ViewerDisplayWidget::GetGizmoTime()
|
||||
@@ -807,4 +873,11 @@ void ViewerDisplayWidget::TextEditChanged()
|
||||
gizmo->UpdateInputHtml(html, GetGizmoTime());
|
||||
}
|
||||
|
||||
void ViewerDisplayWidget::SubtitlesChanged(const TimeRange &r)
|
||||
{
|
||||
if (time_ >= r.in() && time_ < r.out()) {
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "node/color/colormanager/colormanager.h"
|
||||
#include "node/node.h"
|
||||
#include "node/output/track/tracklist.h"
|
||||
#include "render/color.h"
|
||||
#include "tool/tool.h"
|
||||
#include "viewerplaybacktimer.h"
|
||||
@@ -72,6 +73,7 @@ public:
|
||||
void SetGizmos(Node* node);
|
||||
void SetVideoParams(const VideoParams ¶ms);
|
||||
void SetTime(const rational& time);
|
||||
void SetSubtitleTracks(Sequence *list);
|
||||
|
||||
void SetShowWidgetBackground(bool e)
|
||||
{
|
||||
@@ -97,6 +99,9 @@ public:
|
||||
return show_fps_;
|
||||
}
|
||||
|
||||
bool GetShowSubtitles() const { return show_subtitles_; }
|
||||
void SetShowSubtitles(bool e) { show_subtitles_ = e; update(); }
|
||||
|
||||
void IncrementSkippedFrames();
|
||||
|
||||
void IncrementFrameCount()
|
||||
@@ -239,7 +244,7 @@ private:
|
||||
QPointF GetTexturePosition(const QSize& size);
|
||||
QPointF GetTexturePosition(const double& x, const double& y);
|
||||
|
||||
static void DrawTextWithCrudeShadow(QPainter* painter, const QRect& rect, const QString& text);
|
||||
static void DrawTextWithCrudeShadow(QPainter* painter, const QRect& rect, const QString& text, const QTextOption &opt = QTextOption());
|
||||
|
||||
rational GetGizmoTime();
|
||||
|
||||
@@ -312,6 +317,9 @@ private:
|
||||
NodeGizmo *current_gizmo_;
|
||||
bool gizmo_drag_started_;
|
||||
|
||||
bool show_subtitles_;
|
||||
Sequence *subtitle_tracks_;
|
||||
|
||||
rational time_;
|
||||
|
||||
/**
|
||||
@@ -365,6 +373,8 @@ private slots:
|
||||
|
||||
void TextEditChanged();
|
||||
|
||||
void SubtitlesChanged(const TimeRange &r);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user