Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 04d47245 authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Set FontInterpolator cache sizes to fit expected animation steps" into...

Merge "Set FontInterpolator cache sizes to fit expected animation steps" into udc-dev am: 124b12bf

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/23608803



Change-Id: I7009fbae90353a898c0a7b67033bab4a7801b214
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 469a8cee 124b12bf
Loading
Loading
Loading
Loading
+13 −29
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import android.graphics.fonts.FontVariationAxis
import android.util.Log
import android.util.LruCache
import android.util.MathUtils
import android.util.MathUtils.abs
import androidx.annotation.VisibleForTesting
import java.lang.Float.max
import java.lang.Float.min
@@ -30,8 +29,6 @@ private const val TAG_WGHT = "wght"
private const val TAG_ITAL = "ital"

private const val FONT_WEIGHT_DEFAULT_VALUE = 400f
private const val FONT_WEIGHT_ANIMATION_FRAME_COUNT = 100

private const val FONT_ITALIC_MAX = 1f
private const val FONT_ITALIC_MIN = 0f
private const val FONT_ITALIC_ANIMATION_STEP = 0.1f
@@ -39,11 +36,12 @@ private const val FONT_ITALIC_DEFAULT_VALUE = 0f

// Benchmarked via Perfetto, difference between 10 and 50 entries is about 0.3ms in
// frame draw time on a Pixel 6.
@VisibleForTesting const val FONT_CACHE_MAX_ENTRIES = 10
@VisibleForTesting const val DEFAULT_FONT_CACHE_MAX_ENTRIES = 10

/** Provide interpolation of two fonts by adjusting font variation settings. */
class FontInterpolator {

class FontInterpolator(
    numberOfAnimationSteps: Int? = null,
) {
    /**
     * Cache key for the interpolated font.
     *
@@ -88,8 +86,9 @@ class FontInterpolator {
    // Font interpolator has two level caches: one for input and one for font with different
    // variation settings. No synchronization is needed since FontInterpolator is not designed to be
    // thread-safe and can be used only on UI thread.
    private val interpCache = LruCache<InterpKey, Font>(FONT_CACHE_MAX_ENTRIES)
    private val verFontCache = LruCache<VarFontKey, Font>(FONT_CACHE_MAX_ENTRIES)
    val cacheMaxEntries = numberOfAnimationSteps?.let { it * 2 } ?: DEFAULT_FONT_CACHE_MAX_ENTRIES
    private val interpCache = LruCache<InterpKey, Font>(cacheMaxEntries)
    private val verFontCache = LruCache<VarFontKey, Font>(cacheMaxEntries)

    // Mutable keys for recycling.
    private val tmpInterpKey = InterpKey(null, null, 0f)
@@ -128,18 +127,12 @@ class FontInterpolator {
        val newAxes =
            lerp(startAxes, endAxes) { tag, startValue, endValue ->
                when (tag) {
                    // TODO: Good to parse 'fvar' table for retrieving default value.
                    TAG_WGHT -> {
                        adaptiveAdjustWeight(
                    TAG_WGHT ->
                        MathUtils.lerp(
                            startValue ?: FONT_WEIGHT_DEFAULT_VALUE,
                            endValue ?: FONT_WEIGHT_DEFAULT_VALUE,
                            progress
                            ),
                            startValue ?: FONT_WEIGHT_DEFAULT_VALUE,
                            endValue ?: FONT_WEIGHT_DEFAULT_VALUE,
                        )
                    }
                    TAG_ITAL ->
                        adjustItalic(
                            MathUtils.lerp(
@@ -175,9 +168,9 @@ class FontInterpolator {
        val newFont = Font.Builder(start).setFontVariationSettings(newAxes.toTypedArray()).build()
        interpCache.put(InterpKey(start, end, progress), newFont)
        verFontCache.put(VarFontKey(start, newAxes), newFont)
        if (DEBUG) {
            Log.d(LOG_TAG, "[$progress] Cache MISS for $tmpInterpKey / $tmpVarFontKey")
        }

        // Cache misses are likely to create memory leaks, so this is logged at error level.
        Log.e(LOG_TAG, "[$progress] Cache MISS for $tmpInterpKey / $tmpVarFontKey")
        return newFont
    }

@@ -225,15 +218,6 @@ class FontInterpolator {
        return result
    }

    // For the performance reasons, we animate weight with adaptive step. This helps
    // Cache hit ratio in the Skia glyph cache.
    // The reason we don't use fix step is because the range of weight axis is not normalized,
    // some are from 50 to 100, others are from 0 to 1000, so we cannot give a constant proper step
    private fun adaptiveAdjustWeight(value: Float, start: Float, end: Float): Float {
        val step = max(abs(end - start) / FONT_WEIGHT_ANIMATION_FRAME_COUNT, 1F)
        return coerceInWithStep(value, min(start, end), max(start, end), step)
    }

    // For the performance reasons, we animate italic with FONT_ITALIC_ANIMATION_STEP. This helps
    // Cache hit ratio in the Skia glyph cache.
    private fun adjustItalic(value: Float) =
+2 −1
Original line number Diff line number Diff line
@@ -108,7 +108,8 @@ class TextAnimator(
        }

    // Following two members are for mutable for testing purposes.
    public var textInterpolator: TextInterpolator = TextInterpolator(layout, typefaceCache)
    public var textInterpolator: TextInterpolator =
        TextInterpolator(layout, typefaceCache, numberOfAnimationSteps)
    public var animator: ValueAnimator =
        ValueAnimator.ofFloat(1f).apply {
            duration = DEFAULT_ANIMATION_DURATION
+2 −1
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import java.lang.Math.max
class TextInterpolator(
    layout: Layout,
    var typefaceCache: TypefaceVariantCache,
    numberOfAnimationSteps: Int? = null,
) {
    /**
     * Returns base paint used for interpolation.
@@ -85,7 +86,7 @@ class TextInterpolator(
    private class Line(val runs: List<Run>)

    private var lines = listOf<Line>()
    private val fontInterpolator = FontInterpolator()
    private val fontInterpolator = FontInterpolator(numberOfAnimationSteps)

    // Recycling object for glyph drawing and tweaking.
    private val tmpPaint = TextPaint()
+5 −5
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@ class FontInterpolatorTest : SysuiTestCase() {
    private fun assertSameAxes(expect: Font, actual: Font) {
        val expectAxes = expect.axes?.also { it.sortBy { axis -> axis.tag } }
        val actualAxes = actual.axes?.also { it.sortBy { axis -> axis.tag } }
        assertThat(expectAxes).isEqualTo(actualAxes)
        assertThat(actualAxes).isEqualTo(expectAxes)
    }

    private fun assertSameAxes(expectVarSettings: String, actual: Font) {
@@ -46,7 +46,7 @@ class FontInterpolatorTest : SysuiTestCase() {
            it.sortBy { axis -> axis.tag }
        }
        val actualAxes = actual.axes?.also { it.sortBy { axis -> axis.tag } }
        assertThat(expectAxes).isEqualTo(actualAxes)
        assertThat(actualAxes).isEqualTo(expectAxes)
    }

    @Test
@@ -61,7 +61,7 @@ class FontInterpolatorTest : SysuiTestCase() {
        val interp = FontInterpolator()
        assertSameAxes(startFont, interp.lerp(startFont, endFont, 0f))
        assertSameAxes(endFont, interp.lerp(startFont, endFont, 1f))
        assertSameAxes("'wght' 496, 'ital' 0.5, 'GRAD' 450", interp.lerp(startFont, endFont, 0.5f))
        assertSameAxes("'wght' 500, 'ital' 0.5, 'GRAD' 450", interp.lerp(startFont, endFont, 0.5f))
    }

    @Test
@@ -74,7 +74,7 @@ class FontInterpolatorTest : SysuiTestCase() {
                .build()

        val interp = FontInterpolator()
        assertSameAxes("'wght' 249, 'ital' 0.5", interp.lerp(startFont, endFont, 0.5f))
        assertSameAxes("'wght' 250, 'ital' 0.5", interp.lerp(startFont, endFont, 0.5f))
    }

    @Test
@@ -118,7 +118,7 @@ class FontInterpolatorTest : SysuiTestCase() {
                .setFontVariationSettings("'wght' 1")
                .build()
        val resultFont = interp.lerp(startFont, endFont, 0.5f)
        for (i in 0..FONT_CACHE_MAX_ENTRIES + 1) {
        for (i in 0..interp.cacheMaxEntries + 1) {
            val f1 = Font.Builder(sFont)
                    .setFontVariationSettings("'wght' ${i * 100}")
                    .build()