Skip to content

Commit

Permalink
Support range selector in glyph rendering path
Browse files Browse the repository at this point in the history
Also adds support for ranges specified as a percentage. Note that the provided sample has easing values included, which we don't support yet.
  • Loading branch information
allenchen1154 committed Jul 24, 2024
1 parent 6412316 commit dff21b8
Show file tree
Hide file tree
Showing 5 changed files with 1,892 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.airbnb.lottie.model.animatable;

import androidx.annotation.Nullable;
import com.airbnb.lottie.model.content.TextRangeUnits;

/**
* Defines an animated range of text that should have an [AnimatableTextProperties] applied to it.
Expand All @@ -9,14 +10,16 @@ public class AnimatableTextRangeSelector {
@Nullable public final AnimatableIntegerValue start;
@Nullable public final AnimatableIntegerValue end;
@Nullable public final AnimatableIntegerValue offset;
public final TextRangeUnits units;

public AnimatableTextRangeSelector(
@Nullable AnimatableIntegerValue start,
@Nullable AnimatableIntegerValue end,
@Nullable AnimatableIntegerValue offset
) {
@Nullable AnimatableIntegerValue offset,
TextRangeUnits units) {
this.start = start;
this.end = end;
this.offset = offset;
this.units = units;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.airbnb.lottie.model.content;

public enum TextRangeUnits {
PERCENT,
INDEX
}
38 changes: 26 additions & 12 deletions lottie/src/main/java/com/airbnb/lottie/model/layer/TextLayer.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.airbnb.lottie.model.FontCharacter;
import com.airbnb.lottie.model.animatable.AnimatableTextProperties;
import com.airbnb.lottie.model.content.ShapeGroup;
import com.airbnb.lottie.model.content.TextRangeUnits;
import com.airbnb.lottie.utils.Utils;
import com.airbnb.lottie.value.LottieValueCallback;

Expand Down Expand Up @@ -56,6 +57,7 @@ public class TextLayer extends BaseLayer {
private final TextKeyframeAnimation textAnimation;
private final LottieDrawable lottieDrawable;
private final LottieComposition composition;
private TextRangeUnits textRangeUnits = TextRangeUnits.INDEX;
@Nullable
private BaseKeyframeAnimation<Integer, Integer> colorAnimation;
@Nullable
Expand Down Expand Up @@ -142,6 +144,10 @@ public class TextLayer extends BaseLayer {
textRangeOffsetAnimation.addUpdateListener(this);
addAnimation(textRangeOffsetAnimation);
}

if (textProperties != null && textProperties.rangeSelector != null) {
textRangeUnits = textProperties.rangeSelector.units;
}
}

@Override
Expand All @@ -164,7 +170,7 @@ void drawLayer(Canvas canvas, Matrix parentMatrix, int parentAlpha) {
configurePaint(documentData, parentAlpha, 0);

if (lottieDrawable.useTextGlyphs()) {
drawTextWithGlyphs(documentData, parentMatrix, font, canvas);
drawTextWithGlyphs(documentData, parentMatrix, font, canvas, parentAlpha);
} else {
drawTextWithFont(documentData, font, canvas, parentAlpha);
}
Expand Down Expand Up @@ -216,26 +222,31 @@ private void configurePaint(DocumentData documentData, int parentAlpha, int inde
}

private boolean isIndexInRangeSelection(int indexInDocument) {
int textLength = textAnimation.getValue().text.length();
if (textRangeStartAnimation != null && textRangeEndAnimation != null) {
// After effects supports reversed text ranges where the start index is greater than the end index.
// For the purposes of determining if the given index is inside of the range, we take the start as the smaller value.
int rangeStartIndex = Math.min(textRangeStartAnimation.getValue(), textRangeEndAnimation.getValue());
int rangeEndIndex = Math.max(textRangeStartAnimation.getValue(), textRangeEndAnimation.getValue());
int rangeStart = Math.min(textRangeStartAnimation.getValue(), textRangeEndAnimation.getValue());
int rangeEnd = Math.max(textRangeStartAnimation.getValue(), textRangeEndAnimation.getValue());

if (textRangeOffsetAnimation != null) {
int offset = textRangeOffsetAnimation.getValue();
rangeStartIndex += offset;
rangeEndIndex += offset;
rangeStart += offset;
rangeEnd += offset;
}

return indexInDocument >= rangeStartIndex && indexInDocument < rangeEndIndex;
if (textRangeUnits == TextRangeUnits.INDEX) {
return indexInDocument >= rangeStart && indexInDocument < rangeEnd;
} else {
float currentIndexAsPercent = indexInDocument / (float) textLength * 100;
return currentIndexAsPercent >= rangeStart && currentIndexAsPercent < rangeEnd;
}
}

return true;
}

private void drawTextWithGlyphs(
DocumentData documentData, Matrix parentMatrix, Font font, Canvas canvas) {
DocumentData documentData, Matrix parentMatrix, Font font, Canvas canvas, int parentAlpha) {
float textSize;
if (textSizeCallbackAnimation != null) {
textSize = textSizeCallbackAnimation.getValue();
Expand Down Expand Up @@ -269,7 +280,7 @@ private void drawTextWithGlyphs(
canvas.save();

if (offsetCanvas(canvas, documentData, lineIndex, line.width)) {
drawGlyphTextLine(line.text, documentData, font, canvas, parentScale, fontScale, tracking);
drawGlyphTextLine(line.text, documentData, font, canvas, parentScale, fontScale, tracking, parentAlpha);
}

canvas.restore();
Expand All @@ -278,7 +289,7 @@ private void drawTextWithGlyphs(
}

private void drawGlyphTextLine(String text, DocumentData documentData,
Font font, Canvas canvas, float parentScale, float fontScale, float tracking) {
Font font, Canvas canvas, float parentScale, float fontScale, float tracking, int parentAlpha) {
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
int characterHash = FontCharacter.hashFor(c, font.getFamily(), font.getStyle());
Expand All @@ -287,7 +298,7 @@ private void drawGlyphTextLine(String text, DocumentData documentData,
// Something is wrong. Potentially, they didn't export the text as a glyph.
continue;
}
drawCharacterAsGlyph(character, fontScale, documentData, canvas);
drawCharacterAsGlyph(character, fontScale, documentData, canvas, i, parentAlpha);
float tx = (float) character.getWidth() * fontScale * Utils.dpScale() + tracking;
canvas.translate(tx, 0);
}
Expand Down Expand Up @@ -506,7 +517,10 @@ private void drawCharacterAsGlyph(
FontCharacter character,
float fontScale,
DocumentData documentData,
Canvas canvas) {
Canvas canvas,
int indexInDocument,
int parentAlpha) {
configurePaint(documentData, parentAlpha, indexInDocument);
List<ContentGroup> contentGroups = getContentsForCharacter(character);
for (int j = 0; j < contentGroups.size(); j++) {
Path path = contentGroups.get(j).getPath();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import com.airbnb.lottie.model.animatable.AnimatableTextProperties;
import com.airbnb.lottie.model.animatable.AnimatableTextRangeSelector;
import com.airbnb.lottie.model.animatable.AnimatableTextStyle;
import com.airbnb.lottie.model.content.LBlendMode;
import com.airbnb.lottie.model.content.TextRangeUnits;
import com.airbnb.lottie.parser.moshi.JsonReader;
import com.airbnb.lottie.value.Keyframe;

Expand All @@ -21,7 +23,8 @@ public class AnimatableTextPropertiesParser {
private static final JsonReader.Options ANIMATABLE_RANGE_PROPERTIES_NAMES = JsonReader.Options.of(
"s", // start
"e", // end
"o" // offset
"o", // offset
"r" // text range units (percent or index)
);
private static final JsonReader.Options ANIMATABLE_PROPERTIES_NAMES = JsonReader.Options.of(
"fc",
Expand Down Expand Up @@ -63,6 +66,7 @@ private static AnimatableTextRangeSelector parseAnimatableTextRangeSelector(Json
AnimatableIntegerValue start = null;
AnimatableIntegerValue end = null;
AnimatableIntegerValue offset = null;
TextRangeUnits units = null;

reader.beginObject();
while (reader.hasNext()) {
Expand All @@ -76,6 +80,15 @@ private static AnimatableTextRangeSelector parseAnimatableTextRangeSelector(Json
case 2: // offset
offset = AnimatableValueParser.parseInteger(reader, composition);
break;
case 3: // text range units (percent or index)
int textRangeUnits = reader.nextInt();
if (textRangeUnits != 1 && textRangeUnits != 2) {
composition.addWarning("Unsupported text range units: " + textRangeUnits);
units = TextRangeUnits.INDEX;
break;
}
units = textRangeUnits == 1 ? TextRangeUnits.PERCENT : TextRangeUnits.INDEX;
break;
default:
reader.skipName();
reader.skipValue();
Expand All @@ -88,7 +101,7 @@ private static AnimatableTextRangeSelector parseAnimatableTextRangeSelector(Json
start = new AnimatableIntegerValue(Collections.singletonList(new Keyframe<>(0)));
}

return new AnimatableTextRangeSelector(start, end, offset);
return new AnimatableTextRangeSelector(start, end, offset, units);
}

private static AnimatableTextStyle parseAnimatableTextStyle(
Expand Down
Loading

0 comments on commit dff21b8

Please sign in to comment.