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

Commit df08f349 authored by Kohsuke Yatoh's avatar Kohsuke Yatoh Committed by Android (Google) Code Review
Browse files

Merge "Support serializing Java Typeface."

parents 51a14f20 1ca390e9
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