From 1e72e1acfffeee03971286fae720fb9eaf5b98d1 Mon Sep 17 00:00:00 2001 From: Nicolas Noury Date: Wed, 4 Dec 2019 15:50:11 +0100 Subject: [PATCH] Introduce edgeLayout to keep outline color Fix outline style subtitle When a ForegroundColorSpan changes the foreground color, it is also applied to the outline painter. In order to keep the correct color, one needs to filter out theses span. We do this with a new cue that is our text source for the specific edgeLayout. Take care to only apply background color once Test: Manual check - Subtitle view can show custom color subtitles from specific Subtitle Renderer and outline is shown correctly using user defined color. --- .../exoplayer2/ui/SubtitlePainter.java | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitlePainter.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitlePainter.java index 76768804df..114d4cfe04 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitlePainter.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitlePainter.java @@ -33,6 +33,7 @@ import android.text.TextPaint; import android.text.TextUtils; import android.text.style.AbsoluteSizeSpan; import android.text.style.BackgroundColorSpan; +import android.text.style.ForegroundColorSpan; import android.text.style.RelativeSizeSpan; import android.util.DisplayMetrics; import androidx.annotation.Nullable; @@ -98,6 +99,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; // Derived drawing variables. private @MonotonicNonNull StaticLayout textLayout; + private @MonotonicNonNull StaticLayout edgeLayout; private int textLeft; private int textTop; private int textPaddingX; @@ -286,11 +288,32 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; } } + // Remove embedded font color to not destroy edges, otherwise it overrides edge color + SpannableStringBuilder cueTextEdge = new SpannableStringBuilder(cueText); + if(edgeType == CaptionStyleCompat.EDGE_TYPE_OUTLINE) { + int cueLength = cueTextEdge.length(); + ForegroundColorSpan[] foregroundColorSpans = cueTextEdge + .getSpans(0, cueLength, ForegroundColorSpan.class); + for (ForegroundColorSpan foregroundColorSpan : foregroundColorSpans) { + cueTextEdge.removeSpan(foregroundColorSpan); + } + } + + // EDGE_TYPE_NONE & EDGE_TYPE_DROP_SHADOW both paint in one pass, they ignore cueTextEdge. + // Otherwise we use two painters, and one need to apply the background in the first one only if (Color.alpha(backgroundColor) > 0) { - SpannableStringBuilder newCueText = new SpannableStringBuilder(cueText); - newCueText.setSpan( - new BackgroundColorSpan(backgroundColor), 0, newCueText.length(), Spanned.SPAN_PRIORITY); - cueText = newCueText; + if (edgeType == CaptionStyleCompat.EDGE_TYPE_NONE || + edgeType == CaptionStyleCompat.EDGE_TYPE_DROP_SHADOW) { + SpannableStringBuilder newCueText = new SpannableStringBuilder(cueText); + newCueText.setSpan( + new BackgroundColorSpan(backgroundColor), 0, newCueText.length(), Spanned.SPAN_PRIORITY); + cueText = newCueText; + } else { + SpannableStringBuilder newCueText = new SpannableStringBuilder(cueTextEdge); + newCueText.setSpan( + new BackgroundColorSpan(backgroundColor), 0, newCueText.length(), Spanned.SPAN_PRIORITY); + cueTextEdge = newCueText; + } } Alignment textAlignment = cueTextAlignment == null ? Alignment.ALIGN_CENTER : cueTextAlignment; @@ -366,6 +389,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; // Update the derived drawing variables. this.textLayout = new StaticLayout(cueText, textPaint, textWidth, textAlignment, spacingMult, spacingAdd, true); + this.edgeLayout = new StaticLayout(cueTextEdge, textPaint, textWidth, textAlignment, spacingMult, + spacingAdd, true); this.textLeft = textLeft; this.textTop = textTop; this.textPaddingX = textPaddingX; @@ -425,7 +450,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; textPaint.setStrokeWidth(outlineWidth); textPaint.setColor(edgeColor); textPaint.setStyle(Style.FILL_AND_STROKE); - layout.draw(canvas); + edgeLayout.draw(canvas); } else if (edgeType == CaptionStyleCompat.EDGE_TYPE_DROP_SHADOW) { textPaint.setShadowLayer(shadowRadius, shadowOffset, shadowOffset, edgeColor); } else if (edgeType == CaptionStyleCompat.EDGE_TYPE_RAISED @@ -437,7 +462,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; textPaint.setColor(foregroundColor); textPaint.setStyle(Style.FILL); textPaint.setShadowLayer(shadowRadius, -offset, -offset, colorUp); - layout.draw(canvas); + edgeLayout.draw(canvas); textPaint.setShadowLayer(shadowRadius, offset, offset, colorDown); }