From 8a723706d62a4621f9aa97a99af6997d25c72ef5 Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Fri, 18 Sep 2015 18:19:28 +0100 Subject: [PATCH] Provide more flexibility for setting caption font size. - Respect any padding set on SubtitleLayout. - Allow specification of absolute, fractional and fractional-ignoring-padding text sizes. --- .../exoplayer/demo/PlayerActivity.java | 16 +-- .../android/exoplayer/text/CuePainter.java | 39 ++++--- .../exoplayer/text/SubtitleLayout.java | 100 ++++++++++++++++-- 3 files changed, 117 insertions(+), 38 deletions(-) diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java b/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java index 97b9050967..765aaffdb2 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java @@ -562,17 +562,17 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, } private void configureSubtitleView() { - CaptionStyleCompat captionStyle; - float captionFontScale; + CaptionStyleCompat style; + float fontScale; if (Util.SDK_INT >= 19) { - captionStyle = getUserCaptionStyleV19(); - captionFontScale = getUserCaptionFontScaleV19(); + style = getUserCaptionStyleV19(); + fontScale = getUserCaptionFontScaleV19(); } else { - captionStyle = CaptionStyleCompat.DEFAULT; - captionFontScale = 1.0f; + style = CaptionStyleCompat.DEFAULT; + fontScale = 1.0f; } - subtitleLayout.setStyle(captionStyle); - subtitleLayout.setFontScale(captionFontScale); + subtitleLayout.setStyle(style); + subtitleLayout.setFractionalTextSize(SubtitleLayout.DEFAULT_TEXT_SIZE_FRACTION * fontScale); } @TargetApi(19) diff --git a/library/src/main/java/com/google/android/exoplayer/text/CuePainter.java b/library/src/main/java/com/google/android/exoplayer/text/CuePainter.java index 028eb4203d..06dbcf235a 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/CuePainter.java +++ b/library/src/main/java/com/google/android/exoplayer/text/CuePainter.java @@ -45,12 +45,6 @@ import android.util.Log; */ private static final float INNER_PADDING_RATIO = 0.125f; - /** - * Use the same line height ratio as WebVtt to match the display with the preview. - * WebVtt specifies line height as 5.3% of the viewport height. - */ - private static final float LINE_HEIGHT_FRACTION = 0.0533f; - /** * Temporary rectangle used for computing line bounds. */ @@ -77,7 +71,7 @@ import android.util.Log; private int windowColor; private int edgeColor; private int edgeType; - private float fontScale; + private float textSizePx; private float bottomPaddingFraction; private int parentLeft; private int parentTop; @@ -124,7 +118,7 @@ import android.util.Log; * @param cue The cue to draw. * @param applyEmbeddedStyles Whether styling embedded within the cue should be applied. * @param style The style to use when drawing the cue text. - * @param fontScale The font scale. + * @param textSizePx The text size to use when drawing the cue text, in pixels. * @param bottomPaddingFraction The bottom padding fraction to apply when {@link Cue#line} is * {@link Cue#UNSET_VALUE}, as a fraction of the viewport height * @param canvas The canvas into which to draw. @@ -133,7 +127,7 @@ import android.util.Log; * @param cueBoxRight The right position of the enclosing cue box. * @param cueBoxBottom The bottom position of the enclosing cue box. */ - public void draw(Cue cue, boolean applyEmbeddedStyles, CaptionStyleCompat style, float fontScale, + public void draw(Cue cue, boolean applyEmbeddedStyles, CaptionStyleCompat style, float textSizePx, float bottomPaddingFraction, Canvas canvas, int cueBoxLeft, int cueBoxTop, int cueBoxRight, int cueBoxBottom) { CharSequence cueText = cue.text; @@ -155,7 +149,7 @@ import android.util.Log; && this.edgeType == style.edgeType && this.edgeColor == style.edgeColor && Util.areEqual(this.textPaint.getTypeface(), style.typeface) - && this.fontScale == fontScale + && this.textSizePx == textSizePx && this.bottomPaddingFraction == bottomPaddingFraction && this.parentLeft == cueBoxLeft && this.parentTop == cueBoxTop @@ -176,7 +170,7 @@ import android.util.Log; this.edgeType = style.edgeType; this.edgeColor = style.edgeColor; this.textPaint.setTypeface(style.typeface); - this.fontScale = fontScale; + this.textSizePx = textSizePx; this.bottomPaddingFraction = bottomPaddingFraction; this.parentLeft = cueBoxLeft; this.parentTop = cueBoxTop; @@ -186,9 +180,8 @@ import android.util.Log; int parentWidth = parentRight - parentLeft; int parentHeight = parentBottom - parentTop; - float textSize = LINE_HEIGHT_FRACTION * parentHeight * fontScale; - textPaint.setTextSize(textSize); - int textPaddingX = (int) (textSize * INNER_PADDING_RATIO + 0.5f); + textPaint.setTextSize(textSizePx); + int textPaddingX = (int) (textSizePx * INNER_PADDING_RATIO + 0.5f); int availableWidth = parentWidth - textPaddingX * 2; if (availableWidth <= 0) { Log.w(TAG, "Skipped drawing subtitle cue (insufficient space)"); @@ -207,12 +200,8 @@ import android.util.Log; } textWidth += textPaddingX * 2; - int textLeft = (parentWidth - textWidth) / 2; - int textRight = textLeft + textWidth; - int textTop = parentBottom - textHeight - - (int) (parentHeight * bottomPaddingFraction); - int textBottom = textTop + textHeight; - + int textLeft; + int textRight; if (cue.position != Cue.UNSET_VALUE) { if (cue.alignment == Alignment.ALIGN_OPPOSITE) { textRight = (parentWidth * cue.position) / 100 + parentLeft; @@ -221,7 +210,13 @@ import android.util.Log; textLeft = (parentWidth * cue.position) / 100 + parentLeft; textRight = Math.min(textLeft + textWidth, parentRight); } + } else { + textLeft = (parentWidth - textWidth) / 2; + textRight = textLeft + textWidth; } + + int textTop; + int textBottom; if (cue.line != Cue.UNSET_VALUE) { textTop = (parentHeight * cue.line) / 100 + parentTop; textBottom = textTop + textHeight; @@ -229,7 +224,11 @@ import android.util.Log; textTop = parentBottom - textHeight; textBottom = parentBottom; } + } else { + textTop = parentBottom - textHeight - (int) (parentHeight * bottomPaddingFraction); + textBottom = textTop + textHeight; } + textWidth = textRight - textLeft; // Update the derived drawing variables. diff --git a/library/src/main/java/com/google/android/exoplayer/text/SubtitleLayout.java b/library/src/main/java/com/google/android/exoplayer/text/SubtitleLayout.java index 929537b83c..08ddc551bd 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/SubtitleLayout.java +++ b/library/src/main/java/com/google/android/exoplayer/text/SubtitleLayout.java @@ -16,8 +16,10 @@ package com.google.android.exoplayer.text; import android.content.Context; +import android.content.res.Resources; import android.graphics.Canvas; import android.util.AttributeSet; +import android.util.TypedValue; import android.view.View; import java.util.ArrayList; @@ -28,16 +30,30 @@ import java.util.List; */ public final class SubtitleLayout extends View { + /** + * The default fractional text size. + * + * @see #setFractionalTextSize(float, boolean) + */ + public static final float DEFAULT_TEXT_SIZE_FRACTION = 0.0533f; + /** * The default bottom padding to apply when {@link Cue#line} is {@link Cue#UNSET_VALUE}, as a * fraction of the viewport height. + * + * @see #setBottomPaddingFraction(float) */ public static final float DEFAULT_BOTTOM_PADDING_FRACTION = 0.08f; + private static final int FRACTIONAL = 0; + private static final int FRACTIONAL_IGNORE_PADDING = 1; + private static final int ABSOLUTE = 2; + private final List painters; private List cues; - private float fontScale; + private int textSizeType; + private float textSize; private boolean applyEmbeddedStyles; private CaptionStyleCompat style; private float bottomPaddingFraction; @@ -49,7 +65,8 @@ public final class SubtitleLayout extends View { public SubtitleLayout(Context context, AttributeSet attrs) { super(context, attrs); painters = new ArrayList<>(); - fontScale = 1; + textSizeType = FRACTIONAL; + textSize = DEFAULT_TEXT_SIZE_FRACTION; applyEmbeddedStyles = true; style = CaptionStyleCompat.DEFAULT; bottomPaddingFraction = DEFAULT_BOTTOM_PADDING_FRACTION; @@ -75,15 +92,55 @@ public final class SubtitleLayout extends View { } /** - * Sets the scale of the font. + * Set the text size to a given unit and value. + *

+ * See {@link TypedValue} for the possible dimension units. * - * @param fontScale The scale of the font. + * @param unit The desired dimension unit. + * @param size The desired size in the given units. */ - public void setFontScale(float fontScale) { - if (this.fontScale == fontScale) { + public void setFixedTextSize(int unit, float size) { + Context context = getContext(); + Resources resources; + if (context == null) { + resources = Resources.getSystem(); + } else { + resources = context.getResources(); + } + setTextSize(ABSOLUTE, TypedValue.applyDimension(unit, size, resources.getDisplayMetrics())); + } + + /** + * Sets the text size to be a fraction of the view's remaining height after its top and bottom + * padding have been subtracted. + *

+ * Equivalent to {@code #setFractionalTextSize(fractionOfHeight, false)}. + * + * @param fractionOfHeight A fraction between 0 and 1. + */ + public void setFractionalTextSize(float fractionOfHeight) { + setFractionalTextSize(fractionOfHeight, false); + } + + /** + * Sets the text size to be a fraction of the height of this view. + * + * @param fractionOfHeight A fraction between 0 and 1. + * @param ignorePadding Set to true if {@code fractionOfHeight} should be interpreted as a + * fraction of this view's height ignoring any top and bottom padding. Set to false if + * {@code fractionOfHeight} should be interpreted as a fraction of this view's remaining + * height after the top and bottom padding has been subtracted. + */ + public void setFractionalTextSize(float fractionOfHeight, boolean ignorePadding) { + setTextSize(ignorePadding ? FRACTIONAL_IGNORE_PADDING : FRACTIONAL, fractionOfHeight); + } + + private void setTextSize(int textSizeType, float textSize) { + if (this.textSizeType == textSizeType && this.textSize == textSize) { return; } - this.fontScale = fontScale; + this.textSizeType = textSizeType; + this.textSize = textSize; // Invalidate to trigger drawing. invalidate(); } @@ -118,7 +175,10 @@ public final class SubtitleLayout extends View { /** * Sets the bottom padding fraction to apply when {@link Cue#line} is {@link Cue#UNSET_VALUE}, - * as a fraction of the viewport height. + * as a fraction of the view's remaining height after its top and bottom padding have been + * subtracted. + *

+ * Note that this padding is applied in addition to any standard view padding. * * @param bottomPaddingFraction The bottom padding fraction. */ @@ -134,9 +194,29 @@ public final class SubtitleLayout extends View { @Override public void dispatchDraw(Canvas canvas) { int cueCount = (cues == null) ? 0 : cues.size(); + int rawTop = getTop(); + int rawBottom = getBottom(); + + // Calculate the bounds after padding is taken into account. + int left = getLeft() + getPaddingLeft(); + int top = rawTop + getPaddingTop(); + int right = getRight() + getPaddingRight(); + int bottom = rawBottom - getPaddingBottom(); + if (bottom <= top || right <= left) { + // No space to draw subtitles. + return; + } + + float textSizePx = textSizeType == ABSOLUTE ? textSize + : textSize * (textSizeType == FRACTIONAL ? (bottom - top) : (rawBottom - rawTop)); + if (textSizePx <= 0) { + // Text has no height. + return; + } + for (int i = 0; i < cueCount; i++) { - painters.get(i).draw(cues.get(i), applyEmbeddedStyles, style, fontScale, - bottomPaddingFraction, canvas, getLeft(), getTop(), getRight(), getBottom()); + painters.get(i).draw(cues.get(i), applyEmbeddedStyles, style, textSizePx, + bottomPaddingFraction, canvas, left, top, right, bottom); } }