Merge branch 'drhill-dev-v2_imagesubs' into dev-v2

This commit is contained in:
Oliver Woodman 2017-02-20 12:47:00 +00:00
commit 276788bc50
2 changed files with 117 additions and 35 deletions

View File

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.text; package com.google.android.exoplayer2.text;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Bitmap;
import android.support.annotation.IntDef; import android.support.annotation.IntDef;
import android.text.Layout.Alignment; import android.text.Layout.Alignment;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@ -78,7 +79,8 @@ public class Cue {
public static final int LINE_TYPE_NUMBER = 1; public static final int LINE_TYPE_NUMBER = 1;
/** /**
* The cue text. Note the {@link CharSequence} may be decorated with styling spans. * The cue text, or null if this is an image cue. Note the {@link CharSequence} may be decorated
* with styling spans.
*/ */
public final CharSequence text; public final CharSequence text;
@ -87,6 +89,11 @@ public class Cue {
*/ */
public final Alignment textAlignment; public final Alignment textAlignment;
/**
* The cue image, or null if this is a text cue.
*/
public final Bitmap bitmap;
/** /**
* The position of the {@link #lineAnchor} of the cue box within the viewport in the direction * The position of the {@link #lineAnchor} of the cue box within the viewport in the direction
* orthogonal to the writing direction, or {@link #DIMEN_UNSET}. When set, the interpretation of * orthogonal to the writing direction, or {@link #DIMEN_UNSET}. When set, the interpretation of
@ -95,8 +102,8 @@ public class Cue {
* For horizontal text and {@link #lineType} equal to {@link #LINE_TYPE_FRACTION}, this is the * For horizontal text and {@link #lineType} equal to {@link #LINE_TYPE_FRACTION}, this is the
* fractional vertical position relative to the top of the viewport. * fractional vertical position relative to the top of the viewport.
*/ */
public final float line; public final float line;
/** /**
* The type of the {@link #line} value. * The type of the {@link #line} value.
* <p> * <p>
@ -122,9 +129,8 @@ public class Cue {
* {@code (line == -2 && lineAnchor == ANCHOR_TYPE_START)} position a cue so that only its first * {@code (line == -2 && lineAnchor == ANCHOR_TYPE_START)} position a cue so that only its first
* line is visible at the bottom of the viewport. * line is visible at the bottom of the viewport.
*/ */
@LineType public final int lineType;
@LineType
public final int lineType;
/** /**
* The cue box anchor positioned by {@link #line}. One of {@link #ANCHOR_TYPE_START}, * The cue box anchor positioned by {@link #line}. One of {@link #ANCHOR_TYPE_START},
* {@link #ANCHOR_TYPE_MIDDLE}, {@link #ANCHOR_TYPE_END} and {@link #TYPE_UNSET}. * {@link #ANCHOR_TYPE_MIDDLE}, {@link #ANCHOR_TYPE_END} and {@link #TYPE_UNSET}.
@ -133,9 +139,8 @@ public class Cue {
* and {@link #ANCHOR_TYPE_END} correspond to the top, middle and bottom of the cue box * and {@link #ANCHOR_TYPE_END} correspond to the top, middle and bottom of the cue box
* respectively. * respectively.
*/ */
@AnchorType public final int lineAnchor;
@AnchorType
public final int lineAnchor;
/** /**
* The fractional position of the {@link #positionAnchor} of the cue box within the viewport in * The fractional position of the {@link #positionAnchor} of the cue box within the viewport in
* the direction orthogonal to {@link #line}, or {@link #DIMEN_UNSET}. * the direction orthogonal to {@link #line}, or {@link #DIMEN_UNSET}.
@ -154,8 +159,7 @@ public class Cue {
* and {@link #ANCHOR_TYPE_END} correspond to the left, middle and right of the cue box * and {@link #ANCHOR_TYPE_END} correspond to the left, middle and right of the cue box
* respectively. * respectively.
*/ */
@AnchorType @AnchorType public final int positionAnchor;
public final int positionAnchor;
/** /**
* The size of the cue box in the writing direction specified as a fraction of the viewport size * The size of the cue box in the writing direction specified as a fraction of the viewport size
@ -174,7 +178,36 @@ public class Cue {
public final int windowColor; public final int windowColor;
/** /**
* Constructs a cue whose {@link #textAlignment} is null, whose type parameters are set to * Constructs an image cue whose type parameters are set to {@link #TYPE_UNSET} and whose
* dimension parameters are set to {@link #DIMEN_UNSET}.
*
* @param bitmap See {@link #bitmap}.
*/
public Cue(Bitmap bitmap) {
this(bitmap, DIMEN_UNSET, TYPE_UNSET, DIMEN_UNSET, TYPE_UNSET, DIMEN_UNSET);
}
/**
* Creates an image cue.
*
* @param horizontalPosition The position of the horizontal anchor within the viewport, expressed
* as a fraction of the viewport width.
* @param horizontalPositionAnchor The horizontal anchor. One of {@link #ANCHOR_TYPE_START},
* {@link #ANCHOR_TYPE_MIDDLE}, {@link #ANCHOR_TYPE_END} and {@link #TYPE_UNSET}.
* @param verticalPosition The position of the vertical anchor within the viewport, expressed as a
* fraction of the viewport height.
* @param verticalPositionAnchor The vertical anchor. One of {@link #ANCHOR_TYPE_START},
* {@link #ANCHOR_TYPE_MIDDLE}, {@link #ANCHOR_TYPE_END} and {@link #TYPE_UNSET}.
* @param width The width of the cue, expressed as a fraction of the viewport width.
*/
public Cue(Bitmap bitmap, float horizontalPosition, @AnchorType int horizontalPositionAnchor,
float verticalPosition, @AnchorType int verticalPositionAnchor, float width) {
this(null, null, bitmap, verticalPosition, LINE_TYPE_FRACTION, verticalPositionAnchor,
horizontalPosition, horizontalPositionAnchor, width, false, Color.BLACK);
}
/**
* Constructs a text cue whose {@link #textAlignment} is null, whose type parameters are set to
* {@link #TYPE_UNSET} and whose dimension parameters are set to {@link #DIMEN_UNSET}. * {@link #TYPE_UNSET} and whose dimension parameters are set to {@link #DIMEN_UNSET}.
* *
* @param text See {@link #text}. * @param text See {@link #text}.
@ -184,6 +217,8 @@ public class Cue {
} }
/** /**
* Creates a text cue.
*
* @param text See {@link #text}. * @param text See {@link #text}.
* @param textAlignment See {@link #textAlignment}. * @param textAlignment See {@link #textAlignment}.
* @param line See {@link #line}. * @param line See {@link #line}.
@ -200,6 +235,8 @@ public class Cue {
} }
/** /**
* Creates a text cue.
*
* @param text See {@link #text}. * @param text See {@link #text}.
* @param textAlignment See {@link #textAlignment}. * @param textAlignment See {@link #textAlignment}.
* @param line See {@link #line}. * @param line See {@link #line}.
@ -214,8 +251,16 @@ public class Cue {
public Cue(CharSequence text, Alignment textAlignment, float line, @LineType int lineType, public Cue(CharSequence text, Alignment textAlignment, float line, @LineType int lineType,
@AnchorType int lineAnchor, float position, @AnchorType int positionAnchor, float size, @AnchorType int lineAnchor, float position, @AnchorType int positionAnchor, float size,
boolean windowColorSet, int windowColor) { boolean windowColorSet, int windowColor) {
this(text, textAlignment, null, line, lineType, lineAnchor, position, positionAnchor, size,
windowColorSet, windowColor);
}
private Cue(CharSequence text, Alignment textAlignment, Bitmap bitmap, float line,
@LineType int lineType, @AnchorType int lineAnchor, float position,
@AnchorType int positionAnchor, float size, boolean windowColorSet, int windowColor) {
this.text = text; this.text = text;
this.textAlignment = textAlignment; this.textAlignment = textAlignment;
this.bitmap = bitmap;
this.line = line; this.line = line;
this.lineType = lineType; this.lineType = lineType;
this.lineAnchor = lineAnchor; this.lineAnchor = lineAnchor;

View File

@ -18,11 +18,13 @@ package com.google.android.exoplayer2.ui;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Paint.Join; import android.graphics.Paint.Join;
import android.graphics.Paint.Style; import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.graphics.RectF; import android.graphics.RectF;
import android.text.Layout.Alignment; import android.text.Layout.Alignment;
import android.text.StaticLayout; import android.text.StaticLayout;
@ -63,6 +65,7 @@ import com.google.android.exoplayer2.util.Util;
private final Paint paint; private final Paint paint;
// Previous input variables. // Previous input variables.
private Bitmap cueBitmap;
private CharSequence cueText; private CharSequence cueText;
private Alignment cueTextAlignment; private Alignment cueTextAlignment;
private float cueLine; private float cueLine;
@ -93,6 +96,7 @@ import com.google.android.exoplayer2.util.Util;
private int textLeft; private int textLeft;
private int textTop; private int textTop;
private int textPaddingX; private int textPaddingX;
private Rect bitmapRect;
@SuppressWarnings("ResourceType") @SuppressWarnings("ResourceType")
public SubtitlePainter(Context context) { public SubtitlePainter(Context context) {
@ -141,21 +145,25 @@ import com.google.android.exoplayer2.util.Util;
public void draw(Cue cue, boolean applyEmbeddedStyles, CaptionStyleCompat style, float textSizePx, public void draw(Cue cue, boolean applyEmbeddedStyles, CaptionStyleCompat style, float textSizePx,
float bottomPaddingFraction, Canvas canvas, int cueBoxLeft, int cueBoxTop, int cueBoxRight, float bottomPaddingFraction, Canvas canvas, int cueBoxLeft, int cueBoxTop, int cueBoxRight,
int cueBoxBottom) { int cueBoxBottom) {
CharSequence cueText = cue.text; boolean isTextCue = cue.bitmap == null;
CharSequence cueText = null;
Bitmap cueBitmap = null;
if (isTextCue) {
cueText = cue.text;
if (TextUtils.isEmpty(cueText)) { if (TextUtils.isEmpty(cueText)) {
// Nothing to draw. // Nothing to draw.
return; return;
} }
int windowColor = cue.windowColorSet ? cue.windowColor : style.windowColor;
if (!applyEmbeddedStyles) { if (!applyEmbeddedStyles) {
// Strip out any embedded styling. // Strip out any embedded styling.
cueText = cueText.toString(); cueText = cueText.toString();
windowColor = style.windowColor; }
} else {
cueBitmap = cue.bitmap;
} }
if (areCharSequencesEqual(this.cueText, cueText) if (areCharSequencesEqual(this.cueText, cueText)
&& Util.areEqual(this.cueTextAlignment, cue.textAlignment) && Util.areEqual(this.cueTextAlignment, cue.textAlignment)
&& this.cueBitmap == cueBitmap
&& this.cueLine == cue.line && this.cueLine == cue.line
&& this.cueLineType == cue.lineType && this.cueLineType == cue.lineType
&& Util.areEqual(this.cueLineAnchor, cue.lineAnchor) && Util.areEqual(this.cueLineAnchor, cue.lineAnchor)
@ -165,7 +173,7 @@ import com.google.android.exoplayer2.util.Util;
&& this.applyEmbeddedStyles == applyEmbeddedStyles && this.applyEmbeddedStyles == applyEmbeddedStyles
&& this.foregroundColor == style.foregroundColor && this.foregroundColor == style.foregroundColor
&& this.backgroundColor == style.backgroundColor && this.backgroundColor == style.backgroundColor
&& this.windowColor == windowColor && this.windowColor == style.windowColor
&& this.edgeType == style.edgeType && this.edgeType == style.edgeType
&& this.edgeColor == style.edgeColor && this.edgeColor == style.edgeColor
&& Util.areEqual(this.textPaint.getTypeface(), style.typeface) && Util.areEqual(this.textPaint.getTypeface(), style.typeface)
@ -176,12 +184,13 @@ import com.google.android.exoplayer2.util.Util;
&& this.parentRight == cueBoxRight && this.parentRight == cueBoxRight
&& this.parentBottom == cueBoxBottom) { && this.parentBottom == cueBoxBottom) {
// We can use the cached layout. // We can use the cached layout.
drawLayout(canvas); drawLayout(canvas, isTextCue);
return; return;
} }
this.cueText = cueText; this.cueText = cueText;
this.cueTextAlignment = cue.textAlignment; this.cueTextAlignment = cue.textAlignment;
this.cueBitmap = cue.bitmap;
this.cueLine = cue.line; this.cueLine = cue.line;
this.cueLineType = cue.lineType; this.cueLineType = cue.lineType;
this.cueLineAnchor = cue.lineAnchor; this.cueLineAnchor = cue.lineAnchor;
@ -191,7 +200,7 @@ import com.google.android.exoplayer2.util.Util;
this.applyEmbeddedStyles = applyEmbeddedStyles; this.applyEmbeddedStyles = applyEmbeddedStyles;
this.foregroundColor = style.foregroundColor; this.foregroundColor = style.foregroundColor;
this.backgroundColor = style.backgroundColor; this.backgroundColor = style.backgroundColor;
this.windowColor = windowColor; this.windowColor = style.windowColor;
this.edgeType = style.edgeType; this.edgeType = style.edgeType;
this.edgeColor = style.edgeColor; this.edgeColor = style.edgeColor;
this.textPaint.setTypeface(style.typeface); this.textPaint.setTypeface(style.typeface);
@ -202,6 +211,15 @@ import com.google.android.exoplayer2.util.Util;
this.parentRight = cueBoxRight; this.parentRight = cueBoxRight;
this.parentBottom = cueBoxBottom; this.parentBottom = cueBoxBottom;
if (isTextCue) {
setupTextLayout();
} else {
setupBitmapLayout();
}
drawLayout(canvas, isTextCue);
}
private void setupTextLayout() {
int parentWidth = parentRight - parentLeft; int parentWidth = parentRight - parentLeft;
int parentHeight = parentBottom - parentTop; int parentHeight = parentBottom - parentTop;
@ -256,7 +274,7 @@ import com.google.android.exoplayer2.util.Util;
if (cueLine >= 0) { if (cueLine >= 0) {
anchorPosition = Math.round(cueLine * firstLineHeight) + parentTop; anchorPosition = Math.round(cueLine * firstLineHeight) + parentTop;
} else { } else {
anchorPosition = Math.round((cueLine + 1) * firstLineHeight) + parentBottom; anchorPosition = Math.round(cueLine * firstLineHeight) + parentBottom;
} }
} }
textTop = cueLineAnchor == Cue.ANCHOR_TYPE_END ? anchorPosition - textHeight textTop = cueLineAnchor == Cue.ANCHOR_TYPE_END ? anchorPosition - textHeight
@ -279,16 +297,31 @@ import com.google.android.exoplayer2.util.Util;
this.textLeft = textLeft; this.textLeft = textLeft;
this.textTop = textTop; this.textTop = textTop;
this.textPaddingX = textPaddingX; this.textPaddingX = textPaddingX;
drawLayout(canvas);
} }
/** private void setupBitmapLayout() {
* Draws {@link #textLayout} into the provided canvas. int parentWidth = parentRight - parentLeft;
* int parentHeight = parentBottom - parentTop;
* @param canvas The canvas into which to draw. float anchorX = parentLeft + (parentWidth * cuePosition);
*/ float anchorY = parentTop + (parentHeight * cueLine);
private void drawLayout(Canvas canvas) { int width = (int) (parentWidth * cueSize);
int height = (int) (width * ((float) cueBitmap.getHeight() / cueBitmap.getWidth()));
int x = (int) (cueLineAnchor == Cue.ANCHOR_TYPE_END ? (anchorX - width)
: cueLineAnchor == Cue.ANCHOR_TYPE_MIDDLE ? (anchorX - (width / 2)) : anchorX);
int y = (int) (cuePositionAnchor == Cue.ANCHOR_TYPE_END ? (anchorY - width)
: cuePositionAnchor == Cue.ANCHOR_TYPE_MIDDLE ? (anchorY - (width / 2)) : anchorY);
bitmapRect = new Rect(x, y, x + width, y + height);
}
private void drawLayout(Canvas canvas, boolean isTextCue) {
if (isTextCue) {
drawTextLayout(canvas);
} else {
drawBitmapLayout(canvas);
}
}
private void drawTextLayout(Canvas canvas) {
final StaticLayout layout = textLayout; final StaticLayout layout = textLayout;
if (layout == null) { if (layout == null) {
// Nothing to draw. // Nothing to draw.
@ -347,6 +380,10 @@ import com.google.android.exoplayer2.util.Util;
canvas.restoreToCount(saveCount); canvas.restoreToCount(saveCount);
} }
private void drawBitmapLayout(Canvas canvas) {
canvas.drawBitmap(cueBitmap, null, bitmapRect, null);
}
/** /**
* This method is used instead of {@link TextUtils#equals(CharSequence, CharSequence)} because the * This method is used instead of {@link TextUtils#equals(CharSequence, CharSequence)} because the
* latter only checks the text of each sequence, and does not check for equality of styling that * latter only checks the text of each sequence, and does not check for equality of styling that