Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ink/geometry/internal/jni/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,9 @@ cc_library(
"//ink/geometry:mesh",
"//ink/geometry:point",
"//ink/geometry:rect",
"//ink/jni/internal:jni_buffer_util",
"//ink/jni/internal:jni_defines",
"@com_google_absl//absl/base:nullability",
"@com_google_absl//absl/log:absl_check",
"@com_google_absl//absl/types:span",
] + select({
Expand Down
23 changes: 9 additions & 14 deletions ink/geometry/internal/jni/mesh_jni.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,21 @@

#include <cstddef>

#include "absl/base/nullability.h"
#include "absl/log/absl_check.h"
#include "absl/types/span.h"
#include "ink/geometry/internal/jni/box_jni_helper.h"
#include "ink/geometry/internal/jni/mesh_native.h"
#include "ink/geometry/internal/jni/mesh_native_helper.h"
#include "ink/geometry/internal/jni/vec_jni_helper.h"
#include "ink/geometry/mesh.h"
#include "ink/jni/internal/jni_buffer_util.h"
#include "ink/jni/internal/jni_defines.h"

namespace {

using ::ink::Mesh;
using ::ink::jni::ByteSpanToUnsafelyMutableDirectByteBuffer;
using ::ink::jni::CreateJImmutableBoxOrThrow;
using ::ink::jni::FillJMutableVecOrThrow;
using ::ink::native::CastToMesh;
Expand Down Expand Up @@ -63,17 +66,14 @@ JNI_METHOD(geometry, MeshNative, jlong, newCopy)
// Returns a direct [ByteBuffer] wrapped around the contents of
// `ink::Mesh::RawVertexData`. It will be writeable, so be sure to only expose a
// read-only wrapper of it.
JNI_METHOD(geometry, JvmMeshNative, jobject,
JNI_METHOD(geometry, JvmMeshNative, absl_nullable jobject,
createUnsafelyMutableMeshOwnedRawVertexBuffer)
(JNIEnv* env, jobject object, jlong native_pointer) {
const Mesh& mesh = CastToMesh(native_pointer);
const absl::Span<const std::byte> raw_vertex_data = mesh.RawVertexData();
if (raw_vertex_data.data() == nullptr) return nullptr;
return env->NewDirectByteBuffer(
// NewDirectByteBuffer needs a non-const void*. The resulting buffer is
// writeable, but it will be wrapped at the Kotlin layer in a read-only
// buffer that delegates to this one.
const_cast<std::byte*>(raw_vertex_data.data()), raw_vertex_data.size());
// The resulting buffer will be writeable, but it will be wrapped at the
// Kotlin layer in a read-only buffer that delegates to this one.
return ByteSpanToUnsafelyMutableDirectByteBuffer(env, raw_vertex_data);
}

// Return the number of bytes per vertex in `createRawVertexBuffer`.
Expand All @@ -99,13 +99,8 @@ JNI_METHOD(geometry, JvmMeshNative, jobject,
ABSL_CHECK_EQ(mesh.IndexStride(), 2u);
const absl::Span<const std::byte> raw_triangle_index_data =
mesh.RawIndexData();
if (raw_triangle_index_data.data() == nullptr) return nullptr;
return env->NewDirectByteBuffer(
// NewDirectByteBuffer needs a non-const void*. The resulting buffer is
// writeable, but it will be wrapped at the Kotlin layer in a read-only
// buffer that delegates to this one.
const_cast<std::byte*>(raw_triangle_index_data.data()),
raw_triangle_index_data.size());
return ByteSpanToUnsafelyMutableDirectByteBuffer(env,
raw_triangle_index_data);
}

// Return the number of triangles represented in `createRawTriangleIndexBuffer`.
Expand Down
16 changes: 16 additions & 0 deletions ink/jni/internal/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,22 @@ cc_library(
}),
)

cc_library(
name = "jni_buffer_util",
srcs = ["jni_buffer_util.cc"],
hdrs = ["jni_buffer_util.h"],
deps = [
"@com_google_absl//absl/base:nullability",
"@com_google_absl//absl/log:absl_check",
"@com_google_absl//absl/types:span",
] + select({
"@platforms//os:android": [],
"//conditions:default": [
"@rules_jni//jni",
],
}),
)

cc_library(
name = "jni_string_util",
srcs = ["jni_string_util.cc"],
Expand Down
40 changes: 40 additions & 0 deletions ink/jni/internal/jni_buffer_util.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2026 Google LLC
//
// 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.

#include "ink/jni/internal/jni_buffer_util.h"

#include <jni.h>

#include <cstddef>

#include "absl/base/nullability.h"
#include "absl/log/absl_check.h"
#include "absl/types/span.h"

namespace ink::jni {

absl_nullable jobject ByteSpanToUnsafelyMutableDirectByteBuffer(
JNIEnv* env, absl::Span<const std::byte> span) {
// span.data() may be nullptr if empty, which NewDirectByteBuffer does not
// permit.
if (span.empty()) return nullptr;
ABSL_CHECK(span.data() != nullptr);
return env->NewDirectByteBuffer(
// NewDirectByteBuffer needs a non-const void*. The resulting buffer is
// writeable, so it must be wrapped at the Kotlin layer in a read-only
// buffer that delegates to this one.
const_cast<std::byte*>(span.data()), span.size());
}

} // namespace ink::jni
37 changes: 37 additions & 0 deletions ink/jni/internal/jni_buffer_util.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2026 Google LLC
//
// 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 INK_JNI_INTERNAL_JNI_BUFFER_UTIL_H_
#define INK_JNI_INTERNAL_JNI_BUFFER_UTIL_H_

#include <jni.h>

#include <cstddef>

#include "absl/base/nullability.h"
#include "absl/types/span.h"

namespace ink::jni {

// Converts a span of bytes to a Java direct ByteBuffer. Returns nullptr
// if the span is empty because the JNI interface does not provide a good way
// to allocate an empty direct byte-buffer. (When a span is empty, its data
// pointer may be nullptr, which NewDirectByteBuffer does not permit even with
// size 0.)
absl_nullable jobject ByteSpanToUnsafelyMutableDirectByteBuffer(
JNIEnv* env, absl::Span<const std::byte> span);

} // namespace ink::jni

#endif // INK_JNI_INTERNAL_JNI_BUFFER_UTIL_H_
30 changes: 9 additions & 21 deletions ink/strokes/internal/jni/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ cc_library(
srcs = ["in_progress_stroke_jni.cc"],
tags = ["keep_dep"],
deps = [
":in_progress_stroke_jni_helper",
":in_progress_stroke_native_helper",
":stroke_input_batch_native_helper",
":stroke_input_jni_helper",
":stroke_jni_helper",
Expand All @@ -103,6 +103,7 @@ cc_library(
"//ink/geometry/internal/jni:box_accumulator_jni_helper",
"//ink/geometry/internal/jni:mesh_format_native_helper",
"//ink/geometry/internal/jni:vec_jni_helper",
"//ink/jni/internal:jni_buffer_util",
"//ink/jni/internal:jni_defines",
"//ink/jni/internal:status_jni_helper",
"//ink/strokes:in_progress_stroke",
Expand All @@ -125,39 +126,26 @@ cc_library(
)

cc_library(
name = "in_progress_stroke_jni_helper",
srcs = ["in_progress_stroke_jni_helper.cc"],
hdrs = ["in_progress_stroke_jni_helper.h"],
name = "in_progress_stroke_native_helper",
srcs = ["in_progress_stroke_native_helper.cc"],
hdrs = ["in_progress_stroke_native_helper.h"],
deps = [
"//ink/brush",
"//ink/geometry:angle",
"//ink/geometry:mutable_mesh",
"//ink/jni/internal:jni_defines",
"//ink/strokes:in_progress_stroke",
"//ink/strokes/input:stroke_input",
"//ink/strokes/input:stroke_input_batch",
"//ink/types:duration",
"//ink/types:physical_distance",
"@com_google_absl//absl/base:nullability",
"@com_google_absl//absl/container:inlined_vector",
"@com_google_absl//absl/log:absl_check",
"@com_google_absl//absl/log:absl_log",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/types:span",
] + select({
"@platforms//os:android": [],
"//conditions:default": [
"@rules_jni//jni",
],
}),
],
)

cc_test(
name = "in_progress_stroke_jni_helper_test",
srcs = ["in_progress_stroke_jni_helper_test.cc"],
name = "in_progress_stroke_native_helper_test",
srcs = ["in_progress_stroke_native_helper_test.cc"],
deps = [
":in_progress_stroke_jni_helper",
":in_progress_stroke_native_helper",
"@com_google_googletest//:gtest_main",
],
)
Expand Down
34 changes: 21 additions & 13 deletions ink/strokes/internal/jni/in_progress_stroke_jni.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@
#include "ink/geometry/internal/jni/vec_jni_helper.h"
#include "ink/geometry/mutable_mesh.h"
#include "ink/geometry/point.h"
#include "ink/jni/internal/jni_buffer_util.h"
#include "ink/jni/internal/jni_defines.h"
#include "ink/jni/internal/status_jni_helper.h"
#include "ink/strokes/in_progress_stroke.h"
#include "ink/strokes/input/stroke_input.h"
#include "ink/strokes/input/stroke_input_batch.h"
#include "ink/strokes/internal/jni/in_progress_stroke_jni_helper.h"
#include "ink/strokes/internal/jni/in_progress_stroke_native_helper.h"
#include "ink/strokes/internal/jni/stroke_input_batch_native_helper.h"
#include "ink/strokes/internal/jni/stroke_input_jni_helper.h"
#include "ink/strokes/internal/jni/stroke_jni_helper.h"
Expand All @@ -44,19 +45,20 @@ using ::ink::InProgressStroke;
using ::ink::Point;
using ::ink::StrokeInput;
using ::ink::StrokeInputBatch;
using ::ink::jni::CastToInProgressStrokeWrapper;
using ::ink::jni::CastToMutableInProgressStrokeWrapper;
using ::ink::jni::DeleteNativeInProgressStroke;
using ::ink::jni::ByteSpanToUnsafelyMutableDirectByteBuffer;
using ::ink::jni::FillJBoxAccumulatorOrThrow;
using ::ink::jni::FillJMutableVecOrThrow;
using ::ink::jni::InProgressStrokeWrapper;
using ::ink::jni::NewNativeInProgressStroke;
using ::ink::jni::NewNativeStroke;
using ::ink::jni::ThrowExceptionFromStatus;
using ::ink::jni::UpdateJStrokeInputOrThrow;
using ::ink::native::CastToBrush;
using ::ink::native::CastToInProgressStrokeWrapper;
using ::ink::native::CastToMutableInProgressStrokeWrapper;
using ::ink::native::CastToMutableStrokeInputBatch;
using ::ink::native::CastToStrokeInputBatch;
using ::ink::native::DeleteNativeInProgressStroke;
using ::ink::native::InProgressStrokeWrapper;
using ::ink::native::NewNativeInProgressStroke;
using ::ink::native::NewNativeMeshFormat;

extern "C" {
Expand Down Expand Up @@ -277,12 +279,15 @@ JNI_METHOD(strokes, InProgressStrokeNative, jint, getVertexCount)
.VertexCount(coat_index, mesh_index);
}

JNI_METHOD(strokes, InProgressStrokeNative, absl_nullable jobject,
getUnsafelyMutableRawVertexData)
JNI_METHOD(strokes, JvmInProgressStrokeNative, absl_nullable jobject,
getUnsafelyMutableInProgressStrokeOwnedRawVertexData)
(JNIEnv* env, jobject thiz, jlong native_pointer, jint coat_index,
jint mesh_index) {
return CastToInProgressStrokeWrapper(native_pointer)
.GetUnsafelyMutableRawVertexData(env, coat_index, mesh_index);
// The resulting buffer will be writeable, but it will be wrapped at the
// Kotlin layer in a read-only buffer that delegates to this one.
return ByteSpanToUnsafelyMutableDirectByteBuffer(
env, CastToInProgressStrokeWrapper(native_pointer)
.GetRawVertexData(coat_index, mesh_index));
}

// Returns a direct byte buffer of the triangle index data in 16-bit format.
Expand All @@ -294,11 +299,14 @@ JNI_METHOD(strokes, InProgressStrokeNative, absl_nullable jobject,
// TODO: b/294561921 - Simplify this when the underlying index data is in 16 bit
// values.
JNI_METHOD(strokes, InProgressStrokeNative, absl_nullable jobject,
getUnsafelyMutableRawTriangleIndexData)
getUnsafelyMutableInProgressStrokeOwnedRawTriangleIndexData)
(JNIEnv* env, jobject thiz, jlong native_pointer, jint coat_index,
jint mesh_index) {
return CastToInProgressStrokeWrapper(native_pointer)
.GetUnsafelyMutableRawTriangleIndexData(env, coat_index, mesh_index);
// The resulting buffer will be writeable, but it will be wrapped at the
// Kotlin layer in a read-only buffer that delegates to this one.
return ByteSpanToUnsafelyMutableDirectByteBuffer(
env, CastToInProgressStrokeWrapper(native_pointer)
.GetRawTriangleIndexData(coat_index, mesh_index));
}

// Return a newly allocated copy of the given `Mesh`'s `MeshFormat`.
Expand Down
Loading
Loading