Merge "Allow TextAnimator TypefaceCaches to be shared" into udc-dev

This commit is contained in:
Hawkwood Glazier
2023-04-21 14:27:36 +00:00
committed by Android (Google) Code Review
3 changed files with 57 additions and 27 deletions

View File

@@ -24,12 +24,30 @@ import android.graphics.Canvas
import android.graphics.Typeface import android.graphics.Typeface
import android.graphics.fonts.Font import android.graphics.fonts.Font
import android.text.Layout import android.text.Layout
import android.text.TextPaint
import android.util.LruCache import android.util.LruCache
private const val DEFAULT_ANIMATION_DURATION: Long = 300 private const val DEFAULT_ANIMATION_DURATION: Long = 300
private const val TYPEFACE_CACHE_MAX_ENTRIES = 5 private const val TYPEFACE_CACHE_MAX_ENTRIES = 5
typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit
interface TypefaceVariantCache {
fun getTypefaceForVariant(fvar: String, targetPaint: TextPaint): Typeface?
}
class TypefaceVariantCacheImpl() : TypefaceVariantCache {
private val cache = LruCache<String, Typeface>(TYPEFACE_CACHE_MAX_ENTRIES)
override fun getTypefaceForVariant(fvar: String, targetPaint: TextPaint): Typeface? {
cache.get(fvar)?.let {
return it
}
targetPaint.fontVariationSettings = fvar
return targetPaint.typeface?.also { cache.put(fvar, it) }
}
}
/** /**
* This class provides text animation between two styles. * This class provides text animation between two styles.
* *
@@ -56,9 +74,19 @@ typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit
* ``` * ```
* </code> </pre> * </code> </pre>
*/ */
class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) { class TextAnimator(
layout: Layout,
private val invalidateCallback: () -> Unit,
) {
var typefaceCache: TypefaceVariantCache = TypefaceVariantCacheImpl()
get() = field
set(value) {
field = value
textInterpolator.typefaceCache = value
}
// Following two members are for mutable for testing purposes. // Following two members are for mutable for testing purposes.
public var textInterpolator: TextInterpolator = TextInterpolator(layout) public var textInterpolator: TextInterpolator = TextInterpolator(layout, typefaceCache)
public var animator: ValueAnimator = public var animator: ValueAnimator =
ValueAnimator.ofFloat(1f).apply { ValueAnimator.ofFloat(1f).apply {
duration = DEFAULT_ANIMATION_DURATION duration = DEFAULT_ANIMATION_DURATION
@@ -68,9 +96,7 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
} }
addListener( addListener(
object : AnimatorListenerAdapter() { object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) { override fun onAnimationEnd(animation: Animator?) = textInterpolator.rebase()
textInterpolator.rebase()
}
override fun onAnimationCancel(animation: Animator?) = textInterpolator.rebase() override fun onAnimationCancel(animation: Animator?) = textInterpolator.rebase()
} }
) )
@@ -116,8 +142,6 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
private val fontVariationUtils = FontVariationUtils() private val fontVariationUtils = FontVariationUtils()
private val typefaceCache = LruCache<String, Typeface>(TYPEFACE_CACHE_MAX_ENTRIES)
fun updateLayout(layout: Layout) { fun updateLayout(layout: Layout) {
textInterpolator.layout = layout textInterpolator.layout = layout
} }
@@ -220,12 +244,8 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
} }
if (!fvar.isNullOrBlank()) { if (!fvar.isNullOrBlank()) {
textInterpolator.targetPaint.typeface = typefaceCache.get(fvar) ?: run { textInterpolator.targetPaint.typeface =
textInterpolator.targetPaint.fontVariationSettings = fvar typefaceCache.getTypefaceForVariant(fvar, textInterpolator.targetPaint)
textInterpolator.targetPaint.typeface?.also {
typefaceCache.put(fvar, textInterpolator.targetPaint.typeface)
}
}
} }
if (color != null) { if (color != null) {
@@ -304,7 +324,8 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
weight = weight, weight = weight,
width = width, width = width,
opticalSize = opticalSize, opticalSize = opticalSize,
roundness = roundness,) roundness = roundness,
)
setTextStyle( setTextStyle(
fvar = fvar, fvar = fvar,
textSize = textSize, textSize = textSize,

View File

@@ -28,8 +28,10 @@ import com.android.internal.graphics.ColorUtils
import java.lang.Math.max import java.lang.Math.max
/** Provide text style linear interpolation for plain text. */ /** Provide text style linear interpolation for plain text. */
class TextInterpolator(layout: Layout) { class TextInterpolator(
layout: Layout,
var typefaceCache: TypefaceVariantCache,
) {
/** /**
* Returns base paint used for interpolation. * Returns base paint used for interpolation.
* *

View File

@@ -31,6 +31,7 @@ import android.text.TextPaint
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import java.io.File import java.io.File
@@ -64,6 +65,7 @@ private val END_PAINT = TextPaint(PAINT).apply {
@RunWith(AndroidTestingRunner::class) @RunWith(AndroidTestingRunner::class)
@SmallTest @SmallTest
class TextInterpolatorTest : SysuiTestCase() { class TextInterpolatorTest : SysuiTestCase() {
lateinit var typefaceCache: TypefaceVariantCache
private fun makeLayout( private fun makeLayout(
text: String, text: String,
@@ -75,11 +77,16 @@ class TextInterpolatorTest : SysuiTestCase() {
.setTextDirection(dir).build() .setTextDirection(dir).build()
} }
@Before
fun setup() {
typefaceCache = TypefaceVariantCacheImpl()
}
@Test @Test
fun testStartState() { fun testStartState() {
val layout = makeLayout(TEXT, PAINT) val layout = makeLayout(TEXT, PAINT)
val interp = TextInterpolator(layout) val interp = TextInterpolator(layout, typefaceCache)
interp.basePaint.set(START_PAINT) interp.basePaint.set(START_PAINT)
interp.onBasePaintModified() interp.onBasePaintModified()
@@ -98,7 +105,7 @@ class TextInterpolatorTest : SysuiTestCase() {
fun testEndState() { fun testEndState() {
val layout = makeLayout(TEXT, PAINT) val layout = makeLayout(TEXT, PAINT)
val interp = TextInterpolator(layout) val interp = TextInterpolator(layout, typefaceCache)
interp.basePaint.set(START_PAINT) interp.basePaint.set(START_PAINT)
interp.onBasePaintModified() interp.onBasePaintModified()
@@ -116,7 +123,7 @@ class TextInterpolatorTest : SysuiTestCase() {
fun testMiddleState() { fun testMiddleState() {
val layout = makeLayout(TEXT, PAINT) val layout = makeLayout(TEXT, PAINT)
val interp = TextInterpolator(layout) val interp = TextInterpolator(layout, typefaceCache)
interp.basePaint.set(START_PAINT) interp.basePaint.set(START_PAINT)
interp.onBasePaintModified() interp.onBasePaintModified()
@@ -138,7 +145,7 @@ class TextInterpolatorTest : SysuiTestCase() {
fun testRebase() { fun testRebase() {
val layout = makeLayout(TEXT, PAINT) val layout = makeLayout(TEXT, PAINT)
val interp = TextInterpolator(layout) val interp = TextInterpolator(layout, typefaceCache)
interp.basePaint.set(START_PAINT) interp.basePaint.set(START_PAINT)
interp.onBasePaintModified() interp.onBasePaintModified()
@@ -160,7 +167,7 @@ class TextInterpolatorTest : SysuiTestCase() {
fun testBidi_LTR() { fun testBidi_LTR() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.LTR) val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.LTR)
val interp = TextInterpolator(layout) val interp = TextInterpolator(layout, typefaceCache)
interp.basePaint.set(START_PAINT) interp.basePaint.set(START_PAINT)
interp.onBasePaintModified() interp.onBasePaintModified()
@@ -180,7 +187,7 @@ class TextInterpolatorTest : SysuiTestCase() {
fun testBidi_RTL() { fun testBidi_RTL() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL) val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
val interp = TextInterpolator(layout) val interp = TextInterpolator(layout, typefaceCache)
interp.basePaint.set(START_PAINT) interp.basePaint.set(START_PAINT)
interp.onBasePaintModified() interp.onBasePaintModified()
@@ -200,7 +207,7 @@ class TextInterpolatorTest : SysuiTestCase() {
fun testGlyphCallback_Empty() { fun testGlyphCallback_Empty() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL) val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
val interp = TextInterpolator(layout).apply { val interp = TextInterpolator(layout, typefaceCache).apply {
glyphFilter = { glyph, progress -> glyphFilter = { glyph, progress ->
} }
} }
@@ -222,7 +229,7 @@ class TextInterpolatorTest : SysuiTestCase() {
fun testGlyphCallback_Xcoordinate() { fun testGlyphCallback_Xcoordinate() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL) val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
val interp = TextInterpolator(layout).apply { val interp = TextInterpolator(layout, typefaceCache).apply {
glyphFilter = { glyph, progress -> glyphFilter = { glyph, progress ->
glyph.x += 30f glyph.x += 30f
} }
@@ -247,7 +254,7 @@ class TextInterpolatorTest : SysuiTestCase() {
fun testGlyphCallback_Ycoordinate() { fun testGlyphCallback_Ycoordinate() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL) val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
val interp = TextInterpolator(layout).apply { val interp = TextInterpolator(layout, typefaceCache).apply {
glyphFilter = { glyph, progress -> glyphFilter = { glyph, progress ->
glyph.y += 30f glyph.y += 30f
} }
@@ -272,7 +279,7 @@ class TextInterpolatorTest : SysuiTestCase() {
fun testGlyphCallback_TextSize() { fun testGlyphCallback_TextSize() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL) val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
val interp = TextInterpolator(layout).apply { val interp = TextInterpolator(layout, typefaceCache).apply {
glyphFilter = { glyph, progress -> glyphFilter = { glyph, progress ->
glyph.textSize += 10f glyph.textSize += 10f
} }
@@ -297,7 +304,7 @@ class TextInterpolatorTest : SysuiTestCase() {
fun testGlyphCallback_Color() { fun testGlyphCallback_Color() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL) val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
val interp = TextInterpolator(layout).apply { val interp = TextInterpolator(layout, typefaceCache).apply {
glyphFilter = { glyph, progress -> glyphFilter = { glyph, progress ->
glyph.color = Color.RED glyph.color = Color.RED
} }