#include "compose.hpp"
#include <boost/algorithm/string.hpp>
#include <boost/bind/bind.hpp>
+#include <cstdlib>
#include <iostream>
#include <vector>
Colour
h_colour (string s)
{
- /* There are both BGR and ABGR versions of these colours */
- if ((s.length() != 8 && s.length() != 10) || s[0] != '&' || s[1] != 'H') {
+ if (s.empty() || s[0] != '&' || s[1] != 'H') {
throw SSAError(String::compose("Badly formatted colour tag %1", s));
}
- int ir, ig, ib;
- /* XXX: ignoring alpha channel here; note that 00 is opaque and FF is transparent */
- int const off = s.length() == 10 ? 4 : 2;
- if (sscanf(s.c_str() + off, "%2x%2x%2x", &ib, &ig, &ir) < 3) {
- throw SSAError(String::compose("Badly formatted colour tag %1", s));
+
+ auto start = s.c_str();
+ auto const end = start + s.length();
+ while (start < end && (*start == '&' || *start == 'H')) {
+ ++start;
}
- return sub::Colour(ir / 255.0, ig / 255.0, ib / 255.0);
+
+ auto const colour = strtoll(start, nullptr, 16);
+
+ /* XXX: ignoring alpha channel here; note that 00 is opaque and FF is transparent */
+ return sub::Colour(
+ ((colour & 0x000000ff) >> 0) / 255.0,
+ ((colour & 0x0000ff00) >> 8) / 255.0,
+ ((colour & 0x00ff0000) >> 16) / 255.0
+ );
}
class Style
effect = SHADOW;
}
} else if (keys[i] == "Alignment") {
- /* These values from libass' source code */
- switch ((raw_convert<int> (style[i]) - 1) % 3) {
- case 0:
- horizontal_reference = LEFT_OF_SCREEN;
- break;
- case 1:
- horizontal_reference = HORIZONTAL_CENTRE_OF_SCREEN;
- break;
- case 2:
- horizontal_reference = RIGHT_OF_SCREEN;
- break;
- }
- switch (raw_convert<int> (style[i]) & 12) {
- case 4:
+ if (style[i] == "7" || style[i] == "8" || style[i] == "9") {
vertical_reference = TOP_OF_SCREEN;
- break;
- case 8:
+ } else if (style[i] == "4" || style[i] == "5" || style[i] == "6") {
vertical_reference = VERTICAL_CENTRE_OF_SCREEN;
- break;
- case 0:
+ } else {
vertical_reference = BOTTOM_OF_SCREEN;
- break;
+ }
+ if (style[i] == "1" || style[i] == "4" || style[i] == "7") {
+ horizontal_reference = LEFT_OF_SCREEN;
+ } else if (style[i] == "3" || style[i] == "6" || style[i] == "9") {
+ horizontal_reference = RIGHT_OF_SCREEN;
+ } else {
+ horizontal_reference = HORIZONTAL_CENTRE_OF_SCREEN;
}
} else if (keys[i] == "MarginV") {
vertical_margin = raw_convert<int> (style[i]);
string name;
optional<string> font_name;
- int font_size;
+ int font_size; ///< points
Colour primary_colour;
/** outline colour */
optional<Colour> back_colour;
}
void
-SSAReader::parse_style (RawSubtitle& sub, string style, int play_res_x, int play_res_y)
+SSAReader::parse_style(RawSubtitle& sub, string style, int play_res_x, int play_res_y, Colour primary_colour)
{
if (style == "\\i1") {
sub.italic = true;
sub.vertical_position.proportional = raw_convert<float>(bits[2]) / play_res_y;
} else if (boost::starts_with(style, "\\fs")) {
SUB_ASSERT (style.length() > 3);
- sub.font_size.set_points (raw_convert<int>(style.substr(3)));
+ sub.font_size.set_proportional(raw_convert<float>(style.substr(3)) / play_res_y);
} else if (boost::starts_with(style, "\\c")) {
/* \c&Hbbggrr& */
- if (style.length() <= 2) {
+ if (style.length() > 2) {
+ sub.colour = h_colour(style.substr(2, style.length() - 3));
+ } else if (style.length() == 2) {
+ sub.colour = primary_colour;
+ } else {
throw SSAError(String::compose("Badly formatted colour tag %1", style));
}
- sub.colour = h_colour (style.substr(2, style.length() - 3));
}
}
* @return List of RawSubtitles to represent line with vertical reference TOP_OF_SUBTITLE.
*/
vector<RawSubtitle>
-SSAReader::parse_line (RawSubtitle base, string line, int play_res_x, int play_res_y)
+SSAReader::parse_line(RawSubtitle base, string line, int play_res_x, int play_res_y, Colour primary_colour)
{
enum {
TEXT,
current.vertical_position.reference = BOTTOM_OF_SCREEN;
}
- if (!current.vertical_position.proportional) {
- current.vertical_position.proportional = 0;
- }
+ /* Any vertical_position that is set in base (and therefore current) is a margin, which
+ * we need to ignore if we end up vertically centering this subtitle.
+ * Clear out vertical_position from current; we'll re-add it from base later
+ * if required.
+ */
+ 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);
+ if (!current.font_size.proportional()) {
+ current.font_size.set_proportional(72.0 / play_res_y);
}
/* Count the number of 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 VERTICAL_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;
- }
+ /* There are vague indications that with ASS 1 point should equal 1 pixel */
+ double const line_size = current.font_size.proportional(play_res_y) * 1.2;
for (size_t i = 0; i < line.length(); ++i) {
char const c = line[i];
subs.push_back (current);
current.text = "";
}
- parse_style (current, style, play_res_x, play_res_y);
+ parse_style(current, style, play_res_x, play_res_y, primary_colour);
style = "";
}
subs.push_back (current);
}
+ /* Now we definitely know the vertical position reference we can finish off the position */
+ for (auto& sub: subs) {
+ switch (sub.vertical_position.reference.get()) {
+ case TOP_OF_SCREEN:
+ case TOP_OF_SUBTITLE:
+ /* Just re-add any margins we came in with */
+ sub.vertical_position.proportional = sub.vertical_position.proportional.get() + base.vertical_position.proportional.get_value_or(0);
+ break;
+ case VERTICAL_CENTRE_OF_SCREEN:
+ /* Margins are ignored, but we need to centre */
+ sub.vertical_position.proportional = sub.vertical_position.proportional.get() - ((line_breaks + 1) * line_size) / 2;
+ break;
+ case BOTTOM_OF_SCREEN:
+ /* Re-add margins and account for each line */
+ sub.vertical_position.proportional =
+ sub.vertical_position.proportional.get()
+ + base.vertical_position.proportional.get_value_or(0)
+ + line_breaks * line_size;
+ break;
+ }
+ }
+
return subs;
}
SUB_ASSERT (event_format.size() == event.size());
RawSubtitle sub;
+ optional<Style> style;
for (size_t i = 0; i < event.size(); ++i) {
trim (event[i]);
*/
trim_left_if (event[i], boost::is_any_of ("*"));
SUB_ASSERT (styles.find(event[i]) != styles.end());
- Style style = styles[event[i]];
- sub.font = style.font_name;
- sub.font_size = FontSize::from_points (style.font_size);
- sub.colour = style.primary_colour;
- sub.effect_colour = style.back_colour;
- sub.bold = style.bold;
- sub.italic = style.italic;
- sub.underline = style.underline;
- sub.effect = style.effect;
- sub.horizontal_position.reference = style.horizontal_reference;
- sub.vertical_position.reference = style.vertical_reference;
- sub.vertical_position.proportional = float(style.vertical_margin) / play_res_y;
+ style = styles[event[i]];
+ sub.font = style->font_name;
+ sub.font_size = FontSize::from_proportional(static_cast<float>(style->font_size) / play_res_y);
+ sub.colour = style->primary_colour;
+ sub.effect_colour = style->back_colour;
+ sub.bold = style->bold;
+ sub.italic = style->italic;
+ sub.underline = style->underline;
+ sub.effect = style->effect;
+ sub.horizontal_position.reference = style->horizontal_reference;
+ sub.vertical_position.reference = style->vertical_reference;
+ if (sub.vertical_position.reference != sub::VERTICAL_CENTRE_OF_SCREEN) {
+ 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;
+ if (event[i] != "0" && sub.vertical_position.reference != sub::VERTICAL_CENTRE_OF_SCREEN) {
+ /* Override the style if its non-zero */
+ sub.vertical_position.proportional = raw_convert<float>(event[i]) / play_res_y;
+ }
} else if (event_format[i] == "Text") {
- for (auto j: parse_line (sub, event[i], play_res_x, play_res_y)) {
+ for (auto j: parse_line(sub, event[i], play_res_x, play_res_y, style ? style->primary_colour : Colour(1, 1, 1))) {
_subs.push_back (j);
}
}