From 1364c01e09733b49eaaf48ce446cae43e453ec42 Mon Sep 17 00:00:00 2001 From: Arnold Szabo Date: Mon, 25 Jan 2021 23:35:10 +0100 Subject: [PATCH] Improve support of SSA (V4+) PrimaryColour style --- .../exoplayer2/text/ssa/SsaDecoder.java | 4 +- .../android/exoplayer2/text/ssa/SsaStyle.java | 31 ++++++++++++--- .../android/exoplayer2/util/ColorParser.java | 38 ++++++++++++++----- .../exoplayer2/util/ColorParserTest.java | 20 ++++++++-- 4 files changed, 72 insertions(+), 21 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java b/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java index fa66c49dfe..c14767667a 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java @@ -308,8 +308,8 @@ public final class SsaDecoder extends SimpleSubtitleDecoder { // Apply primary color. if (style != null) { - if (style.primaryColor != SsaStyle.SSA_COLOR_UNKNOWN) { - spannableText.setSpan(new ForegroundColorSpan(style.primaryColor), + if (style.primaryColor.isSet) { + spannableText.setSpan(new ForegroundColorSpan(style.primaryColor.value), 0, spannableText.length(), SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaStyle.java b/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaStyle.java index f2c0eb630c..bc6c729eb2 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaStyle.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaStyle.java @@ -89,9 +89,9 @@ import java.util.regex.Pattern; public final String name; @SsaAlignment public final int alignment; - @ColorInt public int primaryColor; + public SsaColor primaryColor; - private SsaStyle(String name, @SsaAlignment int alignment, @ColorInt int primaryColor) { + private SsaStyle(String name, @SsaAlignment int alignment, SsaColor primaryColor) { this.name = name; this.alignment = alignment; this.primaryColor = primaryColor; @@ -152,14 +152,33 @@ import java.util.regex.Pattern; } } - @ColorInt - private static int parsePrimaryColor(String primaryColorStr) { + private static SsaColor parsePrimaryColor(String primaryColorStr) { try { - return ColorParser.parseSsaColor(primaryColorStr); + return SsaColor.from(ColorParser.parseSsaColor(primaryColorStr.trim())); } catch (IllegalArgumentException ex) { Log.w(TAG, "Failed parsing color value: " + primaryColorStr); } - return SSA_COLOR_UNKNOWN; + return SsaColor.UNSET; + } + + /** + * Represents an SSA V4+ style color in ARGB format. + */ + /* package */ static final class SsaColor { + + public static SsaColor UNSET = new SsaColor(0, false); + + public final @ColorInt int value; + public final boolean isSet; + + private SsaColor(@ColorInt int value, boolean isSet) { + this.value = value; + this.isSet = isSet; + } + + public static SsaColor from(@ColorInt int value) { + return new SsaColor(value, true); + } } /** diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/ColorParser.java b/library/core/src/main/java/com/google/android/exoplayer2/util/ColorParser.java index 697c1695e8..7722962262 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/ColorParser.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/ColorParser.java @@ -75,17 +75,35 @@ public final class ColorParser { */ @ColorInt public static int parseSsaColor(String colorExpression) { - // SSA V4+ color format is &HAABBGGRR. - if (colorExpression.length() != 10 || !"&H".equals(colorExpression.substring(0, 2))) { - throw new IllegalArgumentException(); + // The SSA V4+ color can be represented in hex (&HAABBGGRR) or in decimal format (byte order + // AABBGGRR) and in both cases the alpha channel's value needs to be inverted as in case of SSA + // the 0xFF alpha value means transparent and 0x00 means opaque which is the opposite from the + // @ColorInt representation. + int abgr; + try { + // Parse color from hex format (&HAABBGGRR). + if (colorExpression.startsWith("&H")) { + StringBuilder rgbaStringBuilder = new StringBuilder(colorExpression); + if (colorExpression.length() < 10) { + // Add leading zeros if necessary. + while (rgbaStringBuilder.length() != 10) { + rgbaStringBuilder.insert(2, "0"); + } + } + abgr = (int) Long.parseLong(colorExpression.substring(2), 16); + } else { + // Parse color from decimal format (bytes order AABBGGRR). + abgr = (int) Long.parseLong(colorExpression); + } + } catch (NumberFormatException ex) { + throw new IllegalArgumentException(ex); } - // Convert &HAABBGGRR to #RRGGBBAA. - String rgba = new StringBuilder() - .append(colorExpression.substring(2)) - .append("#") - .reverse() - .toString(); - return parseColorInternal(rgba, true); + // Convert ABGR to ARGB. + int a = ((abgr >> 24) & 0xFF) ^ 0xFF; // Flip alpha. + int b = (abgr >> 16) & 0xFF; + int g = (abgr >> 8) & 0xFF; + int r = abgr & 0xff; + return Color.argb(a, r, g, b); } @ColorInt diff --git a/library/core/src/test/java/com/google/android/exoplayer2/util/ColorParserTest.java b/library/core/src/test/java/com/google/android/exoplayer2/util/ColorParserTest.java index a15ef95627..ab302e333d 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/util/ColorParserTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/util/ColorParserTest.java @@ -16,6 +16,8 @@ package com.google.android.exoplayer2.util; import static android.graphics.Color.BLACK; +import static android.graphics.Color.BLUE; +import static android.graphics.Color.GREEN; import static android.graphics.Color.RED; import static android.graphics.Color.WHITE; import static android.graphics.Color.YELLOW; @@ -66,9 +68,21 @@ public final class ColorParserTest { // Hex colors in ColorParser are RGBA, where-as {@link Color#parseColor} takes ARGB. assertThat(parseTtmlColor("#FFFFFF00")).isEqualTo(parseColor("#00FFFFFF")); assertThat(parseTtmlColor("#12345678")).isEqualTo(parseColor("#78123456")); - // SSA colors are in &HAABBGGRR format. - assertThat(parseSsaColor("&HFF0000FF")).isEqualTo(RED); - assertThat(parseSsaColor("&HFF00FFFF")).isEqualTo(YELLOW); + } + + @Test + public void ssaColorParsing() { + // Hex format (&HAABBGGRR). + assertThat(parseSsaColor("&H000000FF")).isEqualTo(RED); + assertThat(parseSsaColor("&H0000FFFF")).isEqualTo(YELLOW); + assertThat(parseSsaColor("&H400000FF")).isEqualTo(parseColor("#BFFF0000")); + // Leading zeros. + assertThat(parseSsaColor("&HFF")).isEqualTo(RED); + assertThat(parseSsaColor("&HFF00")).isEqualTo(GREEN); + assertThat(parseSsaColor("&HFF0000")).isEqualTo(BLUE); + // Decimal format (AABBGGRR byte order). + assertThat(parseSsaColor(/*#000000FF*/"255")).isEqualTo(parseColor("#FFFF0000")); + assertThat(parseSsaColor(/*#FF0000FF*/"4278190335")).isEqualTo(parseColor("#00FF0000")); } @Test