From a988417b6d0b1ea03fb0b40269fbc42313acc6fd Mon Sep 17 00:00:00 2001 From: Samuel Freilich Date: Sat, 18 Apr 2026 10:26:39 -0700 Subject: [PATCH] Factor out KMP interface from EasingFunction JNI PiperOrigin-RevId: 901830735 --- ink/brush/internal/jni/BUILD.bazel | 31 ++- ink/brush/internal/jni/easing_function_jni.cc | 123 +++--------- .../internal/jni/easing_function_native.cc | 183 ++++++++++++++++++ .../internal/jni/easing_function_native.h | 91 +++++++++ ink/jni/internal/status_jni_helper.cc | 4 +- 5 files changed, 327 insertions(+), 105 deletions(-) create mode 100644 ink/brush/internal/jni/easing_function_native.cc create mode 100644 ink/brush/internal/jni/easing_function_native.h diff --git a/ink/brush/internal/jni/BUILD.bazel b/ink/brush/internal/jni/BUILD.bazel index 954050cc..1d3cfa4d 100644 --- a/ink/brush/internal/jni/BUILD.bazel +++ b/ink/brush/internal/jni/BUILD.bazel @@ -34,6 +34,18 @@ cc_library( ], ) +filegroup( + name = "cinterop_headers", + srcs = glob(["*_native.h"]), +) + +cc_library( + name = "cinterop", + deps = [ + ":easing_function_native", + ], +) + cc_library( name = "brush_native_helper", srcs = ["brush_native_helper.cc"], @@ -270,14 +282,10 @@ cc_library( srcs = ["easing_function_jni.cc"], tags = ["keep_dep"], deps = [ - ":brush_native_helper", - "//ink/brush:easing_function", - "//ink/geometry:point", + ":easing_function_native", "//ink/jni/internal:jni_defines", "//ink/jni/internal:status_jni_helper", - "@com_google_absl//absl/functional:overload", "@com_google_absl//absl/log:absl_check", - "@com_google_absl//absl/status", ] + select({ "@platforms//os:android": [], "//conditions:default": [ @@ -286,3 +294,16 @@ cc_library( }), alwayslink = 1, ) + +cc_library( + name = "easing_function_native", + srcs = ["easing_function_native.cc"], + hdrs = ["easing_function_native.h"], + deps = [ + ":brush_native_helper", + "//ink/brush:easing_function", + "//ink/geometry:point", + "@com_google_absl//absl/functional:overload", + "@com_google_absl//absl/status", + ], +) diff --git a/ink/brush/internal/jni/easing_function_jni.cc b/ink/brush/internal/jni/easing_function_jni.cc index d76c8c2b..048ff7c7 100644 --- a/ink/brush/internal/jni/easing_function_jni.cc +++ b/ink/brush/internal/jni/easing_function_jni.cc @@ -14,76 +14,37 @@ #include -#include -#include -#include - -#include "absl/functional/overload.h" #include "absl/log/absl_check.h" -#include "absl/status/status.h" -#include "ink/brush/easing_function.h" -#include "ink/brush/internal/jni/brush_native_helper.h" -#include "ink/geometry/point.h" +#include "ink/brush/internal/jni/easing_function_native.h" #include "ink/jni/internal/jni_defines.h" #include "ink/jni/internal/status_jni_helper.h" -namespace { - -using ink::EasingFunction; -using ink::Point; -using ink::jni::ThrowExceptionFromStatus; -using ink::native::CastToEasingFunction; -using ink::native::DeleteNativeEasingFunction; -using ink::native::NewNativeEasingFunction; - -jlong ValidateAndHoistEasingFunctionOrThrow( - EasingFunction::Parameters parameters, JNIEnv* env) { - EasingFunction easing_function{.parameters = std::move(parameters)}; - if (absl::Status status = - ink::brush_internal::ValidateEasingFunction(easing_function); - !status.ok()) { - ThrowExceptionFromStatus(env, status); - return 0; - } - return NewNativeEasingFunction(std::move(easing_function)); -} - -static constexpr int kPredefined = 0; -static constexpr int kCubicBezier = 1; -static constexpr int kLinear = 2; -static constexpr int kSteps = 3; - -} // namespace +using ::ink::jni::ThrowExceptionFromStatusCallback; extern "C" { JNI_METHOD(brush_behavior, EasingFunctionNative, jlong, createCopyOf) (JNIEnv* env, jobject thiz, jlong other_easing_function_native_pointer) { - return NewNativeEasingFunction( - CastToEasingFunction(other_easing_function_native_pointer)); + return EasingFunctionNative_createCopyOf( + other_easing_function_native_pointer); } JNI_METHOD(brush_behavior, EasingFunctionNative, jlong, createPredefined) (JNIEnv* env, jobject thiz, jint predefined_response_curve) { - return ValidateAndHoistEasingFunctionOrThrow( - static_cast(predefined_response_curve), env); + return EasingFunctionNative_createPredefined( + env, predefined_response_curve, &ThrowExceptionFromStatusCallback); } JNI_METHOD(brush_behavior, EasingFunctionNative, jlong, createCubicBezier) (JNIEnv* env, jobject thiz, jfloat x1, jfloat y1, jfloat x2, jfloat y2) { - return ValidateAndHoistEasingFunctionOrThrow( - EasingFunction::CubicBezier{.x1 = x1, .y1 = y1, .x2 = x2, .y2 = y2}, env); + return EasingFunctionNative_createCubicBezier( + env, x1, y1, x2, y2, &ThrowExceptionFromStatusCallback); } JNI_METHOD(brush_behavior, EasingFunctionNative, jlong, createSteps) (JNIEnv* env, jobject thiz, jint step_count, jint step_position) { - return ValidateAndHoistEasingFunctionOrThrow( - EasingFunction::Steps{ - .step_count = step_count, - .step_position = - static_cast(step_position), - }, - env); + return EasingFunctionNative_createSteps(env, step_count, step_position, + &ThrowExceptionFromStatusCallback); } JNI_METHOD(brush_behavior, EasingFunctionNative, jlong, createLinear) @@ -91,105 +52,71 @@ JNI_METHOD(brush_behavior, EasingFunctionNative, jlong, createLinear) jsize num_points = env->GetArrayLength(points_array) / 2; jfloat* points_elements = env->GetFloatArrayElements(points_array, nullptr); ABSL_CHECK(points_elements != nullptr); - std::vector points_vector; - points_vector.reserve(num_points); - for (int i = 0; i < num_points; ++i) { - float x = points_elements[2 * i]; - float y = points_elements[2 * i + 1]; - points_vector.push_back(Point{x, y}); - } + jlong result = EasingFunctionNative_createLinear( + env, points_elements, num_points, &ThrowExceptionFromStatusCallback); // Don't need to copy back the array, which is not modified. env->ReleaseFloatArrayElements(points_array, points_elements, JNI_ABORT); - return ValidateAndHoistEasingFunctionOrThrow( - EasingFunction::Linear{.points = std::move(points_vector)}, env); + return result; } JNI_METHOD(brush_behavior, EasingFunctionNative, void, free) (JNIEnv* env, jobject thiz, jlong native_pointer) { - DeleteNativeEasingFunction(native_pointer); + EasingFunctionNative_free(native_pointer); } JNI_METHOD(brush_behavior, EasingFunctionNative, jlong, getParametersType) (JNIEnv* env, jobject thiz, jlong native_pointer) { - constexpr auto visitor = absl::Overload{ - [](const EasingFunction::Predefined&) { return kPredefined; }, - [](const EasingFunction::CubicBezier&) { return kCubicBezier; }, - [](const EasingFunction::Steps&) { return kSteps; }, - [](const EasingFunction::Linear&) { return kLinear; }, - }; - return static_cast( - std::visit(visitor, CastToEasingFunction(native_pointer).parameters)); + return EasingFunctionNative_getParametersType(native_pointer); } JNI_METHOD(brush_behavior, EasingFunctionNative, jint, getPredefinedValueInt) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return static_cast(std::get( - CastToEasingFunction(native_pointer).parameters)); + return EasingFunctionNative_getPredefinedValueInt(native_pointer); } JNI_METHOD(brush_behavior, EasingFunctionNative, jfloat, getCubicBezierX1) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return std::get( - CastToEasingFunction(native_pointer).parameters) - .x1; + return EasingFunctionNative_getCubicBezierX1(native_pointer); } JNI_METHOD(brush_behavior, EasingFunctionNative, jfloat, getCubicBezierY1) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return std::get( - CastToEasingFunction(native_pointer).parameters) - .y1; + return EasingFunctionNative_getCubicBezierY1(native_pointer); } JNI_METHOD(brush_behavior, EasingFunctionNative, jfloat, getCubicBezierX2) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return std::get( - CastToEasingFunction(native_pointer).parameters) - .x2; + return EasingFunctionNative_getCubicBezierX2(native_pointer); } JNI_METHOD(brush_behavior, EasingFunctionNative, jfloat, getCubicBezierY2) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return std::get( - CastToEasingFunction(native_pointer).parameters) - .y2; + return EasingFunctionNative_getCubicBezierY2(native_pointer); } JNI_METHOD(brush_behavior, EasingFunctionNative, jint, getLinearNumPoints) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return static_cast(std::get( - CastToEasingFunction(native_pointer).parameters) - .points.size()); + return EasingFunctionNative_getLinearNumPoints(native_pointer); } JNI_METHOD(brush_behavior, EasingFunctionNative, jfloat, getLinearPointX) (JNIEnv* env, jobject thiz, jlong native_pointer, jint index) { - return std::get( - CastToEasingFunction(native_pointer).parameters) - .points[index] - .x; + return EasingFunctionNative_getLinearPointX(native_pointer, index); } JNI_METHOD(brush_behavior, EasingFunctionNative, jfloat, getLinearPointY) (JNIEnv* env, jobject thiz, jlong native_pointer, jint index) { - return std::get( - CastToEasingFunction(native_pointer).parameters) - .points[index] - .y; + return EasingFunctionNative_getLinearPointY(native_pointer, index); } JNI_METHOD(brush_behavior, EasingFunctionNative, jint, getStepsCount) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return std::get( - CastToEasingFunction(native_pointer).parameters) - .step_count; + return EasingFunctionNative_getStepsCount(native_pointer); } JNI_METHOD(brush_behavior, EasingFunctionNative, jint, getStepsPositionInt) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return static_cast(std::get( - CastToEasingFunction(native_pointer).parameters) - .step_position); + return EasingFunctionNative_getStepsPositionInt(native_pointer); } } // extern "C" diff --git a/ink/brush/internal/jni/easing_function_native.cc b/ink/brush/internal/jni/easing_function_native.cc new file mode 100644 index 00000000..fe5c4e0c --- /dev/null +++ b/ink/brush/internal/jni/easing_function_native.cc @@ -0,0 +1,183 @@ +// 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/brush/internal/jni/easing_function_native.h" + +#include +#include +#include +#include + +#include "absl/functional/overload.h" +#include "absl/status/status.h" +#include "ink/brush/easing_function.h" +#include "ink/brush/internal/jni/brush_native_helper.h" +#include "ink/geometry/point.h" + +namespace { + +using ink::EasingFunction; +using ink::Point; +using ink::native::CastToEasingFunction; +using ink::native::DeleteNativeEasingFunction; +using ink::native::NewNativeEasingFunction; + +int64_t ValidateAndHoistEasingFunction( + void* jni_env_pass_through, EasingFunction::Parameters parameters, + void (*throw_from_status_callback)(void*, int, const char*)) { + EasingFunction easing_function{.parameters = std::move(parameters)}; + if (absl::Status status = + ink::brush_internal::ValidateEasingFunction(easing_function); + !status.ok()) { + throw_from_status_callback(jni_env_pass_through, + static_cast(status.code()), + status.message().data()); + return 0; + } + return NewNativeEasingFunction(std::move(easing_function)); +} + +constexpr int kPredefined = 0; +constexpr int kCubicBezier = 1; +constexpr int kLinear = 2; +constexpr int kSteps = 3; + +} // namespace + +extern "C" { + +int64_t EasingFunctionNative_createCopyOf(int64_t other_native_ptr) { + return NewNativeEasingFunction(CastToEasingFunction(other_native_ptr)); +} + +int64_t EasingFunctionNative_createPredefined( + void* jni_env_pass_through, int value, + void (*throw_from_status_callback)(void*, int, const char*)) { + return ValidateAndHoistEasingFunction( + jni_env_pass_through, static_cast(value), + throw_from_status_callback); +} + +int64_t EasingFunctionNative_createCubicBezier( + void* jni_env_pass_through, float x1, float y1, float x2, float y2, + void (*throw_from_status_callback)(void*, int, const char*)) { + return ValidateAndHoistEasingFunction( + jni_env_pass_through, + EasingFunction::CubicBezier{.x1 = x1, .y1 = y1, .x2 = x2, .y2 = y2}, + throw_from_status_callback); +} + +int64_t EasingFunctionNative_createSteps( + void* jni_env_pass_through, int step_count, int step_position, + void (*throw_from_status_callback)(void*, int, const char*)) { + return ValidateAndHoistEasingFunction( + jni_env_pass_through, + EasingFunction::Steps{ + .step_count = step_count, + .step_position = + static_cast(step_position), + }, + throw_from_status_callback); +} + +int64_t EasingFunctionNative_createLinear( + void* jni_env_pass_through, const float* points, int num_points, + void (*throw_from_status_callback)(void*, int, const char*)) { + std::vector points_vector; + points_vector.reserve(num_points); + for (int i = 0; i < num_points; ++i) { + points_vector.push_back(Point{points[2 * i], points[2 * i + 1]}); + } + return ValidateAndHoistEasingFunction( + jni_env_pass_through, + EasingFunction::Linear{.points = std::move(points_vector)}, + throw_from_status_callback); +} + +void EasingFunctionNative_free(int64_t native_ptr) { + DeleteNativeEasingFunction(native_ptr); +} + +int EasingFunctionNative_getParametersType(int64_t native_ptr) { + constexpr auto visitor = absl::Overload{ + [](const EasingFunction::Predefined&) { return kPredefined; }, + [](const EasingFunction::CubicBezier&) { return kCubicBezier; }, + [](const EasingFunction::Steps&) { return kSteps; }, + [](const EasingFunction::Linear&) { return kLinear; }, + }; + return std::visit(visitor, CastToEasingFunction(native_ptr).parameters); +} + +int EasingFunctionNative_getPredefinedValueInt(int64_t native_ptr) { + return static_cast(std::get( + CastToEasingFunction(native_ptr).parameters)); +} + +float EasingFunctionNative_getCubicBezierX1(int64_t native_ptr) { + return std::get( + CastToEasingFunction(native_ptr).parameters) + .x1; +} + +float EasingFunctionNative_getCubicBezierY1(int64_t native_ptr) { + return std::get( + CastToEasingFunction(native_ptr).parameters) + .y1; +} + +float EasingFunctionNative_getCubicBezierX2(int64_t native_ptr) { + return std::get( + CastToEasingFunction(native_ptr).parameters) + .x2; +} + +float EasingFunctionNative_getCubicBezierY2(int64_t native_ptr) { + return std::get( + CastToEasingFunction(native_ptr).parameters) + .y2; +} + +int EasingFunctionNative_getLinearNumPoints(int64_t native_ptr) { + return static_cast(std::get( + CastToEasingFunction(native_ptr).parameters) + .points.size()); +} + +float EasingFunctionNative_getLinearPointX(int64_t native_ptr, int index) { + return std::get( + CastToEasingFunction(native_ptr).parameters) + .points[index] + .x; +} + +float EasingFunctionNative_getLinearPointY(int64_t native_ptr, int index) { + return std::get( + CastToEasingFunction(native_ptr).parameters) + .points[index] + .y; +} + +int EasingFunctionNative_getStepsCount(int64_t native_ptr) { + return std::get( + CastToEasingFunction(native_ptr).parameters) + .step_count; +} + +int EasingFunctionNative_getStepsPositionInt(int64_t native_ptr) { + return static_cast(std::get( + CastToEasingFunction(native_ptr).parameters) + .step_position); +} + +} // extern "C" diff --git a/ink/brush/internal/jni/easing_function_native.h b/ink/brush/internal/jni/easing_function_native.h new file mode 100644 index 00000000..8030456c --- /dev/null +++ b/ink/brush/internal/jni/easing_function_native.h @@ -0,0 +1,91 @@ +// 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_BRUSH_INTERNAL_JNI_EASING_FUNCTION_NATIVE_H_ +#define INK_BRUSH_INTERNAL_JNI_EASING_FUNCTION_NATIVE_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// The native_ptr parameter of these methods contains the raw pointer to a +// C++ EasingFunction object stored in Kotlin on EasingFunction.nativePointer. + +// Creates a new heap-allocated copy of the C++ EasingFunction pointed to by +// other_native_ptr and returns a pointer to it as int64_t, suitable for +// wrapping in a Kotlin EasingFunction. +int64_t EasingFunctionNative_createCopyOf(int64_t other_native_ptr); + +// Creates a new heap-allocated C++ EasingFunction with +// EasingFunction::Predefined parameters and returns a pointer to it as +// int64_t, suitable for wrapping in a Kotlin EasingFunction. If validation +// fails, calls throw_from_status_callback with the value passed as +// jni_env_pass_through, the error status code as an int, and the error status +// message. +int64_t EasingFunctionNative_createPredefined( + void* jni_env_pass_through, int value, + void (*throw_from_status_callback)(void*, int, const char*)); + +// Creates a new heap-allocated C++ EasingFunction with +// EasingFunction::CubicBezier parameters and returns a pointer to it as +// int64_t, suitable for wrapping in a Kotlin EasingFunction. If validation +// fails, calls throw_from_status_callback as above. +int64_t EasingFunctionNative_createCubicBezier( + void* jni_env_pass_through, float x1, float y1, float x2, float y2, + void (*throw_from_status_callback)(void*, int, const char*)); + +// Creates a new heap-allocated C++ EasingFunction with +// EasingFunction::Linear parameters and returns a pointer to it as +// int64_t, suitable for wrapping in a Kotlin EasingFunction. +// The points argument is a pointer to an array of floats of size +// 2 * num_points, consisting of interleaved x- and y-coordinates. If validation +// fails, calls throw_from_status_callback as above. +int64_t EasingFunctionNative_createLinear( + void* jni_env_pass_through, const float* points, int num_points, + void (*throw_from_status_callback)(void*, int, const char*)); + +// Creates a new heap-allocated C++ EasingFunction with +// EasingFunction::Steps parameters and returns a pointer to it as +// int64_t, suitable for wrapping in a Kotlin EasingFunction. If validation +// fails, calls throw_from_status_callback as above. +int64_t EasingFunctionNative_createSteps( + void* jni_env_pass_through, int step_count, int step_position, + void (*throw_from_status_callback)(void*, int, const char*)); + +// Frees a Kotlin EasingFunction.nativePointer. +void EasingFunctionNative_free(int64_t native_ptr); + +int EasingFunctionNative_getParametersType(int64_t native_ptr); + +int EasingFunctionNative_getPredefinedValueInt(int64_t native_ptr); + +float EasingFunctionNative_getCubicBezierX1(int64_t native_ptr); +float EasingFunctionNative_getCubicBezierY1(int64_t native_ptr); +float EasingFunctionNative_getCubicBezierX2(int64_t native_ptr); +float EasingFunctionNative_getCubicBezierY2(int64_t native_ptr); + +int EasingFunctionNative_getLinearNumPoints(int64_t native_ptr); +float EasingFunctionNative_getLinearPointX(int64_t native_ptr, int index); +float EasingFunctionNative_getLinearPointY(int64_t native_ptr, int index); + +int EasingFunctionNative_getStepsCount(int64_t native_ptr); +int EasingFunctionNative_getStepsPositionInt(int64_t native_ptr); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // INK_BRUSH_INTERNAL_JNI_EASING_FUNCTION_NATIVE_H_ diff --git a/ink/jni/internal/status_jni_helper.cc b/ink/jni/internal/status_jni_helper.cc index 0cb74b5f..55e3a4d9 100644 --- a/ink/jni/internal/status_jni_helper.cc +++ b/ink/jni/internal/status_jni_helper.cc @@ -25,11 +25,11 @@ namespace ink::jni { namespace { void ThrowExceptionFromStatus(JNIEnv* env, absl::StatusCode status_code, - absl::string_view status_message) { + absl::string_view status_string) { env->CallStaticVoidMethod( ClassNativeExceptionHandling(env), MethodNativeExceptionHandlingThrowForNonOkStatus(env), status_code, - env->NewStringUTF(status_message.data())); + env->NewStringUTF(status_string.data())); } } // namespace