Merge "Support serializing Java Typeface."

This commit is contained in:
Kohsuke Yatoh
2020-11-10 08:37:55 +00:00
committed by Android (Google) Code Review
6 changed files with 225 additions and 23 deletions

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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;
};
}

View File

@@ -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)

View File

@@ -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

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FONTS_FONT_H_
#define FONTS_FONT_H_
#include <minikin/FontVariation.h>
#include <minikin/MinikinFont.h>
#include <SkRefCnt.h>
#include <string_view>
#include <vector>
class SkData;
namespace android {
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);
} // namespace fonts
} // namespace android
#endif /* FONTS_FONT_H_ */