diff options
| author | 2020-11-06 16:21:30 -0800 | |
|---|---|---|
| committer | 2020-11-09 14:11:25 -0800 | |
| commit | 1ca390e9f6b27da2789d661716fe58d96d167407 (patch) | |
| tree | d625487462874494bcef44f049fa7b5ecea0647e | |
| parent | 9873b97017722e2c0ebfa5a414590eb05f52897f (diff) | |
Support serializing Java Typeface.
Bug: 169871852
Test: atest FrameworksCoreTests:TypefaceTest
Change-Id: I3b4315440e2ef6d45f0f9bf526b1980a974d9807
| -rw-r--r-- | core/tests/coretests/src/android/graphics/TypefaceTest.java | 27 | ||||
| -rw-r--r-- | graphics/java/android/graphics/Typeface.java | 36 | ||||
| -rw-r--r-- | libs/hwui/hwui/Typeface.h | 7 | ||||
| -rw-r--r-- | libs/hwui/jni/Typeface.cpp | 87 | ||||
| -rw-r--r-- | libs/hwui/jni/fonts/Font.cpp | 51 | ||||
| -rw-r--r-- | libs/hwui/jni/fonts/Font.h | 40 |
6 files changed, 225 insertions, 23 deletions
diff --git a/core/tests/coretests/src/android/graphics/TypefaceTest.java b/core/tests/coretests/src/android/graphics/TypefaceTest.java index 2d16f826b243..cfed2cef9ce6 100644 --- a/core/tests/coretests/src/android/graphics/TypefaceTest.java +++ b/core/tests/coretests/src/android/graphics/TypefaceTest.java @@ -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); + } } diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 441c16322645..c58e5f36cde3 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -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); } diff --git a/libs/hwui/hwui/Typeface.h b/libs/hwui/hwui/Typeface.h index ef8d8f4ee4f3..0c3ef01ab26b 100644 --- a/libs/hwui/hwui/Typeface.h +++ b/libs/hwui/hwui/Typeface.h @@ -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; }; } diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp index 2a5f402a4fa6..dc066da36cbe 100644 --- a/libs/hwui/jni/Typeface.cpp +++ b/libs/hwui/jni/Typeface.cpp @@ -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) diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp index 21fcd2ff375b..f0c77930cbe3 100644 --- a/libs/hwui/jni/fonts/Font.cpp +++ b/libs/hwui/jni/fonts/Font.cpp @@ -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 diff --git a/libs/hwui/jni/fonts/Font.h b/libs/hwui/jni/fonts/Font.h new file mode 100644 index 000000000000..b5d20bf8cc3c --- /dev/null +++ b/libs/hwui/jni/fonts/Font.h @@ -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_ */ |