Skip to content

Commit cdeb0d1

Browse files
sfreilichcopybara-github
authored andcommitted
Prune brush serialization JNI to what's used in Jetpack
Rename JNI methods to be more consistent with Ink's naming conventions elsewhere in that interface. PiperOrigin-RevId: 919190017
1 parent ef11c20 commit cdeb0d1

5 files changed

Lines changed: 384 additions & 467 deletions

File tree

ink/storage/internal/jni/BUILD.bazel

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ cc_library(
2323
tags = ["keep_dep"],
2424
visibility = ["//visibility:public"],
2525
deps = [
26-
":brush_serialization_jni",
26+
":brush_family_serialization_jni",
2727
":stroke_input_batch_serialization_jni",
2828
],
2929
)
@@ -56,27 +56,44 @@ cc_library(
5656
)
5757

5858
cc_library(
59-
name = "brush_serialization_jni",
60-
srcs = ["brush_serialization_jni.cc"],
59+
name = "brush_family_serialization_jni",
60+
srcs = ["brush_family_serialization_jni.cc"],
6161
tags = ["keep_dep"],
6262
deps = [
63-
"//ink/brush",
64-
"//ink/brush:brush_behavior",
65-
"//ink/brush:brush_coat",
63+
":brush_family_serialization_jni_helper",
6664
"//ink/brush:brush_family",
67-
"//ink/brush:brush_paint",
68-
"//ink/brush:brush_tip",
6965
"//ink/brush:version",
7066
"//ink/brush/internal/jni:brush_native_helper",
7167
"//ink/jni/internal:jni_defines",
72-
"//ink/jni/internal:jni_jvm_interface",
7368
"//ink/jni/internal:jni_proto_util",
74-
"//ink/jni/internal:jni_string_util",
7569
"//ink/jni/internal:status_jni_helper",
7670
"//ink/storage:brush",
7771
"//ink/storage/proto:brush_cc_proto",
7872
"//ink/storage/proto:brush_family_cc_proto",
7973
"@com_google_absl//absl/base:nullability",
74+
"@com_google_absl//absl/status",
75+
"@com_google_absl//absl/status:statusor",
76+
] + select({
77+
"@platforms//os:android": [],
78+
"//conditions:default": [
79+
"@rules_jni//jni",
80+
],
81+
}),
82+
alwayslink = 1,
83+
)
84+
85+
cc_library(
86+
name = "brush_family_serialization_jni_helper",
87+
srcs = ["brush_family_serialization_jni_helper.cc"],
88+
hdrs = ["brush_family_serialization_jni_helper.h"],
89+
deps = [
90+
"//ink/brush:brush_family",
91+
"//ink/brush:version",
92+
"//ink/jni/internal:jni_string_util",
93+
"//ink/storage:brush",
94+
"//ink/storage/proto:brush_cc_proto",
95+
"//ink/storage/proto:brush_family_cc_proto",
96+
"@com_google_absl//absl/base:nullability",
8097
"@com_google_absl//absl/container:flat_hash_map",
8198
"@com_google_absl//absl/log:absl_check",
8299
"@com_google_absl//absl/status",
@@ -88,5 +105,4 @@ cc_library(
88105
"@rules_jni//jni",
89106
],
90107
}),
91-
alwayslink = 1,
92108
)
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include <jni.h>
16+
17+
#include <memory>
18+
#include <utility>
19+
#include <vector>
20+
21+
#include "absl/base/nullability.h"
22+
#include "absl/status/status.h"
23+
#include "absl/status/statusor.h"
24+
#include "ink/brush/brush_family.h"
25+
#include "ink/brush/internal/jni/brush_native_helper.h"
26+
#include "ink/brush/version.h"
27+
#include "ink/jni/internal/jni_defines.h"
28+
#include "ink/jni/internal/jni_proto_util.h"
29+
#include "ink/jni/internal/status_jni_helper.h"
30+
#include "ink/storage/brush.h"
31+
#include "ink/storage/internal/jni/brush_family_serialization_jni_helper.h"
32+
#include "ink/storage/proto/brush.pb.h"
33+
#include "ink/storage/proto/brush_family.pb.h"
34+
35+
using ::ink::BrushFamily;
36+
using ::ink::ClientTextureIdProviderAndBitmapReceiver;
37+
using ::ink::DecodeBrushFamily;
38+
using ::ink::DecodeMultipleBrushFamilies;
39+
using ::ink::EncodeBrushFamily;
40+
using ::ink::EncodeMultipleBrushFamilies;
41+
using ::ink::TextureBitmapProvider;
42+
using ::ink::Version;
43+
using ::ink::jni::CastToMutableMultipleBrushFamilies;
44+
using ::ink::jni::CreateDecodeTextureJniWrapper;
45+
using ::ink::jni::CreateTextureBitmapProvider;
46+
using ::ink::jni::DeleteMultipleBrushFamilies;
47+
using ::ink::jni::NewNativeMultipleBrushFamilies;
48+
using ::ink::jni::ParseProtoFromByteArray;
49+
using ::ink::jni::SerializeProto;
50+
using ::ink::jni::ThrowExceptionFromStatus;
51+
using ::ink::native::CastToBrushFamily;
52+
using ::ink::native::IntToVersion;
53+
using ::ink::native::NewNativeBrushFamily;
54+
55+
extern "C" {
56+
57+
JNI_METHOD(storage, BrushFamilySerializationNative, jbyteArray, encode)
58+
(JNIEnv* env, jobject object, jlong brush_family_native_pointer,
59+
jobjectArray texture_map_keys, jobjectArray texture_map_values) {
60+
TextureBitmapProvider texture_bitmap_provider =
61+
CreateTextureBitmapProvider(env, texture_map_keys, texture_map_values);
62+
63+
ink::proto::BrushFamily brush_family_proto;
64+
EncodeBrushFamily(CastToBrushFamily(brush_family_native_pointer),
65+
brush_family_proto, texture_bitmap_provider);
66+
return SerializeProto(env, brush_family_proto);
67+
}
68+
69+
JNI_METHOD(storage, BrushFamilySerializationNative, jbyteArray, encodeMultiple)
70+
(JNIEnv* env, jobject object, jlongArray brush_family_native_pointers,
71+
jobjectArray texture_map_keys, jobjectArray texture_map_values) {
72+
TextureBitmapProvider texture_bitmap_provider =
73+
CreateTextureBitmapProvider(env, texture_map_keys, texture_map_values);
74+
75+
jsize pointers_length = env->GetArrayLength(brush_family_native_pointers);
76+
jlong* pointers =
77+
env->GetLongArrayElements(brush_family_native_pointers, nullptr);
78+
std::vector<BrushFamily> families;
79+
families.reserve(pointers_length);
80+
for (jsize i = 0; i < pointers_length; ++i) {
81+
families.push_back(CastToBrushFamily(pointers[i]));
82+
}
83+
env->ReleaseLongArrayElements(brush_family_native_pointers, pointers,
84+
JNI_ABORT);
85+
86+
ink::proto::BrushFamily brush_family_proto;
87+
EncodeMultipleBrushFamilies(families, brush_family_proto,
88+
texture_bitmap_provider);
89+
return SerializeProto(env, brush_family_proto);
90+
}
91+
92+
JNI_METHOD(storage, BrushFamilySerializationNative, jlong, createFromProto)
93+
(JNIEnv* env, jobject object, jbyteArray brush_family_byte_array, jint length,
94+
absl_nullable jobject callback, jint max_version_value) {
95+
ink::proto::BrushFamily brush_family_proto;
96+
constexpr int kOffset = 0;
97+
if (absl::Status status = ParseProtoFromByteArray(
98+
env, brush_family_byte_array, kOffset, length, brush_family_proto);
99+
!status.ok()) {
100+
ThrowExceptionFromStatus(env, status);
101+
return 0;
102+
}
103+
104+
ClientTextureIdProviderAndBitmapReceiver decode_texture_jni_wrapper =
105+
CreateDecodeTextureJniWrapper(env, callback);
106+
107+
absl::StatusOr<Version> max_version = IntToVersion(max_version_value);
108+
if (!max_version.ok()) {
109+
ThrowExceptionFromStatus(env, max_version.status());
110+
return 0;
111+
}
112+
absl::StatusOr<BrushFamily> brush_family = DecodeBrushFamily(
113+
brush_family_proto, decode_texture_jni_wrapper, max_version.value());
114+
if (!brush_family.ok()) {
115+
// If the callback raised an exception we want to raise that as-is
116+
// instead of replacing it with the status.
117+
if (!env->ExceptionCheck()) {
118+
ThrowExceptionFromStatus(env, brush_family.status());
119+
}
120+
return 0;
121+
}
122+
return NewNativeBrushFamily(*std::move(brush_family));
123+
}
124+
125+
JNI_METHOD(storage, MultipleBrushFamiliesNative, jlong, createFromProto)
126+
(JNIEnv* env, jobject object, jbyteArray brush_family_byte_array, jint length,
127+
absl_nullable jobject callback, jint max_version_value) {
128+
constexpr int kOffset = 0;
129+
ink::proto::BrushFamily brush_family_proto;
130+
if (absl::Status status = ParseProtoFromByteArray(
131+
env, brush_family_byte_array, kOffset, length, brush_family_proto);
132+
!status.ok()) {
133+
ThrowExceptionFromStatus(env, status);
134+
return 0;
135+
}
136+
137+
ClientTextureIdProviderAndBitmapReceiver decode_texture_jni_wrapper =
138+
CreateDecodeTextureJniWrapper(env, callback);
139+
140+
absl::StatusOr<Version> max_version = IntToVersion(max_version_value);
141+
if (!max_version.ok()) {
142+
ThrowExceptionFromStatus(env, max_version.status());
143+
return 0;
144+
}
145+
absl::StatusOr<std::vector<BrushFamily>> brush_families =
146+
DecodeMultipleBrushFamilies(
147+
brush_family_proto, decode_texture_jni_wrapper, max_version.value());
148+
if (!brush_families.ok()) {
149+
if (!env->ExceptionCheck()) {
150+
ThrowExceptionFromStatus(env, brush_families.status());
151+
}
152+
return 0;
153+
}
154+
155+
std::vector<std::unique_ptr<BrushFamily>> result;
156+
result.reserve(brush_families->size());
157+
for (BrushFamily& brush_family : *brush_families) {
158+
result.push_back(std::make_unique<BrushFamily>(std::move(brush_family)));
159+
}
160+
return NewNativeMultipleBrushFamilies(std::move(result));
161+
}
162+
163+
JNI_METHOD(storage, MultipleBrushFamiliesNative, jint, getBrushFamilyCount)
164+
(JNIEnv* env, jobject object, jlong native_pointer) {
165+
return CastToMutableMultipleBrushFamilies(native_pointer).size();
166+
}
167+
168+
JNI_METHOD(storage, MultipleBrushFamiliesNative, jlong, releaseBrushFamily)
169+
(JNIEnv* env, jobject object, jlong native_pointer, jint index) {
170+
// This is already a heap-allocated BrushFamily, handed off so that the new
171+
// Kotlin BrushFamily will start managing cleanup.
172+
return reinterpret_cast<jlong>(
173+
CastToMutableMultipleBrushFamilies(native_pointer)[index].release());
174+
}
175+
176+
JNI_METHOD(storage, MultipleBrushFamiliesNative, void, free)
177+
(JNIEnv* env, jobject object, jlong native_pointer) {
178+
DeleteMultipleBrushFamilies(native_pointer);
179+
}
180+
181+
} // extern "C"
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include <jni.h>
16+
17+
#include <memory>
18+
#include <optional>
19+
#include <string>
20+
#include <utility>
21+
22+
#include "absl/base/nullability.h"
23+
#include "absl/container/flat_hash_map.h"
24+
#include "absl/log/absl_check.h"
25+
#include "absl/status/status.h"
26+
#include "absl/status/statusor.h"
27+
#include "absl/strings/string_view.h"
28+
#include "ink/brush/version.h"
29+
#include "ink/jni/internal/jni_string_util.h"
30+
#include "ink/storage/brush.h"
31+
#include "ink/storage/proto/brush.pb.h"
32+
#include "ink/storage/proto/brush_family.pb.h"
33+
34+
namespace ink::jni {
35+
36+
using ::ink::TextureBitmapProvider;
37+
using ::ink::jni::AbslStringViewToJByteArray;
38+
using ::ink::jni::JByteArrayToStdString;
39+
using ::ink::jni::JStringToStdString;
40+
41+
TextureBitmapProvider CreateTextureBitmapProvider(
42+
JNIEnv* env, jobjectArray texture_map_keys,
43+
jobjectArray texture_map_values) {
44+
auto texture_map =
45+
std::make_shared<absl::flat_hash_map<std::string, std::string>>();
46+
47+
jsize key_length = env->GetArrayLength(texture_map_keys);
48+
jsize value_length = env->GetArrayLength(texture_map_values);
49+
ABSL_CHECK_EQ(key_length, value_length);
50+
51+
for (jsize i = 0; i < key_length; ++i) {
52+
jstring j_texture_id =
53+
static_cast<jstring>(env->GetObjectArrayElement(texture_map_keys, i));
54+
std::string texture_id = JStringToStdString(env, j_texture_id);
55+
env->DeleteLocalRef(j_texture_id);
56+
57+
jbyteArray j_png_bytes = static_cast<jbyteArray>(
58+
env->GetObjectArrayElement(texture_map_values, i));
59+
std::string png_bytes = JByteArrayToStdString(env, j_png_bytes);
60+
env->DeleteLocalRef(j_png_bytes);
61+
62+
texture_map->insert({std::move(texture_id), std::move(png_bytes)});
63+
}
64+
65+
return [texture_map](
66+
absl::string_view texture_id) -> std::optional<std::string> {
67+
if (auto it = texture_map->find(texture_id); it != texture_map->end()) {
68+
return it->second;
69+
}
70+
return std::nullopt;
71+
};
72+
}
73+
74+
ClientTextureIdProviderAndBitmapReceiver CreateDecodeTextureJniWrapper(
75+
JNIEnv* env, absl_nullable jobject callback) {
76+
if (callback == nullptr) {
77+
return [](absl::string_view encoded_id,
78+
absl::string_view bitmap) -> absl::StatusOr<std::string> {
79+
return std::string(encoded_id);
80+
};
81+
}
82+
jclass callback_class = env->GetObjectClass(callback);
83+
// Can't cache this method lookup because it's on an interface and we don't
84+
// know what class it will be on in advance.
85+
jmethodID on_decode_texture_method =
86+
env->GetMethodID(callback_class, "onDecodeTexture",
87+
"(Ljava/lang/String;[B)Ljava/lang/String;");
88+
env->DeleteLocalRef(callback_class);
89+
90+
return [env, callback, on_decode_texture_method](
91+
absl::string_view encoded_id,
92+
absl::string_view bitmap) -> absl::StatusOr<std::string> {
93+
if (env->ExceptionCheck()) {
94+
return absl::InternalError("Previously encountered exception in JVM.");
95+
}
96+
jstring encoded_id_jstring =
97+
env->NewStringUTF(std::string(encoded_id).c_str());
98+
jbyteArray pixel_data_jarray =
99+
bitmap.empty() ? nullptr : AbslStringViewToJByteArray(env, bitmap);
100+
101+
jstring new_id_jstring = static_cast<jstring>(
102+
env->CallObjectMethod(callback, on_decode_texture_method,
103+
encoded_id_jstring, pixel_data_jarray));
104+
env->DeleteLocalRef(encoded_id_jstring);
105+
env->DeleteLocalRef(pixel_data_jarray);
106+
107+
if (env->ExceptionCheck()) {
108+
// Note that we're not clearing the exception here since we want to
109+
// raise it as-is later. We're counting on the parsing code bailing out
110+
// on the first error status encountered.
111+
return absl::InternalError("onDecodeTexture raised exception.");
112+
}
113+
std::string new_id = JStringToStdString(env, new_id_jstring);
114+
env->DeleteLocalRef(new_id_jstring);
115+
return new_id;
116+
};
117+
}
118+
119+
} // namespace ink::jni

0 commit comments

Comments
 (0)