Merge "Modify textAnimator to support strokeWidth Change fontInterpolator to use adaptive step when change and cache typeface for different weights" into tm-qpr-dev
This commit is contained in:
@@ -19,14 +19,15 @@ 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.MathUtils
|
import android.util.MathUtils
|
||||||
|
import android.util.MathUtils.abs
|
||||||
|
import java.lang.Float.max
|
||||||
|
import java.lang.Float.min
|
||||||
|
|
||||||
private const val TAG_WGHT = "wght"
|
private const val TAG_WGHT = "wght"
|
||||||
private const val TAG_ITAL = "ital"
|
private const val TAG_ITAL = "ital"
|
||||||
|
|
||||||
private const val FONT_WEIGHT_MAX = 1000f
|
|
||||||
private const val FONT_WEIGHT_MIN = 0f
|
|
||||||
private const val FONT_WEIGHT_ANIMATION_STEP = 10f
|
|
||||||
private const val FONT_WEIGHT_DEFAULT_VALUE = 400f
|
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_MAX = 1f
|
||||||
private const val FONT_ITALIC_MIN = 0f
|
private const val FONT_ITALIC_MIN = 0f
|
||||||
@@ -118,14 +119,17 @@ class FontInterpolator {
|
|||||||
lerp(startAxes, endAxes) { tag, startValue, endValue ->
|
lerp(startAxes, endAxes) { tag, startValue, endValue ->
|
||||||
when (tag) {
|
when (tag) {
|
||||||
// TODO: Good to parse 'fvar' table for retrieving default value.
|
// TODO: Good to parse 'fvar' table for retrieving default value.
|
||||||
TAG_WGHT ->
|
TAG_WGHT -> {
|
||||||
adjustWeight(
|
adaptiveAdjustWeight(
|
||||||
MathUtils.lerp(
|
MathUtils.lerp(
|
||||||
startValue ?: FONT_WEIGHT_DEFAULT_VALUE,
|
startValue ?: FONT_WEIGHT_DEFAULT_VALUE,
|
||||||
endValue ?: FONT_WEIGHT_DEFAULT_VALUE,
|
endValue ?: FONT_WEIGHT_DEFAULT_VALUE,
|
||||||
progress
|
progress
|
||||||
|
),
|
||||||
|
startValue ?: FONT_WEIGHT_DEFAULT_VALUE,
|
||||||
|
endValue ?: FONT_WEIGHT_DEFAULT_VALUE,
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
TAG_ITAL ->
|
TAG_ITAL ->
|
||||||
adjustItalic(
|
adjustItalic(
|
||||||
MathUtils.lerp(
|
MathUtils.lerp(
|
||||||
@@ -205,10 +209,14 @@ class FontInterpolator {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// For the performance reasons, we animate weight with FONT_WEIGHT_ANIMATION_STEP. This helps
|
// For the performance reasons, we animate weight with adaptive step. This helps
|
||||||
// Cache hit ratio in the Skia glyph cache.
|
// Cache hit ratio in the Skia glyph cache.
|
||||||
private fun adjustWeight(value: Float) =
|
// The reason we don't use fix step is because the range of weight axis is not normalized,
|
||||||
coerceInWithStep(value, FONT_WEIGHT_MIN, FONT_WEIGHT_MAX, FONT_WEIGHT_ANIMATION_STEP)
|
// 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
|
// For the performance reasons, we animate italic with FONT_ITALIC_ANIMATION_STEP. This helps
|
||||||
// Cache hit ratio in the Skia glyph cache.
|
// Cache hit ratio in the Skia glyph cache.
|
||||||
|
|||||||
@@ -190,6 +190,7 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
|
|||||||
* @param textSize an optional font size.
|
* @param textSize an optional font size.
|
||||||
* @param colors an optional colors array that must be the same size as numLines passed to
|
* @param colors an optional colors array that must be the same size as numLines passed to
|
||||||
* the TextInterpolator
|
* the TextInterpolator
|
||||||
|
* @param strokeWidth an optional paint stroke width
|
||||||
* @param animate an optional boolean indicating true for showing style transition as animation,
|
* @param animate an optional boolean indicating true for showing style transition as animation,
|
||||||
* false for immediate style transition. True by default.
|
* false for immediate style transition. True by default.
|
||||||
* @param duration an optional animation duration in milliseconds. This is ignored if animate is
|
* @param duration an optional animation duration in milliseconds. This is ignored if animate is
|
||||||
@@ -201,6 +202,7 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
|
|||||||
weight: Int = -1,
|
weight: Int = -1,
|
||||||
textSize: Float = -1f,
|
textSize: Float = -1f,
|
||||||
color: Int? = null,
|
color: Int? = null,
|
||||||
|
strokeWidth: Float = -1f,
|
||||||
animate: Boolean = true,
|
animate: Boolean = true,
|
||||||
duration: Long = -1L,
|
duration: Long = -1L,
|
||||||
interpolator: TimeInterpolator? = null,
|
interpolator: TimeInterpolator? = null,
|
||||||
@@ -254,6 +256,9 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
|
|||||||
if (color != null) {
|
if (color != null) {
|
||||||
textInterpolator.targetPaint.color = color
|
textInterpolator.targetPaint.color = color
|
||||||
}
|
}
|
||||||
|
if (strokeWidth >= 0F) {
|
||||||
|
textInterpolator.targetPaint.strokeWidth = strokeWidth
|
||||||
|
}
|
||||||
textInterpolator.onTargetPaintModified()
|
textInterpolator.onTargetPaintModified()
|
||||||
|
|
||||||
if (animate) {
|
if (animate) {
|
||||||
|
|||||||
@@ -473,6 +473,7 @@ class TextInterpolator(layout: Layout) {
|
|||||||
// TODO(172943390): Add other interpolation or support custom interpolator.
|
// TODO(172943390): Add other interpolation or support custom interpolator.
|
||||||
out.textSize = MathUtils.lerp(from.textSize, to.textSize, progress)
|
out.textSize = MathUtils.lerp(from.textSize, to.textSize, progress)
|
||||||
out.color = ColorUtils.blendARGB(from.color, to.color, progress)
|
out.color = ColorUtils.blendARGB(from.color, to.color, progress)
|
||||||
|
out.strokeWidth = MathUtils.lerp(from.strokeWidth, to.strokeWidth, progress)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shape the text and stores the result to out argument.
|
// Shape the text and stores the result to out argument.
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class FontInterpolatorTest : SysuiTestCase() {
|
|||||||
val interp = FontInterpolator()
|
val interp = FontInterpolator()
|
||||||
assertSameAxes(startFont, interp.lerp(startFont, endFont, 0f))
|
assertSameAxes(startFont, interp.lerp(startFont, endFont, 0f))
|
||||||
assertSameAxes(endFont, interp.lerp(startFont, endFont, 1f))
|
assertSameAxes(endFont, interp.lerp(startFont, endFont, 1f))
|
||||||
assertSameAxes("'wght' 500, 'ital' 0.5, 'GRAD' 450", interp.lerp(startFont, endFont, 0.5f))
|
assertSameAxes("'wght' 496, 'ital' 0.5, 'GRAD' 450", interp.lerp(startFont, endFont, 0.5f))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -74,7 +74,7 @@ class FontInterpolatorTest : SysuiTestCase() {
|
|||||||
.build()
|
.build()
|
||||||
|
|
||||||
val interp = FontInterpolator()
|
val interp = FontInterpolator()
|
||||||
assertSameAxes("'wght' 250, 'ital' 0.5", interp.lerp(startFont, endFont, 0.5f))
|
assertSameAxes("'wght' 249, 'ital' 0.5", interp.lerp(startFont, endFont, 0.5f))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -57,7 +57,18 @@ class AnimatableClockViewTest : SysuiTestCase() {
|
|||||||
clockView.measure(50, 50)
|
clockView.measure(50, 50)
|
||||||
|
|
||||||
verify(mockTextAnimator).glyphFilter = any()
|
verify(mockTextAnimator).glyphFilter = any()
|
||||||
verify(mockTextAnimator).setTextStyle(300, -1.0f, 200, false, 350L, null, 0L, null)
|
verify(mockTextAnimator)
|
||||||
|
.setTextStyle(
|
||||||
|
weight = 300,
|
||||||
|
textSize = -1.0f,
|
||||||
|
color = 200,
|
||||||
|
strokeWidth = -1F,
|
||||||
|
animate = false,
|
||||||
|
duration = 350L,
|
||||||
|
interpolator = null,
|
||||||
|
delay = 0L,
|
||||||
|
onAnimationEnd = null
|
||||||
|
)
|
||||||
verifyNoMoreInteractions(mockTextAnimator)
|
verifyNoMoreInteractions(mockTextAnimator)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,8 +79,30 @@ class AnimatableClockViewTest : SysuiTestCase() {
|
|||||||
clockView.animateAppearOnLockscreen()
|
clockView.animateAppearOnLockscreen()
|
||||||
|
|
||||||
verify(mockTextAnimator, times(2)).glyphFilter = any()
|
verify(mockTextAnimator, times(2)).glyphFilter = any()
|
||||||
verify(mockTextAnimator).setTextStyle(100, -1.0f, 200, false, 0L, null, 0L, null)
|
verify(mockTextAnimator)
|
||||||
verify(mockTextAnimator).setTextStyle(300, -1.0f, 200, true, 350L, null, 0L, null)
|
.setTextStyle(
|
||||||
|
weight = 100,
|
||||||
|
textSize = -1.0f,
|
||||||
|
color = 200,
|
||||||
|
strokeWidth = -1F,
|
||||||
|
animate = false,
|
||||||
|
duration = 0L,
|
||||||
|
interpolator = null,
|
||||||
|
delay = 0L,
|
||||||
|
onAnimationEnd = null
|
||||||
|
)
|
||||||
|
verify(mockTextAnimator)
|
||||||
|
.setTextStyle(
|
||||||
|
weight = 300,
|
||||||
|
textSize = -1.0f,
|
||||||
|
color = 200,
|
||||||
|
strokeWidth = -1F,
|
||||||
|
animate = true,
|
||||||
|
duration = 350L,
|
||||||
|
interpolator = null,
|
||||||
|
delay = 0L,
|
||||||
|
onAnimationEnd = null
|
||||||
|
)
|
||||||
verifyNoMoreInteractions(mockTextAnimator)
|
verifyNoMoreInteractions(mockTextAnimator)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user