From aee3da931a1b499544bdb50e6b78023ff4090b76 Mon Sep 17 00:00:00 2001 From: Samuel Freilich Date: Fri, 1 May 2026 07:59:16 -0700 Subject: [PATCH] Extract KMP interface from BrushPaint JNI PiperOrigin-RevId: 908734647 --- ink/brush/internal/jni/BUILD.bazel | 26 +- ink/brush/internal/jni/brush_jni.cc | 5 +- ink/brush/internal/jni/brush_paint_jni.cc | 295 ++++----------- ink/brush/internal/jni/brush_paint_native.cc | 338 ++++++++++++++++++ ink/brush/internal/jni/brush_paint_native.h | 117 ++++++ ink/color/internal/jni/BUILD.bazel | 12 + ink/color/internal/jni/color_jni_helper.cc | 60 +--- ink/color/internal/jni/color_jni_helper.h | 16 +- ink/color/internal/jni/color_native_helper.cc | 86 +++++ ink/color/internal/jni/color_native_helper.h | 34 ++ ink/jni/internal/jni_jvm_interface.cc | 28 +- ink/jni/internal/jni_jvm_interface.h | 4 +- 12 files changed, 725 insertions(+), 296 deletions(-) create mode 100644 ink/brush/internal/jni/brush_paint_native.cc create mode 100644 ink/brush/internal/jni/brush_paint_native.h create mode 100644 ink/color/internal/jni/color_native_helper.cc create mode 100644 ink/color/internal/jni/color_native_helper.h diff --git a/ink/brush/internal/jni/BUILD.bazel b/ink/brush/internal/jni/BUILD.bazel index a9713617..c45242ac 100644 --- a/ink/brush/internal/jni/BUILD.bazel +++ b/ink/brush/internal/jni/BUILD.bazel @@ -44,6 +44,7 @@ cc_library( deps = [ ":brush_behavior_native", ":brush_behavior_node_native", + ":brush_paint_native", ":brush_tip_native", ":easing_function_native", ], @@ -102,7 +103,6 @@ cc_library( deps = [ ":brush_native_helper", "//ink/brush:brush_behavior", - "//ink/jni/internal:status_jni_helper", "@com_google_absl//absl/functional:overload", "@com_google_absl//absl/log:absl_check", "@com_google_absl//absl/status", @@ -186,12 +186,35 @@ cc_library( alwayslink = 1, ) +cc_library( + name = "brush_paint_native", + srcs = ["brush_paint_native.cc"], + hdrs = ["brush_paint_native.h"], + deps = [ + ":brush_native_helper", + "//ink/brush:brush_paint", + "//ink/brush:color_function", + "//ink/color", + "//ink/color/internal/jni:color_native_helper", + "//ink/geometry:angle", + "//ink/geometry:mesh_format", + "//ink/geometry:vec", + "//ink/geometry/internal/jni:mesh_format_native_helper", + "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/functional:overload", + "@com_google_absl//absl/status", + "@com_google_absl//absl/time", + "@com_google_absl//absl/types:span", + ], +) + cc_library( name = "brush_paint_jni", srcs = ["brush_paint_jni.cc"], tags = ["keep_dep"], deps = [ ":brush_native_helper", + ":brush_paint_native", "//ink/brush:brush_paint", "//ink/brush:color_function", "//ink/color", @@ -313,6 +336,7 @@ cc_library( "//ink/brush:brush_family", "//ink/color", "//ink/color/internal/jni:color_jni_helper", + "//ink/color/internal/jni:color_native_helper", "//ink/jni/internal:jni_defines", "//ink/jni/internal:status_jni_helper", "@com_google_absl//absl/log:absl_check", diff --git a/ink/brush/internal/jni/brush_jni.cc b/ink/brush/internal/jni/brush_jni.cc index 6502773f..f540ebc0 100644 --- a/ink/brush/internal/jni/brush_jni.cc +++ b/ink/brush/internal/jni/brush_jni.cc @@ -21,6 +21,7 @@ #include "ink/brush/internal/jni/brush_native_helper.h" #include "ink/color/color.h" #include "ink/color/internal/jni/color_jni_helper.h" +#include "ink/color/internal/jni/color_native_helper.h" #include "ink/jni/internal/jni_defines.h" #include "ink/jni/internal/status_jni_helper.h" @@ -30,11 +31,11 @@ using ::ink::Brush; using ::ink::BrushFamily; using ::ink::Color; using ::ink::jni::ComputeColorLong; -using ::ink::jni::JIntToColorSpace; using ::ink::jni::ThrowExceptionFromStatus; using ::ink::native::CastToBrush; using ::ink::native::CastToBrushFamily; using ::ink::native::DeleteNativeBrush; +using ::ink::native::IntToColorSpace; using ::ink::native::NewNativeBrush; using ::ink::native::NewNativeBrushFamily; @@ -51,7 +52,7 @@ JNI_METHOD(brush, BrushNative, jlong, create) const Color color = Color::FromFloat( color_red, color_green, color_blue, color_alpha, - Color::Format::kGammaEncoded, JIntToColorSpace(color_space_id)); + Color::Format::kGammaEncoded, IntToColorSpace(color_space_id)); auto brush = Brush::Create(family, color, size, epsilon); if (!brush.ok()) { diff --git a/ink/brush/internal/jni/brush_paint_jni.cc b/ink/brush/internal/jni/brush_paint_jni.cc index 576069bc..5e90d40f 100644 --- a/ink/brush/internal/jni/brush_paint_jni.cc +++ b/ink/brush/internal/jni/brush_paint_jni.cc @@ -14,375 +14,230 @@ #include -#include -#include -#include - -#include "absl/container/flat_hash_set.h" -#include "absl/functional/overload.h" -#include "absl/log/absl_check.h" -#include "absl/status/status.h" -#include "absl/time/time.h" -#include "absl/types/span.h" -#include "ink/brush/brush_paint.h" -#include "ink/brush/color_function.h" -#include "ink/brush/internal/jni/brush_native_helper.h" -#include "ink/color/color.h" +#include +#include + +#include "ink/brush/internal/jni/brush_paint_native.h" #include "ink/color/internal/jni/color_jni_helper.h" -#include "ink/geometry/angle.h" -#include "ink/geometry/internal/jni/mesh_format_native_helper.h" -#include "ink/geometry/mesh_format.h" -#include "ink/geometry/vec.h" #include "ink/jni/internal/jni_defines.h" #include "ink/jni/internal/jni_string_util.h" #include "ink/jni/internal/status_jni_helper.h" -namespace { - -using ::ink::Angle; -using ::ink::BrushPaint; -using ::ink::Color; -using ::ink::ColorFunction; -using ::ink::MeshFormat; -using ::ink::Vec; -using ::ink::brush_internal::AddAttributeIdsRequiredByPaint; -using ::ink::brush_internal::ValidateBrushPaint; -using ::ink::brush_internal::ValidateBrushPaintTextureLayer; -using ::ink::jni::ComputeColorLong; -using ::ink::jni::JIntToColorSpace; +using ::ink::jni::ComputeColorLongFromComponentsCallback; using ::ink::jni::JStringToStdString; -using ::ink::jni::ThrowExceptionFromStatus; -using ::ink::native::CastToBrushPaint; -using ::ink::native::CastToColorFunction; -using ::ink::native::CastToMeshFormat; -using ::ink::native::CastToTextureLayer; -using ::ink::native::DeleteNativeBrushPaint; -using ::ink::native::DeleteNativeColorFunction; -using ::ink::native::DeleteNativeTextureLayer; -using ::ink::native::NewNativeBrushPaint; -using ::ink::native::NewNativeColorFunction; -using ::ink::native::NewNativeTextureLayer; - -BrushPaint::TextureSizeUnit JIntToSizeUnit(jint val) { - return static_cast(val); -} - -BrushPaint::TextureOrigin JIntToOrigin(jint val) { - return static_cast(val); -} - -BrushPaint::TextureMapping JIntToMapping(jint val) { - return static_cast(val); -} - -BrushPaint::TextureWrap JIntToWrap(jint val) { - return static_cast(val); -} - -BrushPaint::BlendMode JIntToBlendMode(jint val) { - return static_cast(val); -} - -BrushPaint::SelfOverlap JIntToSelfOverlap(jint val) { - return static_cast(val); -} - -jlong ValidateAndHoistColorFunctionOrThrow(ColorFunction::Parameters parameters, - JNIEnv* env) { - ColorFunction color_function{.parameters = std::move(parameters)}; - if (absl::Status status = - ink::brush_internal::ValidateColorFunction(color_function); - !status.ok()) { - ThrowExceptionFromStatus(env, status); - return 0; - } - return NewNativeColorFunction(std::move(color_function)); -} - -static constexpr int kOpacityMultiplier = 0; -static constexpr int kReplaceColor = 1; - -} // namespace +using ::ink::jni::ThrowExceptionFromStatusCallback; extern "C" { -// Construct a native BrushPaint and return a pointer to it as a long. JNI_METHOD(brush, BrushPaintNative, jlong, create) (JNIEnv* env, jobject thiz, jlongArray texture_layer_native_pointers_array, jlongArray color_function_native_pointers_array, jint self_overlap_int) { - std::vector texture_layers; - ABSL_CHECK(texture_layer_native_pointers_array != nullptr); const jsize texture_layers_count = env->GetArrayLength(texture_layer_native_pointers_array); - texture_layers.reserve(texture_layers_count); jlong* texture_layer_native_pointers = env->GetLongArrayElements(texture_layer_native_pointers_array, nullptr); - ABSL_CHECK(texture_layer_native_pointers != nullptr); - for (int i = 0; i < texture_layers_count; ++i) { - texture_layers.push_back( - CastToTextureLayer(texture_layer_native_pointers[i])); - } - env->ReleaseLongArrayElements( - texture_layer_native_pointers_array, texture_layer_native_pointers, - // No need to copy back the array, which is not modified. - JNI_ABORT); - - std::vector color_functions; - ABSL_CHECK(color_function_native_pointers_array != nullptr); + const jsize color_functions_count = env->GetArrayLength(color_function_native_pointers_array); - color_functions.reserve(color_functions_count); jlong* color_function_native_pointers = env->GetLongArrayElements(color_function_native_pointers_array, nullptr); - ABSL_CHECK(color_function_native_pointers != nullptr); - for (int i = 0; i < color_functions_count; ++i) { - color_functions.push_back( - CastToColorFunction(color_function_native_pointers[i])); - } - env->ReleaseLongArrayElements( - color_function_native_pointers_array, color_function_native_pointers, - // No need to copy back the array, which is not modified. - JNI_ABORT); - - BrushPaint brush_paint{.texture_layers = std::move(texture_layers), - .color_functions = std::move(color_functions), - .self_overlap = JIntToSelfOverlap(self_overlap_int)}; - if (absl::Status status = ValidateBrushPaint(brush_paint); !status.ok()) { - ThrowExceptionFromStatus(env, status); - return 0; - } - return NewNativeBrushPaint(std::move(brush_paint)); + + // Both `jlong` and `int64_t` are required to be 64-bit integers which JNI and + // Kotlin-cinterop respectively both map to Kotlin `Long`. However, on MacOS + // they represent two distinct (though equivalent) types, `jlong` is `long` + // but `int64_t` is `long long`. + static_assert(sizeof(jlong) == sizeof(int64_t)); + int64_t result = BrushPaintNative_create( + env, reinterpret_cast(texture_layer_native_pointers), + texture_layers_count, + reinterpret_cast(color_function_native_pointers), + color_functions_count, self_overlap_int, + &ThrowExceptionFromStatusCallback); + + env->ReleaseLongArrayElements(texture_layer_native_pointers_array, + texture_layer_native_pointers, JNI_ABORT); + env->ReleaseLongArrayElements(color_function_native_pointers_array, + color_function_native_pointers, JNI_ABORT); + + return result; } JNI_METHOD(brush, BrushPaintNative, void, free) (JNIEnv* env, jobject thiz, jlong native_pointer) { - DeleteNativeBrushPaint(native_pointer); + BrushPaintNative_free(native_pointer); } JNI_METHOD(brush, BrushPaintNative, jint, getTextureLayerCount) (JNIEnv* env, jobject thiz, jlong native_pointer) { - const BrushPaint& brush_paint = CastToBrushPaint(native_pointer); - return brush_paint.texture_layers.size(); + return BrushPaintNative_getTextureLayerCount(native_pointer); } -// Returns a pointer to a newly heap-allocated copy of the texture layer at the -// given instance on this BrushPaint. JNI_METHOD(brush, BrushPaintNative, jlong, newCopyOfTextureLayer) (JNIEnv* env, jobject thiz, jlong native_pointer, jint index) { - const BrushPaint& brush_paint = CastToBrushPaint(native_pointer); - return NewNativeTextureLayer(brush_paint.texture_layers[index]); + return BrushPaintNative_newCopyOfTextureLayer(native_pointer, index); } JNI_METHOD(brush, BrushPaintNative, jint, getColorFunctionCount) (JNIEnv* env, jobject thiz, jlong native_pointer) { - const BrushPaint& brush_paint = CastToBrushPaint(native_pointer); - return brush_paint.color_functions.size(); + return BrushPaintNative_getColorFunctionCount(native_pointer); +} + +JNI_METHOD(brush, BrushPaintNative, jint, getColorFunctionParametersTypeInt) +(JNIEnv* env, jobject thiz, jlong native_pointer, jint index) { + return BrushPaintNative_getColorFunctionParametersTypeInt(native_pointer, + index); } -// Returns a pointer to a newly heap-allocated copy of the color function at the -// given instance on this BrushPaint. JNI_METHOD(brush, BrushPaintNative, jlong, newCopyOfColorFunction) (JNIEnv* env, jobject thiz, jlong native_pointer, jint index) { - const BrushPaint& brush_paint = CastToBrushPaint(native_pointer); - return NewNativeColorFunction(brush_paint.color_functions[index]); + return BrushPaintNative_newCopyOfColorFunction(native_pointer, index); } JNI_METHOD(brush, BrushPaintNative, jint, getSelfOverlapInt) (JNIEnv* env, jobject thiz, jlong native_pointer) { - const BrushPaint& brush_paint = CastToBrushPaint(native_pointer); - return static_cast(brush_paint.self_overlap); + return BrushPaintNative_getSelfOverlapInt(native_pointer); } JNI_METHOD(brush, BrushPaintNative, jboolean, isCompatibleWithMeshFormat) (JNIEnv* env, jobject obj, jlong native_pointer, jlong mesh_format_native_pointer) { - // Gather all the attributes that are required by the brush paint. - absl::flat_hash_set required_attribute_ids; - AddAttributeIdsRequiredByPaint(CastToBrushPaint(native_pointer), - required_attribute_ids); - - // Check if all required attributes are present in the mesh format. - absl::Span mesh_attributes = - CastToMeshFormat(mesh_format_native_pointer).Attributes(); - for (const MeshFormat::Attribute& attr : mesh_attributes) { - required_attribute_ids.erase(attr.id); - } - return required_attribute_ids.empty(); + return BrushPaintNative_isCompatibleWithMeshFormat( + native_pointer, mesh_format_native_pointer); } -// ************ Native Implementation of BrushPaint TextureLayer ************ +// Native Implementation of BrushPaint.TextureLayer: -// Constructs a native BrushPaint TextureLayer and returns a pointer to it as a -// long. JNI_METHOD(brush, TextureLayerNative, jlong, create) (JNIEnv* env, jobject thiz, jstring client_texture_id, jfloat size_x, jfloat size_y, jfloat offset_x, jfloat offset_y, jfloat rotation_degrees, jint animation_frames, jint animation_rows, jint animation_columns, jlong animation_duration_millis, jint size_unit, jint origin, jint mapping, jint wrap_x, jint wrap_y, jint blend_mode) { - BrushPaint::TextureLayer texture_layer{ - .client_texture_id = JStringToStdString(env, client_texture_id), - .mapping = JIntToMapping(mapping), - .origin = JIntToOrigin(origin), - .size_unit = JIntToSizeUnit(size_unit), - .wrap_x = JIntToWrap(wrap_x), - .wrap_y = JIntToWrap(wrap_y), - .size = Vec{size_x, size_y}, - .offset = Vec{offset_x, offset_y}, - .rotation = Angle::Degrees(rotation_degrees), - .animation_frames = animation_frames, - .animation_rows = animation_rows, - .animation_columns = animation_columns, - .animation_duration = absl::Milliseconds(animation_duration_millis), - .blend_mode = JIntToBlendMode(blend_mode), - }; - if (absl::Status status = ValidateBrushPaintTextureLayer(texture_layer); - !status.ok()) { - ThrowExceptionFromStatus(env, status); - return 0; - } - return NewNativeTextureLayer(std::move(texture_layer)); + std::string client_texture_id_str = + JStringToStdString(env, client_texture_id); + return TextureLayerNative_create( + env, client_texture_id_str.c_str(), size_x, size_y, offset_x, offset_y, + rotation_degrees, animation_frames, animation_rows, animation_columns, + animation_duration_millis, size_unit, origin, mapping, wrap_x, wrap_y, + blend_mode, &ThrowExceptionFromStatusCallback); } JNI_METHOD(brush, TextureLayerNative, void, free) (JNIEnv* env, jobject thiz, jlong native_pointer) { - DeleteNativeTextureLayer(native_pointer); + TextureLayerNative_free(native_pointer); } JNI_METHOD(brush, TextureLayerNative, jstring, getClientTextureId) (JNIEnv* env, jobject thiz, jlong native_pointer) { return env->NewStringUTF( - CastToTextureLayer(native_pointer).client_texture_id.c_str()); + TextureLayerNative_getClientTextureId(native_pointer)); } JNI_METHOD(brush, TextureLayerNative, jfloat, getSizeX) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return CastToTextureLayer(native_pointer).size.x; + return TextureLayerNative_getSizeX(native_pointer); } JNI_METHOD(brush, TextureLayerNative, jfloat, getSizeY) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return CastToTextureLayer(native_pointer).size.y; + return TextureLayerNative_getSizeY(native_pointer); } JNI_METHOD(brush, TextureLayerNative, jfloat, getOffsetX) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return CastToTextureLayer(native_pointer).offset.x; + return TextureLayerNative_getOffsetX(native_pointer); } JNI_METHOD(brush, TextureLayerNative, jfloat, getOffsetY) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return CastToTextureLayer(native_pointer).offset.y; + return TextureLayerNative_getOffsetY(native_pointer); } JNI_METHOD(brush, TextureLayerNative, jfloat, getRotationDegrees) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return CastToTextureLayer(native_pointer).rotation.ValueInDegrees(); + return TextureLayerNative_getRotationDegrees(native_pointer); } JNI_METHOD(brush, TextureLayerNative, jint, getAnimationFrames) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return CastToTextureLayer(native_pointer).animation_frames; + return TextureLayerNative_getAnimationFrames(native_pointer); } JNI_METHOD(brush, TextureLayerNative, jint, getAnimationRows) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return CastToTextureLayer(native_pointer).animation_rows; + return TextureLayerNative_getAnimationRows(native_pointer); } JNI_METHOD(brush, TextureLayerNative, jint, getAnimationColumns) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return CastToTextureLayer(native_pointer).animation_columns; + return TextureLayerNative_getAnimationColumns(native_pointer); } JNI_METHOD(brush, TextureLayerNative, jlong, getAnimationDurationMillis) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return absl::ToInt64Milliseconds( - CastToTextureLayer(native_pointer).animation_duration); + return TextureLayerNative_getAnimationDurationMillis(native_pointer); } JNI_METHOD(brush, TextureLayerNative, jint, getSizeUnitInt) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return static_cast(CastToTextureLayer(native_pointer).size_unit); + return TextureLayerNative_getSizeUnitInt(native_pointer); } JNI_METHOD(brush, TextureLayerNative, jint, getOriginInt) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return static_cast(CastToTextureLayer(native_pointer).origin); + return TextureLayerNative_getOriginInt(native_pointer); } JNI_METHOD(brush, TextureLayerNative, jint, getMappingInt) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return static_cast(CastToTextureLayer(native_pointer).mapping); + return TextureLayerNative_getMappingInt(native_pointer); } JNI_METHOD(brush, TextureLayerNative, jint, getWrapXInt) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return static_cast(CastToTextureLayer(native_pointer).wrap_x); + return TextureLayerNative_getWrapXInt(native_pointer); } JNI_METHOD(brush, TextureLayerNative, jint, getWrapYInt) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return static_cast(CastToTextureLayer(native_pointer).wrap_y); + return TextureLayerNative_getWrapYInt(native_pointer); } JNI_METHOD(brush, TextureLayerNative, jint, getBlendModeInt) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return static_cast(CastToTextureLayer(native_pointer).blend_mode); + return TextureLayerNative_getBlendModeInt(native_pointer); } -// ************ Native Implementation of BrushPaint ColorFunction ************ - -// Constructs a native BrushPaint ColorFunction and returns a pointer to it as a -// long. +// Native Implementation of BrushPaint.ColorFunction JNI_METHOD(brush, ColorFunctionNative, jlong, createOpacityMultiplier) (JNIEnv* env, jobject thiz, jfloat multiplier) { - return ValidateAndHoistColorFunctionOrThrow( - ColorFunction::OpacityMultiplier{.multiplier = multiplier}, env); + return ColorFunctionNative_createOpacityMultiplier( + env, multiplier, &ThrowExceptionFromStatusCallback); } JNI_METHOD(brush, ColorFunctionNative, jlong, createReplaceColor) (JNIEnv* env, jobject thiz, jfloat color_red, jfloat color_green, jfloat color_blue, jfloat color_alpha, jint color_space_id) { - return ValidateAndHoistColorFunctionOrThrow( - ColorFunction::ReplaceColor{ - .color = Color::FromFloat(color_red, color_green, color_blue, - color_alpha, Color::Format::kGammaEncoded, - JIntToColorSpace(color_space_id))}, - env); + return ColorFunctionNative_createReplaceColor( + env, color_red, color_green, color_blue, color_alpha, color_space_id, + &ThrowExceptionFromStatusCallback); } JNI_METHOD(brush, ColorFunctionNative, void, free) (JNIEnv* env, jobject thiz, jlong native_pointer) { - DeleteNativeColorFunction(native_pointer); -} - -JNI_METHOD(brush, ColorFunctionNative, jlong, getParametersType) -(JNIEnv* env, jobject thiz, jlong native_pointer) { - constexpr auto visitor = absl::Overload{ - [](const ColorFunction::OpacityMultiplier&) { - return kOpacityMultiplier; - }, - [](const ColorFunction::ReplaceColor&) { return kReplaceColor; }, - }; - return static_cast( - std::visit(visitor, CastToColorFunction(native_pointer).parameters)); + ColorFunctionNative_free(native_pointer); } JNI_METHOD(brush, ColorFunctionNative, jfloat, getOpacityMultiplier) (JNIEnv* env, jobject thiz, jlong native_pointer) { - return std::get( - CastToColorFunction(native_pointer).parameters) - .multiplier; + return ColorFunctionNative_getOpacityMultiplier(native_pointer); } JNI_METHOD(brush, ColorFunctionNative, jlong, computeReplaceColorLong) (JNIEnv* env, jobject object, jlong native_pointer) { - return ComputeColorLong(env, - std::get( - CastToColorFunction(native_pointer).parameters) - .color); + return ColorFunctionNative_computeReplaceColorLong( + env, native_pointer, &ComputeColorLongFromComponentsCallback); } } // extern "C" diff --git a/ink/brush/internal/jni/brush_paint_native.cc b/ink/brush/internal/jni/brush_paint_native.cc new file mode 100644 index 00000000..fcfadc11 --- /dev/null +++ b/ink/brush/internal/jni/brush_paint_native.cc @@ -0,0 +1,338 @@ +// 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/brush_paint_native.h" + +#include +#include +#include +#include + +#include "absl/container/flat_hash_set.h" +#include "absl/functional/overload.h" +#include "absl/status/status.h" +#include "absl/time/time.h" +#include "absl/types/span.h" +#include "ink/brush/brush_paint.h" +#include "ink/brush/color_function.h" +#include "ink/brush/internal/jni/brush_native_helper.h" +#include "ink/color/color.h" +#include "ink/color/internal/jni/color_native_helper.h" +#include "ink/geometry/angle.h" +#include "ink/geometry/internal/jni/mesh_format_native_helper.h" +#include "ink/geometry/mesh_format.h" +#include "ink/geometry/vec.h" + +namespace { + +using ::ink::Angle; +using ::ink::BrushPaint; +using ::ink::Color; +using ::ink::ColorFunction; +using ::ink::MeshFormat; +using ::ink::Vec; +using ::ink::brush_internal::AddAttributeIdsRequiredByPaint; +using ::ink::brush_internal::ValidateBrushPaint; +using ::ink::brush_internal::ValidateBrushPaintTextureLayer; +using ::ink::native::CastToBrushPaint; +using ::ink::native::CastToColorFunction; +using ::ink::native::CastToMeshFormat; +using ::ink::native::CastToTextureLayer; +using ::ink::native::ComputeColorLong; +using ::ink::native::DeleteNativeBrushPaint; +using ::ink::native::DeleteNativeColorFunction; +using ::ink::native::DeleteNativeTextureLayer; +using ::ink::native::IntToColorSpace; +using ::ink::native::NewNativeBrushPaint; +using ::ink::native::NewNativeColorFunction; +using ::ink::native::NewNativeTextureLayer; + +BrushPaint::TextureSizeUnit IntToSizeUnit(int val) { + return static_cast(val); +} + +BrushPaint::TextureOrigin IntToOrigin(int val) { + return static_cast(val); +} + +BrushPaint::TextureMapping IntToMapping(int val) { + return static_cast(val); +} + +BrushPaint::TextureWrap IntToWrap(int val) { + return static_cast(val); +} + +BrushPaint::BlendMode IntToBlendMode(int val) { + return static_cast(val); +} + +BrushPaint::SelfOverlap IntToSelfOverlap(int val) { + return static_cast(val); +} + +int64_t ValidateAndHoistColorFunctionOrThrow( + ColorFunction::Parameters parameters, void* jni_env_pass_through, + void (*throw_from_status_callback)(void*, int, const char*)) { + ColorFunction color_function{.parameters = std::move(parameters)}; + if (absl::Status status = + ink::brush_internal::ValidateColorFunction(color_function); + !status.ok()) { + throw_from_status_callback(jni_env_pass_through, + static_cast(status.code()), + status.message().data()); + return 0; + } + return NewNativeColorFunction(std::move(color_function)); +} + +static constexpr int kOpacityMultiplier = 0; +static constexpr int kReplaceColor = 1; + +} // namespace + +extern "C" { + +int64_t BrushPaintNative_create( + void* jni_env_pass_through, const int64_t* texture_layer_native_pointers, + int num_texture_layers, const int64_t* color_function_native_pointers, + int num_color_functions, int self_overlap_int, + void (*throw_from_status_callback)(void*, int, const char*)) { + std::vector texture_layers; + texture_layers.reserve(num_texture_layers); + for (int i = 0; i < num_texture_layers; ++i) { + texture_layers.push_back( + CastToTextureLayer(texture_layer_native_pointers[i])); + } + + std::vector color_functions; + color_functions.reserve(num_color_functions); + for (int i = 0; i < num_color_functions; ++i) { + color_functions.push_back( + CastToColorFunction(color_function_native_pointers[i])); + } + + BrushPaint brush_paint{.texture_layers = std::move(texture_layers), + .color_functions = std::move(color_functions), + .self_overlap = IntToSelfOverlap(self_overlap_int)}; + if (absl::Status status = ValidateBrushPaint(brush_paint); !status.ok()) { + throw_from_status_callback(jni_env_pass_through, + static_cast(status.code()), + status.message().data()); + return 0; + } + return NewNativeBrushPaint(std::move(brush_paint)); +} + +void BrushPaintNative_free(int64_t native_ptr) { + DeleteNativeBrushPaint(native_ptr); +} + +int BrushPaintNative_getTextureLayerCount(int64_t native_ptr) { + return CastToBrushPaint(native_ptr).texture_layers.size(); +} + +int64_t BrushPaintNative_newCopyOfTextureLayer(int64_t native_ptr, int index) { + return NewNativeTextureLayer( + CastToBrushPaint(native_ptr).texture_layers[index]); +} + +int BrushPaintNative_getColorFunctionCount(int64_t native_ptr) { + return CastToBrushPaint(native_ptr).color_functions.size(); +} + +int BrushPaintNative_getColorFunctionParametersTypeInt(int64_t native_ptr, + int index) { + const ColorFunction::Parameters& parameters = + CastToBrushPaint(native_ptr).color_functions[index].parameters; + constexpr auto visitor = absl::Overload{ + [](const ColorFunction::OpacityMultiplier&) { + return kOpacityMultiplier; + }, + [](const ColorFunction::ReplaceColor&) { return kReplaceColor; }, + }; + return std::visit(visitor, parameters); +} + +int64_t BrushPaintNative_newCopyOfColorFunction(int64_t native_ptr, int index) { + return NewNativeColorFunction( + CastToBrushPaint(native_ptr).color_functions[index]); +} + +int BrushPaintNative_getSelfOverlapInt(int64_t native_ptr) { + return static_cast(CastToBrushPaint(native_ptr).self_overlap); +} + +bool BrushPaintNative_isCompatibleWithMeshFormat( + int64_t native_ptr, int64_t mesh_format_native_ptr) { + absl::flat_hash_set required_attribute_ids; + AddAttributeIdsRequiredByPaint(CastToBrushPaint(native_ptr), + required_attribute_ids); + + absl::Span mesh_attributes = + CastToMeshFormat(mesh_format_native_ptr).Attributes(); + for (const MeshFormat::Attribute& attr : mesh_attributes) { + required_attribute_ids.erase(attr.id); + } + return required_attribute_ids.empty(); +} + +// ************ Native C-API of BrushPaint TextureLayer ************ + +int64_t TextureLayerNative_create( + void* jni_env_pass_through, const char* client_texture_id, float size_x, + float size_y, float offset_x, float offset_y, float rotation_degrees, + int animation_frames, int animation_rows, int animation_columns, + int64_t animation_duration_millis, int size_unit, int origin, int mapping, + int wrap_x, int wrap_y, int blend_mode, + void (*throw_from_status_callback)(void*, int, const char*)) { + BrushPaint::TextureLayer texture_layer{ + .client_texture_id = client_texture_id, + .mapping = IntToMapping(mapping), + .origin = IntToOrigin(origin), + .size_unit = IntToSizeUnit(size_unit), + .wrap_x = IntToWrap(wrap_x), + .wrap_y = IntToWrap(wrap_y), + .size = Vec{size_x, size_y}, + .offset = Vec{offset_x, offset_y}, + .rotation = Angle::Degrees(rotation_degrees), + .animation_frames = animation_frames, + .animation_rows = animation_rows, + .animation_columns = animation_columns, + .animation_duration = absl::Milliseconds(animation_duration_millis), + .blend_mode = IntToBlendMode(blend_mode), + }; + if (absl::Status status = ValidateBrushPaintTextureLayer(texture_layer); + !status.ok()) { + throw_from_status_callback(jni_env_pass_through, + static_cast(status.code()), + status.message().data()); + return 0; + } + return NewNativeTextureLayer(std::move(texture_layer)); +} + +void TextureLayerNative_free(int64_t native_ptr) { + DeleteNativeTextureLayer(native_ptr); +} + +const char* TextureLayerNative_getClientTextureId(int64_t native_ptr) { + return CastToTextureLayer(native_ptr).client_texture_id.c_str(); +} + +float TextureLayerNative_getSizeX(int64_t native_ptr) { + return CastToTextureLayer(native_ptr).size.x; +} + +float TextureLayerNative_getSizeY(int64_t native_ptr) { + return CastToTextureLayer(native_ptr).size.y; +} + +float TextureLayerNative_getOffsetX(int64_t native_ptr) { + return CastToTextureLayer(native_ptr).offset.x; +} + +float TextureLayerNative_getOffsetY(int64_t native_ptr) { + return CastToTextureLayer(native_ptr).offset.y; +} + +float TextureLayerNative_getRotationDegrees(int64_t native_ptr) { + return CastToTextureLayer(native_ptr).rotation.ValueInDegrees(); +} + +int TextureLayerNative_getAnimationFrames(int64_t native_ptr) { + return CastToTextureLayer(native_ptr).animation_frames; +} + +int TextureLayerNative_getAnimationRows(int64_t native_ptr) { + return CastToTextureLayer(native_ptr).animation_rows; +} + +int TextureLayerNative_getAnimationColumns(int64_t native_ptr) { + return CastToTextureLayer(native_ptr).animation_columns; +} + +int64_t TextureLayerNative_getAnimationDurationMillis(int64_t native_ptr) { + return absl::ToInt64Milliseconds( + CastToTextureLayer(native_ptr).animation_duration); +} + +int TextureLayerNative_getSizeUnitInt(int64_t native_ptr) { + return static_cast(CastToTextureLayer(native_ptr).size_unit); +} + +int TextureLayerNative_getOriginInt(int64_t native_ptr) { + return static_cast(CastToTextureLayer(native_ptr).origin); +} + +int TextureLayerNative_getMappingInt(int64_t native_ptr) { + return static_cast(CastToTextureLayer(native_ptr).mapping); +} + +int TextureLayerNative_getWrapXInt(int64_t native_ptr) { + return static_cast(CastToTextureLayer(native_ptr).wrap_x); +} + +int TextureLayerNative_getWrapYInt(int64_t native_ptr) { + return static_cast(CastToTextureLayer(native_ptr).wrap_y); +} + +int TextureLayerNative_getBlendModeInt(int64_t native_ptr) { + return static_cast(CastToTextureLayer(native_ptr).blend_mode); +} + +// ************ Native C-API of BrushPaint ColorFunction ************ + +int64_t ColorFunctionNative_createOpacityMultiplier( + void* jni_env_pass_through, float multiplier, + void (*throw_from_status_callback)(void*, int, const char*)) { + return ValidateAndHoistColorFunctionOrThrow( + ColorFunction::OpacityMultiplier{.multiplier = multiplier}, + jni_env_pass_through, throw_from_status_callback); +} + +int64_t ColorFunctionNative_createReplaceColor( + void* jni_env_pass_through, float color_red, float color_green, + float color_blue, float color_alpha, int color_space_id, + void (*throw_from_status_callback)(void*, int, const char*)) { + return ValidateAndHoistColorFunctionOrThrow( + ColorFunction::ReplaceColor{ + .color = Color::FromFloat(color_red, color_green, color_blue, + color_alpha, Color::Format::kGammaEncoded, + IntToColorSpace(color_space_id))}, + jni_env_pass_through, throw_from_status_callback); +} + +void ColorFunctionNative_free(int64_t native_ptr) { + DeleteNativeColorFunction(native_ptr); +} + +float ColorFunctionNative_getOpacityMultiplier(int64_t native_ptr) { + return std::get( + CastToColorFunction(native_ptr).parameters) + .multiplier; +} + +int64_t ColorFunctionNative_computeReplaceColorLong( + void* jni_env_pass_through, int64_t native_ptr, + int64_t (*compute_compose_color_long_from_components_callback)( + void*, int, float, float, float, float)) { + return ComputeColorLong(jni_env_pass_through, + std::get( + CastToColorFunction(native_ptr).parameters) + .color, + compute_compose_color_long_from_components_callback); +} + +} // extern "C" diff --git a/ink/brush/internal/jni/brush_paint_native.h b/ink/brush/internal/jni/brush_paint_native.h new file mode 100644 index 00000000..d7d2d4c0 --- /dev/null +++ b/ink/brush/internal/jni/brush_paint_native.h @@ -0,0 +1,117 @@ +// 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_BRUSH_PAINT_NATIVE_H_ +#define INK_BRUSH_INTERNAL_JNI_BRUSH_PAINT_NATIVE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int64_t BrushPaintNative_create( + void* jni_env_pass_through, const int64_t* texture_layer_native_pointers, + int num_texture_layers, const int64_t* color_function_native_pointers, + int num_color_functions, int self_overlap_int, + void (*throw_from_status_callback)(void*, int, const char*)); + +void BrushPaintNative_free(int64_t native_ptr); + +int BrushPaintNative_getTextureLayerCount(int64_t native_ptr); + +int64_t BrushPaintNative_newCopyOfTextureLayer(int64_t native_ptr, int index); + +int BrushPaintNative_getColorFunctionCount(int64_t native_ptr); + +int BrushPaintNative_getColorFunctionParametersTypeInt(int64_t native_ptr, + int index); + +int64_t BrushPaintNative_newCopyOfColorFunction(int64_t native_ptr, int index); + +int BrushPaintNative_getSelfOverlapInt(int64_t native_ptr); + +bool BrushPaintNative_isCompatibleWithMeshFormat( + int64_t native_ptr, int64_t mesh_format_native_ptr); + +// Native interface for BrushPaint.TextureLayer: + +int64_t TextureLayerNative_create( + void* jni_env_pass_through, const char* client_texture_id, float size_x, + float size_y, float offset_x, float offset_y, float rotation_degrees, + int animation_frames, int animation_rows, int animation_columns, + int64_t animation_duration_millis, int size_unit, int origin, int mapping, + int wrap_x, int wrap_y, int blend_mode, + void (*throw_from_status_callback)(void*, int, const char*)); + +void TextureLayerNative_free(int64_t native_ptr); + +const char* TextureLayerNative_getClientTextureId(int64_t native_ptr); + +float TextureLayerNative_getSizeX(int64_t native_ptr); + +float TextureLayerNative_getSizeY(int64_t native_ptr); + +float TextureLayerNative_getOffsetX(int64_t native_ptr); + +float TextureLayerNative_getOffsetY(int64_t native_ptr); + +float TextureLayerNative_getRotationDegrees(int64_t native_ptr); + +int TextureLayerNative_getAnimationFrames(int64_t native_ptr); + +int TextureLayerNative_getAnimationRows(int64_t native_ptr); + +int TextureLayerNative_getAnimationColumns(int64_t native_ptr); + +int64_t TextureLayerNative_getAnimationDurationMillis(int64_t native_ptr); + +int TextureLayerNative_getSizeUnitInt(int64_t native_ptr); + +int TextureLayerNative_getOriginInt(int64_t native_ptr); + +int TextureLayerNative_getMappingInt(int64_t native_ptr); + +int TextureLayerNative_getWrapXInt(int64_t native_ptr); + +int TextureLayerNative_getWrapYInt(int64_t native_ptr); + +int TextureLayerNative_getBlendModeInt(int64_t native_ptr); + +// Native interface for BrushPaint.ColorFunction: + +int64_t ColorFunctionNative_createOpacityMultiplier( + void* jni_env_pass_through, float multiplier, + void (*throw_from_status_callback)(void*, int, const char*)); + +int64_t ColorFunctionNative_createReplaceColor( + void* jni_env_pass_through, float color_red, float color_green, + float color_blue, float color_alpha, int color_space_id, + void (*throw_from_status_callback)(void*, int, const char*)); + +void ColorFunctionNative_free(int64_t native_ptr); + +float ColorFunctionNative_getOpacityMultiplier(int64_t native_ptr); + +int64_t ColorFunctionNative_computeReplaceColorLong( + void* jni_env_pass_through, int64_t native_ptr, + int64_t (*compute_compose_color_long_from_components_callback)( + void*, int, float, float, float, float)); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // INK_BRUSH_INTERNAL_JNI_BRUSH_PAINT_NATIVE_H_ diff --git a/ink/color/internal/jni/BUILD.bazel b/ink/color/internal/jni/BUILD.bazel index 19d876d4..1f32a385 100644 --- a/ink/color/internal/jni/BUILD.bazel +++ b/ink/color/internal/jni/BUILD.bazel @@ -23,6 +23,7 @@ cc_library( srcs = ["color_jni_helper.cc"], hdrs = ["color_jni_helper.h"], deps = [ + ":color_native_helper", "//ink/color", "//ink/color:color_space", "//ink/jni/internal:jni_jvm_interface", @@ -34,3 +35,14 @@ cc_library( ], }), ) + +cc_library( + name = "color_native_helper", + srcs = ["color_native_helper.cc"], + hdrs = ["color_native_helper.h"], + deps = [ + "//ink/color", + "//ink/color:color_space", + "@com_google_absl//absl/log:absl_check", + ], +) diff --git a/ink/color/internal/jni/color_jni_helper.cc b/ink/color/internal/jni/color_jni_helper.cc index 071b83c6..bd3ea876 100644 --- a/ink/color/internal/jni/color_jni_helper.cc +++ b/ink/color/internal/jni/color_jni_helper.cc @@ -16,61 +16,27 @@ #include -#include "absl/log/absl_check.h" +#include + #include "ink/color/color.h" -#include "ink/color/color_space.h" +#include "ink/color/internal/jni/color_native_helper.h" #include "ink/jni/internal/jni_jvm_interface.h" namespace ink::jni { -ColorSpace JIntToColorSpace(jint color_space_id) { - switch (color_space_id) { - case kJniColorSpaceIdSrgb: - return ColorSpace::kSrgb; - case kJniColorSpaceIdDisplayP3: - return ColorSpace::kDisplayP3; - default: - ABSL_CHECK(false) << "Unknown color space id: " << color_space_id; - } -} - -jint ColorSpaceToJInt(ColorSpace color_space) { - switch (color_space) { - case ColorSpace::kSrgb: - return kJniColorSpaceIdSrgb; - case ColorSpace::kDisplayP3: - return kJniColorSpaceIdDisplayP3; - } - ABSL_CHECK(false) << "Unknown color space: " << color_space; -} - -bool ColorSpaceIsSupportedInJetpack(ColorSpace color_space) { - // Currently this is defensive coding, all of the color spaces supported in - // Ink C++ code are also supported in the Color class used in Jetpack. - switch (color_space) { - case ColorSpace::kSrgb: - case ColorSpace::kDisplayP3: - return true; - } - return false; +jlong ComputeColorLong(JNIEnv* env, const Color& color) { + return ::ink::native::ComputeColorLong( + static_cast(env), color, ComputeColorLongFromComponentsCallback); } -jlong ComputeColorLong(JNIEnv* env, const Color& color) { - const ColorSpace& original_color_space = color.GetColorSpace(); - // This is currently defensive coding, at this time there aren't color - // spaces supported here that aren't supported in Ink Jetpack. - bool color_space_is_supported = - ColorSpaceIsSupportedInJetpack(original_color_space); - const Color::RgbaFloat rgba = - (color_space_is_supported ? color - : color.InColorSpace(ColorSpace::kDisplayP3)) - .AsFloat(Color::Format::kGammaEncoded); +int64_t ComputeColorLongFromComponentsCallback( + void* jni_env, int color_space_id, float red_gamma_corrected, + float green_gamma_corrected, float blue_gamma_corrected, float alpha) { + JNIEnv* env = static_cast(jni_env); return env->CallStaticLongMethod( - ClassColorNative(env), - MethodColorNativeComposeColorLongFromComponents(env), - ColorSpaceToJInt(color_space_is_supported ? original_color_space - : ColorSpace::kDisplayP3), - rgba.r, rgba.g, rgba.b, rgba.a); + ClassColorCallbacks(env), + MethodColorCallbacksComposeColorLongFromComponents(env), color_space_id, + red_gamma_corrected, green_gamma_corrected, blue_gamma_corrected, alpha); } } // namespace ink::jni diff --git a/ink/color/internal/jni/color_jni_helper.h b/ink/color/internal/jni/color_jni_helper.h index 28afb689..e1565836 100644 --- a/ink/color/internal/jni/color_jni_helper.h +++ b/ink/color/internal/jni/color_jni_helper.h @@ -17,24 +17,20 @@ #include +#include + #include "ink/color/color.h" #include "ink/color/color_space.h" namespace ink::jni { -// Color space ID constants. -// -// These should match the platform constants in ColorExtensions.kt. -constexpr jint kJniColorSpaceIdSrgb = 0; -constexpr jint kJniColorSpaceIdDisplayP3 = 1; - -ColorSpace JIntToColorSpace(jint color_space_id); -jint ColorSpaceToJInt(ColorSpace color_space); -bool ColorSpaceIsSupportedInJetpack(ColorSpace color_space); - // Converts an Ink `Color` into a Kotlin `ColorLong`. jlong ComputeColorLong(JNIEnv* env, const Color& color); +int64_t ComputeColorLongFromComponentsCallback( + void* jni_env, int color_space_id, float red_gamma_corrected, + float green_gamma_corrected, float blue_gamma_corrected, float alpha); + } // namespace ink::jni #endif // INK_COLOR_INTERNAL_JNI_COLOR_JNI_HELPER_H_ diff --git a/ink/color/internal/jni/color_native_helper.cc b/ink/color/internal/jni/color_native_helper.cc new file mode 100644 index 00000000..d0b7b6e1 --- /dev/null +++ b/ink/color/internal/jni/color_native_helper.cc @@ -0,0 +1,86 @@ +// 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/color/internal/jni/color_native_helper.h" + +#include + +#include "absl/log/absl_check.h" +#include "ink/color/color.h" +#include "ink/color/color_space.h" + +namespace ink::native { + +namespace { + +// Color space ID constants. +// +// These should match the platform constants in ColorExtensions.kt. +constexpr int kColorSpaceIdSrgb = 0; +constexpr int kColorSpaceIdDisplayP3 = 1; + +int ColorSpaceToInt(ColorSpace color_space) { + switch (color_space) { + case ColorSpace::kSrgb: + return kColorSpaceIdSrgb; + case ColorSpace::kDisplayP3: + return kColorSpaceIdDisplayP3; + } + ABSL_CHECK(false) << "Unknown color space: " << color_space; +} + +bool ColorSpaceIsSupportedInJetpack(ColorSpace color_space) { + // Currently this is defensive coding, all of the color spaces supported in + // Ink C++ code are also supported in the Color class used in Jetpack. + switch (color_space) { + case ColorSpace::kSrgb: + case ColorSpace::kDisplayP3: + return true; + } + return false; +} + +} // namespace + +ColorSpace IntToColorSpace(int color_space_id) { + switch (color_space_id) { + case kColorSpaceIdSrgb: + return ColorSpace::kSrgb; + case kColorSpaceIdDisplayP3: + return ColorSpace::kDisplayP3; + default: + ABSL_CHECK(false) << "Unknown color space id: " << color_space_id; + } +} + +int64_t ComputeColorLong( + void* jni_env_pass_through, const Color& color, + int64_t (*compute_compose_color_long_from_components_callback)( + void*, int, float, float, float, float)) { + const ColorSpace& original_color_space = color.GetColorSpace(); + // This is currently defensive coding, at this time there aren't color + // spaces supported here that aren't supported in Ink Jetpack. + bool color_space_is_supported = + ColorSpaceIsSupportedInJetpack(original_color_space); + const Color& supported_color = + color_space_is_supported ? color + : color.InColorSpace(ColorSpace::kDisplayP3); + const Color::RgbaFloat rgba = + supported_color.AsFloat(Color::Format::kGammaEncoded); + return compute_compose_color_long_from_components_callback( + jni_env_pass_through, ColorSpaceToInt(supported_color.GetColorSpace()), + rgba.r, rgba.g, rgba.b, rgba.a); +} + +} // namespace ink::native diff --git a/ink/color/internal/jni/color_native_helper.h b/ink/color/internal/jni/color_native_helper.h new file mode 100644 index 00000000..a27e6bf6 --- /dev/null +++ b/ink/color/internal/jni/color_native_helper.h @@ -0,0 +1,34 @@ +// 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_COLOR_INTERNAL_JNI_COLOR_NATIVE_HELPER_H_ +#define INK_COLOR_INTERNAL_JNI_COLOR_NATIVE_HELPER_H_ + +#include + +#include "ink/color/color.h" +#include "ink/color/color_space.h" + +namespace ink::native { + +ColorSpace IntToColorSpace(int color_space_id); + +int64_t ComputeColorLong( + void* jni_env_pass_through, const Color& color, + int64_t (*compute_compose_color_long_from_components_callback)( + void*, int, float, float, float, float)); + +} // namespace ink::native + +#endif // INK_COLOR_INTERNAL_JNI_COLOR_NATIVE_HELPER_H_ diff --git a/ink/jni/internal/jni_jvm_interface.cc b/ink/jni/internal/jni_jvm_interface.cc index e8d00b6f..0eff7b5b 100644 --- a/ink/jni/internal/jni_jvm_interface.cc +++ b/ink/jni/internal/jni_jvm_interface.cc @@ -52,8 +52,8 @@ static jclass class_mutable_parallelogram = nullptr; static jmethodID method_mutable_parallelogram_set_center_dim_rot_shear = nullptr; -static jclass class_color_native = nullptr; -static jmethodID method_color_native_compose_color_long_from_components = +static jclass class_color_callbacks = nullptr; +static jmethodID method_color_callbacks_compose_color_long_from_components = nullptr; static jclass class_input_tool_type = nullptr; @@ -125,8 +125,8 @@ void UnloadJvmInterface(JNIEnv* env) { DeleteCachedClass(env, class_mutable_parallelogram); method_mutable_parallelogram_set_center_dim_rot_shear = nullptr; - DeleteCachedClass(env, class_color_native); - method_color_native_compose_color_long_from_components = nullptr; + DeleteCachedClass(env, class_color_callbacks); + method_color_callbacks_compose_color_long_from_components = nullptr; DeleteCachedClass(env, class_input_tool_type); method_input_tool_type_from = nullptr; @@ -302,21 +302,21 @@ jmethodID MethodMutableParallelogramSetCenterDimensionsRotationInDegreesAndSkew( return method_mutable_parallelogram_set_center_dim_rot_shear; } -jclass ClassColorNative(JNIEnv* env) { - if (class_color_native == nullptr) { - class_color_native = - FindAndCacheClass(env, "androidx/ink/brush/ColorNative"); +jclass ClassColorCallbacks(JNIEnv* env) { + if (class_color_callbacks == nullptr) { + class_color_callbacks = + FindAndCacheClass(env, "androidx/ink/brush/ColorCallbacks"); } - return class_color_native; + return class_color_callbacks; } -jmethodID MethodColorNativeComposeColorLongFromComponents(JNIEnv* env) { - if (method_color_native_compose_color_long_from_components == nullptr) { - method_color_native_compose_color_long_from_components = - GetStaticMethodId(env, ClassColorNative(env), +jmethodID MethodColorCallbacksComposeColorLongFromComponents(JNIEnv* env) { + if (method_color_callbacks_compose_color_long_from_components == nullptr) { + method_color_callbacks_compose_color_long_from_components = + GetStaticMethodId(env, ClassColorCallbacks(env), "composeColorLongFromComponents", "(IFFFF)J"); } - return method_color_native_compose_color_long_from_components; + return method_color_callbacks_compose_color_long_from_components; } jclass ClassInputToolType(JNIEnv* env) { diff --git a/ink/jni/internal/jni_jvm_interface.h b/ink/jni/internal/jni_jvm_interface.h index dcac2699..a46bd769 100644 --- a/ink/jni/internal/jni_jvm_interface.h +++ b/ink/jni/internal/jni_jvm_interface.h @@ -65,8 +65,8 @@ jclass ClassMutableParallelogram(JNIEnv* env); jmethodID MethodMutableParallelogramSetCenterDimensionsRotationInDegreesAndSkew( JNIEnv* env); -jclass ClassColorNative(JNIEnv* env); -jmethodID MethodColorNativeComposeColorLongFromComponents(JNIEnv* env); +jclass ClassColorCallbacks(JNIEnv* env); +jmethodID MethodColorCallbacksComposeColorLongFromComponents(JNIEnv* env); jclass ClassInputToolType(JNIEnv* env); jmethodID MethodInputToolTypeFromInt(JNIEnv* env);