Consider BorderStyle value before applying OutlineColour as background

This commit is contained in:
Egor Neliuba 2022-04-04 18:26:31 +03:00
parent 388b8d1d88
commit 2bd348effd
4 changed files with 82 additions and 13 deletions

View File

@ -320,7 +320,7 @@ public final class SsaDecoder extends SimpleSubtitleDecoder {
/* end= */ spannableText.length(), /* end= */ spannableText.length(),
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
} }
if (style.outlineColor != null) { if (style.borderStyle == SsaStyle.SSA_BORDER_STYLE_BOX && style.outlineColor != null) {
spannableText.setSpan( spannableText.setSpan(
new BackgroundColorSpan(style.outlineColor), new BackgroundColorSpan(style.outlineColor),
/* start= */ 0, /* start= */ 0,

View File

@ -92,6 +92,31 @@ import java.util.regex.Pattern;
public static final int SSA_ALIGNMENT_TOP_CENTER = 8; public static final int SSA_ALIGNMENT_TOP_CENTER = 8;
public static final int SSA_ALIGNMENT_TOP_RIGHT = 9; public static final int SSA_ALIGNMENT_TOP_RIGHT = 9;
/**
* The SSA/ASS BorderStyle.
*
* <p>Allowed values:
*
* <ul>
* <li>{@link #SSA_BORDER_STYLE_UNKNOWN}
* <li>{@link #SSA_BORDER_STYLE_OUTLINE}
* <li>{@link #SSA_BORDER_STYLE_BOX}
* </ul>
*/
@Target(TYPE_USE)
@IntDef({
SSA_BORDER_STYLE_UNKNOWN,
SSA_BORDER_STYLE_OUTLINE,
SSA_BORDER_STYLE_BOX,
})
@Documented
@Retention(SOURCE)
public @interface SsaBorderStyle {}
public static final int SSA_BORDER_STYLE_UNKNOWN = -1;
public static final int SSA_BORDER_STYLE_OUTLINE = 1;
public static final int SSA_BORDER_STYLE_BOX = 3;
public final String name; public final String name;
public final @SsaAlignment int alignment; public final @SsaAlignment int alignment;
@Nullable @ColorInt public final Integer primaryColor; @Nullable @ColorInt public final Integer primaryColor;
@ -101,6 +126,7 @@ import java.util.regex.Pattern;
public final boolean italic; public final boolean italic;
public final boolean underline; public final boolean underline;
public final boolean strikeout; public final boolean strikeout;
public final @SsaBorderStyle int borderStyle;
private SsaStyle( private SsaStyle(
String name, String name,
@ -111,7 +137,8 @@ import java.util.regex.Pattern;
boolean bold, boolean bold,
boolean italic, boolean italic,
boolean underline, boolean underline,
boolean strikeout) { boolean strikeout,
int borderStyle) {
this.name = name; this.name = name;
this.alignment = alignment; this.alignment = alignment;
this.primaryColor = primaryColor; this.primaryColor = primaryColor;
@ -121,6 +148,7 @@ import java.util.regex.Pattern;
this.italic = italic; this.italic = italic;
this.underline = underline; this.underline = underline;
this.strikeout = strikeout; this.strikeout = strikeout;
this.borderStyle = borderStyle;
} }
@Nullable @Nullable
@ -157,7 +185,10 @@ import java.util.regex.Pattern;
format.underlineIndex != C.INDEX_UNSET format.underlineIndex != C.INDEX_UNSET
&& parseBooleanValue(styleValues[format.underlineIndex].trim()), && parseBooleanValue(styleValues[format.underlineIndex].trim()),
format.strikeoutIndex != C.INDEX_UNSET format.strikeoutIndex != C.INDEX_UNSET
&& parseBooleanValue(styleValues[format.strikeoutIndex].trim())); && parseBooleanValue(styleValues[format.strikeoutIndex].trim()),
format.borderStyleIndex != C.INDEX_UNSET
? parseBorderStyle(styleValues[format.borderStyleIndex].trim())
: SSA_BORDER_STYLE_UNKNOWN);
} catch (RuntimeException e) { } catch (RuntimeException e) {
Log.w(TAG, "Skipping malformed 'Style:' line: '" + styleLine + "'", e); Log.w(TAG, "Skipping malformed 'Style:' line: '" + styleLine + "'", e);
return null; return null;
@ -195,6 +226,30 @@ import java.util.regex.Pattern;
} }
} }
private static @SsaBorderStyle int parseBorderStyle(String borderStyleStr) {
try {
@SsaBorderStyle int borderStyle = Integer.parseInt(borderStyleStr.trim());
if (isValidBorderStyle(borderStyle)) {
return borderStyle;
}
} catch (NumberFormatException e) {
// Swallow the exception and return UNKNOWN below.
}
Log.w(TAG, "Ignoring unknown BorderStyle: " + borderStyleStr);
return SSA_BORDER_STYLE_UNKNOWN;
}
private static boolean isValidBorderStyle(@SsaBorderStyle int alignment) {
switch (alignment) {
case SSA_BORDER_STYLE_OUTLINE:
case SSA_BORDER_STYLE_BOX:
return true;
case SSA_BORDER_STYLE_UNKNOWN:
default:
return false;
}
}
/** /**
* Parses a SSA V4+ color expression. * Parses a SSA V4+ color expression.
* *
@ -269,6 +324,7 @@ import java.util.regex.Pattern;
public final int italicIndex; public final int italicIndex;
public final int underlineIndex; public final int underlineIndex;
public final int strikeoutIndex; public final int strikeoutIndex;
public final int borderStyleIndex;
public final int length; public final int length;
private Format( private Format(
@ -281,6 +337,7 @@ import java.util.regex.Pattern;
int italicIndex, int italicIndex,
int underlineIndex, int underlineIndex,
int strikeoutIndex, int strikeoutIndex,
int borderStyleIndex,
int length) { int length) {
this.nameIndex = nameIndex; this.nameIndex = nameIndex;
this.alignmentIndex = alignmentIndex; this.alignmentIndex = alignmentIndex;
@ -291,6 +348,7 @@ import java.util.regex.Pattern;
this.italicIndex = italicIndex; this.italicIndex = italicIndex;
this.underlineIndex = underlineIndex; this.underlineIndex = underlineIndex;
this.strikeoutIndex = strikeoutIndex; this.strikeoutIndex = strikeoutIndex;
this.borderStyleIndex = borderStyleIndex;
this.length = length; this.length = length;
} }
@ -310,6 +368,7 @@ import java.util.regex.Pattern;
int italicIndex = C.INDEX_UNSET; int italicIndex = C.INDEX_UNSET;
int underlineIndex = C.INDEX_UNSET; int underlineIndex = C.INDEX_UNSET;
int strikeoutIndex = C.INDEX_UNSET; int strikeoutIndex = C.INDEX_UNSET;
int borderStyleIndex = C.INDEX_UNSET;
String[] keys = String[] keys =
TextUtils.split(styleFormatLine.substring(SsaDecoder.FORMAT_LINE_PREFIX.length()), ","); TextUtils.split(styleFormatLine.substring(SsaDecoder.FORMAT_LINE_PREFIX.length()), ",");
for (int i = 0; i < keys.length; i++) { for (int i = 0; i < keys.length; i++) {
@ -341,6 +400,9 @@ import java.util.regex.Pattern;
case "strikeout": case "strikeout":
strikeoutIndex = i; strikeoutIndex = i;
break; break;
case "borderstyle":
borderStyleIndex = i;
break;
} }
} }
return nameIndex != C.INDEX_UNSET return nameIndex != C.INDEX_UNSET
@ -354,6 +416,7 @@ import java.util.regex.Pattern;
italicIndex, italicIndex,
underlineIndex, underlineIndex,
strikeoutIndex, strikeoutIndex,
borderStyleIndex,
keys.length) keys.length)
: null; : null;
} }

View File

@ -301,7 +301,7 @@ public final class SsaDecoderTest {
SsaDecoder decoder = new SsaDecoder(); SsaDecoder decoder = new SsaDecoder();
byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), STYLE_COLORS); byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), STYLE_COLORS);
Subtitle subtitle = decoder.decode(bytes, bytes.length, false); Subtitle subtitle = decoder.decode(bytes, bytes.length, false);
assertThat(subtitle.getEventTimeCount()).isEqualTo(16); assertThat(subtitle.getEventTimeCount()).isEqualTo(18);
// &H000000FF (AABBGGRR) -> #FFFF0000 (AARRGGBB) // &H000000FF (AABBGGRR) -> #FFFF0000 (AARRGGBB)
Spanned firstCueText = Spanned firstCueText =
(Spanned) Iterables.getOnlyElement(subtitle.getCues(subtitle.getEventTime(0))).text; (Spanned) Iterables.getOnlyElement(subtitle.getCues(subtitle.getEventTime(0))).text;
@ -342,11 +342,15 @@ public final class SsaDecoderTest {
(Spanned) Iterables.getOnlyElement(subtitle.getCues(subtitle.getEventTime(12))).text; (Spanned) Iterables.getOnlyElement(subtitle.getCues(subtitle.getEventTime(12))).text;
SpannedSubject.assertThat(seventhCueText) SpannedSubject.assertThat(seventhCueText)
.hasNoForegroundColorSpanBetween(0, seventhCueText.length()); .hasNoForegroundColorSpanBetween(0, seventhCueText.length());
// OutlineColour should be treated as background only when BorderStyle=3
Spanned eighthCueText = Spanned eighthCueText =
(Spanned) Iterables.getOnlyElement(subtitle.getCues(subtitle.getEventTime(14))).text; (Spanned) Iterables.getOnlyElement(subtitle.getCues(subtitle.getEventTime(14))).text;
SpannedSubject.assertThat(eighthCueText) SpannedSubject.assertThat(eighthCueText)
.hasBackgroundColorSpanBetween(0, eighthCueText.length()) .hasBackgroundColorSpanBetween(0, eighthCueText.length())
.withColor(Color.BLUE); .withColor(Color.BLUE);
Spanned ninthCueText =
(Spanned) Iterables.getOnlyElement(subtitle.getCues(subtitle.getEventTime(16))).text;
SpannedSubject.assertThat(ninthCueText).hasNoBackgroundColorSpanBetween(0, ninthCueText.length());
} }
@Test @Test

View File

@ -5,15 +5,16 @@ PlayResX: 1280
PlayResY: 720 PlayResY: 720
[V4+ Styles] [V4+ Styles]
Format: Name ,PrimaryColour,OutlineColour Format: Name ,PrimaryColour,OutlineColour,BorderStyle
Style: PrimaryColourStyleHexRed ,&H000000FF ,&H00000000 Style: PrimaryColourStyleHexRed ,&H000000FF ,&H00000000 ,3
Style: PrimaryColourStyleHexYellow ,&H0000FFFF ,&H00000000 Style: PrimaryColourStyleHexYellow ,&H0000FFFF ,&H00000000 ,3
Style: PrimaryColourStyleHexGreen ,&HFF00 ,&H00000000 Style: PrimaryColourStyleHexGreen ,&HFF00 ,&H00000000 ,3
Style: PrimaryColourStyleHexAlpha ,&HA00000FF ,&H00000000 Style: PrimaryColourStyleHexAlpha ,&HA00000FF ,&H00000000 ,3
Style: PrimaryColourStyleDecimal ,16711680 ,&H00000000 Style: PrimaryColourStyleDecimal ,16711680 ,&H00000000 ,3
Style: PrimaryColourStyleDecimalAlpha,2164195328 ,&H00000000 Style: PrimaryColourStyleDecimalAlpha,2164195328 ,&H00000000 ,3
Style: PrimaryColourStyleInvalid ,blue ,&H00000000 Style: PrimaryColourStyleInvalid ,blue ,&H00000000 ,3
Style: OutlineColourStyleBlue ,&H00000000 ,&H00FF0000 Style: OutlineColourStyleBlue ,&H00000000 ,&H00FF0000 ,3
Style: OutlineColourStyleIgnored ,&H00000000 ,&H00FF0000 ,1
[Events] [Events]
@ -26,3 +27,4 @@ Dialogue: 0:00:09.00,0:00:10.00,PrimaryColourStyleDecimal ,Fifth line in BLU
Dialogue: 0:00:11.00,0:00:12.00,PrimaryColourStyleDecimalAlpha,Sixth line in BLUE with alpha (2164195328). Dialogue: 0:00:11.00,0:00:12.00,PrimaryColourStyleDecimalAlpha,Sixth line in BLUE with alpha (2164195328).
Dialogue: 0:00:13.00,0:00:14.00,PrimaryColourInvalid ,Seventh line with invalid color. Dialogue: 0:00:13.00,0:00:14.00,PrimaryColourInvalid ,Seventh line with invalid color.
Dialogue: 0:00:15.00,0:00:16.00,OutlineColourStyleBlue ,Eighth line with BLUE (&H00FF0000) outline. Dialogue: 0:00:15.00,0:00:16.00,OutlineColourStyleBlue ,Eighth line with BLUE (&H00FF0000) outline.
Dialogue: 0:00:17.00,0:00:18.00,OutlineColourStyleIgnored ,Ninth line with ignored outline because BorderStyle is not 3.