Merge "fix(non linear font scaling): fix crash with certain negative SP values"

This commit is contained in:
Tyler Freeman
2022-12-02 04:32:47 +00:00
committed by Android (Google) Code Review
2 changed files with 71 additions and 8 deletions

View File

@@ -36,11 +36,6 @@ import java.util.Arrays;
* @hide
*/
public class FontScaleConverter {
/**
* How close the given SP should be to a canonical SP in the array before they are considered
* the same for lookup purposes.
*/
private static final float THRESHOLD_FOR_MATCHING_SP = 0.02f;
@VisibleForTesting
final float[] mFromSpValues;
@@ -78,10 +73,11 @@ public class FontScaleConverter {
public float convertSpToDp(float sp) {
final float spPositive = Math.abs(sp);
// TODO(b/247861374): find a match at a higher index?
final int spRounded = Math.round(spPositive);
final float sign = Math.signum(sp);
final int index = Arrays.binarySearch(mFromSpValues, spRounded);
if (index >= 0 && Math.abs(spRounded - spPositive) < THRESHOLD_FOR_MATCHING_SP) {
// We search for exact matches only, even if it's just a little off. The interpolation will
// handle any non-exact matches.
final int index = Arrays.binarySearch(mFromSpValues, spPositive);
if (index >= 0) {
// exact match, return the matching dp
return sign * mToDpValues[index];
} else {

View File

@@ -18,8 +18,12 @@ package android.content.res
import androidx.core.util.forEach
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import kotlin.math.ceil
import kotlin.math.floor
import org.junit.Test
import org.junit.runner.RunWith
@@ -72,7 +76,70 @@ class FontScaleConverterFactoryTest {
}
}
@LargeTest
@Test
fun allFeasibleScalesAndConversionsDoNotCrash() {
generateSequenceOfFractions(-10000f..10000f, step = 0.01f)
.mapNotNull{ FontScaleConverterFactory.forScale(it) }
.flatMap{ table ->
generateSequenceOfFractions(-10000f..10000f, step = 0.01f)
.map{ Pair(table, it) }
}
.forEach { (table, sp) ->
try {
assertWithMessage(
"convertSpToDp(%s) on table: %s",
sp.toString(),
table.toString()
)
.that(table.convertSpToDp(sp))
.isFinite()
} catch (e: Exception) {
throw AssertionError("Exception during convertSpToDp($sp) on table: $table", e)
}
}
}
@Test
fun testGenerateSequenceOfFractions() {
val fractions = generateSequenceOfFractions(-1000f..1000f, step = 0.1f)
.toList()
fractions.forEach {
assertThat(it).isAtLeast(-1000f)
assertThat(it).isAtMost(1000f)
}
assertThat(fractions).isInStrictOrder()
assertThat(fractions).hasSize(1000 * 2 * 10 + 1) // Don't forget the 0 in the middle!
assertThat(fractions).contains(100f)
assertThat(fractions).contains(500.1f)
assertThat(fractions).contains(500.2f)
assertThat(fractions).contains(0.2f)
assertThat(fractions).contains(0f)
assertThat(fractions).contains(-10f)
assertThat(fractions).contains(-10f)
assertThat(fractions).contains(-10.3f)
assertThat(fractions).doesNotContain(-10.31f)
assertThat(fractions).doesNotContain(0.35f)
assertThat(fractions).doesNotContain(0.31f)
assertThat(fractions).doesNotContain(-.35f)
}
companion object {
private const val CONVERSION_TOLERANCE = 0.05f
}
}
fun generateSequenceOfFractions(
range: ClosedFloatingPointRange<Float>,
step: Float
): Sequence<Float> {
val multiplier = 1f / step
val start = floor(range.start * multiplier).toInt()
val endInclusive = ceil(range.endInclusive * multiplier).toInt()
return generateSequence(start) { it + 1 }
.takeWhile { it <= endInclusive }
.map{ it.toFloat() / multiplier }
}