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

Commit 9cfa449e authored by Tyler Freeman's avatar Tyler Freeman Committed by Automerger Merge Worker
Browse files

Merge "feat(non linear font scaling): interpolate between two tables if one...

Merge "feat(non linear font scaling): interpolate between two tables if one cannot be found for a given scale" into udc-dev am: 73dc88e2

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



Change-Id: I80ec85098e701d80e95935d994906c8bc9407b66
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 32c22ae4 73dc88e2
Loading
Loading
Loading
Loading
+65 −5
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.content.res;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.MathUtils;
import android.util.SparseArray;

import com.android.internal.annotations.VisibleForTesting;
@@ -98,22 +99,81 @@ public class FontScaleConverterFactory {
    public static FontScaleConverter forScale(float fontScale) {
        if (fontScale <= 1) {
            // We don't need non-linear curves for shrinking text or for 100%.
            // Also, fontScale==0 should not have a curve either
            // Also, fontScale==0 should not have a curve either.
            // And ignore negative font scales; that's just silly.
            return null;
        }

        FontScaleConverter lookupTable = get(fontScale);
        // TODO(b/247861716): interpolate between two tables when null

        if (lookupTable != null) {
            return lookupTable;
        }

        // Didn't find an exact match: interpolate between two existing tables
        final int index = LOOKUP_TABLES.indexOfKey(getKey(fontScale));
        if (index >= 0) {
            // This should never happen, should have been covered by get() above.
            return LOOKUP_TABLES.valueAt(index);
        }
        // Didn't find an exact match: interpolate between two existing tables
        final int lowerIndex = -(index + 1) - 1;
        final int higherIndex = lowerIndex + 1;
        if (lowerIndex < 0 || higherIndex >= LOOKUP_TABLES.size()) {
            // We have gone beyond our bounds and have nothing to interpolate between. Just give
            // them a straight linear table instead.
            // This works because when FontScaleConverter encounters a size beyond its bounds, it
            // calculates a linear fontScale factor using the ratio of the last element pair.
            return new FontScaleConverter(new float[] {1f}, new float[] {fontScale});
        } else {
            float startScale = getScaleFromKey(LOOKUP_TABLES.keyAt(lowerIndex));
            float endScale = getScaleFromKey(LOOKUP_TABLES.keyAt(higherIndex));
            float interpolationPoint = MathUtils.constrainedMap(
                    /* rangeMin= */ 0f,
                    /* rangeMax= */ 1f,
                    startScale,
                    endScale,
                    fontScale
            );
            return createInterpolatedTableBetween(
                    LOOKUP_TABLES.valueAt(lowerIndex),
                    LOOKUP_TABLES.valueAt(higherIndex),
                    interpolationPoint);
        }
    }

    @NonNull
    private static FontScaleConverter createInterpolatedTableBetween(
            FontScaleConverter start,
            FontScaleConverter end,
            float interpolationPoint
    ) {
        float[] commonSpSizes = new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100f};
        float[] dpInterpolated = new float[commonSpSizes.length];

        for (int i = 0; i < commonSpSizes.length; i++) {
            float sp = commonSpSizes[i];
            float startDp = start.convertSpToDp(sp);
            float endDp = end.convertSpToDp(sp);
            dpInterpolated[i] = MathUtils.lerp(startDp, endDp, interpolationPoint);
        }

        return new FontScaleConverter(commonSpSizes, dpInterpolated);
    }

    private static int getKey(float fontScale) {
        return (int) (fontScale * SCALE_KEY_MULTIPLIER);
    }

    private static float getScaleFromKey(int key) {
        return (float) key / SCALE_KEY_MULTIPLIER;
    }

    private static void put(float scaleKey, @NonNull FontScaleConverter fontScaleConverter) {
        LOOKUP_TABLES.put((int) (scaleKey * SCALE_KEY_MULTIPLIER), fontScaleConverter);
        LOOKUP_TABLES.put(getKey(scaleKey), fontScaleConverter);
    }

    @Nullable
    private static FontScaleConverter get(float scaleKey) {
        return LOOKUP_TABLES.get((int) (scaleKey * SCALE_KEY_MULTIPLIER));
        return LOOKUP_TABLES.get(getKey(scaleKey));
    }
}
+48 −6
Original line number Diff line number Diff line
@@ -43,14 +43,56 @@ class FontScaleConverterFactoryTest {
        assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f)
    }

    @SmallTest
    fun missingLookupTableReturnsNull() {
        assertThat(FontScaleConverterFactory.forScale(3F)).isNull()
    @LargeTest
    @Test
    fun missingLookupTablePastEnd_returnsLinear() {
        val table = FontScaleConverterFactory.forScale(3F)!!
        generateSequenceOfFractions(-10000f..10000f, step = 0.01f)
            .map {
                assertThat(table.convertSpToDp(it)).isWithin(CONVERSION_TOLERANCE).of(it * 3f)
            }
        assertThat(table.convertSpToDp(1F)).isWithin(CONVERSION_TOLERANCE).of(3f)
        assertThat(table.convertSpToDp(8F)).isWithin(CONVERSION_TOLERANCE).of(24f)
        assertThat(table.convertSpToDp(10F)).isWithin(CONVERSION_TOLERANCE).of(30f)
        assertThat(table.convertSpToDp(5F)).isWithin(CONVERSION_TOLERANCE).of(15f)
        assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f)
        assertThat(table.convertSpToDp(50F)).isWithin(CONVERSION_TOLERANCE).of(150f)
        assertThat(table.convertSpToDp(100F)).isWithin(CONVERSION_TOLERANCE).of(300f)
    }

    @SmallTest
    fun missingLookupTable105ReturnsNull() {
        assertThat(FontScaleConverterFactory.forScale(1.05F)).isNull()
    fun missingLookupTable110_returnsInterpolated() {
        val table = FontScaleConverterFactory.forScale(1.1F)!!

        assertThat(table.convertSpToDp(1F)).isWithin(CONVERSION_TOLERANCE).of(1.1f)
        assertThat(table.convertSpToDp(8F)).isWithin(CONVERSION_TOLERANCE).of(8f * 1.1f)
        assertThat(table.convertSpToDp(10F)).isWithin(CONVERSION_TOLERANCE).of(11f)
        assertThat(table.convertSpToDp(5F)).isWithin(CONVERSION_TOLERANCE).of(5f * 1.1f)
        assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f)
        assertThat(table.convertSpToDp(50F)).isLessThan(50f * 1.1f)
        assertThat(table.convertSpToDp(100F)).isLessThan(100f * 1.1f)
    }

    @Test
    fun missingLookupTable199_returnsInterpolated() {
        val table = FontScaleConverterFactory.forScale(1.9999F)!!
        assertThat(table.convertSpToDp(1F)).isWithin(CONVERSION_TOLERANCE).of(2f)
        assertThat(table.convertSpToDp(8F)).isWithin(CONVERSION_TOLERANCE).of(16f)
        assertThat(table.convertSpToDp(10F)).isWithin(CONVERSION_TOLERANCE).of(20f)
        assertThat(table.convertSpToDp(5F)).isWithin(CONVERSION_TOLERANCE).of(10f)
        assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f)
    }

    @Test
    fun missingLookupTable160_returnsInterpolated() {
        val table = FontScaleConverterFactory.forScale(1.6F)!!
        assertThat(table.convertSpToDp(1F)).isWithin(CONVERSION_TOLERANCE).of(1f * 1.6F)
        assertThat(table.convertSpToDp(8F)).isWithin(CONVERSION_TOLERANCE).of(8f * 1.6F)
        assertThat(table.convertSpToDp(10F)).isWithin(CONVERSION_TOLERANCE).of(10f * 1.6F)
        assertThat(table.convertSpToDp(20F)).isLessThan(20f * 1.6F)
        assertThat(table.convertSpToDp(100F)).isLessThan(100f * 1.6F)
        assertThat(table.convertSpToDp(5F)).isWithin(CONVERSION_TOLERANCE).of(5f * 1.6F)
        assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f)
    }

    @SmallTest
@@ -83,7 +125,7 @@ class FontScaleConverterFactoryTest {
    @Test
    fun allFeasibleScalesAndConversionsDoNotCrash() {
        generateSequenceOfFractions(-10f..10f, step = 0.01f)
            .mapNotNull{ FontScaleConverterFactory.forScale(it) }
            .mapNotNull{ FontScaleConverterFactory.forScale(it) }!!
            .flatMap{ table ->
                generateSequenceOfFractions(-2000f..2000f, step = 0.01f)
                    .map{ Pair(table, it) }