Merge "Support serializing Java Typeface."
This commit is contained in:
committed by
Android (Google) Code Review
commit
df08f349d3
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
40
libs/hwui/jni/fonts/Font.h
Normal file
40
libs/hwui/jni/fonts/Font.h
Normal 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_ */
|
||||
Reference in New Issue
Block a user