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

Commit 1ca390e9 authored by Kohsuke Yatoh's avatar Kohsuke Yatoh
Browse files

Support serializing Java Typeface.

Bug: 169871852
Test: atest FrameworksCoreTests:TypefaceTest
Change-Id: I3b4315440e2ef6d45f0f9bf526b1980a974d9807
parent 9873b970
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.graphics;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import android.content.Context;
@@ -34,6 +35,9 @@ import com.android.frameworks.coretests.R;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

@RunWith(AndroidJUnit4.class)
@@ -171,4 +175,27 @@ public class TypefaceTest {

    }

    @SmallTest
    @Test
    public void testSerialize() throws Exception {
        int size = Typeface.writeTypefaces(null, Arrays.asList(mFaces));
        ByteBuffer buffer = ByteBuffer.allocateDirect(size);
        Typeface.writeTypefaces(buffer, Arrays.asList(mFaces));
        List<Typeface> copiedTypefaces = Typeface.readTypefaces(buffer);
        assertNotNull(copiedTypefaces);
        assertEquals(mFaces.length, copiedTypefaces.size());
        for (int i = 0; i < mFaces.length; i++) {
            Typeface original = mFaces[i];
            Typeface copied = copiedTypefaces.get(i);
            assertEquals(original.getStyle(), copied.getStyle());
            assertEquals(original.getWeight(), copied.getWeight());
            assertEquals(measureText(original, "hello"), measureText(copied, "hello"), 1e-6);
        }
    }

    private static float measureText(Typeface typeface, String text) {
        Paint paint = new Paint();
        paint.setTypeface(typeface);
        return paint.measureText(text);
    }
}
+36 −0
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -1209,6 +1210,36 @@ public class Typeface {
        return Arrays.binarySearch(mSupportedAxes, axis) >= 0;
    }

    /**
     * Writes Typeface instances to the ByteBuffer and returns the number of bytes written.
     *
     * <p>If {@code buffer} is null, this method returns the number of bytes required to serialize
     * the typefaces, without writing anything.
     * @hide
     */
    public static int writeTypefaces(
            @Nullable ByteBuffer buffer, @NonNull List<Typeface> typefaces) {
        long[] nativePtrs = new long[typefaces.size()];
        for (int i = 0; i < nativePtrs.length; i++) {
            nativePtrs[i] = typefaces.get(i).native_instance;
        }
        return nativeWriteTypefaces(buffer, nativePtrs);
    }

    /**
     * Reads serialized Typeface instances from the ByteBuffer. Returns null on errors.
     * @hide
     */
    public static @Nullable List<Typeface> readTypefaces(@NonNull ByteBuffer buffer) {
        long[] nativePtrs = nativeReadTypefaces(buffer);
        if (nativePtrs == null) return null;
        List<Typeface> typefaces = new ArrayList<>(nativePtrs.length);
        for (long nativePtr : nativePtrs) {
            typefaces.add(new Typeface(nativePtr));
        }
        return typefaces;
    }

    private static native long nativeCreateFromTypeface(long native_instance, int style);
    private static native long nativeCreateFromTypefaceWithExactStyle(
            long native_instance, int weight, boolean italic);
@@ -1234,4 +1265,9 @@ public class Typeface {
    private static native long nativeGetReleaseFunc();

    private static native void nativeRegisterGenericFamily(String str, long nativePtr);

    private static native int nativeWriteTypefaces(
            @Nullable ByteBuffer buffer, @NonNull long[] nativePtrs);

    private static native @Nullable long[] nativeReadTypefaces(@NonNull ByteBuffer buffer);
}
+3 −4
Original line number Diff line number Diff line
@@ -41,6 +41,9 @@ public:
    enum Style : uint8_t { kNormal = 0, kBold = 0x01, kItalic = 0x02, kBoldItalic = 0x03 };
    Style fAPIStyle;

    // base weight in CSS-style units, 1..1000
    int fBaseWeight;

    static const Typeface* resolveDefault(const Typeface* src);

    // The following three functions create new Typeface from an existing Typeface with a different
@@ -81,10 +84,6 @@ public:

    // Sets roboto font as the default typeface for testing purpose.
    static void setRobotoTypefaceForTest();

private:
    // base weight in CSS-style units, 1..1000
    int fBaseWeight;
};
}

+87 −0
Original line number Diff line number Diff line
@@ -16,10 +16,13 @@

#include "FontUtils.h"
#include "GraphicsJNI.h"
#include "fonts/Font.h"
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
#include "SkData.h"
#include "SkTypeface.h"
#include <hwui/Typeface.h>
#include <minikin/FontCollection.h>
#include <minikin/FontFamily.h>
#include <minikin/SystemFonts.h>

@@ -132,6 +135,88 @@ static void Typeface_registerGenericFamily(JNIEnv *env, jobject, jstring familyN
                                           toTypeface(ptr)->fFontCollection);
}

static std::function<std::shared_ptr<minikin::MinikinFont>()> readMinikinFontSkia(
        minikin::BufferReader* reader) {
    std::string_view fontPath = reader->readString();
    int fontIndex = reader->read<int>();
    const minikin::FontVariation* axesPtr;
    uint32_t axesCount;
    std::tie(axesPtr, axesCount) = reader->readArray<minikin::FontVariation>();
    return [fontPath, fontIndex, axesPtr, axesCount]() -> std::shared_ptr<minikin::MinikinFont> {
        std::string path(fontPath.data(), fontPath.size());
        sk_sp<SkData> data = SkData::MakeFromFileName(path.c_str());
        const void* fontPtr = data->data();
        size_t fontSize = data->size();
        std::vector<minikin::FontVariation> axes(axesPtr, axesPtr + axesCount);
        std::shared_ptr<minikin::MinikinFont> minikinFont =
                fonts::createMinikinFontSkia(std::move(data), fontPath, fontPtr, fontSize,
                                             fontIndex, axes);
        if (minikinFont == nullptr) {
            ALOGE("Failed to create MinikinFontSkia: %s", path.c_str());
            return nullptr;
        }
        return minikinFont;
    };
}

static void writeMinikinFontSkia(minikin::BufferWriter* writer,
        const minikin::MinikinFont* typeface) {
    writer->writeString(typeface->GetFontPath());
    writer->write<int>(typeface->GetFontIndex());
    const std::vector<minikin::FontVariation>& axes = typeface->GetAxes();
    writer->writeArray<minikin::FontVariation>(axes.data(), axes.size());
}

static jint Typeface_writeTypefaces(JNIEnv *env, jobject, jobject buffer, jlongArray faceHandles) {
    ScopedLongArrayRO faces(env, faceHandles);
    std::vector<Typeface*> typefaces;
    typefaces.reserve(faces.size());
    for (size_t i = 0; i < faces.size(); i++) {
        typefaces.push_back(toTypeface(faces[i]));
    }
    void* addr = buffer == nullptr ? nullptr : env->GetDirectBufferAddress(buffer);
    minikin::BufferWriter writer(addr);
    std::vector<std::shared_ptr<minikin::FontCollection>> fontCollections;
    std::unordered_map<std::shared_ptr<minikin::FontCollection>, size_t> fcToIndex;
    for (Typeface* typeface : typefaces) {
        bool inserted = fcToIndex.emplace(typeface->fFontCollection, fontCollections.size()).second;
        if (inserted) {
            fontCollections.push_back(typeface->fFontCollection);
        }
    }
    minikin::FontCollection::writeVector<writeMinikinFontSkia>(&writer, fontCollections);
    writer.write<uint32_t>(typefaces.size());
    for (Typeface* typeface : typefaces) {
      writer.write<uint32_t>(fcToIndex.find(typeface->fFontCollection)->second);
      typeface->fStyle.writeTo(&writer);
      writer.write<Typeface::Style>(typeface->fAPIStyle);
      writer.write<int>(typeface->fBaseWeight);
    }
    return static_cast<jint>(writer.size());
}

static jlongArray Typeface_readTypefaces(JNIEnv *env, jobject, jobject buffer) {
    void* addr = buffer == nullptr ? nullptr : env->GetDirectBufferAddress(buffer);
    if (addr == nullptr) return nullptr;
    minikin::BufferReader reader(addr);
    std::vector<std::shared_ptr<minikin::FontCollection>> fontCollections =
            minikin::FontCollection::readVector<readMinikinFontSkia>(&reader);
    uint32_t typefaceCount = reader.read<uint32_t>();
    std::vector<jlong> faceHandles;
    faceHandles.reserve(typefaceCount);
    for (uint32_t i = 0; i < typefaceCount; i++) {
        Typeface* typeface = new Typeface;
        typeface->fFontCollection = fontCollections[reader.read<uint32_t>()];
        typeface->fStyle = minikin::FontStyle(&reader);
        typeface->fAPIStyle = reader.read<Typeface::Style>();
        typeface->fBaseWeight = reader.read<int>();
        faceHandles.push_back(toJLong(typeface));
    }
    const jlongArray result = env->NewLongArray(typefaceCount);
    env->SetLongArrayRegion(result, 0, typefaceCount, faceHandles.data());
    return result;
}

///////////////////////////////////////////////////////////////////////////////

static const JNINativeMethod gTypefaceMethods[] = {
@@ -150,6 +235,8 @@ static const JNINativeMethod gTypefaceMethods[] = {
    { "nativeGetSupportedAxes",   "(J)[I",  (void*)Typeface_getSupportedAxes },
    { "nativeRegisterGenericFamily", "(Ljava/lang/String;J)V",
          (void*)Typeface_registerGenericFamily },
    { "nativeWriteTypefaces", "(Ljava/nio/ByteBuffer;[J)I", (void*)Typeface_writeTypefaces},
    { "nativeReadTypefaces", "(Ljava/nio/ByteBuffer;)[J", (void*)Typeface_readTypefaces},
};

int register_android_graphics_Typeface(JNIEnv* env)
+32 −19
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#undef LOG_TAG
#define LOG_TAG "Minikin"

#include "Font.h"
#include "SkData.h"
#include "SkFont.h"
#include "SkFontMetrics.h"
@@ -95,29 +96,14 @@ static jlong Font_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jo
    jobject fontRef = MakeGlobalRefOrDie(env, buffer);
    sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize,
            release_global_ref, reinterpret_cast<void*>(fontRef)));

    FatVector<SkFontArguments::VariationPosition::Coordinate, 2> skVariation;
    for (const auto& axis : builder->axes) {
        skVariation.push_back({axis.axisTag, axis.value});
    }

    std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data)));

    SkFontArguments args;
    args.setCollectionIndex(ttcIndex);
    args.setVariationDesignPosition({skVariation.data(), static_cast<int>(skVariation.size())});

    sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
    sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), args));
    if (face == nullptr) {
    std::shared_ptr<minikin::MinikinFont> minikinFont = fonts::createMinikinFontSkia(
        std::move(data), std::string_view(fontPath.c_str(), fontPath.size()),
        fontPtr, fontSize, ttcIndex, builder->axes);
    if (minikinFont == nullptr) {
        jniThrowException(env, "java/lang/IllegalArgumentException",
                          "Failed to create internal object. maybe invalid font data.");
        return 0;
    }
    std::shared_ptr<minikin::MinikinFont> minikinFont =
            std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize,
                                              std::string_view(fontPath.c_str(), fontPath.size()),
                                              ttcIndex, builder->axes);
    std::shared_ptr<minikin::Font> font = minikin::Font::Builder(minikinFont).setWeight(weight)
                    .setSlant(static_cast<minikin::FontStyle::Slant>(italic)).build();
    return reinterpret_cast<jlong>(new FontWrapper(std::move(font)));
@@ -312,4 +298,31 @@ int register_android_graphics_fonts_Font(JNIEnv* env) {
            gFontBufferHelperMethods, NELEM(gFontBufferHelperMethods));
}

namespace fonts {

std::shared_ptr<minikin::MinikinFont> createMinikinFontSkia(
        sk_sp<SkData>&& data, std::string_view fontPath, const void *fontPtr, size_t fontSize,
        int ttcIndex, const std::vector<minikin::FontVariation>& axes) {
    FatVector<SkFontArguments::VariationPosition::Coordinate, 2> skVariation;
    for (const auto& axis : axes) {
        skVariation.push_back({axis.axisTag, axis.value});
    }

    std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data)));

    SkFontArguments args;
    args.setCollectionIndex(ttcIndex);
    args.setVariationDesignPosition({skVariation.data(), static_cast<int>(skVariation.size())});

    sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
    sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), args));
    if (face == nullptr) {
        return nullptr;
    }
    return std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize,
                                             fontPath, ttcIndex, axes);
}

}  // namespace fonts

}  // namespace android
Loading