diff options
| author | Carl Hetherington <cth@carlh.net> | 2016-06-09 15:32:34 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2016-06-09 15:32:34 +0100 |
| commit | ca9dfb8c67721ad667e204e22908f4e5c723c2ce (patch) | |
| tree | 512b6f335a077d26a9c5b8cd2054cc89a1be9834 | |
| parent | dca56d88f34daa66d0df30e966030f84e99b4bea (diff) | |
Various work on improving vertical position handling.
| -rw-r--r-- | src/font_size.cc | 6 | ||||
| -rw-r--r-- | src/font_size.h | 2 | ||||
| -rw-r--r-- | src/ssa_reader.cc | 92 | ||||
| -rw-r--r-- | src/ssa_reader.h | 6 | ||||
| -rw-r--r-- | src/vertical_position.cc | 2 | ||||
| -rw-r--r-- | test/data/test.ssa | 2 | ||||
| -rw-r--r-- | test/ssa_reader_test.cc | 26 |
7 files changed, 120 insertions, 16 deletions
diff --git a/src/font_size.cc b/src/font_size.cc index f3e6c89..5e5fd4c 100644 --- a/src/font_size.cc +++ b/src/font_size.cc @@ -48,3 +48,9 @@ FontSize::from_points (int p) s.set_points (p); return s; } + +bool +FontSize::specified () const +{ + return _proportional || _points; +} diff --git a/src/font_size.h b/src/font_size.h index 62bbd09..2ff4a7c 100644 --- a/src/font_size.h +++ b/src/font_size.h @@ -49,6 +49,8 @@ public: return _points; } + bool specified () const; + float proportional (int screen_height_in_points) const; int points (int screen_height_in_points) const; diff --git a/src/ssa_reader.cc b/src/ssa_reader.cc index c76a78d..b3df458 100644 --- a/src/ssa_reader.cc +++ b/src/ssa_reader.cc @@ -57,17 +57,21 @@ class Style { public: Style () - : font_size (24) + : font_size (72) , primary_colour (255, 255, 255) , bold (false) , italic (false) + , vertical_reference (BOTTOM_OF_SCREEN) + , vertical_margin (0) {} Style (string format_line, string style_line) - : font_size (24) + : font_size (72) , primary_colour (255, 255, 255) , bold (false) , italic (false) + , vertical_reference (BOTTOM_OF_SCREEN) + , vertical_margin (0) { vector<string> keys; split (keys, format_line, is_any_of (",")); @@ -99,6 +103,21 @@ public: if (style[i] == "1") { effect = SHADOW; } + } else if (keys[i] == "Alignment") { + /* These values from libass' source code */ + switch (raw_convert<int> (style[i]) & 12) { + case 4: + vertical_reference = TOP_OF_SCREEN; + break; + case 8: + vertical_reference = CENTRE_OF_SCREEN; + break; + case 0: + vertical_reference = BOTTOM_OF_SCREEN; + break; + } + } else if (keys[i] == "MarginV") { + vertical_margin = raw_convert<int> (style[i]); } } } @@ -112,6 +131,8 @@ public: bool bold; bool italic; optional<Effect> effect; + VerticalReference vertical_reference; + int vertical_margin; private: Colour colour (int c) const @@ -139,7 +160,7 @@ SSAReader::parse_time (string t) const } /** @param base RawSubtitle filled in with any required common values. - * @param line SSA line string. + * @param line SSA line string (i.e. just the subtitle, possibly with embedded stuff) * @return List of RawSubtitles to represent line with vertical reference TOP_OF_SUBTITLE. */ list<RawSubtitle> @@ -155,10 +176,46 @@ SSAReader::parse_line (RawSubtitle base, string line) RawSubtitle current = base; string style; - current.vertical_position.line = 0; - /* XXX: arbitrary */ - current.vertical_position.lines = 32; - current.vertical_position.reference = TOP_OF_SUBTITLE; + if (!current.vertical_position.reference) { + current.vertical_position.reference = BOTTOM_OF_SCREEN; + } + + if (!current.vertical_position.proportional) { + current.vertical_position.proportional = 0; + } + + /* We must have a font size, as there could be a margin specified + in pixels and in that case we must know how big the subtitle + lines are to work out the position on screen. + */ + if (!current.font_size.points()) { + current.font_size.set_points (72); + } + + /* Count the number of line breaks */ + int line_breaks = 0; + for (size_t i = 0; i < line.length() - 1; ++i) { + if (line[i] == '\\' && (line[i+1] == 'n' || line[i+1] == 'N')) { + ++line_breaks; + } + } + + /* Imagine that the screen is 792 points (i.e. 11 inches) high (as with DCP) */ + double const line_size = current.font_size.proportional(792) * 1.2; + + /* Tweak vertical_position accordingly */ + switch (current.vertical_position.reference.get()) { + case TOP_OF_SCREEN: + case TOP_OF_SUBTITLE: + /* Nothing to do */ + break; + case CENTRE_OF_SCREEN: + current.vertical_position.proportional = current.vertical_position.proportional.get() - ((line_breaks + 1) * line_size) / 2; + break; + case BOTTOM_OF_SCREEN: + current.vertical_position.proportional = current.vertical_position.proportional.get() + line_breaks * line_size; + break; + } for (size_t i = 0; i < line.length(); ++i) { char const c = line[i]; @@ -193,7 +250,12 @@ SSAReader::parse_line (RawSubtitle base, string line) if ((c == 'n' || c == 'N') && !current.text.empty ()) { subs.push_back (current); current.text = ""; - current.vertical_position.line = current.vertical_position.line.get() + 1; + /* Move down one line (1.2 times the font size) */ + if (current.vertical_position.reference.get() == BOTTOM_OF_SCREEN) { + current.vertical_position.proportional = current.vertical_position.proportional.get() - line_size; + } else { + current.vertical_position.proportional = current.vertical_position.proportional.get() + line_size; + } } state = TEXT; break; @@ -216,6 +278,7 @@ SSAReader::read (function<optional<string> ()> get_line) EVENTS } part = INFO; + int play_res_y = 288; map<string, Style> styles; string style_format_line; vector<string> event_format; @@ -253,6 +316,9 @@ SSAReader::read (function<optional<string> ()> get_line) switch (part) { case INFO: + if (type == "PlayResY") { + play_res_y = raw_convert<int> (body); + } break; case STYLES: if (type == "Format") { @@ -304,12 +370,10 @@ SSAReader::read (function<optional<string> ()> get_line) sub.bold = style.bold; sub.italic = style.italic; sub.effect = style.effect; - - /* XXX: arbitrary */ - sub.vertical_position.lines = 32; - sub.vertical_position.reference = TOP_OF_SUBTITLE; - sub.vertical_position.line = 0; - + sub.vertical_position.reference = style.vertical_reference; + sub.vertical_position.proportional = float(style.vertical_margin) / play_res_y; + } else if (event_format[i] == "MarginV") { + sub.vertical_position.proportional = raw_convert<float>(event[i]) / play_res_y; } else if (event_format[i] == "Text") { BOOST_FOREACH (sub::RawSubtitle j, parse_line (sub, event[i])) { _subs.push_back (j); diff --git a/src/ssa_reader.h b/src/ssa_reader.h index 1fe64f3..59c9e86 100644 --- a/src/ssa_reader.h +++ b/src/ssa_reader.h @@ -29,6 +29,12 @@ namespace sub { +/** @class SSAReader + * @brief Reader for SubStation Alpha (SSA) and Advanced Substation Alpha (ASS) subtitles. + * + * This reader implements some of the SSA and ASS "standards", as gathered from various + * documents on the web. + */ class SSAReader : public Reader { public: diff --git a/src/vertical_position.cc b/src/vertical_position.cc index a680188..817c366 100644 --- a/src/vertical_position.cc +++ b/src/vertical_position.cc @@ -18,10 +18,10 @@ */ #include "vertical_position.h" +#include <iostream> using namespace sub; - float VerticalPosition::fraction_from_screen_top () const { diff --git a/test/data/test.ssa b/test/data/test.ssa index 578c3d4..8ff209d 100644 --- a/test/data/test.ssa +++ b/test/data/test.ssa @@ -17,4 +17,4 @@ Style: Default,Arial,20,16777215,255,0,0,0,0,1,2,2,2,10,10,10,0,1 [Events] Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text Dialogue: Marked=0,0:00:01.23,0:00:04.55,Default,,0,0,0,,Hello world -Dialogue: Marked=0,0:00:05.74,0:00:11.00,Default,,0,0,900,,This is vertically moved +Dialogue: Marked=0,0:00:05.74,0:00:11.00,Default,,0,0,900,,This is vertically moved\nand has two lines. diff --git a/test/ssa_reader_test.cc b/test/ssa_reader_test.cc index 7689f4f..c62505a 100644 --- a/test/ssa_reader_test.cc +++ b/test/ssa_reader_test.cc @@ -140,6 +140,10 @@ BOOST_AUTO_TEST_CASE (ssa_reader_test3) BOOST_CHECK_EQUAL (i->to, sub::Time::from_hms (0, 0, 4, 550)); list<sub::Line>::iterator j = i->lines.begin(); BOOST_REQUIRE (j != i->lines.end ()); + BOOST_CHECK (j->vertical_position.proportional); + BOOST_CHECK_EQUAL (j->vertical_position.proportional.get(), 0); + BOOST_CHECK (j->vertical_position.reference); + BOOST_CHECK_EQUAL (j->vertical_position.reference.get(), sub::BOTTOM_OF_SCREEN); BOOST_REQUIRE_EQUAL (j->blocks.size(), 1); sub::Block b = j->blocks.front (); BOOST_CHECK_EQUAL (b.text, "Hello world"); @@ -155,6 +159,14 @@ BOOST_AUTO_TEST_CASE (ssa_reader_test3) BOOST_CHECK_EQUAL (i->to, sub::Time::from_hms (0, 0, 11, 0)); j = i->lines.begin(); BOOST_REQUIRE (j != i->lines.end ()); + BOOST_CHECK (j->vertical_position.proportional); + /* The first line should be 900 pixels and one line (20 + points, 1.2 times spaced, as a proportion of the total + screen height 729 points) up. + */ + BOOST_CHECK (fabs (j->vertical_position.proportional.get() - (900.0 / 1080) - (20.0 * 1.2 / 792)) < 1e-5); + BOOST_CHECK (j->vertical_position.reference); + BOOST_CHECK_EQUAL (j->vertical_position.reference.get(), sub::BOTTOM_OF_SCREEN); BOOST_REQUIRE_EQUAL (j->blocks.size(), 1); b = j->blocks.front (); BOOST_CHECK_EQUAL (b.text, "This is vertically moved"); @@ -163,6 +175,20 @@ BOOST_AUTO_TEST_CASE (ssa_reader_test3) BOOST_CHECK_EQUAL (b.bold, false); BOOST_CHECK_EQUAL (b.italic, false); BOOST_CHECK_EQUAL (b.underline, false); + ++j; + BOOST_CHECK (fabs (j->vertical_position.proportional.get() - (900.0 / 1080)) < 1e-5); + BOOST_CHECK (j->vertical_position.reference); + BOOST_CHECK_EQUAL (j->vertical_position.reference.get(), sub::BOTTOM_OF_SCREEN); + BOOST_REQUIRE_EQUAL (j->blocks.size(), 1); + b = j->blocks.front (); + BOOST_CHECK_EQUAL (b.text, "and has two lines."); + BOOST_CHECK_EQUAL (b.font.get(), "Arial"); + BOOST_CHECK_EQUAL (b.font_size.points().get(), 20); + BOOST_CHECK_EQUAL (b.bold, false); + BOOST_CHECK_EQUAL (b.italic, false); + BOOST_CHECK_EQUAL (b.underline, false); + ++j; + BOOST_REQUIRE (j == i->lines.end()); ++i; BOOST_REQUIRE (i == subs.end ()); |
