Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
private float textSize;
private CaptionStyleCompat style;
private float bottomPaddingFraction;
private int horizontalPadding = 0;

public CanvasSubtitleOutput(Context context) {
this(context, /* attrs= */ null);
Expand All @@ -56,6 +57,10 @@ public CanvasSubtitleOutput(Context context, @Nullable AttributeSet attrs) {
bottomPaddingFraction = DEFAULT_BOTTOM_PADDING_FRACTION;
}

public void setHorizontalPadding(int horizontalPadding) {
this.horizontalPadding = horizontalPadding;
}

@Override
public void update(
List<Cue> cues,
Expand All @@ -70,7 +75,7 @@ public void update(
this.bottomPaddingFraction = bottomPaddingFraction;
// Ensure we have sufficient painters.
while (painters.size() < cues.size()) {
painters.add(new SubtitlePainter(getContext()));
painters.add(new SubtitlePainter(getContext(), horizontalPadding));
}
// Invalidate to trigger drawing.
invalidate();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package androidx.media3.ui;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.style.LineBackgroundSpan;
import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;
import androidx.annotation.Px;

public class PaddingLineBackgroundSpan implements LineBackgroundSpan {

private final int lineBackgroundColor;
private final int horizontalPadding;
private final float left;
private final float top;
private final float right;
private final float bottom;
private final float measureScale;
@Nullable
private final BackgroundSpanInfo[] spanInfos;

public PaddingLineBackgroundSpan(
@ColorInt int lineBackgroundColor,
int horizontalPadding,
float left,
float top,
float right,
float bottom,
float measureScale,
@Nullable BackgroundSpanInfo[] spanInfos) {
this.lineBackgroundColor = lineBackgroundColor;
this.horizontalPadding = horizontalPadding;
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
this.measureScale = measureScale;
this.spanInfos = spanInfos;
}

@Override
public void drawBackground(
Canvas canvas,
Paint paint,
@Px int left,
@Px int right,
@Px int top,
@Px int baseline,
@Px int bottom,
CharSequence text,
int start,
int end,
int lineNumber) {
drawBackgroundWithPadding(canvas, paint, text, start, end);
}

private void drawBackgroundWithPadding(
Canvas canvas,
Paint paint,
CharSequence text,
int start,
int end) {
final int originColor = paint.getColor();
if (spanInfos != null && spanInfos.length > 0) {
float left = this.left;
float right;
int textPos = start;
for (int index = 0; index < spanInfos.length; index++) {
BackgroundSpanInfo info = spanInfos[index];
if (info.start > end) {
continue;
}
// draw line background
if (info.start > textPos && info.start < end) {
paint.setColor(lineBackgroundColor);
// draw left padding
if (index == 0) {
canvas.drawRect(this.left - horizontalPadding, top, this.left, bottom, paint);
}

float textWidth = measureText(paint, text, textPos, info.start);
textPos = info.start;
right = left + textWidth;
canvas.drawRect(left, top, right, bottom, paint);
left = right;
}
// draw span background
if (info.end <= end) {
paint.setColor(info.color);
// draw left padding
if (info.start == start) {
canvas.drawRect(this.left - horizontalPadding, top, this.left, bottom, paint);
}

float textWidth = measureText(paint, text, textPos, info.end);
textPos = info.end;
right = left + textWidth;
canvas.drawRect(left, top, right, bottom, paint);
left = right;
}

BackgroundSpanInfo nextInfo = index < spanInfos.length - 1 ? spanInfos[index + 1] : null;
if (nextInfo == null) {
// draw line background and right padding
paint.setColor(info.end == end ? info.color : lineBackgroundColor);
right = this.right;
canvas.drawRect(left, top, right + horizontalPadding, bottom, paint);
left = right;
}
}
} else {
paint.setColor(lineBackgroundColor);
canvas.drawRect(left - horizontalPadding, top, right + horizontalPadding, bottom, paint);
}
paint.setColor(originColor);
}

private float measureText(Paint paint, CharSequence text, int start, int end) {
if (start < 0 || end < start || end > text.length()) {
return 0f;
}
return paint.measureText(text, start, end) * measureScale;
}

public static class BackgroundSpanInfo implements Comparable<BackgroundSpanInfo> {

public final int start;
public final int end;
public final int color;

public BackgroundSpanInfo(int start, int end, int color) {
this.start = start;
this.end = end;
this.color = color;
}

@Override
public int compareTo(BackgroundSpanInfo info) {
return start - info.start;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.text.Layout.Alignment;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.StaticLayout;
Expand All @@ -41,6 +42,9 @@
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.Util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;

Expand Down Expand Up @@ -95,9 +99,10 @@
private int textTop;
private int textPaddingX;
private @MonotonicNonNull Rect bitmapRect;
private final int horizontalPadding;

@SuppressWarnings("ResourceType")
public SubtitlePainter(Context context) {
public SubtitlePainter(Context context, int horizontalPadding) {
int[] viewAttr = {android.R.attr.lineSpacingExtra, android.R.attr.lineSpacingMultiplier};
TypedArray styledAttributes = context.obtainStyledAttributes(null, viewAttr, 0, 0);
spacingAdd = styledAttributes.getDimensionPixelSize(0, 0);
Expand All @@ -122,6 +127,8 @@ public SubtitlePainter(Context context) {
bitmapPaint = new Paint();
bitmapPaint.setAntiAlias(true);
bitmapPaint.setFilterBitmap(true);

this.horizontalPadding = horizontalPadding;
}

/**
Expand Down Expand Up @@ -370,6 +377,9 @@ private void setupTextLayout() {
this.textLeft = textLeft;
this.textTop = textTop;
this.textPaddingX = textPaddingX;

// reset spannable text background span and draw padding area
setupPaddingSpan(backgroundColor, horizontalPadding);
}

@RequiresNonNull("cueBitmap")
Expand Down Expand Up @@ -470,6 +480,63 @@ private void drawTextLayout(Canvas canvas) {
canvas.restoreToCount(saveCount);
}

private void setupPaddingSpan(int backgroundColor, int horizontalPadding) {
if (horizontalPadding <= 0 || !(textLayout.getText() instanceof Spannable)) {
return;
}
List<PaddingLineBackgroundSpan.BackgroundSpanInfo> backgroundSpanInfos = new ArrayList<>();
int lineBackgroundColor = backgroundColor;
Spannable spannableText = (Spannable) textLayout.getText();
BackgroundColorSpan[] colorSpans = spannableText.getSpans(
0,
spannableText.length(),
BackgroundColorSpan.class);
if (colorSpans != null) {
for (BackgroundColorSpan span : colorSpans) {
int color = span.getBackgroundColor();
int spanStart = spannableText.getSpanStart(span);
int spanEnd = spannableText.getSpanEnd(span);
if (spanStart == 0 && spanEnd == spannableText.length()) {
lineBackgroundColor = color;
} else {
backgroundSpanInfos.add(
new PaddingLineBackgroundSpan.BackgroundSpanInfo(
spanStart,
spanEnd,
color));
}
// remove original background span, background will drawn by PaddingLineBackgroundSpan.
if (color != Color.TRANSPARENT) {
spannableText.removeSpan(span);
}
}
Collections.sort(backgroundSpanInfos); // sort by start position.
}

if (lineBackgroundColor != Color.TRANSPARENT) {
for (int line = 0; line < textLayout.getLineCount(); line++) {
final float lineWidth = textLayout.getLineMax(line);
int start = textLayout.getLineStart(line);
int end = textLayout.getLineVisibleEnd(line);
float measureScale = lineWidth / textPaint.measureText(spannableText, start, end);
spannableText.setSpan(
new PaddingLineBackgroundSpan(
lineBackgroundColor,
horizontalPadding,
textLayout.getLineLeft(line),
textLayout.getLineTop(line),
textLayout.getLineRight(line),
textLayout.getLineBottom(line),
measureScale,
backgroundSpanInfos.toArray(new PaddingLineBackgroundSpan.BackgroundSpanInfo[0])
),
start,
end,
Spanned.SPAN_PRIORITY);
}
}
}

@RequiresNonNull({"cueBitmap", "bitmapRect"})
private void drawBitmapLayout(Canvas canvas) {
canvas.drawBitmap(cueBitmap, /* src= */ null, bitmapRect, bitmapPaint);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,12 @@ public SubtitleView(Context context, @Nullable AttributeSet attrs) {
viewType = VIEW_TYPE_CANVAS;
}

public void setSubtitleHorizontalPadding(int horizontalPadding) {
if (innerSubtitleView instanceof CanvasSubtitleOutput) {
((CanvasSubtitleOutput) innerSubtitleView).setHorizontalPadding(horizontalPadding);
}
}

/**
* Sets the cues to be displayed by the view.
*
Expand Down