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

Commit c9b04d73 authored by Tyler Freeman's avatar Tyler Freeman Committed by Android (Google) Code Review
Browse files

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

parents e1dcbe2e bae1102a
Loading
Loading
Loading
Loading
+4 −8
Original line number Diff line number Diff line
@@ -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 {
+67 −0
Original line number Diff line number Diff line
@@ -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 }
}