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

Commit c3039468 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Restrict maximum size of FontInterpolator font caches" into tm-qpr-dev

parents 784a9ec8 39dbe900
Loading
Loading
Loading
Loading
+11 −5
Original line number Original line Diff line number Diff line
@@ -18,8 +18,10 @@ package com.android.systemui.animation


import android.graphics.fonts.Font
import android.graphics.fonts.Font
import android.graphics.fonts.FontVariationAxis
import android.graphics.fonts.FontVariationAxis
import android.util.LruCache
import android.util.MathUtils
import android.util.MathUtils
import android.util.MathUtils.abs
import android.util.MathUtils.abs
import androidx.annotation.VisibleForTesting
import java.lang.Float.max
import java.lang.Float.max
import java.lang.Float.min
import java.lang.Float.min


@@ -34,6 +36,10 @@ private const val FONT_ITALIC_MIN = 0f
private const val FONT_ITALIC_ANIMATION_STEP = 0.1f
private const val FONT_ITALIC_ANIMATION_STEP = 0.1f
private const val FONT_ITALIC_DEFAULT_VALUE = 0f
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

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


@@ -81,8 +87,8 @@ class FontInterpolator {
    // Font interpolator has two level caches: one for input and one for font with different
    // 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
    // variation settings. No synchronization is needed since FontInterpolator is not designed to be
    // thread-safe and can be used only on UI thread.
    // thread-safe and can be used only on UI thread.
    private val interpCache = hashMapOf<InterpKey, Font>()
    private val interpCache = LruCache<InterpKey, Font>(FONT_CACHE_MAX_ENTRIES)
    private val verFontCache = hashMapOf<VarFontKey, Font>()
    private val verFontCache = LruCache<VarFontKey, Font>(FONT_CACHE_MAX_ENTRIES)


    // Mutable keys for recycling.
    // Mutable keys for recycling.
    private val tmpInterpKey = InterpKey(null, null, 0f)
    private val tmpInterpKey = InterpKey(null, null, 0f)
@@ -152,7 +158,7 @@ class FontInterpolator {
        tmpVarFontKey.set(start, newAxes)
        tmpVarFontKey.set(start, newAxes)
        val axesCachedFont = verFontCache[tmpVarFontKey]
        val axesCachedFont = verFontCache[tmpVarFontKey]
        if (axesCachedFont != null) {
        if (axesCachedFont != null) {
            interpCache[InterpKey(start, end, progress)] = axesCachedFont
            interpCache.put(InterpKey(start, end, progress), axesCachedFont)
            return axesCachedFont
            return axesCachedFont
        }
        }


@@ -160,8 +166,8 @@ class FontInterpolator {
        // Font.Builder#build won't throw IOException since creating fonts from existing fonts will
        // Font.Builder#build won't throw IOException since creating fonts from existing fonts will
        // not do any IO work.
        // not do any IO work.
        val newFont = Font.Builder(start).setFontVariationSettings(newAxes.toTypedArray()).build()
        val newFont = Font.Builder(start).setFontVariationSettings(newAxes.toTypedArray()).build()
        interpCache[InterpKey(start, end, progress)] = newFont
        interpCache.put(InterpKey(start, end, progress), newFont)
        verFontCache[VarFontKey(start, newAxes)] = newFont
        verFontCache.put(VarFontKey(start, newAxes), newFont)
        return newFont
        return newFont
    }
    }


+7 −5
Original line number Original line Diff line number Diff line
@@ -24,8 +24,10 @@ 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.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


typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit
typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit
/**
/**
@@ -114,7 +116,7 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {


    private val fontVariationUtils = FontVariationUtils()
    private val fontVariationUtils = FontVariationUtils()


    private val typefaceCache = HashMap<String, Typeface?>()
    private val typefaceCache = LruCache<String, Typeface>(TYPEFACE_CACHE_MAX_ENTRIES)


    fun updateLayout(layout: Layout) {
    fun updateLayout(layout: Layout) {
        textInterpolator.layout = layout
        textInterpolator.layout = layout
@@ -218,11 +220,11 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
        }
        }


        if (!fvar.isNullOrBlank()) {
        if (!fvar.isNullOrBlank()) {
            textInterpolator.targetPaint.typeface =
            textInterpolator.targetPaint.typeface = typefaceCache.get(fvar) ?: run {
                typefaceCache.getOrElse(fvar) {
                textInterpolator.targetPaint.fontVariationSettings = fvar
                textInterpolator.targetPaint.fontVariationSettings = fvar
                textInterpolator.targetPaint.typeface?.also {
                    typefaceCache.put(fvar, textInterpolator.targetPaint.typeface)
                    typefaceCache.put(fvar, textInterpolator.targetPaint.typeface)
                    textInterpolator.targetPaint.typeface
                }
            }
            }
        }
        }


+25 −0
Original line number Original line Diff line number Diff line
@@ -106,4 +106,29 @@ class FontInterpolatorTest : SysuiTestCase() {
        val reversedFont = interp.lerp(endFont, startFont, 0.5f)
        val reversedFont = interp.lerp(endFont, startFont, 0.5f)
        assertThat(resultFont).isSameInstanceAs(reversedFont)
        assertThat(resultFont).isSameInstanceAs(reversedFont)
    }
    }

    @Test
    fun testCacheMaxSize() {
        val interp = FontInterpolator()

        val startFont = Font.Builder(sFont)
                .setFontVariationSettings("'wght' 100")
                .build()
        val endFont = Font.Builder(sFont)
                .setFontVariationSettings("'wght' 1")
                .build()
        val resultFont = interp.lerp(startFont, endFont, 0.5f)
        for (i in 0..FONT_CACHE_MAX_ENTRIES + 1) {
            val f1 = Font.Builder(sFont)
                    .setFontVariationSettings("'wght' ${i * 100}")
                    .build()
            val f2 = Font.Builder(sFont)
                    .setFontVariationSettings("'wght' $i")
                    .build()
            interp.lerp(f1, f2, 0.5f)
        }

        val cachedFont = interp.lerp(startFont, endFont, 0.5f)
        assertThat(resultFont).isNotSameInstanceAs(cachedFont)
    }
}
}