-
Notifications
You must be signed in to change notification settings - Fork 453
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for SSA (v4+) MarginL, MarginR, MarginV style #2008
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -351,7 +351,23 @@ private void parseDialogueLine( | |
.replace("\\N", "\n") | ||
.replace("\\n", "\n") | ||
.replace("\\h", "\u00A0"); | ||
Cue cue = createCue(text, style, styleOverrides, screenWidth, screenHeight); | ||
|
||
float dialogueMarginLeft = format.marginLeftIndex != C.INDEX_UNSET | ||
? SsaStyle.parseMargin(lineValues[format.marginLeftIndex]) : 0f; | ||
float dialogueMarginRight = format.marginRightIndex != C.INDEX_UNSET | ||
? SsaStyle.parseMargin(lineValues[format.marginRightIndex]) : 0f; | ||
float dialogueMarginVertical = format.marginVerticalIndex != C.INDEX_UNSET | ||
? SsaStyle.parseMargin(lineValues[format.marginVerticalIndex]) : 0f; | ||
|
||
Cue cue = createCue( | ||
text, | ||
style, | ||
styleOverrides, | ||
dialogueMarginLeft, | ||
dialogueMarginRight, | ||
dialogueMarginVertical, | ||
screenWidth, | ||
screenHeight); | ||
|
||
int startTimeIndex = addCuePlacerholderByTime(startTimeUs, cueTimesUs, cues); | ||
int endTimeIndex = addCuePlacerholderByTime(endTimeUs, cueTimesUs, cues); | ||
|
@@ -384,65 +400,14 @@ private static Cue createCue( | |
String text, | ||
@Nullable SsaStyle style, | ||
SsaStyle.Overrides styleOverrides, | ||
float dialogueMarginLeft, | ||
float dialogueMarginRight, | ||
float dialogueMarginVertical, | ||
float screenWidth, | ||
float screenHeight) { | ||
SpannableString spannableText = new SpannableString(text); | ||
Cue.Builder cue = new Cue.Builder().setText(spannableText); | ||
|
||
if (style != null) { | ||
if (style.primaryColor != null) { | ||
spannableText.setSpan( | ||
new ForegroundColorSpan(style.primaryColor), | ||
/* start= */ 0, | ||
/* end= */ spannableText.length(), | ||
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); | ||
} | ||
if (style.borderStyle == SsaStyle.SSA_BORDER_STYLE_BOX && style.outlineColor != null) { | ||
spannableText.setSpan( | ||
new BackgroundColorSpan(style.outlineColor), | ||
/* start= */ 0, | ||
/* end= */ spannableText.length(), | ||
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); | ||
} | ||
if (style.fontSize != Cue.DIMEN_UNSET && screenHeight != Cue.DIMEN_UNSET) { | ||
cue.setTextSize( | ||
style.fontSize / screenHeight, Cue.TEXT_SIZE_TYPE_FRACTIONAL_IGNORE_PADDING); | ||
} | ||
if (style.bold && style.italic) { | ||
spannableText.setSpan( | ||
new StyleSpan(Typeface.BOLD_ITALIC), | ||
/* start= */ 0, | ||
/* end= */ spannableText.length(), | ||
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); | ||
} else if (style.bold) { | ||
spannableText.setSpan( | ||
new StyleSpan(Typeface.BOLD), | ||
/* start= */ 0, | ||
/* end= */ spannableText.length(), | ||
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); | ||
} else if (style.italic) { | ||
spannableText.setSpan( | ||
new StyleSpan(Typeface.ITALIC), | ||
/* start= */ 0, | ||
/* end= */ spannableText.length(), | ||
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); | ||
} | ||
if (style.underline) { | ||
spannableText.setSpan( | ||
new UnderlineSpan(), | ||
/* start= */ 0, | ||
/* end= */ spannableText.length(), | ||
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); | ||
} | ||
if (style.strikeout) { | ||
spannableText.setSpan( | ||
new StrikethroughSpan(), | ||
/* start= */ 0, | ||
/* end= */ spannableText.length(), | ||
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); | ||
} | ||
} | ||
|
||
@SsaStyle.SsaAlignment int alignment; | ||
if (styleOverrides.alignment != SsaStyle.SSA_ALIGNMENT_UNKNOWN) { | ||
alignment = styleOverrides.alignment; | ||
|
@@ -461,11 +426,104 @@ private static Cue createCue( | |
cue.setPosition(styleOverrides.position.x / screenWidth); | ||
cue.setLine(styleOverrides.position.y / screenHeight, LINE_TYPE_FRACTION); | ||
} else { | ||
// TODO: Read the MarginL, MarginR and MarginV values from the Style & Dialogue lines. | ||
cue.setPosition(computeDefaultLineOrPosition(cue.getPositionAnchor())); | ||
cue.setLine(computeDefaultLineOrPosition(cue.getLineAnchor()), LINE_TYPE_FRACTION); | ||
} | ||
|
||
// Apply margins if there are no overrides and we have valid positions. | ||
if (styleOverrides.alignment == SsaStyle.SSA_ALIGNMENT_UNKNOWN | ||
&& styleOverrides.position == null | ||
&& cue.getPosition() != Cue.DIMEN_UNSET | ||
&& cue.getLine() != Cue.DIMEN_UNSET) { | ||
|
||
// Margin from Dialogue lines takes precedence over margin from Style line. | ||
float marginLeft = dialogueMarginLeft != 0f | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: there's a bit of naming confusion here imo
But this local This distinction is important, and fiddly, so I think we should make it clear with the variable naming - probably by tweaking the name of this local to distinguish it from the other two? |
||
? dialogueMarginLeft / screenWidth | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: imo double-nested ternaries are quite hard to read I would suggest unrolling this to: float marginLeft;
if (dialogueMarginLeft != 0f) {
marginLeft = dialogueMarginLeft / screenWidth;
} else if (style != null) {
marginLeft = style.marginLeft / screenWidth;
} else {
marginLeft = 0f;
} |
||
: style != null ? style.marginLeft / screenWidth : 0f; | ||
float marginRight = dialogueMarginRight != 0f | ||
? dialogueMarginRight / screenWidth | ||
: style != null ? style.marginRight / screenWidth : 0f; | ||
|
||
// Apply margin left, margin right. | ||
if (SsaStyle.hasLeftAlignment(style)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't you use the (related to comment above) |
||
cue.setPosition(cue.getPosition() + marginLeft); | ||
cue.setSize(1 - marginRight - marginLeft); | ||
} else if (SsaStyle.hasRightAlignment(style)) { | ||
cue.setPosition(cue.getPosition() - marginRight); | ||
cue.setSize(1 - marginRight - marginLeft); | ||
} else { | ||
// Center alignment or unknown. | ||
cue.setPosition(cue.getPosition() + (marginLeft - marginRight) / 2); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure I understand the maths here (or for the
In general applying these margins after the size/position has already been calculated seems hard to reason about - why not apply them as part of computing the size and position? |
||
cue.setSize(1 - marginRight - marginLeft); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this line is the same is all 3 |
||
} | ||
|
||
// Apply margin vertical, ignore it when alignment is middle. | ||
if (!SsaStyle.hasMiddleAlignment(style)) { | ||
float marginVertical = dialogueMarginVertical != 0f ? dialogueMarginVertical / screenHeight | ||
: style != null ? style.marginVertical / screenHeight : 0f; | ||
cue.setLine( | ||
cue.getLine() - (SsaStyle.hasTopAlignment(style) ? -marginVertical : marginVertical), | ||
LINE_TYPE_FRACTION); | ||
} | ||
} | ||
|
||
if (style == null) { | ||
return cue.build(); | ||
} | ||
|
||
// Apply rest of the styles. | ||
if (style.primaryColor != null) { | ||
spannableText.setSpan( | ||
new ForegroundColorSpan(style.primaryColor), | ||
/* start= */ 0, | ||
/* end= */ spannableText.length(), | ||
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); | ||
} | ||
if (style.borderStyle == SsaStyle.SSA_BORDER_STYLE_BOX && style.outlineColor != null) { | ||
spannableText.setSpan( | ||
new BackgroundColorSpan(style.outlineColor), | ||
/* start= */ 0, | ||
/* end= */ spannableText.length(), | ||
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); | ||
} | ||
if (style.fontSize != Cue.DIMEN_UNSET && screenHeight != Cue.DIMEN_UNSET) { | ||
cue.setTextSize( | ||
style.fontSize / screenHeight, Cue.TEXT_SIZE_TYPE_FRACTIONAL_IGNORE_PADDING); | ||
} | ||
if (style.bold && style.italic) { | ||
spannableText.setSpan( | ||
new StyleSpan(Typeface.BOLD_ITALIC), | ||
/* start= */ 0, | ||
/* end= */ spannableText.length(), | ||
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); | ||
} else if (style.bold) { | ||
spannableText.setSpan( | ||
new StyleSpan(Typeface.BOLD), | ||
/* start= */ 0, | ||
/* end= */ spannableText.length(), | ||
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); | ||
} else if (style.italic) { | ||
spannableText.setSpan( | ||
new StyleSpan(Typeface.ITALIC), | ||
/* start= */ 0, | ||
/* end= */ spannableText.length(), | ||
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); | ||
} | ||
if (style.underline) { | ||
spannableText.setSpan( | ||
new UnderlineSpan(), | ||
/* start= */ 0, | ||
/* end= */ spannableText.length(), | ||
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); | ||
} | ||
if (style.strikeout) { | ||
spannableText.setSpan( | ||
new StrikethroughSpan(), | ||
/* start= */ 0, | ||
/* end= */ spannableText.length(), | ||
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); | ||
} | ||
|
||
return cue.build(); | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we check specifically
styleOverrides.alignment
here when we've already resolvedalignment
from bothstyleOverrides
andstyle
on L411?(related comment below)