Add support for italics in subtitles

This commit is contained in:
Torarin 2013-06-06 21:53:19 +02:00
parent b853c39772
commit 57464a429f
9 changed files with 149 additions and 45 deletions

View file

@ -57,6 +57,7 @@ OMXPlayerSubtitles::~OMXPlayerSubtitles() BOOST_NOEXCEPT
bool OMXPlayerSubtitles::Open(size_t stream_count,
vector<Subtitle>&& external_subtitles,
const string& font_path,
const string& italic_font_path,
float font_size,
bool centered,
unsigned int lines,
@ -75,6 +76,7 @@ bool OMXPlayerSubtitles::Open(size_t stream_count,
m_thread_stopped.store(false, memory_order_relaxed);
m_font_path = font_path;
m_italic_font_path = italic_font_path;
m_font_size = font_size;
m_centered = centered;
m_lines = lines;
@ -112,7 +114,8 @@ void OMXPlayerSubtitles::Process()
{
try
{
RenderLoop(m_font_path, m_font_size, m_centered, m_lines, m_av_clock);
RenderLoop(m_font_path, m_italic_font_path, m_font_size, m_centered,
m_lines, m_av_clock);
}
catch(Enforce_error& e)
{
@ -144,6 +147,7 @@ Iterator FindSubtitle(Iterator begin, Iterator end, int time)
void OMXPlayerSubtitles::
RenderLoop(const string& font_path,
const string& italic_font_path,
float font_size,
bool centered,
unsigned int lines,
@ -151,6 +155,7 @@ RenderLoop(const string& font_path,
{
SubtitleRenderer renderer(1,
font_path,
italic_font_path,
font_size,
0.01f, 0.06f,
centered,

View file

@ -41,6 +41,7 @@ public:
bool Open(size_t stream_count,
std::vector<Subtitle>&& external_subtitles,
const std::string& font_path,
const std::string& italic_font_path,
float font_size,
bool centered,
unsigned int lines,
@ -112,6 +113,7 @@ private:
void Process();
void RenderLoop(const std::string& font_path,
const std::string& italic_font_path,
float font_size,
bool centered,
unsigned int lines,
@ -135,6 +137,7 @@ private:
int m_delay;
std::atomic<bool> m_thread_stopped;
std::string m_font_path;
std::string m_italic_font_path;
float m_font_size;
bool m_centered;
unsigned int m_lines;

View file

@ -76,13 +76,13 @@ void COMXSubtitleTagSami::ConvertLine(COMXOverlayText* pOverlay, const char* lin
else if (fullTag == "<i>" || fullTag == "{\\i1}")
{
m_flag[FLAG_ITALIC] = true;
// strUTF8.insert(pos, "[I]");
strUTF8.insert(pos, "<i>");
pos += 3;
}
else if ((fullTag == "</i>" || fullTag == "{\\i0}") && m_flag[FLAG_ITALIC])
{
m_flag[FLAG_ITALIC] = false;
// strUTF8.insert(pos, "[/I]");
strUTF8.insert(pos, "</i>");
pos += 4;
}
else if ((fullTag == "</font>" || fullTag == "{\\c}") && m_flag[FLAG_COLOR])

View file

@ -70,6 +70,7 @@ Using OMXPlayer
--subtitles path external subtitles in UTF-8 srt format
--font path subtitle font
(default: /usr/share/fonts/truetype/freefont/FreeSans.ttf)
--italic-font path (default: /usr/share/fonts/truetype/freefont/FreeSansOblique.ttf)
--font-size size font size as thousandths of screen height
(default: 55)
--align left/center subtitle alignment (default: left)

View file

@ -88,13 +88,13 @@ public:
}
};
void SubtitleRenderer::load_glyph(char32_t codepoint) {
void SubtitleRenderer::load_glyph(InternalChar ch) {
VGfloat escapement[2]{};
auto load_glyph_internal =
[&](FT_Face ft_face, VGFont vg_font, bool border) {
try {
auto glyph_index = FT_Get_Char_Index(ft_face, codepoint);
auto glyph_index = FT_Get_Char_Index(ft_face, ch.codepoint());
ENFORCE(!FT_Load_Glyph(ft_face, glyph_index, FT_LOAD_NO_HINTING));
FT_Glyph glyph;
@ -165,7 +165,7 @@ void SubtitleRenderer::load_glyph(char32_t codepoint) {
escapement[0] = static_cast<VGfloat>((ft_face->glyph->advance.x + 32) / 64);
escapement[1] = 0;
vgSetGlyphToImage(vg_font, codepoint, image, glyph_origin, escapement);
vgSetGlyphToImage(vg_font, ch.val, image, glyph_origin, escapement);
assert(!vgGetError());
if (image) {
@ -175,34 +175,40 @@ void SubtitleRenderer::load_glyph(char32_t codepoint) {
} catch(...) {
escapement[0] = 0;
escapement[1] = 0;
vgSetGlyphToImage(vg_font, codepoint, VG_INVALID_HANDLE, escapement, escapement);
vgSetGlyphToImage(vg_font, ch.val, VG_INVALID_HANDLE, escapement, escapement);
assert(!vgGetError());
}
};
load_glyph_internal(ft_face_, vg_font_, false);
glyphs_[codepoint].advance = escapement[0];
load_glyph_internal(ft_face_, vg_font_border_, true);
if (!ch.italic()) {
load_glyph_internal(ft_face_, vg_font_, false);
glyphs_[ch].advance = escapement[0];
load_glyph_internal(ft_face_, vg_font_border_, true);
} else {
load_glyph_internal(ft_face_italic_, vg_font_, false);
glyphs_[ch].advance = escapement[0];
load_glyph_internal(ft_face_italic_, vg_font_border_, true);
}
}
int SubtitleRenderer::get_text_width(const std::vector<InternalChar>& text) {
int width = 0;
for (auto c = text.begin(); c != text.end(); ++c) {
width += c->italic ? (assert(0), 0)
: glyphs_.at(c->codepoint).advance;
width += glyphs_.at(*c).advance;
}
return width;
}
std::vector<SubtitleRenderer::InternalChar> SubtitleRenderer::
get_internal_chars(const std::string& str) {
get_internal_chars(const std::string& str, TagTracker& tag_tracker) {
std::vector<InternalChar> internal_chars;
bool italic{};
auto c_str = str.c_str();
for (size_t i = 0, len = str.length(); i < len;) {
try {
auto cp = decodeUtf8(c_str, len, i);
internal_chars.push_back(InternalChar({cp, italic}));
tag_tracker.put(cp);
if (!tag_tracker.in_tag())
internal_chars.push_back(InternalChar(cp, tag_tracker.italic()));
} catch (...) {
++i; // Keep going
}
@ -213,14 +219,13 @@ get_internal_chars(const std::string& str) {
void SubtitleRenderer::
prepare_glyphs(const std::vector<InternalChar>& text) {
for (auto c = text.begin(); c != text.end(); ++c) {
if (glyphs_.find(c->codepoint) == glyphs_.end()) {
load_glyph(c->codepoint);
}
if (glyphs_.find(*c) == glyphs_.end())
load_glyph(*c);
}
}
void SubtitleRenderer::
draw_text(VGFont font, VGFont italic_font,
draw_text(VGFont font,
const std::vector<SubtitleRenderer::InternalChar>& text,
int x, int y,
unsigned int lightness) {
@ -245,10 +250,7 @@ draw_text(VGFont font, VGFont italic_font,
assert(!vgGetError());
for (auto c = text.begin(); c != text.end(); ++c) {
vgDrawGlyph(c->italic ? italic_font : font,
c->codepoint,
VG_FILL_PATH,
VG_FALSE);
vgDrawGlyph(font, c->val, VG_FILL_PATH, VG_FALSE);
assert(!vgGetError());
}
}
@ -261,6 +263,7 @@ SubtitleRenderer::~SubtitleRenderer() BOOST_NOEXCEPT {
SubtitleRenderer::
SubtitleRenderer(int layer,
const std::string& font_path,
const std::string& italic_font_path,
float font_size,
float margin_left,
float margin_bottom,
@ -276,10 +279,9 @@ SubtitleRenderer(int layer,
surface_(),
vg_font_(),
vg_font_border_(),
vg_font_italic_(),
vg_font_italic_border_(),
ft_library_(),
ft_face_(),
ft_face_italic_(),
ft_stroker_(),
line_height_(),
box_offset_(),
@ -296,7 +298,7 @@ SubtitleRenderer(int layer,
uint32_t screen_width, screen_height;
ENFORCE(graphics_get_display_size(0, &screen_width, &screen_height) >= 0);
initialize_fonts(font_path, font_size*screen_height);
initialize_fonts(font_path, italic_font_path, font_size*screen_height);
int abs_margin_bottom =
static_cast<int>(margin_bottom * screen_height + 0.5f) - box_offset_;
@ -333,11 +335,16 @@ void SubtitleRenderer::destroy() {
}
void SubtitleRenderer::
initialize_fonts(const std::string& font_path, unsigned int font_size) {
initialize_fonts(const std::string& font_path,
const std::string& italic_font_path,
unsigned int font_size) {
ENFORCE(!FT_Init_FreeType(&ft_library_));
ENFORCE2(!FT_New_Face(ft_library_, font_path.c_str(), 0, &ft_face_),
"Unable to open font");
ENFORCE2(!FT_New_Face(ft_library_, italic_font_path.c_str(), 0, &ft_face_italic_),
"Unable to open italic font");
ENFORCE(!FT_Set_Pixel_Sizes(ft_face_, 0, font_size));
ENFORCE(!FT_Set_Pixel_Sizes(ft_face_italic_, 0, font_size));
auto get_bbox = [this](char32_t cp) {
auto glyph_index = FT_Get_Char_Index(ft_face_, cp);
@ -376,6 +383,7 @@ void SubtitleRenderer::destroy_fonts() {
assert(!error);
ft_library_ = {};
ft_face_ = {};
ft_face_italic_ = {};
ft_stroker_ = {};
}
}
@ -522,12 +530,13 @@ void SubtitleRenderer::destroy_vg() {
void SubtitleRenderer::
prepare(const std::vector<std::string>& text_lines) BOOST_NOEXCEPT {
const int n_lines = text_lines.size();
TagTracker tag_tracker;
internal_lines_.resize(n_lines);
line_widths_.resize(n_lines);
line_positions_.resize(n_lines);
for (int i = 0; i < n_lines; ++i) {
internal_lines_[i] = get_internal_chars(text_lines[i]);
internal_lines_[i] = get_internal_chars(text_lines[i], tag_tracker);
prepare_glyphs(internal_lines_[i]);
line_widths_[i] = get_text_width(internal_lines_[i]);
line_positions_[i].second = margin_bottom_ + (n_lines-i-1)*line_height_;
@ -562,14 +571,14 @@ void SubtitleRenderer::draw() BOOST_NOEXCEPT {
}
for (size_t i = 0; i < n_lines; ++i) {
draw_text(vg_font_border_, vg_font_italic_border_,
draw_text(vg_font_border_,
internal_lines_[i],
line_positions_[i].first, line_positions_[i].second,
0);
}
for (size_t i = 0; i < n_lines; ++i) {
draw_text(vg_font_, vg_font_italic_,
draw_text(vg_font_,
internal_lines_[i],
line_positions_[i].first, line_positions_[i].second,
white_level_);

View file

@ -36,12 +36,58 @@
#include <unordered_map>
#include <string>
class TagTracker {
public:
TagTracker() : italic_(), state_(), closing_() {};
void put(char32_t cp) {
if (state_ == '>')
state_ = 0;
switch (cp) {
case '<':
state_ = '<';
closing_ = false;
break;
case '/':
if (state_ == '<')
closing_ = true;
break;
case 'i':
if (state_)
state_ = 'i';
break;
case '>':
if (state_) {
if (state_ == 'i')
italic_ = !closing_;
state_ = '>';
}
break;
}
}
bool italic() {
return italic_;
}
bool in_tag() {
return state_;
}
private:
bool italic_;
char state_;
bool closing_;
};
class SubtitleRenderer {
public:
SubtitleRenderer(const SubtitleRenderer&) = delete;
SubtitleRenderer& operator=(const SubtitleRenderer&) = delete;
SubtitleRenderer(int level,
const std::string& font_path,
const std::string& italic_font_path,
float font_size,
float margin_left,
float margin_bottom,
@ -58,8 +104,7 @@ public:
}
void show_next() BOOST_NOEXCEPT {
if (prepared_)
{
if (prepared_) {
// puts("Expensive show_next!");
draw();
}
@ -75,21 +120,40 @@ public:
private:
struct InternalChar {
char32_t codepoint;
bool italic;
InternalChar() = default;
InternalChar(char32_t codepoint, bool italic) {
val = codepoint | (static_cast<char32_t>(italic) << 31);
}
bool operator ==(const InternalChar& other) const {
return val == other.val;
}
char32_t codepoint() const { return val & 0x7FFFFFFF; }
bool italic() const { return val >> 31; }
char32_t val;
};
struct InternalCharHash {
size_t operator()(InternalChar ch) const noexcept {
return static_cast<size_t>(ch.val);
}
};
struct InternalGlyph {
int advance;
};
static void draw_text(VGFont font, VGFont italic_font,
static void draw_text(VGFont font,
const std::vector<InternalChar>& text,
int x, int y,
unsigned int lightness);
void destroy();
void initialize_fonts(const std::string& font_name, unsigned int font_size);
void initialize_fonts(const std::string& font_name,
const std::string& italic_font_path,
unsigned int font_size);
void destroy_fonts();
void initialize_vg();
void destroy_vg();
@ -103,9 +167,10 @@ private:
void draw() BOOST_NOEXCEPT;
void swap_buffers() BOOST_NOEXCEPT;
void prepare_glyphs(const std::vector<InternalChar>& text);
void load_glyph(char32_t codepoint);
void load_glyph(InternalChar ch);
int get_text_width(const std::vector<InternalChar>& text);
std::vector<InternalChar> get_internal_chars(const std::string& str);
std::vector<InternalChar> get_internal_chars(const std::string& str,
TagTracker& tag_tracker);
bool prepared_;
DISPMANX_ELEMENT_HANDLE_T dispman_element_;
@ -115,12 +180,11 @@ private:
EGLSurface surface_;
VGFont vg_font_;
VGFont vg_font_border_;
VGFont vg_font_italic_;
VGFont vg_font_italic_border_;
FT_Library ft_library_;
FT_Face ft_face_;
FT_Face ft_face_italic_;
FT_Stroker ft_stroker_;
std::unordered_map<char32_t,InternalGlyph> glyphs_;
std::unordered_map<InternalChar,InternalGlyph, InternalCharHash> glyphs_;
std::vector<std::vector<InternalChar>> internal_lines_;
std::vector<std::pair<int,int>> line_positions_;
std::vector<int> line_widths_;

BIN
fonts/FreeSansOblique.ttf Normal file

Binary file not shown.

View file

@ -12,6 +12,12 @@ else
FONT="fonts/FreeSans.ttf"
fi
if [ -e /usr/share/fonts/truetype/freefont/FreeSansOblique.ttf ]; then
ITALIC_FONT="/usr/share/fonts/truetype/freefont/FreeSansOblique.ttf"
else
ITALIC_FONT="fonts/FreeSansOblique.ttf"
fi
if [ -e /usr/bin/omxplayer.bin ]; then
OMXPLAYER="/usr/bin/omxplayer.bin"
else
@ -35,7 +41,7 @@ if [ -e $FBSET ]; then
fbset -xres 1 -yres 1
fi
$OMXPLAYER --font $FONT "$@"
$OMXPLAYER --font $FONT --italic-font $ITALIC_FONT "$@"
if [ -e $FBSET ]; then
fbset -xres ${XRES} -yres ${YRES}

View file

@ -78,7 +78,9 @@ int m_use_hw_audio = false;
std::string m_external_subtitles_path;
bool m_has_external_subtitles = false;
std::string m_font_path = "/usr/share/fonts/truetype/freefont/FreeSans.ttf";
bool m_has_font = false;
std::string m_italic_font_path = "/usr/share/fonts/truetype/freefont/FreeSansOblique.ttf";
bool m_asked_for_font = false;
bool m_asked_for_italic_font = false;
float m_font_size = 0.055f;
bool m_centered = false;
unsigned int m_subtitle_lines = 3;
@ -167,6 +169,7 @@ void print_usage()
printf(" --subtitles path external subtitles in UTF-8 srt format\n");
printf(" --font path subtitle font\n");
printf(" (default: /usr/share/fonts/truetype/freefont/FreeSans.ttf)\n");
printf(" --italic-font path (default: /usr/share/fonts/truetype/freefont/FreeSansOblique.ttf)\n");
printf(" --font-size size font size as thousandths of screen height\n");
printf(" (default: 55)\n");
printf(" --align left/center subtitle alignment (default: left)\n");
@ -555,6 +558,7 @@ int main(int argc, char *argv[])
TV_DISPLAY_STATE_T tv_state;
const int font_opt = 0x100;
const int italic_font_opt = 0x201;
const int font_size_opt = 0x101;
const int align_opt = 0x102;
const int subtitles_opt = 0x103;
@ -591,6 +595,7 @@ int main(int argc, char *argv[])
{ "pos", required_argument, NULL, 'l' },
{ "blank", no_argument, NULL, 'b' },
{ "font", required_argument, NULL, font_opt },
{ "italic-font", required_argument, NULL, italic_font_opt },
{ "font-size", required_argument, NULL, font_size_opt },
{ "align", required_argument, NULL, align_opt },
{ "subtitles", required_argument, NULL, subtitles_opt },
@ -683,7 +688,11 @@ int main(int argc, char *argv[])
break;
case font_opt:
m_font_path = optarg;
m_has_font = true;
m_asked_for_font = true;
break;
case italic_font_opt:
m_italic_font_path = optarg;
m_asked_for_italic_font = true;
break;
case font_size_opt:
{
@ -772,12 +781,18 @@ int main(int argc, char *argv[])
return 0;
}
if(m_has_font && !Exists(m_font_path))
if(m_asked_for_font && !Exists(m_font_path))
{
PrintFileNotFound(m_font_path);
return 0;
}
if(m_asked_for_italic_font && !Exists(m_italic_font_path))
{
PrintFileNotFound(m_italic_font_path);
return 0;
}
if(m_has_external_subtitles && !Exists(m_external_subtitles_path))
{
PrintFileNotFound(m_external_subtitles_path);
@ -896,6 +911,7 @@ int main(int argc, char *argv[])
!m_player_subtitles.Open(m_omx_reader.SubtitleStreamCount(),
std::move(external_subtitles),
m_font_path,
m_italic_font_path,
m_font_size,
m_centered,
m_subtitle_lines,