From 6c95fc6851b6b918886034f57fd2a3636d41f3a7 Mon Sep 17 00:00:00 2001 From: William Candillon Date: Thu, 23 Jul 2026 17:22:17 +0200 Subject: [PATCH 1/3] =?UTF-8?q?chore(=F0=9F=90=99):=20Typed=20JSI=20bindin?= =?UTF-8?q?gs=20=20(#3965)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: typed JSI bindings infra + JsiVideo/JsiSkFont exemplars Phase-2 wave 1 of the NativeObject migration: convert raw JSI_HOST_FUNCTION method bodies to typed C++ signatures converted through rnwgpu::JSIConverter. - Add cpp/api/JsiSkConverters.h: JSIConverter specializations for wrapped Skia pointer types (sk_sp, std::shared_ptr) keyed on a JsiSkWrapperFor trait, delegating to each wrapper's fromValue so plain-object fallbacks keep working. Value converters for SkRect, SkPoint, SkMatrix (fromJSI only) and SkISize. All converters are partial specializations so bodies instantiate lazily and headers stay cycle-free. - Add installChainableMethod to JsiSkNativeObject for methods returning this. - Qualify fromValue lookup in NativeObject typed installers as NativeObject::fromValue: RNSkia wrappers shadow fromValue with a public static returning the wrapped inner object. - Convert JsiVideo (all methods typed) and JsiSkFont (typed except getMetrics, which keeps a conditionally-present bounds property) as the reference pattern. * feat: typed bindings for simple factories Wave 2: convert JsiSkColorFilterFactory, JsiSkMaskFilterFactory, JsiSkPathEffectFactory, JsiSkTextBlobFactory, JsiSkTypefaceFactory, JsiSkFontMgrFactory, JsiSkRuntimeEffectFactory and JsiSkDataFactory::fromBase64 to typed signatures. Converter additions: JsiColor strong typedef (SkColor is a uint32_t typedef, so colors need their own argument type), SkRSXform value converter, and JsiOptional (omitted | undefined | null all treated as absent, matching the hasOptionalArgument pattern of the raw bindings). JsiSkFont::getGlyphIDs and MakeDash switch to JsiOptional accordingly. Still raw (cannot be typed): JsiSkDataFactory::fromURI (returns a promise built from the runtime), fromBytes (typed-array introspection), JsiSkPictureFactory::MakePicture (heterogeneous argument dispatch). * feat: typed bindings for image filter and shader factories Wave 3: convert JsiSkImageFilterFactory and JsiSkShaderFactory to typed signatures using JsiOptional for the pervasive optional input-filter/crop-rect/local-matrix arguments, plus a SkPoint3 converter for lighting filters. This also fixes a latent dangling pointer in the gradient factories: getLocalMatrix() returned the raw pointer of a temporary std::shared_ptr, which dangled when the matrix came from the plain-array fallback; the typed signatures keep the shared_ptr alive for the whole call. * feat: typed bindings for path, image and surface factories Wave 4: convert JsiSkPathFactory (all methods except Stroke, whose options object is read leniently property by property), JsiSkPathBuilderFactory, JsiSkAnimatedImageFactory, JsiSkTypefaceFontProviderFactory, and the typeable parts of JsiSkImageFactory (MakeNull, MakeImageFromEncoded, MakeImageFromNativeBuffer, MakeImage) and JsiSkSurfaceFactory (Make). Still raw: image factory promise/texture methods (MakeImageFromViewTag, MakeImageFromNativeTextureUnstable, MakeImageFromTexture, MakeTextureFromImage), SurfaceFactory::MakeOffscreen (options object), and JsiSkSVGFactory (lenient fontMgr/asset-map parsing). * feat: typed bindings for small instance classes Wave 5: convert JsiSkRect, JsiSkPoint and JsiSkRRect (typed getters via installGetter, which keeps them enumerable so the generic toJSON keeps working), JsiSkPicture::makeShader, JsiSkAnimatedImage, JsiSkRuntimeShaderBuilder, JsiSkSVG, JsiSkFontMgr, JsiSkTypefaceFontProvider, JsiSkContourMeasure, JsiSkContourMeasureIter (next returns std::optional so the JS result stays undefined, not null), JsiSkVertices and JsiSkTypeface. Still raw: JsiSkPicture::serialize (constructs a Uint8Array). * feat: typed bindings for JsiSkMatrix and JsiSkPaint Wave 6: JsiSkMatrix converts to typed chainable methods via the new installChainableMethod (the JS functions still return this); concat keeps accepting both Matrix wrappers and plain 9/16-element arrays through the shared_ptr converter. JsiSkPaint converts fully: colors go through the JsiColor strong typedef, and the nullable setShader/setColorFilter/setImageFilter/setMaskFilter/setPathEffect arguments use JsiOptional (null | undefined clear the effect, as before). --- packages/skia/cpp/api/JsiSkAnimatedImage.h | 29 +- .../skia/cpp/api/JsiSkAnimatedImageFactory.h | 16 +- .../skia/cpp/api/JsiSkColorFilterFactory.h | 108 +- packages/skia/cpp/api/JsiSkContourMeasure.h | 48 +- .../skia/cpp/api/JsiSkContourMeasureIter.h | 12 +- packages/skia/cpp/api/JsiSkConverters.h | 333 ++++++ packages/skia/cpp/api/JsiSkDataFactory.h | 24 +- packages/skia/cpp/api/JsiSkFont.h | 279 ++--- packages/skia/cpp/api/JsiSkFontMgr.h | 40 +- packages/skia/cpp/api/JsiSkFontMgrFactory.h | 9 +- packages/skia/cpp/api/JsiSkImageFactory.h | 51 +- .../skia/cpp/api/JsiSkImageFilterFactory.h | 962 +++++++----------- .../skia/cpp/api/JsiSkMaskFilterFactory.h | 19 +- packages/skia/cpp/api/JsiSkMatrix.h | 109 +- packages/skia/cpp/api/JsiSkNativeObjects.h | 33 + packages/skia/cpp/api/JsiSkPaint.h | 204 ++-- .../skia/cpp/api/JsiSkPathBuilderFactory.h | 22 +- .../skia/cpp/api/JsiSkPathEffectFactory.h | 124 +-- packages/skia/cpp/api/JsiSkPathFactory.h | 324 +++--- packages/skia/cpp/api/JsiSkPicture.h | 30 +- packages/skia/cpp/api/JsiSkPoint.h | 8 +- packages/skia/cpp/api/JsiSkRRect.h | 15 +- packages/skia/cpp/api/JsiSkRect.h | 54 +- .../skia/cpp/api/JsiSkRuntimeEffectFactory.h | 16 +- .../skia/cpp/api/JsiSkRuntimeShaderBuilder.h | 19 +- packages/skia/cpp/api/JsiSkSVG.h | 8 +- packages/skia/cpp/api/JsiSkShaderFactory.h | 378 +++---- packages/skia/cpp/api/JsiSkSurfaceFactory.h | 13 +- packages/skia/cpp/api/JsiSkTextBlobFactory.h | 85 +- packages/skia/cpp/api/JsiSkTypeface.h | 20 +- packages/skia/cpp/api/JsiSkTypefaceFactory.h | 16 +- .../skia/cpp/api/JsiSkTypefaceFontProvider.h | 45 +- .../api/JsiSkTypefaceFontProviderFactory.h | 11 +- packages/skia/cpp/api/JsiSkVertices.h | 14 +- packages/skia/cpp/api/JsiVideo.h | 102 +- packages/skia/cpp/jsi/NativeObject.h | 17 +- 36 files changed, 1617 insertions(+), 1980 deletions(-) create mode 100644 packages/skia/cpp/api/JsiSkConverters.h diff --git a/packages/skia/cpp/api/JsiSkAnimatedImage.h b/packages/skia/cpp/api/JsiSkAnimatedImage.h index eb181ccd3b..38667f1af5 100644 --- a/packages/skia/cpp/api/JsiSkAnimatedImage.h +++ b/packages/skia/cpp/api/JsiSkAnimatedImage.h @@ -35,34 +35,31 @@ class JsiSkAnimatedImage static constexpr const char *CLASS_NAME = "AnimatedImage"; // TODO-API: Properties? - JSI_HOST_FUNCTION(getCurrentFrame) { + std::shared_ptr getCurrentFrame() { auto image = getObject()->getCurrentFrame(); - return makeJsiObject( - runtime, std::make_shared(getContext(), std::move(image))); + return std::make_shared(getContext(), std::move(image)); } - JSI_HOST_FUNCTION(getFrameCount) { - return static_cast(getObject()->getFrameCount()); - } + int getFrameCount() { return static_cast(getObject()->getFrameCount()); } - JSI_HOST_FUNCTION(currentFrameDuration) { + int currentFrameDuration() { return static_cast(getObject()->currentFrameDuration()); } - JSI_HOST_FUNCTION(decodeNextFrame) { + int decodeNextFrame() { return static_cast(getObject()->decodeNextFrame()); } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostMethod(runtime, prototype, "getFrameCount", - &JsiSkAnimatedImage::getFrameCount); - installHostMethod(runtime, prototype, "getCurrentFrame", - &JsiSkAnimatedImage::getCurrentFrame); - installHostMethod(runtime, prototype, "currentFrameDuration", - &JsiSkAnimatedImage::currentFrameDuration); - installHostMethod(runtime, prototype, "decodeNextFrame", - &JsiSkAnimatedImage::decodeNextFrame); + installMethod(runtime, prototype, "getFrameCount", + &JsiSkAnimatedImage::getFrameCount); + installMethod(runtime, prototype, "getCurrentFrame", + &JsiSkAnimatedImage::getCurrentFrame); + installMethod(runtime, prototype, "currentFrameDuration", + &JsiSkAnimatedImage::currentFrameDuration); + installMethod(runtime, prototype, "decodeNextFrame", + &JsiSkAnimatedImage::decodeNextFrame); } JsiSkAnimatedImage(std::shared_ptr context, diff --git a/packages/skia/cpp/api/JsiSkAnimatedImageFactory.h b/packages/skia/cpp/api/JsiSkAnimatedImageFactory.h index d6ecff500b..5c981c1455 100644 --- a/packages/skia/cpp/api/JsiSkAnimatedImageFactory.h +++ b/packages/skia/cpp/api/JsiSkAnimatedImageFactory.h @@ -2,10 +2,12 @@ #include #include +#include #include #include "JsiSkAnimatedImage.h" +#include "JsiSkConverters.h" #include "JsiSkData.h" #include "JsiSkNativeObjects.h" #include "jsi/JsiPromises.h" @@ -19,22 +21,22 @@ class JsiSkAnimatedImageFactory public: static constexpr const char *CLASS_NAME = "AnimatedImageFactory"; - JSI_HOST_FUNCTION(MakeAnimatedImageFromEncoded) { - auto data = JsiSkData::fromValue(runtime, arguments[0]); + std::variant> + MakeAnimatedImageFromEncoded(sk_sp data) { auto codec = SkAndroidCodec::MakeFromData(data); auto image = SkAnimatedImage::Make(std::move(codec)); if (image == nullptr) { - return jsi::Value::null(); + return nullptr; } - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(image))); + return std::make_shared(getContext(), + std::move(image)); } size_t getMemoryPressure() override { return 1024; } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { - installHostMethod(runtime, prototype, "MakeAnimatedImageFromEncoded", - &JsiSkAnimatedImageFactory::MakeAnimatedImageFromEncoded); + installMethod(runtime, prototype, "MakeAnimatedImageFromEncoded", + &JsiSkAnimatedImageFactory::MakeAnimatedImageFromEncoded); } explicit JsiSkAnimatedImageFactory( diff --git a/packages/skia/cpp/api/JsiSkColorFilterFactory.h b/packages/skia/cpp/api/JsiSkColorFilterFactory.h index 70e2a64d00..4776f914a2 100644 --- a/packages/skia/cpp/api/JsiSkColorFilterFactory.h +++ b/packages/skia/cpp/api/JsiSkColorFilterFactory.h @@ -2,10 +2,12 @@ #include "JsiSkColor.h" #include "JsiSkColorFilter.h" +#include "JsiSkConverters.h" #include "JsiSkNativeObjects.h" #include #include #include +#include #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdocumentation" @@ -24,87 +26,67 @@ class JsiSkColorFilterFactory public: static constexpr const char *CLASS_NAME = "ColorFilterFactory"; - JSI_HOST_FUNCTION(MakeMatrix) { - auto jsiMatrix = arguments[0].asObject(runtime).asArray(runtime); - float matrix[20]; - for (int i = 0; i < 20; i++) { - if (jsiMatrix.size(runtime) > i) { - matrix[i] = jsiMatrix.getValueAtIndex(runtime, i).asNumber(); - } + std::shared_ptr MakeMatrix(std::vector values) { + float matrix[20] = {0}; + for (size_t i = 0; i < 20 && i < values.size(); i++) { + matrix[i] = values[i]; } - // Return the newly constructed object - return makeJsiObject( - runtime, std::make_shared( - getContext(), SkColorFilters::Matrix(std::move(matrix)))); + return std::make_shared(getContext(), + SkColorFilters::Matrix(matrix)); } - JSI_HOST_FUNCTION(MakeBlend) { - auto color = JsiSkColor::fromValue(runtime, arguments[0]); - SkBlendMode blend = (SkBlendMode)arguments[1].asNumber(); - // Return the newly constructed object - return makeJsiObject( - runtime, std::make_shared( - getContext(), SkColorFilters::Blend(color, blend))); + std::shared_ptr MakeBlend(JsiColor color, double blend) { + return std::make_shared( + getContext(), + SkColorFilters::Blend(color, static_cast(blend))); } - JSI_HOST_FUNCTION(MakeCompose) { - auto outer = JsiSkColorFilter::fromValue(runtime, arguments[0]); - auto inner = JsiSkColorFilter::fromValue(runtime, arguments[1]); - // Return the newly constructed object - return makeJsiObject( - runtime, std::make_shared( - getContext(), SkColorFilters::Compose(std::move(outer), - std::move(inner)))); + std::shared_ptr MakeCompose(sk_sp outer, + sk_sp inner) { + return std::make_shared( + getContext(), + SkColorFilters::Compose(std::move(outer), std::move(inner))); } - JSI_HOST_FUNCTION(MakeLerp) { - auto t = arguments[0].asNumber(); - auto dst = JsiSkColorFilter::fromValue(runtime, arguments[1]); - auto src = JsiSkColorFilter::fromValue(runtime, arguments[2]); - // Return the newly constructed object - return makeJsiObject( - runtime, std::make_shared( - getContext(), - SkColorFilters::Lerp(t, std::move(dst), std::move(src)))); + std::shared_ptr MakeLerp(double t, + sk_sp dst, + sk_sp src) { + return std::make_shared( + getContext(), SkColorFilters::Lerp(t, std::move(dst), std::move(src))); } - JSI_HOST_FUNCTION(MakeSRGBToLinearGamma) { - // Return the newly constructed object - return makeJsiObject( - runtime, std::make_shared( - getContext(), SkColorFilters::SRGBToLinearGamma())); + std::shared_ptr MakeSRGBToLinearGamma() { + return std::make_shared( + getContext(), SkColorFilters::SRGBToLinearGamma()); } - JSI_HOST_FUNCTION(MakeLinearToSRGBGamma) { - // Return the newly constructed object - return makeJsiObject( - runtime, std::make_shared( - getContext(), SkColorFilters::LinearToSRGBGamma())); + std::shared_ptr MakeLinearToSRGBGamma() { + return std::make_shared( + getContext(), SkColorFilters::LinearToSRGBGamma()); } - JSI_HOST_FUNCTION(MakeLumaColorFilter) { - // Return the newly constructed object - return makeJsiObject(runtime, std::make_shared( - getContext(), SkLumaColorFilter::Make())); + std::shared_ptr MakeLumaColorFilter() { + return std::make_shared(getContext(), + SkLumaColorFilter::Make()); } size_t getMemoryPressure() override { return 1024; } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { - installHostMethod(runtime, prototype, "MakeMatrix", - &JsiSkColorFilterFactory::MakeMatrix); - installHostMethod(runtime, prototype, "MakeBlend", - &JsiSkColorFilterFactory::MakeBlend); - installHostMethod(runtime, prototype, "MakeCompose", - &JsiSkColorFilterFactory::MakeCompose); - installHostMethod(runtime, prototype, "MakeLerp", - &JsiSkColorFilterFactory::MakeLerp); - installHostMethod(runtime, prototype, "MakeSRGBToLinearGamma", - &JsiSkColorFilterFactory::MakeSRGBToLinearGamma); - installHostMethod(runtime, prototype, "MakeLinearToSRGBGamma", - &JsiSkColorFilterFactory::MakeLinearToSRGBGamma); - installHostMethod(runtime, prototype, "MakeLumaColorFilter", - &JsiSkColorFilterFactory::MakeLumaColorFilter); + installMethod(runtime, prototype, "MakeMatrix", + &JsiSkColorFilterFactory::MakeMatrix); + installMethod(runtime, prototype, "MakeBlend", + &JsiSkColorFilterFactory::MakeBlend); + installMethod(runtime, prototype, "MakeCompose", + &JsiSkColorFilterFactory::MakeCompose); + installMethod(runtime, prototype, "MakeLerp", + &JsiSkColorFilterFactory::MakeLerp); + installMethod(runtime, prototype, "MakeSRGBToLinearGamma", + &JsiSkColorFilterFactory::MakeSRGBToLinearGamma); + installMethod(runtime, prototype, "MakeLinearToSRGBGamma", + &JsiSkColorFilterFactory::MakeLinearToSRGBGamma); + installMethod(runtime, prototype, "MakeLumaColorFilter", + &JsiSkColorFilterFactory::MakeLumaColorFilter); } explicit JsiSkColorFilterFactory(std::shared_ptr context) diff --git a/packages/skia/cpp/api/JsiSkContourMeasure.h b/packages/skia/cpp/api/JsiSkContourMeasure.h index a0f137ddb6..c3d50a4821 100644 --- a/packages/skia/cpp/api/JsiSkContourMeasure.h +++ b/packages/skia/cpp/api/JsiSkContourMeasure.h @@ -2,10 +2,12 @@ #include #include +#include #include #include "JsiSkNativeObjects.h" +#include "JsiSkPoint.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdocumentation" @@ -31,55 +33,43 @@ class JsiSkContourMeasure : JsiSkWrappingSkPtrNativeObject( std::move(context), std::move(contourMeasure)) {} - JSI_HOST_FUNCTION(getPosTan) { - auto dist = arguments[0].asNumber(); + std::vector> getPosTan(double dist) { SkPoint position; SkPoint tangent; auto result = getObject()->getPosTan(dist, &position, &tangent); if (!result) { - throw jsi::JSError(runtime, "getPosTan() failed"); + throw std::runtime_error("getPosTan() failed"); } - auto posTan = jsi::Array(runtime, 2); - auto pos = makeJsiObject( - runtime, std::make_shared(getContext(), position)); - auto tan = makeJsiObject( - runtime, std::make_shared(getContext(), tangent)); - posTan.setValueAtIndex(runtime, 0, pos); - posTan.setValueAtIndex(runtime, 1, tan); - return posTan; + return {std::make_shared(getContext(), position), + std::make_shared(getContext(), tangent)}; } - JSI_HOST_FUNCTION(length) { - return jsi::Value(SkScalarToDouble(getObject()->length())); - } + double length() { return SkScalarToDouble(getObject()->length()); } - JSI_HOST_FUNCTION(isClosed) { return jsi::Value(getObject()->isClosed()); } + bool isClosed() { return getObject()->isClosed(); } - JSI_HOST_FUNCTION(getSegment) { - auto start = arguments[0].asNumber(); - auto end = arguments[1].asNumber(); - auto startWithMoveTo = arguments[2].getBool(); + std::shared_ptr getSegment(double start, double end, + bool startWithMoveTo) { SkPathBuilder builder; auto result = getObject()->getSegment(start, end, &builder, startWithMoveTo); if (!result) { - throw jsi::JSError(runtime, "getSegment() failed"); + throw std::runtime_error("getSegment() failed"); } - return JsiSkPath::toValue(runtime, getContext(), builder.snapshot()); + return std::make_shared(getContext(), builder.snapshot()); } size_t getMemoryPressure() override { return 1024; } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostMethod(runtime, prototype, "getPosTan", - &JsiSkContourMeasure::getPosTan); - installHostMethod(runtime, prototype, "length", - &JsiSkContourMeasure::length); - installHostMethod(runtime, prototype, "isClosed", - &JsiSkContourMeasure::isClosed); - installHostMethod(runtime, prototype, "getSegment", - &JsiSkContourMeasure::getSegment); + installMethod(runtime, prototype, "getPosTan", + &JsiSkContourMeasure::getPosTan); + installMethod(runtime, prototype, "length", &JsiSkContourMeasure::length); + installMethod(runtime, prototype, "isClosed", + &JsiSkContourMeasure::isClosed); + installMethod(runtime, prototype, "getSegment", + &JsiSkContourMeasure::getSegment); } }; } // namespace RNSkia diff --git a/packages/skia/cpp/api/JsiSkContourMeasureIter.h b/packages/skia/cpp/api/JsiSkContourMeasureIter.h index 7102cf2678..867e82068a 100644 --- a/packages/skia/cpp/api/JsiSkContourMeasureIter.h +++ b/packages/skia/cpp/api/JsiSkContourMeasureIter.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include "JsiSkContourMeasure.h" @@ -33,19 +34,18 @@ class JsiSkContourMeasureIter std::move(context), std::make_shared( path, forceClosed, resScale)) {} - JSI_HOST_FUNCTION(next) { + std::optional> next() { auto next = getObject()->next(); if (next == nullptr) { - return jsi::Value::undefined(); + return std::nullopt; } - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(next))); + return std::make_shared(getContext(), + std::move(next)); } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostMethod(runtime, prototype, "next", - &JsiSkContourMeasureIter::next); + installMethod(runtime, prototype, "next", &JsiSkContourMeasureIter::next); } size_t getMemoryPressure() override { diff --git a/packages/skia/cpp/api/JsiSkConverters.h b/packages/skia/cpp/api/JsiSkConverters.h new file mode 100644 index 0000000000..f130c4b53b --- /dev/null +++ b/packages/skia/cpp/api/JsiSkConverters.h @@ -0,0 +1,333 @@ +#pragma once + +#include +#include +#include +#include + +#include + +#include "jsi/JSIConverter.h" + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdocumentation" + +#include "include/core/SkColor.h" +#include "include/core/SkPoint3.h" +#include "include/core/SkRefCnt.h" +#include "include/core/SkSize.h" + +#pragma clang diagnostic pop + +// Forward declarations of the wrapped Skia types. Only the trait below needs +// them; the converter bodies are templates and are instantiated lazily, at +// which point the classes are complete. +class SkImage; +class SkShader; +class SkTypeface; +class SkColorFilter; +class SkImageFilter; +class SkMaskFilter; +class SkPathEffect; +class SkData; +class SkPicture; +class SkTextBlob; +class SkRuntimeEffect; +class SkRuntimeEffectBuilder; +class SkVertices; +class SkFontMgr; +class SkSVGDOM; +class SkPaint; +class SkFont; +class SkMatrix; +class SkPathBuilder; +struct SkRect; +struct SkPoint; +class SkRRect; +class SkFontStyle; +struct SkImageInfo; +struct SkRSXform; + +namespace RNSkia { + +namespace jsi = facebook::jsi; + +class JsiSkImage; +class JsiSkShader; +class JsiSkTypeface; +class JsiSkColorFilter; +class JsiSkImageFilter; +class JsiSkMaskFilter; +class JsiSkPathEffect; +class JsiSkData; +class JsiSkPicture; +class JsiSkTextBlob; +class JsiSkRuntimeEffect; +class JsiSkRuntimeShaderBuilder; +class JsiSkVertices; +class JsiSkFontMgr; +class JsiSkSVG; +class JsiSkPaint; +class JsiSkFont; +class JsiSkMatrix; +class JsiSkPath; +class JsiSkRect; +class JsiSkPoint; +class JsiSkRRect; +class JsiSkFontStyle; +class JsiSkImageInfo; +class JsiSkRSXform; + +/** + * Maps a wrapped Skia type to the JsiSk* class whose `fromValue` knows how to + * read it from a JS value (including the plain-object fallbacks, e.g. SkRect + * from {x, y, width, height}). The JSIConverter specializations below are + * enabled for exactly the types listed here. + */ +template struct JsiSkWrapperFor; + +template <> struct JsiSkWrapperFor { + using type = JsiSkImage; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkShader; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkTypeface; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkColorFilter; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkImageFilter; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkMaskFilter; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkPathEffect; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkData; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkPicture; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkTextBlob; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkRuntimeEffect; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkRuntimeShaderBuilder; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkVertices; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkFontMgr; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkSVG; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkPaint; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkFont; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkMatrix; +}; +// Note: JsiSkPath wraps an SkPathBuilder (not an SkPath). +template <> struct JsiSkWrapperFor { + using type = JsiSkPath; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkRect; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkPoint; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkRRect; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkFontStyle; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkImageInfo; +}; +template <> struct JsiSkWrapperFor { + using type = JsiSkRSXform; +}; + +template +using JsiSkWrapperFor_t = typename JsiSkWrapperFor::type; + +class JsiSkColor; + +/** + * SkColor is a typedef of uint32_t, which already has a JSIConverter, so + * color arguments use this strong typedef to route through + * JsiSkColor::fromValue (array | Float32Array) instead. + */ +struct JsiColor { + SkColor color; + operator SkColor() const { return color; } +}; + +template <> struct JsiSkWrapperFor { + using type = JsiSkColor; +}; + +/** + * An argument the JS API treats as absent when it is omitted, undefined, or + * null. This matches the pervasive `hasOptionalArgument` pattern of the raw + * bindings. Use std::optional instead when null must be distinguished from + * undefined (it treats only omitted/undefined as absent). + */ +template struct JsiOptional : std::optional { + using std::optional::optional; +}; + +} // namespace RNSkia + +namespace rnwgpu { + +/** + * sk_sp arguments (SkImage, SkShader, ...). Delegates to the wrapper + * class's fromValue, so wrapper objects and any legacy fallback shapes keep + * working. Conversion is strict: null/undefined throw, matching the previous + * raw JSI_HOST_FUNCTION bodies. Arguments that explicitly accept null use + * std::variant> (see below); arguments that may be + * omitted use std::optional. + * + * There is intentionally no toJSI: building a wrapper requires the platform + * context, so methods return std::shared_ptr built with + * getContext() instead (handled by the NativeObject converter). + */ +template +struct JSIConverter, + std::void_t::type>> { + static sk_sp fromJSI(jsi::Runtime &runtime, const jsi::Value &arg, + bool outOfBound) { + return RNSkia::JsiSkWrapperFor_t::fromValue(runtime, arg); + } +}; + +/** + * std::shared_ptr arguments for the wrapped non-refcounted Skia types + * (SkPaint, SkFont, SkMatrix, SkRect, ...). Same rules as sk_sp above. + */ +template +struct JSIConverter, + std::void_t::type>> { + static std::shared_ptr fromJSI(jsi::Runtime &runtime, + const jsi::Value &arg, bool outOfBound) { + return RNSkia::JsiSkWrapperFor_t::fromValue(runtime, arg); + } +}; + +/** + * Nullable sk_sp arguments: JS null maps to the std::nullptr_t + * alternative, anything else goes through the strict converter above. + */ +template +struct JSIConverter>, + std::void_t::type>> { + using Target = std::variant>; + + static Target fromJSI(jsi::Runtime &runtime, const jsi::Value &arg, + bool outOfBound) { + if (arg.isNull()) { + return Target(nullptr); + } + return Target(JSIConverter>::fromJSI(runtime, arg, outOfBound)); + } +}; + +/** + * SkRect / SkPoint / SkMatrix / SkRSXform by value — the wrapper's fromValue + * provides the plain-object/array fallbacks. fromJSI only; returning these + * types is done through std::shared_ptr etc. (see above). + * + * All bodies below reach the wrapper through JsiSkWrapperFor_t so the + * name stays dependent on T and is only looked up at instantiation, when the + * wrapper class is complete (two-phase lookup would otherwise bind — and + * reject — the incomplete forward declaration at definition time). + */ +template +struct JSIConverter || + std::is_same_v || + std::is_same_v || + std::is_same_v>> { + static T fromJSI(jsi::Runtime &runtime, const jsi::Value &arg, + bool outOfBound) { + return *RNSkia::JsiSkWrapperFor_t::fromValue(runtime, arg); + } +}; + +// Colors via the JsiColor strong typedef (see above). +template +struct JSIConverter>> { + static T fromJSI(jsi::Runtime &runtime, const jsi::Value &arg, + bool outOfBound) { + return {RNSkia::JsiSkWrapperFor_t::fromValue(runtime, arg)}; + } + static jsi::Value toJSI(jsi::Runtime &runtime, const T &arg) { + return RNSkia::JsiSkWrapperFor_t::toValue(runtime, arg.color); + } +}; + +// JsiOptional: omitted | undefined | null -> absent +template struct JSIConverter> { + static RNSkia::JsiOptional fromJSI(jsi::Runtime &runtime, + const jsi::Value &arg, + bool outOfBound) { + if (outOfBound || arg.isUndefined() || arg.isNull()) { + return {}; + } + return {JSIConverter::fromJSI(runtime, arg, outOfBound)}; + } + static jsi::Value toJSI(jsi::Runtime &runtime, + const RNSkia::JsiOptional &arg) { + if (!arg.has_value()) { + return jsi::Value::null(); + } + return JSIConverter::toJSI(runtime, arg.value()); + } +}; + +// SkPoint3 <- {x, y, z} +template <> struct JSIConverter { + static SkPoint3 fromJSI(jsi::Runtime &runtime, const jsi::Value &arg, + bool outOfBound) { + auto object = arg.asObject(runtime); + auto x = object.getProperty(runtime, "x").asNumber(); + auto y = object.getProperty(runtime, "y").asNumber(); + auto z = object.getProperty(runtime, "z").asNumber(); + return SkPoint3::Make(x, y, z); + } +}; + +// SkISize <> {width, height} +template <> struct JSIConverter { + static SkISize fromJSI(jsi::Runtime &runtime, const jsi::Value &arg, + bool outOfBound) { + auto object = arg.asObject(runtime); + auto width = object.getProperty(runtime, "width").asNumber(); + auto height = object.getProperty(runtime, "height").asNumber(); + return SkISize::Make(static_cast(width), + static_cast(height)); + } + static jsi::Value toJSI(jsi::Runtime &runtime, const SkISize &size) { + jsi::Object result(runtime); + result.setProperty(runtime, "width", static_cast(size.width())); + result.setProperty(runtime, "height", static_cast(size.height())); + return result; + } +}; + +} // namespace rnwgpu diff --git a/packages/skia/cpp/api/JsiSkDataFactory.h b/packages/skia/cpp/api/JsiSkDataFactory.h index 0b9cc868ab..5952700f90 100644 --- a/packages/skia/cpp/api/JsiSkDataFactory.h +++ b/packages/skia/cpp/api/JsiSkDataFactory.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -62,31 +63,24 @@ class JsiSkDataFactory : public JsiSkNativeObject { runtime, std::make_shared(getContext(), std::move(data))); } - JSI_HOST_FUNCTION(fromBase64) { - auto base64 = arguments[0].asString(runtime); - auto base64Str = base64.utf8(runtime); + std::shared_ptr fromBase64(std::string base64Str) { auto size = base64Str.size(); // Calculate length size_t len; - auto err = - Base64::Decode(&base64.utf8(runtime).c_str()[0], size, nullptr, &len); + auto err = Base64::Decode(base64Str.c_str(), size, nullptr, &len); if (err != Base64::Error::kNone) { - throw jsi::JSError(runtime, "Error decoding base64 string"); - return jsi::Value::undefined(); + throw std::runtime_error("Error decoding base64 string"); } // Create data object and decode auto data = SkData::MakeUninitialized(len); - err = Base64::Decode(&base64.utf8(runtime).c_str()[0], size, - data->writable_data(), &len); + err = Base64::Decode(base64Str.c_str(), size, data->writable_data(), &len); if (err != Base64::Error::kNone) { - throw jsi::JSError(runtime, "Error decoding base64 string"); - return jsi::Value::undefined(); + throw std::runtime_error("Error decoding base64 string"); } - return makeJsiObject( - runtime, std::make_shared(getContext(), std::move(data))); + return std::make_shared(getContext(), std::move(data)); } size_t getMemoryPressure() override { return 1024; } @@ -96,8 +90,8 @@ class JsiSkDataFactory : public JsiSkNativeObject { &JsiSkDataFactory::fromURI); installHostMethod(runtime, prototype, "fromBytes", &JsiSkDataFactory::fromBytes); - installHostMethod(runtime, prototype, "fromBase64", - &JsiSkDataFactory::fromBase64); + installMethod(runtime, prototype, "fromBase64", + &JsiSkDataFactory::fromBase64); } explicit JsiSkDataFactory(std::shared_ptr context) diff --git a/packages/skia/cpp/api/JsiSkFont.h b/packages/skia/cpp/api/JsiSkFont.h index 0b85c4fbdf..59a0e12938 100644 --- a/packages/skia/cpp/api/JsiSkFont.h +++ b/packages/skia/cpp/api/JsiSkFont.h @@ -2,13 +2,17 @@ #include #include +#include +#include #include +#include #include #include "JsiSkNativeObjects.h" #include "utils/RNSkLog.h" #include +#include "JsiSkConverters.h" #include "JsiSkPaint.h" #include "JsiSkPoint.h" #include "JsiSkRect.h" @@ -30,39 +34,26 @@ class JsiSkFont : public JsiSkWrappingSharedPtrNativeObject { public: static constexpr const char *CLASS_NAME = "Font"; - JSI_HOST_FUNCTION(getGlyphWidths) { - auto jsiGlyphs = arguments[0].asObject(runtime).asArray(runtime); - std::vector glyphs; - int glyphsSize = static_cast(jsiGlyphs.size(runtime)); - - std::vector widthPtrs; - widthPtrs.resize(glyphsSize); - - glyphs.reserve(glyphsSize); - for (int i = 0; i < glyphsSize; i++) { - glyphs.push_back(jsiGlyphs.getValueAtIndex(runtime, i).asNumber()); - } - auto widths = SkSpan(static_cast(widthPtrs.data()), glyphsSize); - auto g = SkSpan(glyphs.data(), glyphs.size()); - if (count > 1) { - auto paint = JsiSkPaint::fromValue(runtime, arguments[1]); - getObject()->getWidthsBounds(g, widths, {}, paint.get()); - } else { - getObject()->getWidthsBounds(g, widths, {}, nullptr); - } - auto jsiWidths = jsi::Array(runtime, glyphsSize); - for (int i = 0; i < glyphsSize; i++) { - jsiWidths.setValueAtIndex( - runtime, i, - jsi::Value( - SkScalarToDouble(static_cast(widthPtrs.data())[i]))); + std::vector + getGlyphWidths(std::vector glyphs, + std::optional> paint) { + std::vector glyphIds; + glyphIds.reserve(glyphs.size()); + for (auto glyph : glyphs) { + glyphIds.push_back(static_cast(glyph)); } - return jsiWidths; + std::vector widths; + widths.resize(glyphIds.size()); + auto g = SkSpan(glyphIds.data(), glyphIds.size()); + auto w = SkSpan(widths.data(), widths.size()); + getObject()->getWidthsBounds( + g, w, {}, paint.has_value() ? paint.value().get() : nullptr); + return std::vector(widths.begin(), widths.end()); } // TODO: deprecate - JSI_HOST_FUNCTION(getTextWidth) { - auto str = arguments[0].asString(runtime).utf8(runtime); + int getTextWidth(std::string str, + std::optional> paint) { auto numGlyphIDs = getObject()->countText(str.c_str(), str.length(), SkTextEncoding::kUTF8); std::vector glyphs; @@ -70,33 +61,25 @@ class JsiSkFont : public JsiSkWrappingSharedPtrNativeObject { auto g = SkSpan(glyphs.data(), glyphs.size()); getObject()->textToGlyphs(str.c_str(), str.length(), SkTextEncoding::kUTF8, g); - std::vector widthPtrs; - widthPtrs.resize(numGlyphIDs); - auto widths = SkSpan(widthPtrs.data(), widthPtrs.size()); - if (count > 1) { - auto paint = JsiSkPaint::fromValue(runtime, arguments[1]); - getObject()->getWidthsBounds(g, widths, {}, paint.get()); - } else { - getObject()->getWidthsBounds(g, widths, {}, nullptr); - } - return jsi::Value(std::accumulate(widthPtrs.begin(), widthPtrs.end(), 0)); + std::vector widths; + widths.resize(numGlyphIDs); + auto w = SkSpan(widths.data(), widths.size()); + getObject()->getWidthsBounds( + g, w, {}, paint.has_value() ? paint.value().get() : nullptr); + return std::accumulate(widths.begin(), widths.end(), 0); } - JSI_HOST_FUNCTION(measureText) { - auto str = arguments[0].asString(runtime).utf8(runtime); + std::shared_ptr + measureText(std::string str, std::optional> paint) { SkRect bounds; - if (count > 1) { - auto paint = JsiSkPaint::fromValue(runtime, arguments[1]); - getObject()->measureText(str.c_str(), str.length(), SkTextEncoding::kUTF8, - &bounds, paint.get()); - } else { - getObject()->measureText(str.c_str(), str.length(), SkTextEncoding::kUTF8, - &bounds); - } - return makeJsiObject( - runtime, std::make_shared(getContext(), std::move(bounds))); + getObject()->measureText(str.c_str(), str.length(), SkTextEncoding::kUTF8, + &bounds, + paint.has_value() ? paint.value().get() : nullptr); + return std::make_shared(getContext(), bounds); } + // Stays raw: the result object has a conditionally present `bounds` + // property, which a typed return value cannot express. JSI_HOST_FUNCTION(getMetrics) { SkFontMetrics fm; getObject()->getMetrics(&fm); @@ -113,178 +96,114 @@ class JsiSkFont : public JsiSkWrappingSharedPtrNativeObject { return metrics; } - JSI_HOST_FUNCTION(getGlyphIDs) { - auto str = arguments[0].asString(runtime).utf8(runtime); - int numGlyphIDs = - count > 1 && !arguments[1].isNull() && !arguments[1].isUndefined() - ? static_cast(arguments[1].asNumber()) - : getObject()->countText(str.c_str(), str.length(), - SkTextEncoding::kUTF8); + std::vector getGlyphIDs(std::string str, JsiOptional numGlyphs) { + int numGlyphIDs = numGlyphs.has_value() + ? *numGlyphs + : getObject()->countText(str.c_str(), str.length(), + SkTextEncoding::kUTF8); std::vector glyphIDs; glyphIDs.resize(numGlyphIDs); auto g = SkSpan(static_cast(glyphIDs.data()), glyphIDs.size()); getObject()->textToGlyphs(str.c_str(), str.length(), SkTextEncoding::kUTF8, g); - auto jsiGlyphIDs = jsi::Array(runtime, numGlyphIDs); - for (int i = 0; i < numGlyphIDs; i++) { - jsiGlyphIDs.setValueAtIndex(runtime, i, - jsi::Value(static_cast(glyphIDs[i]))); - } - return jsiGlyphIDs; + return std::vector(glyphIDs.begin(), glyphIDs.end()); } - JSI_HOST_FUNCTION(getGlyphIntercepts) { - auto jsiGlyphs = arguments[0].asObject(runtime).asArray(runtime); - auto jsiPositions = arguments[1].asObject(runtime).asArray(runtime); - auto top = arguments[2].asNumber(); - auto bottom = arguments[3].asNumber(); - std::vector positions; - int pointsSize = static_cast(jsiPositions.size(runtime)); - positions.reserve(pointsSize); - for (int i = 0; i < pointsSize; i++) { - std::shared_ptr point = JsiSkPoint::fromValue( - runtime, jsiPositions.getValueAtIndex(runtime, i).asObject(runtime)); - positions.push_back(*point.get()); - } - - std::vector glyphs; - int glyphsSize = static_cast(jsiGlyphs.size(runtime)); - glyphs.reserve(glyphsSize); - for (int i = 0; i < glyphsSize; i++) { - glyphs.push_back(jsiGlyphs.getValueAtIndex(runtime, i).asNumber()); + std::vector getGlyphIntercepts(std::vector glyphs, + std::vector positions, + double top, double bottom) { + std::vector glyphIds; + glyphIds.reserve(glyphs.size()); + for (auto glyph : glyphs) { + glyphIds.push_back(static_cast(glyph)); } - - if (glyphs.size() > positions.size()) { - throw jsi::JSError(runtime, "Not enough x,y position pairs for glyphs"); - return jsi::Value::null(); + if (glyphIds.size() > positions.size()) { + throw std::runtime_error("Not enough x,y position pairs for glyphs"); } - auto g = SkSpan(glyphs.data(), glyphs.size()); + auto g = SkSpan(glyphIds.data(), glyphIds.size()); auto p = SkSpan(positions.data(), positions.size()); auto sects = getObject()->getIntercepts(g, p, top, bottom); - auto jsiSects = jsi::Array(runtime, sects.size()); - for (int i = 0; i < sects.size(); i++) { - jsiSects.setValueAtIndex(runtime, i, - jsi::Value(static_cast(sects.at(i)))); - } - return jsiSects; + return std::vector(sects.begin(), sects.end()); } - JSI_HOST_FUNCTION(getScaleX) { - return jsi::Value(SkScalarToDouble(getObject()->getScaleX())); - } + double getScaleX() { return SkScalarToDouble(getObject()->getScaleX()); } - JSI_HOST_FUNCTION(getSize) { - return jsi::Value(SkScalarToDouble(getObject()->getSize())); - } + double getSize() { return SkScalarToDouble(getObject()->getSize()); } - JSI_HOST_FUNCTION(getSkewX) { - return jsi::Value(SkScalarToDouble(getObject()->getSkewX())); - } + double getSkewX() { return SkScalarToDouble(getObject()->getSkewX()); } - JSI_HOST_FUNCTION(isEmbolden) { - return jsi::Value(getObject()->isEmbolden()); - } + bool isEmbolden() { return getObject()->isEmbolden(); } - JSI_HOST_FUNCTION(getTypeface) { - return JsiSkTypeface::toValue( - runtime, getContext(), sk_sp(getObject()->getTypeface())); + std::shared_ptr getTypeface() { + return std::make_shared( + getContext(), sk_sp(getObject()->getTypeface())); } - JSI_HOST_FUNCTION(setEdging) { - auto edging = arguments[0].asNumber(); + void setEdging(double edging) { getObject()->setEdging(static_cast(edging)); - return jsi::Value::undefined(); } - JSI_HOST_FUNCTION(embeddedBitmaps) { - auto embeddedBitmaps = arguments[0].getBool(); + void setEmbeddedBitmaps(bool embeddedBitmaps) { getObject()->setEmbeddedBitmaps(embeddedBitmaps); - return jsi::Value::undefined(); } - JSI_HOST_FUNCTION(setHinting) { - auto hinting = arguments[0].asNumber(); + void setHinting(double hinting) { getObject()->setHinting(static_cast(hinting)); - return jsi::Value::undefined(); } - JSI_HOST_FUNCTION(setLinearMetrics) { - auto linearMetrics = arguments[0].getBool(); + void setLinearMetrics(bool linearMetrics) { getObject()->setLinearMetrics(linearMetrics); - return jsi::Value::undefined(); } - JSI_HOST_FUNCTION(setScaleX) { - auto scaleX = arguments[0].asNumber(); - getObject()->setScaleX(scaleX); - return jsi::Value::undefined(); - } + void setScaleX(double scaleX) { getObject()->setScaleX(scaleX); } - JSI_HOST_FUNCTION(setSkewX) { - auto skewX = arguments[0].asNumber(); - getObject()->setSkewX(skewX); - return jsi::Value::undefined(); - } + void setSkewX(double skewX) { getObject()->setSkewX(skewX); } - JSI_HOST_FUNCTION(setSize) { - auto size = arguments[0].asNumber(); - getObject()->setSize(size); - return jsi::Value::undefined(); - } + void setSize(double size) { getObject()->setSize(size); } - JSI_HOST_FUNCTION(setEmbolden) { - auto embolden = arguments[0].asNumber(); - getObject()->setEmbolden(embolden); - return jsi::Value::undefined(); + // The JS API declares booleans here, but the previous implementation read + // the arguments with asNumber() — keep accepting numbers. + void setEmbolden(double embolden) { + getObject()->setEmbolden(static_cast(embolden)); } - JSI_HOST_FUNCTION(setSubpixel) { - auto subpixel = arguments[0].asNumber(); - getObject()->setSubpixel(subpixel); - return jsi::Value::undefined(); + void setSubpixel(double subpixel) { + getObject()->setSubpixel(static_cast(subpixel)); } - JSI_HOST_FUNCTION(setTypeface) { - auto typeface = arguments[0].isNull() - ? nullptr - : JsiSkTypeface::fromValue(runtime, arguments[0]); - getObject()->setTypeface(typeface); - return jsi::Value::undefined(); + void setTypeface(std::variant> typeface) { + getObject()->setTypeface( + std::holds_alternative(typeface) + ? nullptr + : std::get>(std::move(typeface))); } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostMethod(runtime, prototype, "getSize", &JsiSkFont::getSize); + installMethod(runtime, prototype, "getSize", &JsiSkFont::getSize); installHostMethod(runtime, prototype, "getMetrics", &JsiSkFont::getMetrics); - installHostMethod(runtime, prototype, "getGlyphIDs", - &JsiSkFont::getGlyphIDs); - installHostMethod(runtime, prototype, "getGlyphIntercepts", - &JsiSkFont::getGlyphIntercepts); - installHostMethod(runtime, prototype, "getScaleX", &JsiSkFont::getScaleX); - installHostMethod(runtime, prototype, "getSkewX", &JsiSkFont::getSkewX); - installHostMethod(runtime, prototype, "getTypeface", - &JsiSkFont::getTypeface); - installHostMethod(runtime, prototype, "setEdging", &JsiSkFont::setEdging); - installHostMethod(runtime, prototype, "embeddedBitmaps", - &JsiSkFont::embeddedBitmaps); - installHostMethod(runtime, prototype, "setHinting", &JsiSkFont::setHinting); - installHostMethod(runtime, prototype, "setLinearMetrics", - &JsiSkFont::setLinearMetrics); - installHostMethod(runtime, prototype, "setScaleX", &JsiSkFont::setScaleX); - installHostMethod(runtime, prototype, "setSkewX", &JsiSkFont::setSkewX); - installHostMethod(runtime, prototype, "setSize", &JsiSkFont::setSize); - installHostMethod(runtime, prototype, "setEmbolden", - &JsiSkFont::setEmbolden); - installHostMethod(runtime, prototype, "setSubpixel", - &JsiSkFont::setSubpixel); - installHostMethod(runtime, prototype, "setTypeface", - &JsiSkFont::setTypeface); - installHostMethod(runtime, prototype, "getGlyphWidths", - &JsiSkFont::getGlyphWidths); - installHostMethod(runtime, prototype, "getTextWidth", - &JsiSkFont::getTextWidth); - installHostMethod(runtime, prototype, "measureText", - &JsiSkFont::measureText); + installMethod(runtime, prototype, "getGlyphIDs", &JsiSkFont::getGlyphIDs); + installMethod(runtime, prototype, "getGlyphIntercepts", + &JsiSkFont::getGlyphIntercepts); + installMethod(runtime, prototype, "getScaleX", &JsiSkFont::getScaleX); + installMethod(runtime, prototype, "getSkewX", &JsiSkFont::getSkewX); + installMethod(runtime, prototype, "getTypeface", &JsiSkFont::getTypeface); + installMethod(runtime, prototype, "setEdging", &JsiSkFont::setEdging); + installMethod(runtime, prototype, "embeddedBitmaps", + &JsiSkFont::setEmbeddedBitmaps); + installMethod(runtime, prototype, "setHinting", &JsiSkFont::setHinting); + installMethod(runtime, prototype, "setLinearMetrics", + &JsiSkFont::setLinearMetrics); + installMethod(runtime, prototype, "setScaleX", &JsiSkFont::setScaleX); + installMethod(runtime, prototype, "setSkewX", &JsiSkFont::setSkewX); + installMethod(runtime, prototype, "setSize", &JsiSkFont::setSize); + installMethod(runtime, prototype, "setEmbolden", &JsiSkFont::setEmbolden); + installMethod(runtime, prototype, "setSubpixel", &JsiSkFont::setSubpixel); + installMethod(runtime, prototype, "setTypeface", &JsiSkFont::setTypeface); + installMethod(runtime, prototype, "getGlyphWidths", + &JsiSkFont::getGlyphWidths); + installMethod(runtime, prototype, "getTextWidth", &JsiSkFont::getTextWidth); + installMethod(runtime, prototype, "measureText", &JsiSkFont::measureText); } JsiSkFont(std::shared_ptr context, const SkFont &font) diff --git a/packages/skia/cpp/api/JsiSkFontMgr.h b/packages/skia/cpp/api/JsiSkFontMgr.h index c4f1936d57..33eb9ef649 100644 --- a/packages/skia/cpp/api/JsiSkFontMgr.h +++ b/packages/skia/cpp/api/JsiSkFontMgr.h @@ -2,11 +2,14 @@ #include #include +#include #include #include +#include "JsiSkConverters.h" #include "JsiSkFontStyle.h" #include "JsiSkNativeObjects.h" +#include "JsiSkTypeface.h" #include "utils/RNSkLog.h" #include @@ -32,41 +35,36 @@ class JsiSkFontMgr fontMgr), _systemFontFamilies(context->getSystemFontFamilies()) {} - JSI_HOST_FUNCTION(countFamilies) { + int countFamilies() { return static_cast(getObject()->countFamilies() + _systemFontFamilies.size()); } - JSI_HOST_FUNCTION(getFamilyName) { - auto i = static_cast(arguments[0].asNumber()); + std::string getFamilyName(int i) { auto baseFamilyCount = getObject()->countFamilies(); if (i < baseFamilyCount) { SkString name; getObject()->getFamilyName(i, &name); - return jsi::String::createFromUtf8(runtime, name.c_str()); + return std::string(name.c_str()); } auto systemIndex = i - baseFamilyCount; if (systemIndex < static_cast(_systemFontFamilies.size())) { - return jsi::String::createFromUtf8(runtime, - _systemFontFamilies[systemIndex]); + return _systemFontFamilies[systemIndex]; } - throw jsi::JSError( - runtime, + throw std::runtime_error( "Font family index out of bounds: " + std::to_string(i) + - " (total families: " + - std::to_string(baseFamilyCount + _systemFontFamilies.size()) + ")"); + " (total families: " + + std::to_string(baseFamilyCount + _systemFontFamilies.size()) + ")"); } - JSI_HOST_FUNCTION(matchFamilyStyle) { - auto name = arguments[0].asString(runtime).utf8(runtime); + std::shared_ptr + matchFamilyStyle(std::string name, std::shared_ptr fontStyle) { // Resolve font family aliases (e.g., "System" -> ".AppleSystemUIFont" on // iOS) auto resolvedName = getContext()->resolveFontFamily(name); - auto fontStyle = JsiSkFontStyle::fromValue(runtime, arguments[1]); auto typeface = getObject()->matchFamilyStyle(resolvedName.c_str(), *fontStyle); - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(typeface))); + return std::make_shared(getContext(), std::move(typeface)); } size_t getMemoryPressure() override { return 2048; } @@ -78,12 +76,12 @@ class JsiSkFontMgr static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostMethod(runtime, prototype, "countFamilies", - &JsiSkFontMgr::countFamilies); - installHostMethod(runtime, prototype, "getFamilyName", - &JsiSkFontMgr::getFamilyName); - installHostMethod(runtime, prototype, "matchFamilyStyle", - &JsiSkFontMgr::matchFamilyStyle); + installMethod(runtime, prototype, "countFamilies", + &JsiSkFontMgr::countFamilies); + installMethod(runtime, prototype, "getFamilyName", + &JsiSkFontMgr::getFamilyName); + installMethod(runtime, prototype, "matchFamilyStyle", + &JsiSkFontMgr::matchFamilyStyle); } private: diff --git a/packages/skia/cpp/api/JsiSkFontMgrFactory.h b/packages/skia/cpp/api/JsiSkFontMgrFactory.h index 879a4d4149..d57343732a 100644 --- a/packages/skia/cpp/api/JsiSkFontMgrFactory.h +++ b/packages/skia/cpp/api/JsiSkFontMgrFactory.h @@ -6,6 +6,7 @@ #include +#include "JsiSkFontMgr.h" #include "JsiSkNativeObjects.h" #pragma clang diagnostic push @@ -32,17 +33,15 @@ class JsiSkFontMgrFactory : public JsiSkNativeObject { return fontMgr; } - JSI_HOST_FUNCTION(System) { + std::shared_ptr System() { auto fontMgr = JsiSkFontMgrFactory::getFontMgr(getContext()); - return makeJsiObject(runtime, - std::make_shared(getContext(), fontMgr)); + return std::make_shared(getContext(), fontMgr); } size_t getMemoryPressure() override { return 1024; } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { - installHostMethod(runtime, prototype, "System", - &JsiSkFontMgrFactory::System); + installMethod(runtime, prototype, "System", &JsiSkFontMgrFactory::System); } explicit JsiSkFontMgrFactory(std::shared_ptr context) diff --git a/packages/skia/cpp/api/JsiSkImageFactory.h b/packages/skia/cpp/api/JsiSkImageFactory.h index 4ad5d400a6..67198e55e8 100644 --- a/packages/skia/cpp/api/JsiSkImageFactory.h +++ b/packages/skia/cpp/api/JsiSkImageFactory.h @@ -2,9 +2,11 @@ #include #include +#include #include +#include "JsiSkConverters.h" #include "JsiSkData.h" #include "JsiSkImage.h" #include "JsiSkImageInfo.h" @@ -24,43 +26,35 @@ class JsiSkImageFactory : public JsiSkNativeObject { public: static constexpr const char *CLASS_NAME = "ImageFactory"; - JSI_HOST_FUNCTION(MakeNull) { - return makeJsiObject(runtime, - std::make_shared(getContext(), nullptr)); + std::shared_ptr MakeNull() { + return std::make_shared(getContext(), nullptr); } - JSI_HOST_FUNCTION(MakeImageFromEncoded) { - auto data = JsiSkData::fromValue(runtime, arguments[0]); + std::variant> + MakeImageFromEncoded(sk_sp data) { auto image = SkImages::DeferredFromEncodedData(data); if (image == nullptr) { - return jsi::Value::null(); + return nullptr; } - return makeJsiObject( - runtime, std::make_shared(getContext(), std::move(image))); + return std::make_shared(getContext(), std::move(image)); } - JSI_HOST_FUNCTION(MakeImageFromNativeBuffer) { - jsi::BigInt pointer = arguments[0].asBigInt(runtime); - const uintptr_t nativeBufferPointer = pointer.asUint64(runtime); - void *rawPointer = reinterpret_cast(nativeBufferPointer); + std::shared_ptr MakeImageFromNativeBuffer(void *rawPointer) { auto image = getContext()->makeImageFromNativeBuffer(rawPointer); if (image == nullptr) { throw std::runtime_error("Failed to convert NativeBuffer to SkImage!"); } - return makeJsiObject( - runtime, std::make_shared(getContext(), std::move(image))); + return std::make_shared(getContext(), std::move(image)); } - JSI_HOST_FUNCTION(MakeImage) { - auto imageInfo = JsiSkImageInfo::fromValue(runtime, arguments[0]); - auto pixelData = JsiSkData::fromValue(runtime, arguments[1]); - auto bytesPerRow = arguments[2].asNumber(); + std::variant> + MakeImage(std::shared_ptr imageInfo, sk_sp pixelData, + double bytesPerRow) { auto image = SkImages::RasterFromData(*imageInfo, pixelData, bytesPerRow); if (image == nullptr) { - return jsi::Value::null(); + return nullptr; } - return makeJsiObject( - runtime, std::make_shared(getContext(), std::move(image))); + return std::make_shared(getContext(), std::move(image)); } JSI_HOST_FUNCTION(MakeImageFromViewTag) { @@ -171,18 +165,17 @@ class JsiSkImageFactory : public JsiSkNativeObject { size_t getMemoryPressure() override { return 1024; } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { - installHostMethod(runtime, prototype, "MakeImageFromEncoded", - &JsiSkImageFactory::MakeImageFromEncoded); + installMethod(runtime, prototype, "MakeImageFromEncoded", + &JsiSkImageFactory::MakeImageFromEncoded); installHostMethod(runtime, prototype, "MakeImageFromViewTag", &JsiSkImageFactory::MakeImageFromViewTag); - installHostMethod(runtime, prototype, "MakeImageFromNativeBuffer", - &JsiSkImageFactory::MakeImageFromNativeBuffer); + installMethod(runtime, prototype, "MakeImageFromNativeBuffer", + &JsiSkImageFactory::MakeImageFromNativeBuffer); installHostMethod(runtime, prototype, "MakeImageFromNativeTextureUnstable", &JsiSkImageFactory::MakeImageFromNativeTextureUnstable); - installHostMethod(runtime, prototype, "MakeImage", - &JsiSkImageFactory::MakeImage); - installHostMethod(runtime, prototype, "MakeNull", - &JsiSkImageFactory::MakeNull); + installMethod(runtime, prototype, "MakeImage", + &JsiSkImageFactory::MakeImage); + installMethod(runtime, prototype, "MakeNull", &JsiSkImageFactory::MakeNull); installHostMethod(runtime, prototype, "MakeImageFromTexture", &JsiSkImageFactory::MakeImageFromTexture); installHostMethod(runtime, prototype, "MakeTextureFromImage", diff --git a/packages/skia/cpp/api/JsiSkImageFilterFactory.h b/packages/skia/cpp/api/JsiSkImageFilterFactory.h index 77119119d9..c63f600eeb 100644 --- a/packages/skia/cpp/api/JsiSkImageFilterFactory.h +++ b/packages/skia/cpp/api/JsiSkImageFilterFactory.h @@ -1,14 +1,24 @@ #pragma once #include +#include #include +#include +#include #include +#include "JsiSkColor.h" +#include "JsiSkColorFilter.h" +#include "JsiSkConverters.h" +#include "JsiSkImage.h" #include "JsiSkImageFilter.h" +#include "JsiSkMatrix.h" #include "JsiSkNativeObjects.h" #include "JsiSkPicture.h" +#include "JsiSkRect.h" #include "JsiSkRuntimeShaderBuilder.h" +#include "JsiSkShader.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdocumentation" @@ -22,681 +32,417 @@ namespace RNSkia { namespace jsi = facebook::jsi; -inline bool hasOptionalArgument(const jsi::Value *arguments, size_t count, - size_t index) { - return (index < count && !arguments[index].isNull() && - !arguments[index].isUndefined()); -} - class JsiSkImageFilterFactory : public JsiSkNativeObject { public: static constexpr const char *CLASS_NAME = "ImageFilterFactory"; - JSI_HOST_FUNCTION(MakeBlur) { - float sigmaX = arguments[0].asNumber(); - float sigmaY = arguments[1].asNumber(); - int tileMode = arguments[2].asNumber(); - sk_sp imageFilter = nullptr; - if (hasOptionalArgument(arguments, count, 3)) { - imageFilter = JsiSkImageFilter::fromValue(runtime, arguments[3]); - } - SkImageFilters::CropRect cropRect = {}; - if (hasOptionalArgument(arguments, count, 4)) { - cropRect = *JsiSkRect::fromValue(runtime, arguments[4]); - } - auto filter = std::make_shared( - getContext(), SkImageFilters::Blur(sigmaX, sigmaY, (SkTileMode)tileMode, - imageFilter, cropRect)); - return makeJsiObject(runtime, std::move(filter)); + std::shared_ptr + MakeBlur(float sigmaX, float sigmaY, int tileMode, + JsiOptional> input, + JsiOptional crop) { + return std::make_shared( + getContext(), + SkImageFilters::Blur(sigmaX, sigmaY, static_cast(tileMode), + orNull(std::move(input)), toCropRect(crop))); } - JSI_HOST_FUNCTION(MakeColorFilter) { - auto cf = JsiSkColorFilter::fromValue(runtime, arguments[0]); - sk_sp input = nullptr; - if (hasOptionalArgument(arguments, count, 1)) { - input = JsiSkImageFilter::fromValue(runtime, arguments[1]); - } - SkImageFilters::CropRect cropRect = {}; - if (hasOptionalArgument(arguments, count, 2)) { - cropRect = *JsiSkRect::fromValue(runtime, arguments[2]); - } - auto filter = std::make_shared( + std::shared_ptr + MakeColorFilter(sk_sp cf, + JsiOptional> input, + JsiOptional crop) { + return std::make_shared( getContext(), - SkImageFilters::ColorFilter(std::move(cf), std::move(input), cropRect)); - return makeJsiObject(runtime, std::move(filter)); + SkImageFilters::ColorFilter(std::move(cf), orNull(std::move(input)), + toCropRect(crop))); } - JSI_HOST_FUNCTION(MakeOffset) { - auto x = arguments[0].asNumber(); - auto y = arguments[1].asNumber(); - sk_sp input = nullptr; - if (hasOptionalArgument(arguments, count, 2)) { - input = JsiSkImageFilter::fromValue(runtime, arguments[2]); - } - SkImageFilters::CropRect cropRect = {}; - if (hasOptionalArgument(arguments, count, 3)) { - cropRect = *JsiSkRect::fromValue(runtime, arguments[3]); - } - auto filter = std::make_shared( - getContext(), SkImageFilters::Offset(x, y, std::move(input), cropRect)); - return makeJsiObject(runtime, std::move(filter)); - } - - JSI_HOST_FUNCTION(MakeDisplacementMap) { - auto fXChannelSelector = - static_cast(arguments[0].asNumber()); - auto fYChannelSelector = - static_cast(arguments[1].asNumber()); - auto scale = arguments[2].asNumber(); - auto in2 = JsiSkImageFilter::fromValue(runtime, arguments[3]); - sk_sp input = nullptr; - if (hasOptionalArgument(arguments, count, 4)) { - input = JsiSkImageFilter::fromValue(runtime, arguments[4]); - } - SkImageFilters::CropRect cropRect = {}; - if (hasOptionalArgument(arguments, count, 5)) { - cropRect = *JsiSkRect::fromValue(runtime, arguments[5]); - } - auto filter = std::make_shared( - getContext(), SkImageFilters::DisplacementMap( - fXChannelSelector, fYChannelSelector, scale, - std::move(in2), std::move(input), cropRect)); - return makeJsiObject(runtime, std::move(filter)); - } - - JSI_HOST_FUNCTION(MakeShader) { - auto shader = JsiSkShader::fromValue(runtime, arguments[0]); - SkImageFilters::Dither dither = SkImageFilters::Dither::kNo; - if (hasOptionalArgument(arguments, count, 1)) { - dither = arguments[1].asBool() ? SkImageFilters::Dither::kYes - : SkImageFilters::Dither::kNo; - } - SkImageFilters::CropRect cropRect = {}; - if (hasOptionalArgument(arguments, count, 2)) { - cropRect = *JsiSkRect::fromValue(runtime, arguments[2]); - } - auto filter = std::make_shared( - getContext(), - SkImageFilters::Shader(std::move(shader), dither, cropRect)); - return makeJsiObject(runtime, std::move(filter)); + std::shared_ptr + MakeOffset(double x, double y, JsiOptional> input, + JsiOptional crop) { + return std::make_shared( + getContext(), SkImageFilters::Offset(x, y, orNull(std::move(input)), + toCropRect(crop))); } - JSI_HOST_FUNCTION(MakeCompose) { - sk_sp outer = nullptr; - if (hasOptionalArgument(arguments, count, 0)) { - outer = JsiSkImageFilter::fromValue(runtime, arguments[0]); - } - sk_sp inner = nullptr; - if (hasOptionalArgument(arguments, count, 1)) { - inner = JsiSkImageFilter::fromValue(runtime, arguments[1]); - } - auto filter = std::make_shared( + std::shared_ptr + MakeDisplacementMap(double xChannelSelector, double yChannelSelector, + double scale, sk_sp in2, + JsiOptional> input, + JsiOptional crop) { + return std::make_shared( getContext(), - SkImageFilters::Compose(std::move(outer), std::move(inner))); - return makeJsiObject(runtime, std::move(filter)); + SkImageFilters::DisplacementMap( + static_cast(xChannelSelector), + static_cast(yChannelSelector), scale, + std::move(in2), orNull(std::move(input)), toCropRect(crop))); } - JSI_HOST_FUNCTION(MakeBlend) { - auto mode = static_cast(arguments[0].asNumber()); - sk_sp background = - JsiSkImageFilter::fromValue(runtime, arguments[1]); - sk_sp foreground = nullptr; - - if (hasOptionalArgument(arguments, count, 2)) { - foreground = JsiSkImageFilter::fromValue(runtime, arguments[2]); - } + std::shared_ptr MakeShader(sk_sp shader, + JsiOptional dither, + JsiOptional crop) { + auto ditherMode = dither.has_value() && *dither + ? SkImageFilters::Dither::kYes + : SkImageFilters::Dither::kNo; + return std::make_shared( + getContext(), + SkImageFilters::Shader(std::move(shader), ditherMode, toCropRect(crop))); + } - SkImageFilters::CropRect cropRect = {}; - if (hasOptionalArgument(arguments, count, 3)) { - cropRect = *JsiSkRect::fromValue(runtime, arguments[3]); - } + std::shared_ptr + MakeCompose(JsiOptional> outer, + JsiOptional> inner) { + return std::make_shared( + getContext(), SkImageFilters::Compose(orNull(std::move(outer)), + orNull(std::move(inner)))); + } - auto filter = std::make_shared( + std::shared_ptr + MakeBlend(double mode, sk_sp background, + JsiOptional> foreground, + JsiOptional crop) { + return std::make_shared( getContext(), - SkImageFilters::Blend(std::move(mode), std::move(background), - std::move(foreground), cropRect)); - return makeJsiObject(runtime, std::move(filter)); - } - - JSI_HOST_FUNCTION(MakeDropShadow) { - auto dx = arguments[0].asNumber(); - auto dy = arguments[1].asNumber(); - auto sigmaX = arguments[2].asNumber(); - auto sigmaY = arguments[3].asNumber(); - auto color = JsiSkColor::fromValue(runtime, arguments[4]); - sk_sp input = nullptr; - if (hasOptionalArgument(arguments, count, 5)) { - input = JsiSkImageFilter::fromValue(runtime, arguments[5]); - } - SkImageFilters::CropRect cropRect = {}; - if (hasOptionalArgument(arguments, count, 6)) { - cropRect = *JsiSkRect::fromValue(runtime, arguments[6]); - } - auto filter = std::make_shared( - getContext(), SkImageFilters::DropShadow(dx, dy, sigmaX, sigmaY, color, - std::move(input), cropRect)); - return makeJsiObject(runtime, std::move(filter)); - } - - JSI_HOST_FUNCTION(MakeDropShadowOnly) { - auto dx = arguments[0].asNumber(); - auto dy = arguments[1].asNumber(); - auto sigmaX = arguments[2].asNumber(); - auto sigmaY = arguments[3].asNumber(); - auto color = JsiSkColor::fromValue(runtime, arguments[4]); - sk_sp input = nullptr; - if (hasOptionalArgument(arguments, count, 5)) { - input = JsiSkImageFilter::fromValue(runtime, arguments[5]); - } - SkImageFilters::CropRect cropRect = {}; - if (hasOptionalArgument(arguments, count, 6)) { - cropRect = *JsiSkRect::fromValue(runtime, arguments[6]); - } - auto filter = std::make_shared( - getContext(), - SkImageFilters::DropShadowOnly(dx, dy, sigmaX, sigmaY, color, - std::move(input), cropRect)); - return makeJsiObject(runtime, std::move(filter)); + SkImageFilters::Blend(static_cast(mode), + std::move(background), + orNull(std::move(foreground)), toCropRect(crop))); } - JSI_HOST_FUNCTION(MakeErode) { - auto rx = arguments[0].asNumber(); - auto ry = arguments[1].asNumber(); - sk_sp input = nullptr; - if (hasOptionalArgument(arguments, count, 2)) { - input = JsiSkImageFilter::fromValue(runtime, arguments[2]); - } - SkImageFilters::CropRect cropRect = {}; - if (hasOptionalArgument(arguments, count, 3)) { - cropRect = *JsiSkRect::fromValue(runtime, arguments[3]); - } - auto filter = std::make_shared( + std::shared_ptr + MakeDropShadow(double dx, double dy, double sigmaX, double sigmaY, + JsiColor color, JsiOptional> input, + JsiOptional crop) { + return std::make_shared( getContext(), - SkImageFilters::Erode(rx, ry, std::move(input), cropRect)); - return makeJsiObject(runtime, std::move(filter)); + SkImageFilters::DropShadow(dx, dy, sigmaX, sigmaY, color, + orNull(std::move(input)), toCropRect(crop))); } - JSI_HOST_FUNCTION(MakeDilate) { - auto rx = arguments[0].asNumber(); - auto ry = arguments[1].asNumber(); - sk_sp input = nullptr; - if (hasOptionalArgument(arguments, count, 2)) { - input = JsiSkImageFilter::fromValue(runtime, arguments[2]); - } - SkImageFilters::CropRect cropRect = {}; - if (hasOptionalArgument(arguments, count, 3)) { - cropRect = *JsiSkRect::fromValue(runtime, arguments[3]); - } - auto filter = std::make_shared( - getContext(), - SkImageFilters::Dilate(rx, ry, std::move(input), cropRect)); - return makeJsiObject(runtime, std::move(filter)); + std::shared_ptr + MakeDropShadowOnly(double dx, double dy, double sigmaX, double sigmaY, + JsiColor color, JsiOptional> input, + JsiOptional crop) { + return std::make_shared( + getContext(), SkImageFilters::DropShadowOnly( + dx, dy, sigmaX, sigmaY, color, + orNull(std::move(input)), toCropRect(crop))); } - JSI_HOST_FUNCTION(MakeRuntimeShader) { - auto rtb = JsiSkRuntimeShaderBuilder::fromValue(runtime, arguments[0]); + std::shared_ptr + MakeErode(double rx, double ry, JsiOptional> input, + JsiOptional crop) { + return std::make_shared( + getContext(), SkImageFilters::Erode(rx, ry, orNull(std::move(input)), + toCropRect(crop))); + } - std::string childNameStr = ""; - const char *childName = childNameStr.c_str(); - if (hasOptionalArgument(arguments, count, 1)) { - childNameStr = arguments[1].asString(runtime).utf8(runtime); - childName = childNameStr.c_str(); - } + std::shared_ptr + MakeDilate(double rx, double ry, JsiOptional> input, + JsiOptional crop) { + return std::make_shared( + getContext(), SkImageFilters::Dilate(rx, ry, orNull(std::move(input)), + toCropRect(crop))); + } - sk_sp input = nullptr; - if (hasOptionalArgument(arguments, count, 2)) { - input = JsiSkImageFilter::fromValue(runtime, arguments[2]); - } - auto filter = std::make_shared( + std::shared_ptr + MakeRuntimeShader(std::shared_ptr rtb, + JsiOptional childName, + JsiOptional> input) { + std::string childNameStr = childName.value_or(""); + return std::make_shared( getContext(), - SkImageFilters::RuntimeShader(*rtb, childName, std::move(input))); - return makeJsiObject(runtime, std::move(filter)); - } - - JSI_HOST_FUNCTION(MakeArithmetic) { - float k1 = arguments[0].asNumber(); - float k2 = arguments[1].asNumber(); - float k3 = arguments[2].asNumber(); - float k4 = arguments[3].asNumber(); - bool enforcePMColor = arguments[4].asBool(); - sk_sp background = nullptr; - if (hasOptionalArgument(arguments, count, 5)) { - background = JsiSkImageFilter::fromValue(runtime, arguments[5]); - } - sk_sp foreground = nullptr; - if (hasOptionalArgument(arguments, count, 6)) { - foreground = JsiSkImageFilter::fromValue(runtime, arguments[6]); - } - SkImageFilters::CropRect cropRect = {}; - if (hasOptionalArgument(arguments, count, 7)) { - cropRect = *JsiSkRect::fromValue(runtime, arguments[7]); - } - auto filter = std::make_shared( - getContext(), SkImageFilters::Arithmetic( - k1, k2, k3, k4, enforcePMColor, std::move(background), - std::move(foreground), cropRect)); - return makeJsiObject(runtime, std::move(filter)); - } - - JSI_HOST_FUNCTION(MakeCrop) { - SkRect rect = *JsiSkRect::fromValue(runtime, arguments[0]); - SkTileMode tileMode = SkTileMode::kDecal; - if (hasOptionalArgument(arguments, count, 1)) { - tileMode = (SkTileMode)arguments[1].asNumber(); - } - sk_sp imageFilter = nullptr; - if (hasOptionalArgument(arguments, count, 2)) { - imageFilter = JsiSkImageFilter::fromValue(runtime, arguments[2]); - } - auto filter = std::make_shared( + SkImageFilters::RuntimeShader(*rtb, childNameStr.c_str(), + orNull(std::move(input)))); + } + + std::shared_ptr + MakeArithmetic(float k1, float k2, float k3, float k4, bool enforcePMColor, + JsiOptional> background, + JsiOptional> foreground, + JsiOptional crop) { + return std::make_shared( getContext(), - SkImageFilters::Crop(rect, tileMode, std::move(imageFilter))); - return makeJsiObject(runtime, std::move(filter)); + SkImageFilters::Arithmetic(k1, k2, k3, k4, enforcePMColor, + orNull(std::move(background)), + orNull(std::move(foreground)), + toCropRect(crop))); } - JSI_HOST_FUNCTION(MakeEmpty) { - auto filter = std::make_shared(getContext(), - SkImageFilters::Empty()); - return makeJsiObject(runtime, std::move(filter)); + std::shared_ptr + MakeCrop(SkRect rect, JsiOptional tileMode, + JsiOptional> imageFilter) { + auto mode = tileMode.has_value() ? static_cast(*tileMode) + : SkTileMode::kDecal; + return std::make_shared( + getContext(), + SkImageFilters::Crop(rect, mode, orNull(std::move(imageFilter)))); } - inline SkPoint3 SkPoint3FromValue(jsi::Runtime &runtime, - const jsi::Value &obj) { - const auto &object = obj.asObject(runtime); - auto x = object.getProperty(runtime, "x").asNumber(); - auto y = object.getProperty(runtime, "y").asNumber(); - auto z = object.getProperty(runtime, "z").asNumber(); - return SkPoint3::Make(x, y, z); + std::shared_ptr MakeEmpty() { + return std::make_shared(getContext(), + SkImageFilters::Empty()); } - JSI_HOST_FUNCTION(MakeDistantLitDiffuse) { - SkPoint3 direction = SkPoint3FromValue(runtime, arguments[0]); - SkColor lightColor = JsiSkColor::fromValue(runtime, arguments[1]); - float surfaceScale = arguments[2].asNumber(); - float kd = arguments[3].asNumber(); - sk_sp input = nullptr; - if (hasOptionalArgument(arguments, count, 4)) { - input = JsiSkImageFilter::fromValue(runtime, arguments[4]); - } - SkImageFilters::CropRect cropRect = {}; - if (hasOptionalArgument(arguments, count, 5)) { - cropRect = *JsiSkRect::fromValue(runtime, arguments[5]); - } - auto filter = std::make_shared( - getContext(), - SkImageFilters::DistantLitDiffuse(direction, lightColor, surfaceScale, - kd, std::move(input), cropRect)); - return makeJsiObject(runtime, std::move(filter)); - } - - JSI_HOST_FUNCTION(MakePointLitDiffuse) { - SkPoint3 location = SkPoint3FromValue(runtime, arguments[0]); - SkColor lightColor = JsiSkColor::fromValue(runtime, arguments[1]); - float surfaceScale = arguments[2].asNumber(); - float kd = arguments[3].asNumber(); - sk_sp input = nullptr; - if (hasOptionalArgument(arguments, count, 4)) { - input = JsiSkImageFilter::fromValue(runtime, arguments[4]); - } - SkImageFilters::CropRect cropRect = {}; - if (hasOptionalArgument(arguments, count, 5)) { - cropRect = *JsiSkRect::fromValue(runtime, arguments[5]); - } - auto filter = std::make_shared( - getContext(), - SkImageFilters::PointLitDiffuse(location, lightColor, surfaceScale, kd, - std::move(input), cropRect)); - return makeJsiObject(runtime, std::move(filter)); - } - - JSI_HOST_FUNCTION(MakeSpotLitDiffuse) { - SkPoint3 location = SkPoint3FromValue(runtime, arguments[0]); - SkPoint3 target = SkPoint3FromValue(runtime, arguments[1]); - float falloffExponent = arguments[2].asNumber(); - float cutoffAngle = arguments[3].asNumber(); - SkColor lightColor = JsiSkColor::fromValue(runtime, arguments[4]); - float surfaceScale = arguments[5].asNumber(); - float kd = arguments[6].asNumber(); - sk_sp input = nullptr; - if (hasOptionalArgument(arguments, count, 7)) { - input = JsiSkImageFilter::fromValue(runtime, arguments[7]); - } - SkImageFilters::CropRect cropRect = {}; - if (hasOptionalArgument(arguments, count, 8)) { - cropRect = *JsiSkRect::fromValue(runtime, arguments[8]); - } - auto filter = std::make_shared( - getContext(), - SkImageFilters::SpotLitDiffuse(location, target, falloffExponent, - cutoffAngle, lightColor, surfaceScale, - kd, std::move(input), cropRect)); - return makeJsiObject(runtime, std::move(filter)); - } - - JSI_HOST_FUNCTION(MakeDistantLitSpecular) { - SkPoint3 direction = SkPoint3FromValue(runtime, arguments[0]); - SkColor lightColor = JsiSkColor::fromValue(runtime, arguments[1]); - float surfaceScale = arguments[2].asNumber(); - float ks = arguments[3].asNumber(); - float shininess = arguments[4].asNumber(); - sk_sp input = nullptr; - if (hasOptionalArgument(arguments, count, 5)) { - input = JsiSkImageFilter::fromValue(runtime, arguments[5]); - } - SkImageFilters::CropRect cropRect = {}; - if (hasOptionalArgument(arguments, count, 6)) { - cropRect = *JsiSkRect::fromValue(runtime, arguments[6]); - } - auto filter = std::make_shared( + std::shared_ptr + MakeDistantLitDiffuse(SkPoint3 direction, JsiColor lightColor, + float surfaceScale, float kd, + JsiOptional> input, + JsiOptional crop) { + return std::make_shared( + getContext(), SkImageFilters::DistantLitDiffuse( + direction, lightColor, surfaceScale, kd, + orNull(std::move(input)), toCropRect(crop))); + } + + std::shared_ptr + MakePointLitDiffuse(SkPoint3 location, JsiColor lightColor, + float surfaceScale, float kd, + JsiOptional> input, + JsiOptional crop) { + return std::make_shared( + getContext(), SkImageFilters::PointLitDiffuse( + location, lightColor, surfaceScale, kd, + orNull(std::move(input)), toCropRect(crop))); + } + + std::shared_ptr + MakeSpotLitDiffuse(SkPoint3 location, SkPoint3 target, float falloffExponent, + float cutoffAngle, JsiColor lightColor, float surfaceScale, + float kd, JsiOptional> input, + JsiOptional crop) { + return std::make_shared( + getContext(), SkImageFilters::SpotLitDiffuse( + location, target, falloffExponent, cutoffAngle, + lightColor, surfaceScale, kd, + orNull(std::move(input)), toCropRect(crop))); + } + + std::shared_ptr + MakeDistantLitSpecular(SkPoint3 direction, JsiColor lightColor, + float surfaceScale, float ks, float shininess, + JsiOptional> input, + JsiOptional crop) { + return std::make_shared( getContext(), SkImageFilters::DistantLitSpecular( direction, lightColor, surfaceScale, ks, shininess, - std::move(input), cropRect)); - return makeJsiObject(runtime, std::move(filter)); - } - - JSI_HOST_FUNCTION(MakePointLitSpecular) { - SkPoint3 location = SkPoint3FromValue(runtime, arguments[0]); - SkColor lightColor = JsiSkColor::fromValue(runtime, arguments[1]); - float surfaceScale = arguments[2].asNumber(); - float ks = arguments[3].asNumber(); - float shininess = arguments[4].asNumber(); - sk_sp input = nullptr; - if (hasOptionalArgument(arguments, count, 5)) { - input = JsiSkImageFilter::fromValue(runtime, arguments[5]); - } - SkImageFilters::CropRect cropRect = {}; - if (hasOptionalArgument(arguments, count, 6)) { - cropRect = *JsiSkRect::fromValue(runtime, arguments[6]); - } - auto filter = std::make_shared( + orNull(std::move(input)), toCropRect(crop))); + } + + std::shared_ptr + MakePointLitSpecular(SkPoint3 location, JsiColor lightColor, + float surfaceScale, float ks, float shininess, + JsiOptional> input, + JsiOptional crop) { + return std::make_shared( getContext(), SkImageFilters::PointLitSpecular( location, lightColor, surfaceScale, ks, shininess, - std::move(input), cropRect)); - return makeJsiObject(runtime, std::move(filter)); - } - - JSI_HOST_FUNCTION(MakeSpotLitSpecular) { - SkPoint3 location = SkPoint3FromValue(runtime, arguments[0]); - SkPoint3 target = SkPoint3FromValue(runtime, arguments[1]); - float falloffExponent = arguments[2].asNumber(); - float cutoffAngle = arguments[3].asNumber(); - SkColor lightColor = JsiSkColor::fromValue(runtime, arguments[4]); - float surfaceScale = arguments[5].asNumber(); - float ks = arguments[6].asNumber(); - float shininess = arguments[7].asNumber(); - sk_sp input = nullptr; - if (hasOptionalArgument(arguments, count, 8)) { - input = JsiSkImageFilter::fromValue(runtime, arguments[8]); - } - SkImageFilters::CropRect cropRect = {}; - if (hasOptionalArgument(arguments, count, 9)) { - cropRect = *JsiSkRect::fromValue(runtime, arguments[9]); - } - auto filter = std::make_shared( + orNull(std::move(input)), toCropRect(crop))); + } + + std::shared_ptr + MakeSpotLitSpecular(SkPoint3 location, SkPoint3 target, + float falloffExponent, float cutoffAngle, + JsiColor lightColor, float surfaceScale, float ks, + float shininess, JsiOptional> input, + JsiOptional crop) { + return std::make_shared( getContext(), - SkImageFilters::SpotLitSpecular( - location, target, falloffExponent, cutoffAngle, lightColor, - surfaceScale, ks, shininess, std::move(input), cropRect)); - return makeJsiObject(runtime, std::move(filter)); - } - - JSI_HOST_FUNCTION(MakeImage) { - sk_sp image = JsiSkImage::fromValue(runtime, arguments[0]); - SkRect srcRect; - if (hasOptionalArgument(arguments, count, 1)) { - srcRect = *JsiSkRect::fromValue(runtime, arguments[1]); - } else { - srcRect = SkRect::Make(image->bounds()); - } - SkRect dstRect; - if (hasOptionalArgument(arguments, count, 2)) { - dstRect = *JsiSkRect::fromValue(runtime, arguments[2]); - } else { - dstRect = srcRect; - } - SkFilterMode filterMode = SkFilterMode::kNearest; - if (hasOptionalArgument(arguments, count, 3)) { - filterMode = (SkFilterMode)arguments[3].asNumber(); - } - SkMipmapMode mipmap = SkMipmapMode::kNone; - if (hasOptionalArgument(arguments, count, 4)) { - mipmap = (SkMipmapMode)arguments[4].asNumber(); - } - auto filter = std::make_shared( + SkImageFilters::SpotLitSpecular(location, target, falloffExponent, + cutoffAngle, lightColor, surfaceScale, + ks, shininess, orNull(std::move(input)), + toCropRect(crop))); + } + + std::shared_ptr + MakeImage(sk_sp image, JsiOptional src, + JsiOptional dst, JsiOptional filterMode, + JsiOptional mipmap) { + SkRect srcRect = src.has_value() ? *src : SkRect::Make(image->bounds()); + SkRect dstRect = dst.has_value() ? *dst : srcRect; + auto sampling = SkSamplingOptions(toFilterMode(filterMode), + toMipmapMode(mipmap)); + return std::make_shared( getContext(), - SkImageFilters::Image(std::move(image), srcRect, dstRect, - SkSamplingOptions(filterMode, mipmap))); - return makeJsiObject(runtime, std::move(filter)); - } - - JSI_HOST_FUNCTION(MakeMagnifier) { - SkRect lensBounds = *JsiSkRect::fromValue(runtime, arguments[0]); - float zoomAmount = arguments[1].asNumber(); - float inset = arguments[2].asNumber(); - SkFilterMode filterMode = SkFilterMode::kNearest; - if (hasOptionalArgument(arguments, count, 3)) { - filterMode = (SkFilterMode)arguments[3].asNumber(); - } - SkMipmapMode mipmap = SkMipmapMode::kNone; - if (hasOptionalArgument(arguments, count, 4)) { - mipmap = (SkMipmapMode)arguments[4].asNumber(); - } - sk_sp input = nullptr; - if (hasOptionalArgument(arguments, count, 5)) { - input = JsiSkImageFilter::fromValue(runtime, arguments[5]); - } - SkImageFilters::CropRect cropRect = {}; - if (hasOptionalArgument(arguments, count, 6)) { - cropRect = *JsiSkRect::fromValue(runtime, arguments[6]); - } - auto filter = std::make_shared( + SkImageFilters::Image(std::move(image), srcRect, dstRect, sampling)); + } + + std::shared_ptr + MakeMagnifier(SkRect lensBounds, float zoomAmount, float inset, + JsiOptional filterMode, JsiOptional mipmap, + JsiOptional> input, + JsiOptional crop) { + auto sampling = SkSamplingOptions(toFilterMode(filterMode), + toMipmapMode(mipmap)); + return std::make_shared( getContext(), - SkImageFilters::Magnifier(lensBounds, zoomAmount, inset, - SkSamplingOptions(filterMode, mipmap), input, - cropRect)); - return makeJsiObject(runtime, std::move(filter)); - } - - JSI_HOST_FUNCTION(MakeMatrixConvolution) { - SkISize kernelSize = - SkISize::Make(arguments[0].asNumber(), arguments[1].asNumber()); - std::vector kernel; - auto kernelArray = arguments[2].asObject(runtime).asArray(runtime); - auto size = kernelArray.size(runtime); - for (size_t i = 0; i < size; i++) { - kernel.push_back(kernelArray.getValueAtIndex(runtime, i).asNumber()); - } - auto gain = arguments[3].asNumber(); - auto bias = arguments[4].asNumber(); - SkIPoint kernelOffset = - SkIPoint::Make(arguments[5].asNumber(), arguments[6].asNumber()); - auto tileMode = static_cast(arguments[7].asNumber()); - bool convolveAlpha = arguments[8].asBool(); - sk_sp input = nullptr; - if (hasOptionalArgument(arguments, count, 9)) { - input = JsiSkImageFilter::fromValue(runtime, arguments[9]); - } - SkImageFilters::CropRect cropRect = {}; - if (hasOptionalArgument(arguments, count, 10)) { - cropRect = *JsiSkRect::fromValue(runtime, arguments[10]); - } - auto filter = std::make_shared( - getContext(), SkImageFilters::MatrixConvolution( - kernelSize, kernel.data(), gain, bias, kernelOffset, - tileMode, convolveAlpha, std::move(input), cropRect)); - return makeJsiObject(runtime, std::move(filter)); - } - - JSI_HOST_FUNCTION(MakeMatrixTransform) { - SkMatrix matrix = *JsiSkMatrix::fromValue(runtime, arguments[0]); - SkFilterMode filterMode = SkFilterMode::kNearest; - if (hasOptionalArgument(arguments, count, 1)) { - filterMode = (SkFilterMode)arguments[1].asNumber(); - } - SkMipmapMode mipmap = SkMipmapMode::kNone; - if (hasOptionalArgument(arguments, count, 2)) { - mipmap = (SkMipmapMode)arguments[2].asNumber(); - } - sk_sp input = nullptr; - if (hasOptionalArgument(arguments, count, 3)) { - input = JsiSkImageFilter::fromValue(runtime, arguments[3]); - } - auto filter = std::make_shared( + SkImageFilters::Magnifier(lensBounds, zoomAmount, inset, sampling, + orNull(std::move(input)), toCropRect(crop))); + } + + std::shared_ptr MakeMatrixConvolution( + int kernelSizeX, int kernelSizeY, std::vector kernel, double gain, + double bias, int kernelOffsetX, int kernelOffsetY, double tileMode, + bool convolveAlpha, JsiOptional> input, + JsiOptional crop) { + auto kernelSize = SkISize::Make(kernelSizeX, kernelSizeY); + auto kernelOffset = SkIPoint::Make(kernelOffsetX, kernelOffsetY); + return std::make_shared( getContext(), - SkImageFilters::MatrixTransform( - matrix, SkSamplingOptions(filterMode, mipmap), std::move(input))); - return makeJsiObject(runtime, std::move(filter)); + SkImageFilters::MatrixConvolution( + kernelSize, kernel.data(), gain, bias, kernelOffset, + static_cast(tileMode), convolveAlpha, + orNull(std::move(input)), toCropRect(crop))); } - JSI_HOST_FUNCTION(MakeMerge) { + std::shared_ptr + MakeMatrixTransform(SkMatrix matrix, JsiOptional filterMode, + JsiOptional mipmap, + JsiOptional> input) { + auto sampling = SkSamplingOptions(toFilterMode(filterMode), + toMipmapMode(mipmap)); + return std::make_shared( + getContext(), SkImageFilters::MatrixTransform( + matrix, sampling, orNull(std::move(input)))); + } + + std::shared_ptr + MakeMerge(std::vector>> jsiFilters, + JsiOptional crop) { std::vector> filters; - auto filtersArray = arguments[0].asObject(runtime).asArray(runtime); - auto filtersCount = filtersArray.size(runtime); - for (size_t i = 0; i < filtersCount; ++i) { - auto element = filtersArray.getValueAtIndex(runtime, i); - if (element.isNull()) { - filters.push_back(nullptr); - } else { - filters.push_back(JsiSkImageFilter::fromValue(runtime, element)); - } + filters.reserve(jsiFilters.size()); + for (auto &filter : jsiFilters) { + filters.push_back(orNull(std::move(filter))); } - SkImageFilters::CropRect cropRect = {}; - if (hasOptionalArgument(arguments, count, 1)) { - cropRect = *JsiSkRect::fromValue(runtime, arguments[1]); - } - auto filter = std::make_shared( + return std::make_shared( getContext(), - SkImageFilters::Merge(filters.data(), filtersCount, cropRect)); - return makeJsiObject(runtime, std::move(filter)); + SkImageFilters::Merge(filters.data(), filters.size(), toCropRect(crop))); } - JSI_HOST_FUNCTION(MakePicture) { - sk_sp picture = JsiSkPicture::fromValue(runtime, arguments[0]); - SkRect targetRect; - if (hasOptionalArgument(arguments, count, 1)) { - targetRect = *JsiSkRect::fromValue(runtime, arguments[1]); - } else { - targetRect = picture ? picture->cullRect() : SkRect::MakeEmpty(); - } - auto filter = std::make_shared( + std::shared_ptr MakePicture(sk_sp picture, + JsiOptional target) { + SkRect targetRect = target.has_value() + ? *target + : (picture ? picture->cullRect() + : SkRect::MakeEmpty()); + return std::make_shared( getContext(), SkImageFilters::Picture(std::move(picture), targetRect)); - return makeJsiObject(runtime, std::move(filter)); - } - - JSI_HOST_FUNCTION(MakeRuntimeShaderWithChildren) { - auto rtb = JsiSkRuntimeShaderBuilder::fromValue(runtime, arguments[0]); - float maxSampleRadius = arguments[1].asNumber(); - std::vector childNames; - auto childNamesJS = arguments[2].asObject(runtime).asArray(runtime); - size_t length = childNamesJS.size(runtime); - for (size_t i = 0; i < length; ++i) { - auto element = childNamesJS.getValueAtIndex(runtime, i); - childNames.push_back(element.asString(runtime).utf8(runtime).c_str()); + } + + std::variant> + MakeRuntimeShaderWithChildren( + std::shared_ptr rtb, float maxSampleRadius, + std::vector childNames, + std::vector>> jsiInputs) { + if (jsiInputs.size() != childNames.size()) { + return nullptr; } std::vector childNamesStringView; childNamesStringView.reserve(childNames.size()); for (const auto &name : childNames) { childNamesStringView.push_back(std::string_view(name)); } - std::vector> inputs; - auto inputsJS = arguments[3].asObject(runtime).asArray(runtime); - if (inputsJS.size(runtime) != length) { - return jsi::Value::null(); - } - for (size_t i = 0; i < length; ++i) { - auto element = inputsJS.getValueAtIndex(runtime, i); - if (element.isNull()) { - inputs.push_back(nullptr); - } else { - inputs.push_back(JsiSkImageFilter::fromValue(runtime, element)); - } - } - auto filter = std::make_shared( - getContext(), SkImageFilters::RuntimeShader(*rtb, maxSampleRadius, - childNamesStringView.data(), - inputs.data(), length)); - return makeJsiObject(runtime, std::move(filter)); - } - - JSI_HOST_FUNCTION(MakeTile) { - SkRect src = *JsiSkRect::fromValue(runtime, arguments[0]); - SkRect dst = *JsiSkRect::fromValue(runtime, arguments[1]); - sk_sp input = nullptr; - if (hasOptionalArgument(arguments, count, 2)) { - input = JsiSkImageFilter::fromValue(runtime, arguments[2]); - } - auto filter = std::make_shared( - getContext(), SkImageFilters::Tile(src, dst, std::move(input))); - return makeJsiObject(runtime, std::move(filter)); + inputs.reserve(jsiInputs.size()); + for (auto &input : jsiInputs) { + inputs.push_back(orNull(std::move(input))); + } + return std::make_shared( + getContext(), SkImageFilters::RuntimeShader( + *rtb, maxSampleRadius, childNamesStringView.data(), + inputs.data(), inputs.size())); + } + + std::shared_ptr + MakeTile(SkRect src, SkRect dst, JsiOptional> input) { + return std::make_shared( + getContext(), SkImageFilters::Tile(src, dst, orNull(std::move(input)))); } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { - installHostMethod(runtime, prototype, "MakeBlur", - &JsiSkImageFilterFactory::MakeBlur); - installHostMethod(runtime, prototype, "MakeOffset", - &JsiSkImageFilterFactory::MakeOffset); - installHostMethod(runtime, prototype, "MakeColorFilter", - &JsiSkImageFilterFactory::MakeColorFilter); - installHostMethod(runtime, prototype, "MakeShader", - &JsiSkImageFilterFactory::MakeShader); - installHostMethod(runtime, prototype, "MakeDisplacementMap", - &JsiSkImageFilterFactory::MakeDisplacementMap); - installHostMethod(runtime, prototype, "MakeCompose", - &JsiSkImageFilterFactory::MakeCompose); - installHostMethod(runtime, prototype, "MakeErode", - &JsiSkImageFilterFactory::MakeErode); - installHostMethod(runtime, prototype, "MakeDilate", - &JsiSkImageFilterFactory::MakeDilate); - installHostMethod(runtime, prototype, "MakeBlend", - &JsiSkImageFilterFactory::MakeBlend); - installHostMethod(runtime, prototype, "MakeDropShadow", - &JsiSkImageFilterFactory::MakeDropShadow); - installHostMethod(runtime, prototype, "MakeDropShadowOnly", - &JsiSkImageFilterFactory::MakeDropShadowOnly); - installHostMethod(runtime, prototype, "MakeRuntimeShader", - &JsiSkImageFilterFactory::MakeRuntimeShader); - installHostMethod(runtime, prototype, "MakeArithmetic", - &JsiSkImageFilterFactory::MakeArithmetic); - installHostMethod(runtime, prototype, "MakeCrop", - &JsiSkImageFilterFactory::MakeCrop); - installHostMethod(runtime, prototype, "MakeEmpty", - &JsiSkImageFilterFactory::MakeEmpty); - installHostMethod(runtime, prototype, "MakeImage", - &JsiSkImageFilterFactory::MakeImage); - installHostMethod(runtime, prototype, "MakeMagnifier", - &JsiSkImageFilterFactory::MakeMagnifier); - installHostMethod(runtime, prototype, "MakeMatrixConvolution", - &JsiSkImageFilterFactory::MakeMatrixConvolution); - installHostMethod(runtime, prototype, "MakeMatrixTransform", - &JsiSkImageFilterFactory::MakeMatrixTransform); - installHostMethod(runtime, prototype, "MakeMerge", - &JsiSkImageFilterFactory::MakeMerge); - installHostMethod(runtime, prototype, "MakePicture", - &JsiSkImageFilterFactory::MakePicture); - installHostMethod(runtime, prototype, "MakeRuntimeShaderWithChildren", - &JsiSkImageFilterFactory::MakeRuntimeShaderWithChildren); - installHostMethod(runtime, prototype, "MakeTile", - &JsiSkImageFilterFactory::MakeTile); - installHostMethod(runtime, prototype, "MakeDistantLitDiffuse", - &JsiSkImageFilterFactory::MakeDistantLitDiffuse); - installHostMethod(runtime, prototype, "MakePointLitDiffuse", - &JsiSkImageFilterFactory::MakePointLitDiffuse); - installHostMethod(runtime, prototype, "MakeSpotLitDiffuse", - &JsiSkImageFilterFactory::MakeSpotLitDiffuse); - installHostMethod(runtime, prototype, "MakeDistantLitSpecular", - &JsiSkImageFilterFactory::MakeDistantLitSpecular); - installHostMethod(runtime, prototype, "MakePointLitSpecular", - &JsiSkImageFilterFactory::MakePointLitSpecular); - installHostMethod(runtime, prototype, "MakeSpotLitSpecular", - &JsiSkImageFilterFactory::MakeSpotLitSpecular); + installMethod(runtime, prototype, "MakeBlur", + &JsiSkImageFilterFactory::MakeBlur); + installMethod(runtime, prototype, "MakeOffset", + &JsiSkImageFilterFactory::MakeOffset); + installMethod(runtime, prototype, "MakeColorFilter", + &JsiSkImageFilterFactory::MakeColorFilter); + installMethod(runtime, prototype, "MakeShader", + &JsiSkImageFilterFactory::MakeShader); + installMethod(runtime, prototype, "MakeDisplacementMap", + &JsiSkImageFilterFactory::MakeDisplacementMap); + installMethod(runtime, prototype, "MakeCompose", + &JsiSkImageFilterFactory::MakeCompose); + installMethod(runtime, prototype, "MakeErode", + &JsiSkImageFilterFactory::MakeErode); + installMethod(runtime, prototype, "MakeDilate", + &JsiSkImageFilterFactory::MakeDilate); + installMethod(runtime, prototype, "MakeBlend", + &JsiSkImageFilterFactory::MakeBlend); + installMethod(runtime, prototype, "MakeDropShadow", + &JsiSkImageFilterFactory::MakeDropShadow); + installMethod(runtime, prototype, "MakeDropShadowOnly", + &JsiSkImageFilterFactory::MakeDropShadowOnly); + installMethod(runtime, prototype, "MakeRuntimeShader", + &JsiSkImageFilterFactory::MakeRuntimeShader); + installMethod(runtime, prototype, "MakeArithmetic", + &JsiSkImageFilterFactory::MakeArithmetic); + installMethod(runtime, prototype, "MakeCrop", + &JsiSkImageFilterFactory::MakeCrop); + installMethod(runtime, prototype, "MakeEmpty", + &JsiSkImageFilterFactory::MakeEmpty); + installMethod(runtime, prototype, "MakeImage", + &JsiSkImageFilterFactory::MakeImage); + installMethod(runtime, prototype, "MakeMagnifier", + &JsiSkImageFilterFactory::MakeMagnifier); + installMethod(runtime, prototype, "MakeMatrixConvolution", + &JsiSkImageFilterFactory::MakeMatrixConvolution); + installMethod(runtime, prototype, "MakeMatrixTransform", + &JsiSkImageFilterFactory::MakeMatrixTransform); + installMethod(runtime, prototype, "MakeMerge", + &JsiSkImageFilterFactory::MakeMerge); + installMethod(runtime, prototype, "MakePicture", + &JsiSkImageFilterFactory::MakePicture); + installMethod(runtime, prototype, "MakeRuntimeShaderWithChildren", + &JsiSkImageFilterFactory::MakeRuntimeShaderWithChildren); + installMethod(runtime, prototype, "MakeTile", + &JsiSkImageFilterFactory::MakeTile); + installMethod(runtime, prototype, "MakeDistantLitDiffuse", + &JsiSkImageFilterFactory::MakeDistantLitDiffuse); + installMethod(runtime, prototype, "MakePointLitDiffuse", + &JsiSkImageFilterFactory::MakePointLitDiffuse); + installMethod(runtime, prototype, "MakeSpotLitDiffuse", + &JsiSkImageFilterFactory::MakeSpotLitDiffuse); + installMethod(runtime, prototype, "MakeDistantLitSpecular", + &JsiSkImageFilterFactory::MakeDistantLitSpecular); + installMethod(runtime, prototype, "MakePointLitSpecular", + &JsiSkImageFilterFactory::MakePointLitSpecular); + installMethod(runtime, prototype, "MakeSpotLitSpecular", + &JsiSkImageFilterFactory::MakeSpotLitSpecular); } size_t getMemoryPressure() override { return 2048; } explicit JsiSkImageFilterFactory(std::shared_ptr context) : JsiSkNativeObject(std::move(context)) {} + +private: + static sk_sp orNull(JsiOptional> filter) { + return filter.has_value() ? std::move(*filter) : nullptr; + } + + static SkImageFilters::CropRect toCropRect(const JsiOptional &rect) { + return rect.has_value() ? SkImageFilters::CropRect(*rect) + : SkImageFilters::CropRect{}; + } + + static SkFilterMode toFilterMode(const JsiOptional &mode) { + return mode.has_value() ? static_cast(*mode) + : SkFilterMode::kNearest; + } + + static SkMipmapMode toMipmapMode(const JsiOptional &mode) { + return mode.has_value() ? static_cast(*mode) + : SkMipmapMode::kNone; + } }; } // namespace RNSkia diff --git a/packages/skia/cpp/api/JsiSkMaskFilterFactory.h b/packages/skia/cpp/api/JsiSkMaskFilterFactory.h index 6fa1be0fa8..ee352b0018 100644 --- a/packages/skia/cpp/api/JsiSkMaskFilterFactory.h +++ b/packages/skia/cpp/api/JsiSkMaskFilterFactory.h @@ -6,6 +6,7 @@ #include #include "JsiSkColorFilter.h" +#include "JsiSkConverters.h" #include "JsiSkMaskFilter.h" #include "JsiSkNativeObjects.h" @@ -25,22 +26,18 @@ class JsiSkMaskFilterFactory public: static constexpr const char *CLASS_NAME = "MaskFilterFactory"; - JSI_HOST_FUNCTION(MakeBlur) { - int blurStyle = arguments[0].asNumber(); - float sigma = arguments[1].asNumber(); - bool respectCTM = arguments[2].getBool(); - return makeJsiObject( - runtime, - std::make_shared( - getContext(), - SkMaskFilter::MakeBlur((SkBlurStyle)blurStyle, sigma, respectCTM))); + std::shared_ptr MakeBlur(int blurStyle, float sigma, + bool respectCTM) { + return std::make_shared( + getContext(), SkMaskFilter::MakeBlur(static_cast(blurStyle), + sigma, respectCTM)); } size_t getMemoryPressure() override { return 1024; } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { - installHostMethod(runtime, prototype, "MakeBlur", - &JsiSkMaskFilterFactory::MakeBlur); + installMethod(runtime, prototype, "MakeBlur", + &JsiSkMaskFilterFactory::MakeBlur); } explicit JsiSkMaskFilterFactory(std::shared_ptr context) diff --git a/packages/skia/cpp/api/JsiSkMatrix.h b/packages/skia/cpp/api/JsiSkMatrix.h index 9732b38da1..a7c6e531ce 100644 --- a/packages/skia/cpp/api/JsiSkMatrix.h +++ b/packages/skia/cpp/api/JsiSkMatrix.h @@ -2,9 +2,11 @@ #include #include +#include #include +#include "JsiSkConverters.h" #include "JsiSkNativeObjects.h" #pragma clang diagnostic push @@ -59,82 +61,39 @@ class JsiSkMatrix std::to_string(array.size(runtime))); } - JSI_HOST_FUNCTION(concat) { - auto matrix = tryGetJsiObject(runtime, arguments[0]); - if (matrix) { - getObject()->preConcat(*matrix->getObject()); - } else { - auto m3 = JsiSkMatrix::getMatrix(runtime, arguments[0]); - getObject()->preConcat(m3); - } - return thisValue.asObject(runtime); + void concat(std::shared_ptr matrix) { + getObject()->preConcat(*matrix); } - JSI_HOST_FUNCTION(translate) { - auto x = arguments[0].asNumber(); - auto y = arguments[1].asNumber(); - getObject()->preTranslate(x, y); - return thisValue.asObject(runtime); - } + void translate(double x, double y) { getObject()->preTranslate(x, y); } - JSI_HOST_FUNCTION(postTranslate) { - auto x = arguments[0].asNumber(); - auto y = arguments[1].asNumber(); - getObject()->postTranslate(x, y); - return thisValue.asObject(runtime); - } + void postTranslate(double x, double y) { getObject()->postTranslate(x, y); } - JSI_HOST_FUNCTION(scale) { - auto x = arguments[0].asNumber(); - auto y = count > 1 ? arguments[1].asNumber() : 1; - getObject()->preScale(x, y); - return thisValue.asObject(runtime); + void scale(double x, JsiOptional y) { + getObject()->preScale(x, y.has_value() ? *y : 1); } - JSI_HOST_FUNCTION(postScale) { - auto x = arguments[0].asNumber(); - auto y = count > 1 ? arguments[1].asNumber() : 1; - getObject()->postScale(x, y); - return thisValue.asObject(runtime); + void postScale(double x, JsiOptional y) { + getObject()->postScale(x, y.has_value() ? *y : 1); } - JSI_HOST_FUNCTION(skew) { - auto x = arguments[0].asNumber(); - auto y = arguments[1].asNumber(); - getObject()->preSkew(x, y); - return thisValue.asObject(runtime); - } + void skew(double x, double y) { getObject()->preSkew(x, y); } - JSI_HOST_FUNCTION(postSkew) { - auto x = arguments[0].asNumber(); - auto y = arguments[1].asNumber(); - getObject()->postSkew(x, y); - return thisValue.asObject(runtime); - } + void postSkew(double x, double y) { getObject()->postSkew(x, y); } - JSI_HOST_FUNCTION(rotate) { - auto a = arguments[0].asNumber(); - getObject()->preRotate(SkRadiansToDegrees(a)); - return thisValue.asObject(runtime); - } + void rotate(double a) { getObject()->preRotate(SkRadiansToDegrees(a)); } - JSI_HOST_FUNCTION(postRotate) { - auto a = arguments[0].asNumber(); - getObject()->postRotate(SkRadiansToDegrees(a)); - return thisValue.asObject(runtime); - } + void postRotate(double a) { getObject()->postRotate(SkRadiansToDegrees(a)); } - JSI_HOST_FUNCTION(identity) { - getObject()->setIdentity(); - return thisValue.asObject(runtime); - } + void identity() { getObject()->setIdentity(); } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Woverloaded-virtual" - JSI_HOST_FUNCTION(get) { - auto values = jsi::Array(runtime, 9); + std::vector get() { + std::vector values; + values.reserve(9); for (auto i = 0; i < 9; i++) { - values.setValueAtIndex(runtime, i, getObject()->get(i)); + values.push_back(getObject()->get(i)); } return values; } @@ -142,19 +101,23 @@ class JsiSkMatrix static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostMethod(runtime, prototype, "concat", &JsiSkMatrix::concat); - installHostMethod(runtime, prototype, "translate", &JsiSkMatrix::translate); - installHostMethod(runtime, prototype, "postTranslate", - &JsiSkMatrix::postTranslate); - installHostMethod(runtime, prototype, "scale", &JsiSkMatrix::scale); - installHostMethod(runtime, prototype, "postScale", &JsiSkMatrix::postScale); - installHostMethod(runtime, prototype, "skew", &JsiSkMatrix::skew); - installHostMethod(runtime, prototype, "postSkew", &JsiSkMatrix::postSkew); - installHostMethod(runtime, prototype, "rotate", &JsiSkMatrix::rotate); - installHostMethod(runtime, prototype, "postRotate", - &JsiSkMatrix::postRotate); - installHostMethod(runtime, prototype, "identity", &JsiSkMatrix::identity); - installHostMethod(runtime, prototype, "get", &JsiSkMatrix::get); + installChainableMethod(runtime, prototype, "concat", &JsiSkMatrix::concat); + installChainableMethod(runtime, prototype, "translate", + &JsiSkMatrix::translate); + installChainableMethod(runtime, prototype, "postTranslate", + &JsiSkMatrix::postTranslate); + installChainableMethod(runtime, prototype, "scale", &JsiSkMatrix::scale); + installChainableMethod(runtime, prototype, "postScale", + &JsiSkMatrix::postScale); + installChainableMethod(runtime, prototype, "skew", &JsiSkMatrix::skew); + installChainableMethod(runtime, prototype, "postSkew", + &JsiSkMatrix::postSkew); + installChainableMethod(runtime, prototype, "rotate", &JsiSkMatrix::rotate); + installChainableMethod(runtime, prototype, "postRotate", + &JsiSkMatrix::postRotate); + installChainableMethod(runtime, prototype, "identity", + &JsiSkMatrix::identity); + installMethod(runtime, prototype, "get", &JsiSkMatrix::get); } /** diff --git a/packages/skia/cpp/api/JsiSkNativeObjects.h b/packages/skia/cpp/api/JsiSkNativeObjects.h index 09b7f52ecc..5955911dab 100644 --- a/packages/skia/cpp/api/JsiSkNativeObjects.h +++ b/packages/skia/cpp/api/JsiSkNativeObjects.h @@ -280,7 +280,40 @@ class JsiSkNativeObject : public rnwgpu::NativeObject { installHostSetter(runtime, prototype, name, setter); } + /** + * Installs a typed mutating method that returns `this` for chaining + * (e.g. path.moveTo(...).lineTo(...)). Arguments are converted through + * rnwgpu::JSIConverter like installMethod; the member function's return + * value (if any) is ignored and the JS function returns thisValue. + */ + template + static void installChainableMethod(jsi::Runtime &runtime, + jsi::Object &prototype, const char *name, + ReturnType (Derived::*method)(Args...)) { + auto func = jsi::Function::createFromHostFunction( + runtime, jsi::PropNameID::forUtf8(runtime, name), sizeof...(Args), + [method](jsi::Runtime &rt, const jsi::Value &thisValue, + const jsi::Value *args, size_t count) -> jsi::Value { + auto native = fromThis(rt, thisValue); + invokeChainable(native.get(), method, rt, args, + std::index_sequence_for{}, count); + return jsi::Value(rt, thisValue); + }); + prototype.setProperty(runtime, name, func); + } + private: + // Invokes a typed member function with JSI argument conversion, discarding + // the result. Used by installChainableMethod, which returns thisValue. + template + static void invokeChainable(Derived *obj, + ReturnType (Derived::*method)(Args...), + jsi::Runtime &runtime, const jsi::Value *args, + std::index_sequence, size_t count) { + (obj->*method)(rnwgpu::JSIConverter>::fromJSI( + runtime, args[Is], Is >= count)...); + } + static void defineProperty(jsi::Runtime &runtime, jsi::Object &prototype, const char *name, jsi::Function *getter, jsi::Function *setter) { diff --git a/packages/skia/cpp/api/JsiSkPaint.h b/packages/skia/cpp/api/JsiSkPaint.h index a7e63994de..dddffcb7ae 100644 --- a/packages/skia/cpp/api/JsiSkPaint.h +++ b/packages/skia/cpp/api/JsiSkPaint.h @@ -7,6 +7,7 @@ #include "CustomBlendModes.h" #include "JsiSkColor.h" +#include "JsiSkConverters.h" #include "JsiSkColorFilter.h" #include "JsiSkImageFilter.h" #include "JsiSkMaskFilter.h" @@ -29,188 +30,129 @@ class JsiSkPaint public: static constexpr const char *CLASS_NAME = "Paint"; - JSI_HOST_FUNCTION(assign) { - SkPaint *paint = JsiSkPaint::fromValue(runtime, arguments[0]).get(); - *getObject() = *paint; - return jsi::Value::undefined(); - } + void assign(std::shared_ptr paint) { *getObject() = *paint; } - JSI_HOST_FUNCTION(copy) { + std::shared_ptr copy() { const auto *paint = getObject().get(); - return makeJsiObject( - runtime, std::make_shared(getContext(), SkPaint(*paint))); + return std::make_shared(getContext(), SkPaint(*paint)); } - JSI_HOST_FUNCTION(reset) { - getObject()->reset(); - return jsi::Value::undefined(); - } + void reset() { getObject()->reset(); } - JSI_HOST_FUNCTION(getColor) { - return JsiSkColor::toValue(runtime, getObject()->getColor()); - } + JsiColor getColor() { return {getObject()->getColor()}; } - JSI_HOST_FUNCTION(getStrokeCap) { + double getStrokeCap() { return static_cast(getObject()->getStrokeCap()); } - JSI_HOST_FUNCTION(getStrokeJoin) { + double getStrokeJoin() { return static_cast(getObject()->getStrokeJoin()); } - JSI_HOST_FUNCTION(getStrokeMiter) { + double getStrokeMiter() { return static_cast(getObject()->getStrokeMiter()); } - JSI_HOST_FUNCTION(getStrokeWidth) { + double getStrokeWidth() { return static_cast(getObject()->getStrokeWidth()); } - JSI_HOST_FUNCTION(getAlphaf) { - float alphaf = getObject()->getAlphaf(); - return jsi::Value(SkScalarToDouble(alphaf)); - } + double getAlphaf() { return SkScalarToDouble(getObject()->getAlphaf()); } - JSI_HOST_FUNCTION(setColor) { - SkColor color = JsiSkColor::fromValue(runtime, arguments[0]); - getObject()->setColor(color); - return jsi::Value::undefined(); - } + void setColor(JsiColor color) { getObject()->setColor(color); } - JSI_HOST_FUNCTION(setAlphaf) { - SkScalar alpha = arguments[0].asNumber(); - getObject()->setAlphaf(alpha); - return jsi::Value::undefined(); - } + void setAlphaf(double alpha) { getObject()->setAlphaf(alpha); } - JSI_HOST_FUNCTION(setAntiAlias) { - bool antiAliased = arguments[0].getBool(); + void setAntiAlias(bool antiAliased) { getObject()->setAntiAlias(antiAliased); - return jsi::Value::undefined(); } - JSI_HOST_FUNCTION(setDither) { - bool dithered = arguments[0].getBool(); - getObject()->setDither(dithered); - return jsi::Value::undefined(); - } + void setDither(bool dithered) { getObject()->setDither(dithered); } - JSI_HOST_FUNCTION(setStrokeWidth) { - SkScalar width = arguments[0].asNumber(); - getObject()->setStrokeWidth(width); - return jsi::Value::undefined(); - } + void setStrokeWidth(double width) { getObject()->setStrokeWidth(width); } - JSI_HOST_FUNCTION(setStyle) { - auto style = arguments[0].asNumber(); + void setStyle(double style) { getObject()->setStyle(static_cast(style)); - return jsi::Value::undefined(); } - JSI_HOST_FUNCTION(setStrokeCap) { - auto cap = arguments[0].asNumber(); + void setStrokeCap(double cap) { getObject()->setStrokeCap(static_cast(cap)); - return jsi::Value::undefined(); } - JSI_HOST_FUNCTION(setStrokeJoin) { - int join = arguments[0].asNumber(); + void setStrokeJoin(int join) { getObject()->setStrokeJoin(static_cast(join)); - return jsi::Value::undefined(); } - JSI_HOST_FUNCTION(setStrokeMiter) { - int limit = arguments[0].asNumber(); - getObject()->setStrokeMiter(limit); - return jsi::Value::undefined(); - } + void setStrokeMiter(int limit) { getObject()->setStrokeMiter(limit); } - JSI_HOST_FUNCTION(setBlendMode) { - int blendModeValue = static_cast(arguments[0].asNumber()); + void setBlendMode(int blendModeValue) { applyBlendMode(*getObject(), blendModeValue); - return jsi::Value::undefined(); } - JSI_HOST_FUNCTION(setMaskFilter) { - auto maskFilter = arguments[0].isNull() || arguments[0].isUndefined() - ? nullptr - : JsiSkMaskFilter::fromValue(runtime, arguments[0]); - getObject()->setMaskFilter(std::move(maskFilter)); - return jsi::Value::undefined(); + void setMaskFilter(JsiOptional> maskFilter) { + getObject()->setMaskFilter( + maskFilter.has_value() ? std::move(*maskFilter) : nullptr); } - JSI_HOST_FUNCTION(setImageFilter) { - auto imageFilter = arguments[0].isNull() || arguments[0].isUndefined() - ? nullptr - : JsiSkImageFilter::fromValue(runtime, arguments[0]); - getObject()->setImageFilter(std::move(imageFilter)); - return jsi::Value::undefined(); + void setImageFilter(JsiOptional> imageFilter) { + getObject()->setImageFilter( + imageFilter.has_value() ? std::move(*imageFilter) : nullptr); } - JSI_HOST_FUNCTION(setColorFilter) { - auto colorFilter = arguments[0].isNull() || arguments[0].isUndefined() - ? nullptr - : JsiSkColorFilter::fromValue(runtime, arguments[0]); - getObject()->setColorFilter(std::move(colorFilter)); - return jsi::Value::undefined(); + void setColorFilter(JsiOptional> colorFilter) { + getObject()->setColorFilter( + colorFilter.has_value() ? std::move(*colorFilter) : nullptr); } - JSI_HOST_FUNCTION(setShader) { - auto shader = arguments[0].isNull() || arguments[0].isUndefined() - ? nullptr - : JsiSkShader::fromValue(runtime, arguments[0]); - getObject()->setShader(std::move(shader)); - return jsi::Value::undefined(); + void setShader(JsiOptional> shader) { + getObject()->setShader(shader.has_value() ? std::move(*shader) : nullptr); } - JSI_HOST_FUNCTION(setPathEffect) { - auto pathEffect = arguments[0].isNull() || arguments[0].isUndefined() - ? nullptr - : JsiSkPathEffect::fromValue(runtime, arguments[0]); - getObject()->setPathEffect(std::move(pathEffect)); - return jsi::Value::undefined(); + void setPathEffect(JsiOptional> pathEffect) { + getObject()->setPathEffect( + pathEffect.has_value() ? std::move(*pathEffect) : nullptr); } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostMethod(runtime, prototype, "assign", &JsiSkPaint::assign); - installHostMethod(runtime, prototype, "copy", &JsiSkPaint::copy); - installHostMethod(runtime, prototype, "reset", &JsiSkPaint::reset); - installHostMethod(runtime, prototype, "getAlphaf", &JsiSkPaint::getAlphaf); - installHostMethod(runtime, prototype, "getColor", &JsiSkPaint::getColor); - installHostMethod(runtime, prototype, "getStrokeCap", - &JsiSkPaint::getStrokeCap); - installHostMethod(runtime, prototype, "getStrokeJoin", - &JsiSkPaint::getStrokeJoin); - installHostMethod(runtime, prototype, "getStrokeMiter", - &JsiSkPaint::getStrokeMiter); - installHostMethod(runtime, prototype, "getStrokeWidth", - &JsiSkPaint::getStrokeWidth); - installHostMethod(runtime, prototype, "setPathEffect", - &JsiSkPaint::setPathEffect); - installHostMethod(runtime, prototype, "setShader", &JsiSkPaint::setShader); - installHostMethod(runtime, prototype, "setColorFilter", - &JsiSkPaint::setColorFilter); - installHostMethod(runtime, prototype, "setImageFilter", - &JsiSkPaint::setImageFilter); - installHostMethod(runtime, prototype, "setMaskFilter", - &JsiSkPaint::setMaskFilter); - installHostMethod(runtime, prototype, "setBlendMode", - &JsiSkPaint::setBlendMode); - installHostMethod(runtime, prototype, "setStrokeMiter", - &JsiSkPaint::setStrokeMiter); - installHostMethod(runtime, prototype, "setStrokeJoin", - &JsiSkPaint::setStrokeJoin); - installHostMethod(runtime, prototype, "setStrokeCap", - &JsiSkPaint::setStrokeCap); - installHostMethod(runtime, prototype, "setAntiAlias", - &JsiSkPaint::setAntiAlias); - installHostMethod(runtime, prototype, "setDither", &JsiSkPaint::setDither); - installHostMethod(runtime, prototype, "setStrokeWidth", - &JsiSkPaint::setStrokeWidth); - installHostMethod(runtime, prototype, "setStyle", &JsiSkPaint::setStyle); - installHostMethod(runtime, prototype, "setColor", &JsiSkPaint::setColor); - installHostMethod(runtime, prototype, "setAlphaf", &JsiSkPaint::setAlphaf); + installMethod(runtime, prototype, "assign", &JsiSkPaint::assign); + installMethod(runtime, prototype, "copy", &JsiSkPaint::copy); + installMethod(runtime, prototype, "reset", &JsiSkPaint::reset); + installMethod(runtime, prototype, "getAlphaf", &JsiSkPaint::getAlphaf); + installMethod(runtime, prototype, "getColor", &JsiSkPaint::getColor); + installMethod(runtime, prototype, "getStrokeCap", + &JsiSkPaint::getStrokeCap); + installMethod(runtime, prototype, "getStrokeJoin", + &JsiSkPaint::getStrokeJoin); + installMethod(runtime, prototype, "getStrokeMiter", + &JsiSkPaint::getStrokeMiter); + installMethod(runtime, prototype, "getStrokeWidth", + &JsiSkPaint::getStrokeWidth); + installMethod(runtime, prototype, "setPathEffect", + &JsiSkPaint::setPathEffect); + installMethod(runtime, prototype, "setShader", &JsiSkPaint::setShader); + installMethod(runtime, prototype, "setColorFilter", + &JsiSkPaint::setColorFilter); + installMethod(runtime, prototype, "setImageFilter", + &JsiSkPaint::setImageFilter); + installMethod(runtime, prototype, "setMaskFilter", + &JsiSkPaint::setMaskFilter); + installMethod(runtime, prototype, "setBlendMode", + &JsiSkPaint::setBlendMode); + installMethod(runtime, prototype, "setStrokeMiter", + &JsiSkPaint::setStrokeMiter); + installMethod(runtime, prototype, "setStrokeJoin", + &JsiSkPaint::setStrokeJoin); + installMethod(runtime, prototype, "setStrokeCap", + &JsiSkPaint::setStrokeCap); + installMethod(runtime, prototype, "setAntiAlias", + &JsiSkPaint::setAntiAlias); + installMethod(runtime, prototype, "setDither", &JsiSkPaint::setDither); + installMethod(runtime, prototype, "setStrokeWidth", + &JsiSkPaint::setStrokeWidth); + installMethod(runtime, prototype, "setStyle", &JsiSkPaint::setStyle); + installMethod(runtime, prototype, "setColor", &JsiSkPaint::setColor); + installMethod(runtime, prototype, "setAlphaf", &JsiSkPaint::setAlphaf); } JsiSkPaint(std::shared_ptr context, SkPaint paint) diff --git a/packages/skia/cpp/api/JsiSkPathBuilderFactory.h b/packages/skia/cpp/api/JsiSkPathBuilderFactory.h index acb21b4ec5..2fef2d585f 100644 --- a/packages/skia/cpp/api/JsiSkPathBuilderFactory.h +++ b/packages/skia/cpp/api/JsiSkPathBuilderFactory.h @@ -5,7 +5,9 @@ #include +#include "JsiSkConverters.h" #include "JsiSkNativeObjects.h" +#include "JsiSkPath.h" #include "JsiSkPathBuilder.h" #pragma clang diagnostic push @@ -25,24 +27,22 @@ class JsiSkPathBuilderFactory public: static constexpr const char *CLASS_NAME = "PathBuilderFactory"; - JSI_HOST_FUNCTION(Make) { - return makeJsiObject(runtime, std::make_shared( - getContext(), SkPathBuilder())); + std::shared_ptr Make() { + return std::make_shared(getContext(), SkPathBuilder()); } - JSI_HOST_FUNCTION(MakeFromPath) { - auto path = JsiSkPath::fromValue(runtime, arguments[0]); - return makeJsiObject(runtime, std::make_shared( - getContext(), SkPathBuilder(*path))); + std::shared_ptr + MakeFromPath(std::shared_ptr path) { + return std::make_shared(getContext(), + SkPathBuilder(*path)); } size_t getMemoryPressure() override { return 1024; } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { - installHostMethod(runtime, prototype, "Make", - &JsiSkPathBuilderFactory::Make); - installHostMethod(runtime, prototype, "MakeFromPath", - &JsiSkPathBuilderFactory::MakeFromPath); + installMethod(runtime, prototype, "Make", &JsiSkPathBuilderFactory::Make); + installMethod(runtime, prototype, "MakeFromPath", + &JsiSkPathBuilderFactory::MakeFromPath); } explicit JsiSkPathBuilderFactory(std::shared_ptr context) diff --git a/packages/skia/cpp/api/JsiSkPathEffectFactory.h b/packages/skia/cpp/api/JsiSkPathEffectFactory.h index 4ffa4d598d..3286eae5c2 100644 --- a/packages/skia/cpp/api/JsiSkPathEffectFactory.h +++ b/packages/skia/cpp/api/JsiSkPathEffectFactory.h @@ -1,12 +1,17 @@ #pragma once #include +#include #include +#include #include #include +#include "JsiSkConverters.h" +#include "JsiSkMatrix.h" #include "JsiSkNativeObjects.h" +#include "JsiSkPath.h" #include "JsiSkPathEffect.h" #pragma clang diagnostic push @@ -30,104 +35,79 @@ class JsiSkPathEffectFactory public: static constexpr const char *CLASS_NAME = "PathEffectFactory"; - JSI_HOST_FUNCTION(MakeCorner) { - int radius = arguments[0].asNumber(); - auto pathEffect = std::make_shared( - getContext(), SkCornerPathEffect::Make(radius)); - return makeJsiObject(runtime, std::move(pathEffect)); + std::shared_ptr MakeCorner(int radius) { + return std::make_shared(getContext(), + SkCornerPathEffect::Make(radius)); } - JSI_HOST_FUNCTION(MakeDash) { - auto jsiIntervals = arguments[0].asObject(runtime).asArray(runtime); - auto size = static_cast(jsiIntervals.size(runtime)); - std::vector intervals; - intervals.reserve(size); - for (int i = 0; i < size; i++) { - SkScalar interval = jsiIntervals.getValueAtIndex(runtime, i).asNumber(); - intervals.push_back(interval); - } - int phase = - count >= 2 && !arguments[1].isUndefined() && !arguments[1].isNull() - ? arguments[1].asNumber() - : 0; + std::shared_ptr + MakeDash(std::vector intervals, JsiOptional phaseValue) { + int phase = phaseValue.has_value() ? *phaseValue : 0; auto i = SkSpan(intervals.data(), intervals.size()); - auto pathEffect = std::make_shared( - getContext(), SkDashPathEffect::Make(i, phase)); - return makeJsiObject(runtime, std::move(pathEffect)); + return std::make_shared(getContext(), + SkDashPathEffect::Make(i, phase)); } - JSI_HOST_FUNCTION(MakeDiscrete) { - int segLength = arguments[0].asNumber(); - int dec = arguments[1].asNumber(); - int seedAssist = arguments[2].asNumber(); - auto pathEffect = std::make_shared( + std::shared_ptr MakeDiscrete(int segLength, int dec, + int seedAssist) { + return std::make_shared( getContext(), SkDiscretePathEffect::Make(segLength, dec, seedAssist)); - return makeJsiObject(runtime, std::move(pathEffect)); } - JSI_HOST_FUNCTION(MakeCompose) { - auto outer = JsiSkPathEffect::fromValue(runtime, arguments[0]); - auto inner = JsiSkPathEffect::fromValue(runtime, arguments[1]); - auto pathEffect = std::make_shared( + std::shared_ptr MakeCompose(sk_sp outer, + sk_sp inner) { + return std::make_shared( getContext(), SkPathEffect::MakeCompose(std::move(outer), std::move(inner))); - return makeJsiObject(runtime, std::move(pathEffect)); } - JSI_HOST_FUNCTION(MakeSum) { - auto outer = JsiSkPathEffect::fromValue(runtime, arguments[0]); - auto inner = JsiSkPathEffect::fromValue(runtime, arguments[1]); - auto pathEffect = std::make_shared( + std::shared_ptr MakeSum(sk_sp outer, + sk_sp inner) { + return std::make_shared( getContext(), SkPathEffect::MakeSum(std::move(outer), std::move(inner))); - return makeJsiObject(runtime, std::move(pathEffect)); } - JSI_HOST_FUNCTION(MakePath1D) { - auto path = JsiSkPath::fromValue(runtime, arguments[0]); - auto advance = arguments[1].asNumber(); - auto phase = arguments[2].asNumber(); - auto style = - static_cast(arguments[3].asNumber()); - auto pathEffect = std::make_shared( + std::shared_ptr + MakePath1D(std::shared_ptr path, double advance, double phase, + double style) { + return std::make_shared( getContext(), - SkPath1DPathEffect::Make(path->snapshot(), advance, phase, style)); - return makeJsiObject(runtime, std::move(pathEffect)); + SkPath1DPathEffect::Make( + path->snapshot(), advance, phase, + static_cast(style))); } - JSI_HOST_FUNCTION(MakePath2D) { - auto matrix = JsiSkMatrix::fromValue(runtime, arguments[0]); - auto path = JsiSkPath::fromValue(runtime, arguments[1]); - auto pathEffect = std::make_shared( + std::shared_ptr + MakePath2D(std::shared_ptr matrix, + std::shared_ptr path) { + return std::make_shared( getContext(), SkPath2DPathEffect::Make(*matrix, path->snapshot())); - return makeJsiObject(runtime, std::move(pathEffect)); } - JSI_HOST_FUNCTION(MakeLine2D) { - auto width = arguments[0].asNumber(); - auto matrix = JsiSkMatrix::fromValue(runtime, arguments[1]); - auto pathEffect = std::make_shared( + std::shared_ptr + MakeLine2D(double width, std::shared_ptr matrix) { + return std::make_shared( getContext(), SkLine2DPathEffect::Make(width, *matrix)); - return makeJsiObject(runtime, std::move(pathEffect)); } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { - installHostMethod(runtime, prototype, "MakeCorner", - &JsiSkPathEffectFactory::MakeCorner); - installHostMethod(runtime, prototype, "MakeDash", - &JsiSkPathEffectFactory::MakeDash); - installHostMethod(runtime, prototype, "MakeDiscrete", - &JsiSkPathEffectFactory::MakeDiscrete); - installHostMethod(runtime, prototype, "MakeCompose", - &JsiSkPathEffectFactory::MakeCompose); - installHostMethod(runtime, prototype, "MakeSum", - &JsiSkPathEffectFactory::MakeSum); - installHostMethod(runtime, prototype, "MakeLine2D", - &JsiSkPathEffectFactory::MakeLine2D); - installHostMethod(runtime, prototype, "MakePath1D", - &JsiSkPathEffectFactory::MakePath1D); - installHostMethod(runtime, prototype, "MakePath2D", - &JsiSkPathEffectFactory::MakePath2D); + installMethod(runtime, prototype, "MakeCorner", + &JsiSkPathEffectFactory::MakeCorner); + installMethod(runtime, prototype, "MakeDash", + &JsiSkPathEffectFactory::MakeDash); + installMethod(runtime, prototype, "MakeDiscrete", + &JsiSkPathEffectFactory::MakeDiscrete); + installMethod(runtime, prototype, "MakeCompose", + &JsiSkPathEffectFactory::MakeCompose); + installMethod(runtime, prototype, "MakeSum", + &JsiSkPathEffectFactory::MakeSum); + installMethod(runtime, prototype, "MakeLine2D", + &JsiSkPathEffectFactory::MakeLine2D); + installMethod(runtime, prototype, "MakePath1D", + &JsiSkPathEffectFactory::MakePath1D); + installMethod(runtime, prototype, "MakePath2D", + &JsiSkPathEffectFactory::MakePath2D); } size_t getMemoryPressure() override { return 1024; } diff --git a/packages/skia/cpp/api/JsiSkPathFactory.h b/packages/skia/cpp/api/JsiSkPathFactory.h index 519caae739..fbc3811c8e 100644 --- a/packages/skia/cpp/api/JsiSkPathFactory.h +++ b/packages/skia/cpp/api/JsiSkPathFactory.h @@ -2,13 +2,18 @@ #include #include +#include #include +#include #include #include +#include "JsiSkConverters.h" +#include "JsiSkFont.h" #include "JsiSkMatrix.h" #include "JsiSkNativeObjects.h" +#include "JsiSkPath.h" #include "JsiSkPathEffect.h" #include "JsiSkPoint.h" #include "JsiSkRRect.h" @@ -42,107 +47,83 @@ class JsiSkPathFactory : public JsiSkNativeObject { static const int CUBIC = 4; static const int CLOSE = 5; + using NullablePath = std::variant>; + public: static constexpr const char *CLASS_NAME = "PathFactory"; - JSI_HOST_FUNCTION(Make) { - return makeJsiObject(runtime, - std::make_shared(getContext(), SkPath())); + std::shared_ptr Make() { + return std::make_shared(getContext(), SkPath()); } - JSI_HOST_FUNCTION(MakeFromSVGString) { - auto svgString = arguments[0].asString(runtime).utf8(runtime); + std::shared_ptr MakeFromSVGString(std::string svgString) { auto result = SkParsePath::FromSVGString(svgString.c_str()); if (!result.has_value()) { - throw jsi::JSError(runtime, "Could not parse Svg path"); - return jsi::Value(nullptr); + throw std::runtime_error("Could not parse Svg path"); } - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(result.value()))); + return std::make_shared(getContext(), + std::move(result.value())); } - JSI_HOST_FUNCTION(MakeFromOp) { - auto one = JsiSkPath::fromValue(runtime, arguments[0])->snapshot(); - auto two = JsiSkPath::fromValue(runtime, arguments[1])->snapshot(); - SkPathOp op = (SkPathOp)arguments[2].asNumber(); - auto result = Op(one, two, op); + NullablePath MakeFromOp(std::shared_ptr pathOne, + std::shared_ptr pathTwo, double op) { + auto one = pathOne->snapshot(); + auto two = pathTwo->snapshot(); + auto result = Op(one, two, static_cast(op)); if (!result.has_value()) { - return jsi::Value(nullptr); + return nullptr; } - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(result.value()))); + return std::make_shared(getContext(), + std::move(result.value())); } - JSI_HOST_FUNCTION(MakeFromCmds) { + NullablePath MakeFromCmds(std::vector> cmds) { SkPathBuilder builder; - auto cmds = arguments[0].asObject(runtime).asArray(runtime); - auto cmdCount = cmds.size(runtime); - for (int i = 0; i < cmdCount; i++) { - auto cmd = - cmds.getValueAtIndex(runtime, i).asObject(runtime).asArray(runtime); - if (cmd.size(runtime) < 1) { + for (const auto &cmd : cmds) { + if (cmd.size() < 1) { RNSkLogger::logToConsole("Invalid command found (got an empty array)"); - return jsi::Value::null(); + return nullptr; } - auto verb = static_cast(cmd.getValueAtIndex(runtime, 0).asNumber()); + auto verb = static_cast(cmd[0]); switch (verb) { case MOVE: { - if (cmd.size(runtime) < 3) { + if (cmd.size() < 3) { RNSkLogger::logToConsole("Invalid move command found"); - return jsi::Value::null(); + return nullptr; } - auto x = cmd.getValueAtIndex(runtime, 1).asNumber(); - auto y = cmd.getValueAtIndex(runtime, 2).asNumber(); - builder.moveTo(x, y); + builder.moveTo(cmd[1], cmd[2]); break; } case LINE: { - if (cmd.size(runtime) < 3) { + if (cmd.size() < 3) { RNSkLogger::logToConsole("Invalid line command found"); - return jsi::Value::null(); + return nullptr; } - auto x = cmd.getValueAtIndex(runtime, 1).asNumber(); - auto y = cmd.getValueAtIndex(runtime, 2).asNumber(); - builder.lineTo(x, y); + builder.lineTo(cmd[1], cmd[2]); break; } case QUAD: { - if (cmd.size(runtime) < 5) { + if (cmd.size() < 5) { RNSkLogger::logToConsole("Invalid line command found"); - return jsi::Value::null(); + return nullptr; } - auto x1 = cmd.getValueAtIndex(runtime, 1).asNumber(); - auto y1 = cmd.getValueAtIndex(runtime, 2).asNumber(); - auto x2 = cmd.getValueAtIndex(runtime, 3).asNumber(); - auto y2 = cmd.getValueAtIndex(runtime, 4).asNumber(); - builder.quadTo(x1, y1, x2, y2); + builder.quadTo(cmd[1], cmd[2], cmd[3], cmd[4]); break; } case CONIC: { - if (cmd.size(runtime) < 6) { + if (cmd.size() < 6) { RNSkLogger::logToConsole("Invalid line command found"); - return jsi::Value::null(); + return nullptr; } - auto x1 = cmd.getValueAtIndex(runtime, 1).asNumber(); - auto y1 = cmd.getValueAtIndex(runtime, 2).asNumber(); - auto x2 = cmd.getValueAtIndex(runtime, 3).asNumber(); - auto y2 = cmd.getValueAtIndex(runtime, 4).asNumber(); - auto w = cmd.getValueAtIndex(runtime, 5).asNumber(); - builder.conicTo(x1, y1, x2, y2, w); + builder.conicTo(cmd[1], cmd[2], cmd[3], cmd[4], cmd[5]); break; } case CUBIC: { - if (cmd.size(runtime) < 7) { + if (cmd.size() < 7) { RNSkLogger::logToConsole("Invalid line command found"); - return jsi::Value::null(); + return nullptr; } - auto x1 = cmd.getValueAtIndex(runtime, 1).asNumber(); - auto y1 = cmd.getValueAtIndex(runtime, 2).asNumber(); - auto x2 = cmd.getValueAtIndex(runtime, 3).asNumber(); - auto y2 = cmd.getValueAtIndex(runtime, 4).asNumber(); - auto x3 = cmd.getValueAtIndex(runtime, 5).asNumber(); - auto y3 = cmd.getValueAtIndex(runtime, 6).asNumber(); - builder.cubicTo(x1, y1, x2, y2, x3, y3); + builder.cubicTo(cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6]); break; } case CLOSE: { @@ -151,102 +132,68 @@ class JsiSkPathFactory : public JsiSkNativeObject { } default: { RNSkLogger::logToConsole("Found an unknown command"); - return jsi::Value::null(); + return nullptr; } } } - return makeJsiObject( - runtime, std::make_shared(getContext(), builder.snapshot())); + return std::make_shared(getContext(), builder.snapshot()); } - JSI_HOST_FUNCTION(MakeFromText) { - auto text = arguments[0].asString(runtime).utf8(runtime); - auto x = arguments[1].asNumber(); - auto y = arguments[2].asNumber(); - auto font = JsiSkFont::fromValue(runtime, arguments[3]); + std::shared_ptr MakeFromText(std::string text, double x, double y, + std::shared_ptr font) { SkPath path; SkTextUtils::GetPath(text.c_str(), strlen(text.c_str()), SkTextEncoding::kUTF8, x, y, *font, &path); - return makeJsiObject( - runtime, std::make_shared(getContext(), std::move(path))); + return std::make_shared(getContext(), std::move(path)); } // Static shape factories - JSI_HOST_FUNCTION(Rect) { - auto rect = JsiSkRect::fromValue(runtime, arguments[0]); - auto direction = SkPathDirection::kCW; - if (count >= 2 && arguments[1].getBool()) { - direction = SkPathDirection::kCCW; - } + std::shared_ptr Rect(SkRect rect, JsiOptional isCCW) { SkPathBuilder builder; - builder.addRect(*rect, direction); - return makeJsiObject( - runtime, std::make_shared(getContext(), builder.snapshot())); + builder.addRect(rect, toDirection(isCCW)); + return std::make_shared(getContext(), builder.snapshot()); } - JSI_HOST_FUNCTION(Oval) { - auto rect = JsiSkRect::fromValue(runtime, arguments[0]); - auto direction = SkPathDirection::kCW; - if (count >= 2 && arguments[1].getBool()) { - direction = SkPathDirection::kCCW; - } - unsigned startIndex = count < 3 ? 0 : arguments[2].asNumber(); + std::shared_ptr Oval(SkRect rect, JsiOptional isCCW, + JsiOptional startIndex) { SkPathBuilder builder; - builder.addOval(*rect, direction, startIndex); - return makeJsiObject( - runtime, std::make_shared(getContext(), builder.snapshot())); + builder.addOval(rect, toDirection(isCCW), + startIndex.has_value() + ? static_cast(*startIndex) + : 0); + return std::make_shared(getContext(), builder.snapshot()); } - JSI_HOST_FUNCTION(Circle) { - auto x = arguments[0].asNumber(); - auto y = arguments[1].asNumber(); - auto r = arguments[2].asNumber(); + std::shared_ptr Circle(double x, double y, double r) { SkPathBuilder builder; builder.addCircle(x, y, r); - return makeJsiObject( - runtime, std::make_shared(getContext(), builder.snapshot())); + return std::make_shared(getContext(), builder.snapshot()); } - JSI_HOST_FUNCTION(RRect) { - auto rrect = JsiSkRRect::fromValue(runtime, arguments[0]); - auto direction = SkPathDirection::kCW; - if (count >= 2 && arguments[1].getBool()) { - direction = SkPathDirection::kCCW; - } + std::shared_ptr RRect(std::shared_ptr rrect, + JsiOptional isCCW) { SkPathBuilder builder; - builder.addRRect(*rrect, direction); - return makeJsiObject( - runtime, std::make_shared(getContext(), builder.snapshot())); + builder.addRRect(*rrect, toDirection(isCCW)); + return std::make_shared(getContext(), builder.snapshot()); } - JSI_HOST_FUNCTION(Line) { - auto p1 = JsiSkPoint::fromValue(runtime, arguments[0].asObject(runtime)); - auto p2 = JsiSkPoint::fromValue(runtime, arguments[1].asObject(runtime)); + std::shared_ptr Line(SkPoint p1, SkPoint p2) { SkPathBuilder builder; - builder.moveTo(*p1); - builder.lineTo(*p2); - return makeJsiObject( - runtime, std::make_shared(getContext(), builder.snapshot())); + builder.moveTo(p1); + builder.lineTo(p2); + return std::make_shared(getContext(), builder.snapshot()); } - JSI_HOST_FUNCTION(Polygon) { - std::vector points; - auto jsiPoints = arguments[0].asObject(runtime).asArray(runtime); - auto close = arguments[1].getBool(); - auto pointsSize = jsiPoints.size(runtime); - points.reserve(pointsSize); - for (int i = 0; i < pointsSize; i++) { - std::shared_ptr point = JsiSkPoint::fromValue( - runtime, jsiPoints.getValueAtIndex(runtime, i).asObject(runtime)); - points.push_back(*point.get()); - } + std::shared_ptr Polygon(std::vector points, bool close) { SkPathBuilder builder; builder.addPolygon(SkSpan(points), close); - return makeJsiObject( - runtime, std::make_shared(getContext(), builder.snapshot())); + return std::make_shared(getContext(), builder.snapshot()); } // Static path operations + + // Stays raw: the options object is read leniently property by property + // (unknown properties and non-numeric values are ignored). JSI_HOST_FUNCTION(Stroke) { auto srcPath = JsiSkPath::fromValue(runtime, arguments[0]); SkPath path = srcPath->snapshot(); @@ -303,128 +250,115 @@ class JsiSkPathFactory : public JsiSkNativeObject { return jsi::Value::null(); } - JSI_HOST_FUNCTION(Trim) { - auto srcPath = JsiSkPath::fromValue(runtime, arguments[0]); - float start = - std::clamp(static_cast(arguments[1].asNumber()), 0.0f, 1.0f); - float end = - std::clamp(static_cast(arguments[2].asNumber()), 0.0f, 1.0f); - auto isComplement = arguments[3].getBool(); + NullablePath Trim(std::shared_ptr srcPath, double startValue, + double endValue, bool isComplement) { + float start = std::clamp(static_cast(startValue), 0.0f, 1.0f); + float end = std::clamp(static_cast(endValue), 0.0f, 1.0f); // If requesting the full path in normal mode, just return a copy if (start <= 0 && end >= 1 && !isComplement) { - SkPath result = srcPath->snapshot(); - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(result))); + return std::make_shared(getContext(), srcPath->snapshot()); } SkPath path = srcPath->snapshot(); auto mode = isComplement ? SkTrimPathEffect::Mode::kInverted : SkTrimPathEffect::Mode::kNormal; auto pe = SkTrimPathEffect::Make(start, end, mode); if (!pe) { - return jsi::Value::null(); + return nullptr; } SkStrokeRec rec(SkStrokeRec::InitStyle::kHairline_InitStyle); SkPathBuilder resultBuilder; if (pe->filterPath(&resultBuilder, path, &rec)) { - auto result = resultBuilder.detach(); - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(result))); + return std::make_shared(getContext(), resultBuilder.detach()); } - return jsi::Value::null(); + return nullptr; } - JSI_HOST_FUNCTION(Simplify) { - auto srcPath = JsiSkPath::fromValue(runtime, arguments[0]); + NullablePath Simplify(std::shared_ptr srcPath) { auto result = ::Simplify(srcPath->snapshot()); if (result.has_value()) { - return makeJsiObject( - runtime, - std::make_shared(getContext(), std::move(result.value()))); + return std::make_shared(getContext(), + std::move(result.value())); } - return jsi::Value::null(); + return nullptr; } - JSI_HOST_FUNCTION(Dash) { - auto srcPath = JsiSkPath::fromValue(runtime, arguments[0]); - SkScalar on = arguments[1].asNumber(); - SkScalar off = arguments[2].asNumber(); - auto phase = arguments[3].asNumber(); - SkScalar intervals[] = {on, off}; + NullablePath Dash(std::shared_ptr srcPath, double on, + double off, double phase) { + SkScalar intervals[] = {static_cast(on), + static_cast(off)}; auto i = SkSpan(intervals, 2); auto pe = SkDashPathEffect::Make(i, phase); if (!pe) { - return jsi::Value::null(); + return nullptr; } SkStrokeRec rec(SkStrokeRec::InitStyle::kHairline_InitStyle); SkPathBuilder resultBuilder; if (pe->filterPath(&resultBuilder, srcPath->snapshot(), &rec)) { - auto result = resultBuilder.detach(); - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(result))); + return std::make_shared(getContext(), resultBuilder.detach()); } - return jsi::Value::null(); + return nullptr; } - JSI_HOST_FUNCTION(AsWinding) { - auto srcPath = JsiSkPath::fromValue(runtime, arguments[0]); + NullablePath AsWinding(std::shared_ptr srcPath) { auto result = ::AsWinding(srcPath->snapshot()); if (result.has_value()) { - return makeJsiObject( - runtime, - std::make_shared(getContext(), std::move(result.value()))); + return std::make_shared(getContext(), + std::move(result.value())); } - return jsi::Value::null(); + return nullptr; } - JSI_HOST_FUNCTION(Interpolate) { - auto path1 = JsiSkPath::fromValue(runtime, arguments[0]); - auto path2 = JsiSkPath::fromValue(runtime, arguments[1]); - auto weight = arguments[2].asNumber(); - auto p1 = path1->snapshot(); - auto p2 = path2->snapshot(); + NullablePath Interpolate(std::shared_ptr pathOne, + std::shared_ptr pathTwo, + double weight) { + auto p1 = pathOne->snapshot(); + auto p2 = pathTwo->snapshot(); SkPath result; auto succeed = p1.interpolate(p2, weight, &result); if (!succeed) { - return jsi::Value::null(); + return nullptr; } - return makeJsiObject( - runtime, std::make_shared(getContext(), std::move(result))); + return std::make_shared(getContext(), std::move(result)); } size_t getMemoryPressure() override { return 1024; } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { - installHostMethod(runtime, prototype, "Make", &JsiSkPathFactory::Make); - installHostMethod(runtime, prototype, "MakeFromSVGString", - &JsiSkPathFactory::MakeFromSVGString); - installHostMethod(runtime, prototype, "MakeFromOp", - &JsiSkPathFactory::MakeFromOp); - installHostMethod(runtime, prototype, "MakeFromCmds", - &JsiSkPathFactory::MakeFromCmds); - installHostMethod(runtime, prototype, "MakeFromText", - &JsiSkPathFactory::MakeFromText); + installMethod(runtime, prototype, "Make", &JsiSkPathFactory::Make); + installMethod(runtime, prototype, "MakeFromSVGString", + &JsiSkPathFactory::MakeFromSVGString); + installMethod(runtime, prototype, "MakeFromOp", + &JsiSkPathFactory::MakeFromOp); + installMethod(runtime, prototype, "MakeFromCmds", + &JsiSkPathFactory::MakeFromCmds); + installMethod(runtime, prototype, "MakeFromText", + &JsiSkPathFactory::MakeFromText); // Static shape factories - installHostMethod(runtime, prototype, "Rect", &JsiSkPathFactory::Rect); - installHostMethod(runtime, prototype, "Oval", &JsiSkPathFactory::Oval); - installHostMethod(runtime, prototype, "Circle", &JsiSkPathFactory::Circle); - installHostMethod(runtime, prototype, "RRect", &JsiSkPathFactory::RRect); - installHostMethod(runtime, prototype, "Line", &JsiSkPathFactory::Line); - installHostMethod(runtime, prototype, "Polygon", - &JsiSkPathFactory::Polygon); + installMethod(runtime, prototype, "Rect", &JsiSkPathFactory::Rect); + installMethod(runtime, prototype, "Oval", &JsiSkPathFactory::Oval); + installMethod(runtime, prototype, "Circle", &JsiSkPathFactory::Circle); + installMethod(runtime, prototype, "RRect", &JsiSkPathFactory::RRect); + installMethod(runtime, prototype, "Line", &JsiSkPathFactory::Line); + installMethod(runtime, prototype, "Polygon", &JsiSkPathFactory::Polygon); // Static path operations installHostMethod(runtime, prototype, "Stroke", &JsiSkPathFactory::Stroke); - installHostMethod(runtime, prototype, "Trim", &JsiSkPathFactory::Trim); - installHostMethod(runtime, prototype, "Simplify", - &JsiSkPathFactory::Simplify); - installHostMethod(runtime, prototype, "Dash", &JsiSkPathFactory::Dash); - installHostMethod(runtime, prototype, "AsWinding", - &JsiSkPathFactory::AsWinding); - installHostMethod(runtime, prototype, "Interpolate", - &JsiSkPathFactory::Interpolate); + installMethod(runtime, prototype, "Trim", &JsiSkPathFactory::Trim); + installMethod(runtime, prototype, "Simplify", &JsiSkPathFactory::Simplify); + installMethod(runtime, prototype, "Dash", &JsiSkPathFactory::Dash); + installMethod(runtime, prototype, "AsWinding", + &JsiSkPathFactory::AsWinding); + installMethod(runtime, prototype, "Interpolate", + &JsiSkPathFactory::Interpolate); } explicit JsiSkPathFactory(std::shared_ptr context) : JsiSkNativeObject(std::move(context)) {} + +private: + static SkPathDirection toDirection(const JsiOptional &isCCW) { + return isCCW.has_value() && *isCCW ? SkPathDirection::kCCW + : SkPathDirection::kCW; + } }; } // namespace RNSkia diff --git a/packages/skia/cpp/api/JsiSkPicture.h b/packages/skia/cpp/api/JsiSkPicture.h index 71b2f28c68..1761e29246 100644 --- a/packages/skia/cpp/api/JsiSkPicture.h +++ b/packages/skia/cpp/api/JsiSkPicture.h @@ -1,7 +1,9 @@ #pragma once #include +#include +#include "JsiSkConverters.h" #include "JsiSkData.h" #include "JsiSkDispatcher.h" #include "JsiSkMatrix.h" @@ -56,22 +58,15 @@ class JsiSkPicture } } - JSI_HOST_FUNCTION(makeShader) { - auto tmx = (SkTileMode)arguments[0].asNumber(); - auto tmy = (SkTileMode)arguments[1].asNumber(); - auto fm = (SkFilterMode)arguments[2].asNumber(); - auto m = count > 3 && !arguments[3].isUndefined() - ? JsiSkMatrix::fromValue(runtime, arguments[3]).get() - : nullptr; - - auto tr = count > 4 && !arguments[4].isUndefined() - ? JsiSkRect::fromValue(runtime, arguments[4]).get() - : nullptr; - - // Create shader - auto shader = getObject()->makeShader(tmx, tmy, fm, m, tr); - return makeJsiObject(runtime, - std::make_shared(getContext(), shader)); + std::shared_ptr + makeShader(double tmx, double tmy, double fm, + std::optional> m, + std::optional> tr) { + auto shader = getObject()->makeShader( + static_cast(tmx), static_cast(tmy), + static_cast(fm), m.has_value() ? m->get() : nullptr, + tr.has_value() ? tr->get() : nullptr); + return std::make_shared(getContext(), shader); } JSI_HOST_FUNCTION(serialize) { @@ -103,8 +98,7 @@ class JsiSkPicture static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostMethod(runtime, prototype, "makeShader", - &JsiSkPicture::makeShader); + installMethod(runtime, prototype, "makeShader", &JsiSkPicture::makeShader); installHostMethod(runtime, prototype, "serialize", &JsiSkPicture::serialize); } diff --git a/packages/skia/cpp/api/JsiSkPoint.h b/packages/skia/cpp/api/JsiSkPoint.h index 5bff76ac59..267afeb690 100644 --- a/packages/skia/cpp/api/JsiSkPoint.h +++ b/packages/skia/cpp/api/JsiSkPoint.h @@ -23,14 +23,14 @@ class JsiSkPoint public: static constexpr const char *CLASS_NAME = "Point"; - JSI_PROPERTY_GET(x) { return static_cast(getObject()->x()); } + double getX() { return static_cast(getObject()->x()); } - JSI_PROPERTY_GET(y) { return static_cast(getObject()->y()); } + double getY() { return static_cast(getObject()->y()); } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostGetter(runtime, prototype, "x", &JsiSkPoint::get_x); - installHostGetter(runtime, prototype, "y", &JsiSkPoint::get_y); + installGetter(runtime, prototype, "x", &JsiSkPoint::getX); + installGetter(runtime, prototype, "y", &JsiSkPoint::getY); } JsiSkPoint(std::shared_ptr context, const SkPoint &point) diff --git a/packages/skia/cpp/api/JsiSkRRect.h b/packages/skia/cpp/api/JsiSkRRect.h index 9f455966f9..873969de54 100644 --- a/packages/skia/cpp/api/JsiSkRRect.h +++ b/packages/skia/cpp/api/JsiSkRRect.h @@ -26,22 +26,21 @@ class JsiSkRRect public: static constexpr const char *CLASS_NAME = "RRect"; - JSI_PROPERTY_GET(rx) { + double getRx() { return static_cast(getObject()->getSimpleRadii().x()); } - JSI_PROPERTY_GET(ry) { + double getRy() { return static_cast(getObject()->getSimpleRadii().y()); } - JSI_PROPERTY_GET(rect) { - return makeJsiObject(runtime, std::make_shared( - getContext(), getObject()->getBounds())); + std::shared_ptr getRect() { + return std::make_shared(getContext(), getObject()->getBounds()); } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostGetter(runtime, prototype, "rx", &JsiSkRRect::get_rx); - installHostGetter(runtime, prototype, "ry", &JsiSkRRect::get_ry); - installHostGetter(runtime, prototype, "rect", &JsiSkRRect::get_rect); + installGetter(runtime, prototype, "rx", &JsiSkRRect::getRx); + installGetter(runtime, prototype, "ry", &JsiSkRRect::getRy); + installGetter(runtime, prototype, "rect", &JsiSkRRect::getRect); } JsiSkRRect(std::shared_ptr context, const SkRRect &rect) diff --git a/packages/skia/cpp/api/JsiSkRect.h b/packages/skia/cpp/api/JsiSkRect.h index 407c831188..47377186e1 100644 --- a/packages/skia/cpp/api/JsiSkRect.h +++ b/packages/skia/cpp/api/JsiSkRect.h @@ -22,43 +22,35 @@ class JsiSkRect : public JsiSkWrappingSharedPtrNativeObject { public: static constexpr const char *CLASS_NAME = "Rect"; - JSI_PROPERTY_GET(x) { return static_cast(getObject()->x()); } - JSI_PROPERTY_GET(y) { return static_cast(getObject()->y()); } - JSI_PROPERTY_GET(width) { return static_cast(getObject()->width()); } - JSI_PROPERTY_GET(height) { - return static_cast(getObject()->height()); - } - JSI_PROPERTY_GET(left) { return static_cast(getObject()->left()); } - JSI_PROPERTY_GET(top) { return static_cast(getObject()->top()); } - JSI_PROPERTY_GET(right) { return static_cast(getObject()->right()); } - JSI_PROPERTY_GET(bottom) { - return static_cast(getObject()->bottom()); - } - - JSI_HOST_FUNCTION(setXYWH) { - getObject()->setXYWH(arguments[0].asNumber(), arguments[1].asNumber(), - arguments[2].asNumber(), arguments[3].asNumber()); - return jsi::Value::undefined(); + double getX() { return static_cast(getObject()->x()); } + double getY() { return static_cast(getObject()->y()); } + double getWidth() { return static_cast(getObject()->width()); } + double getHeight() { return static_cast(getObject()->height()); } + double getLeft() { return static_cast(getObject()->left()); } + double getTop() { return static_cast(getObject()->top()); } + double getRight() { return static_cast(getObject()->right()); } + double getBottom() { return static_cast(getObject()->bottom()); } + + void setXYWH(double x, double y, double width, double height) { + getObject()->setXYWH(x, y, width, height); } - JSI_HOST_FUNCTION(setLTRB) { - getObject()->setLTRB(arguments[0].asNumber(), arguments[1].asNumber(), - arguments[2].asNumber(), arguments[3].asNumber()); - return jsi::Value::undefined(); + void setLTRB(double left, double top, double right, double bottom) { + getObject()->setLTRB(left, top, right, bottom); } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostGetter(runtime, prototype, "x", &JsiSkRect::get_x); - installHostGetter(runtime, prototype, "y", &JsiSkRect::get_y); - installHostGetter(runtime, prototype, "width", &JsiSkRect::get_width); - installHostGetter(runtime, prototype, "height", &JsiSkRect::get_height); - installHostGetter(runtime, prototype, "left", &JsiSkRect::get_left); - installHostGetter(runtime, prototype, "top", &JsiSkRect::get_top); - installHostGetter(runtime, prototype, "right", &JsiSkRect::get_right); - installHostGetter(runtime, prototype, "bottom", &JsiSkRect::get_bottom); - installHostMethod(runtime, prototype, "setXYWH", &JsiSkRect::setXYWH); - installHostMethod(runtime, prototype, "setLTRB", &JsiSkRect::setLTRB); + installGetter(runtime, prototype, "x", &JsiSkRect::getX); + installGetter(runtime, prototype, "y", &JsiSkRect::getY); + installGetter(runtime, prototype, "width", &JsiSkRect::getWidth); + installGetter(runtime, prototype, "height", &JsiSkRect::getHeight); + installGetter(runtime, prototype, "left", &JsiSkRect::getLeft); + installGetter(runtime, prototype, "top", &JsiSkRect::getTop); + installGetter(runtime, prototype, "right", &JsiSkRect::getRight); + installGetter(runtime, prototype, "bottom", &JsiSkRect::getBottom); + installMethod(runtime, prototype, "setXYWH", &JsiSkRect::setXYWH); + installMethod(runtime, prototype, "setLTRB", &JsiSkRect::setLTRB); } /** diff --git a/packages/skia/cpp/api/JsiSkRuntimeEffectFactory.h b/packages/skia/cpp/api/JsiSkRuntimeEffectFactory.h index 9f0fe96e99..aceb09c663 100644 --- a/packages/skia/cpp/api/JsiSkRuntimeEffectFactory.h +++ b/packages/skia/cpp/api/JsiSkRuntimeEffectFactory.h @@ -18,26 +18,22 @@ class JsiSkRuntimeEffectFactory public: static constexpr const char *CLASS_NAME = "RuntimeEffectFactory"; - JSI_HOST_FUNCTION(Make) { - auto sksl = arguments[0].asString(runtime).utf8(runtime); + std::shared_ptr Make(std::string sksl) { auto result = SkRuntimeEffect::MakeForShader(SkString(sksl)); auto effect = result.effect; auto errorText = result.errorText; if (!effect) { - throw jsi::JSError(runtime, std::string("Error in sksl:\n" + - std::string(errorText.c_str())) - .c_str()); - return jsi::Value::null(); + throw std::runtime_error("Error in sksl:\n" + + std::string(errorText.c_str())); } - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(effect))); + return std::make_shared(getContext(), + std::move(effect)); } size_t getMemoryPressure() override { return 1024; } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { - installHostMethod(runtime, prototype, "Make", - &JsiSkRuntimeEffectFactory::Make); + installMethod(runtime, prototype, "Make", &JsiSkRuntimeEffectFactory::Make); } explicit JsiSkRuntimeEffectFactory( diff --git a/packages/skia/cpp/api/JsiSkRuntimeShaderBuilder.h b/packages/skia/cpp/api/JsiSkRuntimeShaderBuilder.h index 725802aa45..38056bd1cf 100644 --- a/packages/skia/cpp/api/JsiSkRuntimeShaderBuilder.h +++ b/packages/skia/cpp/api/JsiSkRuntimeShaderBuilder.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -35,26 +36,16 @@ class JsiSkRuntimeShaderBuilder SkRuntimeShaderBuilder>( std::move(context), std::make_shared(rt)) {} - JSI_HOST_FUNCTION(setUniform) { - auto name = arguments[0].asString(runtime).utf8(runtime); - auto jsiValue = arguments[1].asObject(runtime).asArray(runtime); - auto size = jsiValue.size(runtime); - std::vector value; - value.reserve(size); - for (int i = 0; i < size; i++) { - auto e = jsiValue.getValueAtIndex(runtime, i).asNumber(); - value.push_back(e); - } + void setUniform(std::string name, std::vector value) { getObject() ->uniform(name.c_str()) - .set(value.data(), static_cast(size)); - return jsi::Value::undefined(); + .set(value.data(), static_cast(value.size())); } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostMethod(runtime, prototype, "setUniform", - &JsiSkRuntimeShaderBuilder::setUniform); + installMethod(runtime, prototype, "setUniform", + &JsiSkRuntimeShaderBuilder::setUniform); } /** diff --git a/packages/skia/cpp/api/JsiSkSVG.h b/packages/skia/cpp/api/JsiSkSVG.h index 85e6defb9e..16ad91ad6a 100644 --- a/packages/skia/cpp/api/JsiSkSVG.h +++ b/packages/skia/cpp/api/JsiSkSVG.h @@ -29,18 +29,18 @@ class JsiSkSVG : public JsiSkWrappingSkPtrNativeObject { ~JsiSkSVG() = default; - JSI_HOST_FUNCTION(width) { + double width() { return static_cast(getObject()->containerSize().width()); } - JSI_HOST_FUNCTION(height) { + double height() { return static_cast(getObject()->containerSize().height()); } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostMethod(runtime, prototype, "width", &JsiSkSVG::width); - installHostMethod(runtime, prototype, "height", &JsiSkSVG::height); + installMethod(runtime, prototype, "width", &JsiSkSVG::width); + installMethod(runtime, prototype, "height", &JsiSkSVG::height); } /** diff --git a/packages/skia/cpp/api/JsiSkShaderFactory.h b/packages/skia/cpp/api/JsiSkShaderFactory.h index e77b473539..b1b7b3a576 100644 --- a/packages/skia/cpp/api/JsiSkShaderFactory.h +++ b/packages/skia/cpp/api/JsiSkShaderFactory.h @@ -6,8 +6,13 @@ #include +#include "JsiSkColor.h" #include "JsiSkColorFilter.h" +#include "JsiSkConverters.h" +#include "JsiSkMatrix.h" #include "JsiSkNativeObjects.h" +#include "JsiSkPoint.h" +#include "JsiSkShader.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdocumentation" @@ -22,269 +27,192 @@ namespace RNSkia { namespace jsi = facebook::jsi; -int getFlag(const jsi::Value *values, int i, size_t size) { - if (i >= size || values[i].isUndefined()) { - return 0; - } - return values[i].asNumber(); -} - -SkMatrix *getLocalMatrix(jsi::Runtime &runtime, const jsi::Value *values, int i, - size_t size) { - if (i >= size || values[i].isUndefined()) { - return nullptr; - } - return JsiSkMatrix::fromValue(runtime, values[i]).get(); -} - -SkTileMode getTileMode(const jsi::Value *values, int i, size_t size) { - if (i >= size || values[i].isUndefined()) { - return SkTileMode::kClamp; - } - return static_cast(values[i].asNumber()); -} - -std::vector getColors(jsi::Runtime &runtime, - const jsi::Value &value) { - std::vector colors; - if (!value.isNull()) { - auto jsiColors = value.asObject(runtime).asArray(runtime); - auto size = jsiColors.size(runtime); - colors.reserve(size); - for (int i = 0; i < size; i++) { - SkColor color = - JsiSkColor::fromValue(runtime, jsiColors.getValueAtIndex(runtime, i)); - colors.push_back(SkColor4f::FromColor(color)); - } - } - return colors; -} - -std::vector getPositions(jsi::Runtime &runtime, - const jsi::Value &value) { - std::vector positions; - if (!value.isNull()) { - auto jsiPositions = value.asObject(runtime).asArray(runtime); - auto size = jsiPositions.size(runtime); - positions.reserve(size); - for (int i = 0; i < size; i++) { - SkScalar position = jsiPositions.getValueAtIndex(runtime, i).asNumber(); - positions.push_back(position); - } - } - return positions; -} - class JsiSkShaderFactory : public JsiSkNativeObject { public: static constexpr const char *CLASS_NAME = "ShaderFactory"; - JSI_HOST_FUNCTION(MakeLinearGradient) { - auto p1 = - *JsiSkPoint::fromValue(runtime, arguments[0].asObject(runtime)).get(); - auto p2 = - *JsiSkPoint::fromValue(runtime, arguments[1].asObject(runtime)).get(); + std::shared_ptr + MakeLinearGradient(SkPoint p1, SkPoint p2, + JsiOptional> jsiColors, + JsiOptional> jsiPositions, + JsiOptional tileMode, + JsiOptional> matrix, + JsiOptional flag) { SkPoint pts[] = {p1, p2}; - - std::vector colors = getColors(runtime, arguments[2]); - auto colorsSize = colors.size(); - if (colorsSize < 2) { - throw std::invalid_argument("colors must have at least 2 colors"); - } - std::vector positions = getPositions(runtime, arguments[3]); - if (!positions.empty() && positions.size() != colorsSize) { - throw std::invalid_argument( - "positions must be empty or have the same size as colors"); - } - auto tileMode = getTileMode(arguments, 4, count); - auto flag = getFlag(arguments, 6, count); - auto localMatrix = getLocalMatrix(runtime, arguments, 5, count); - - SkGradient::Colors gradColors( - SkSpan(colors), - !positions.empty() - ? SkSpan(positions.data(), positions.size()) - : SkSpan(), - tileMode); - SkGradient grad(gradColors, SkGradient::Interpolation::FromFlags(flag)); + auto colors = toColors(jsiColors); + auto positions = toPositions(jsiPositions, colors.size()); + SkGradient::Colors gradColors(SkSpan(colors), toSpan(positions), + toTileMode(tileMode)); + SkGradient grad(gradColors, + SkGradient::Interpolation::FromFlags(toFlag(flag))); sk_sp gradient = - SkShaders::LinearGradient(pts, grad, localMatrix); - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(gradient))); + SkShaders::LinearGradient(pts, grad, toLocalMatrix(matrix)); + return std::make_shared(getContext(), std::move(gradient)); } - JSI_HOST_FUNCTION(MakeRadialGradient) { - auto center = - *JsiSkPoint::fromValue(runtime, arguments[0].asObject(runtime)).get(); - auto r = arguments[1].asNumber(); - - std::vector colors = getColors(runtime, arguments[2]); - auto colorsSize = colors.size(); - if (colorsSize < 2) { - throw std::invalid_argument("colors must have at least 2 colors"); - } - std::vector positions = getPositions(runtime, arguments[3]); - if (!positions.empty() && positions.size() != colorsSize) { - throw std::invalid_argument( - "positions must be empty or the same size as colors"); - } - auto tileMode = getTileMode(arguments, 4, count); - auto flag = getFlag(arguments, 6, count); - auto localMatrix = getLocalMatrix(runtime, arguments, 5, count); - - SkGradient::Colors gradColors( - SkSpan(colors), - !positions.empty() - ? SkSpan(positions.data(), positions.size()) - : SkSpan(), - tileMode); - SkGradient grad(gradColors, SkGradient::Interpolation::FromFlags(flag)); + std::shared_ptr + MakeRadialGradient(SkPoint center, double r, + JsiOptional> jsiColors, + JsiOptional> jsiPositions, + JsiOptional tileMode, + JsiOptional> matrix, + JsiOptional flag) { + auto colors = toColors(jsiColors); + auto positions = toPositions(jsiPositions, colors.size()); + SkGradient::Colors gradColors(SkSpan(colors), toSpan(positions), + toTileMode(tileMode)); + SkGradient grad(gradColors, + SkGradient::Interpolation::FromFlags(toFlag(flag))); sk_sp gradient = - SkShaders::RadialGradient(center, r, grad, localMatrix); - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(gradient))); + SkShaders::RadialGradient(center, r, grad, toLocalMatrix(matrix)); + return std::make_shared(getContext(), std::move(gradient)); } - JSI_HOST_FUNCTION(MakeSweepGradient) { - auto x = arguments[0].asNumber(); - auto y = arguments[1].asNumber(); - std::vector colors = getColors(runtime, arguments[2]); - auto colorsSize = colors.size(); - if (colorsSize < 2) { - throw std::invalid_argument("colors must have at least 2 colors"); - } - std::vector positions = getPositions(runtime, arguments[3]); - if (!positions.empty() && positions.size() != colorsSize) { - throw std::invalid_argument( - "positions must be empty or the same size as colors"); - } - auto tileMode = getTileMode(arguments, 4, count); - auto localMatrix = getLocalMatrix(runtime, arguments, 5, count); - auto flag = getFlag(arguments, 6, count); - auto startAngle = (count < 8 || arguments[7].isUndefined()) - ? 0.0f - : static_cast(arguments[7].asNumber()); - auto endAngle = (count < 9 || arguments[8].isUndefined()) - ? 360.0f - : static_cast(arguments[8].asNumber()); - - SkGradient::Colors gradColors( - SkSpan(colors), - !positions.empty() - ? SkSpan(positions.data(), positions.size()) - : SkSpan(), - tileMode); - SkGradient grad(gradColors, SkGradient::Interpolation::FromFlags(flag)); - sk_sp gradient = SkShaders::SweepGradient( - SkPoint::Make(x, y), startAngle, endAngle, grad, localMatrix); - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(gradient))); + std::shared_ptr + MakeSweepGradient(double x, double y, + JsiOptional> jsiColors, + JsiOptional> jsiPositions, + JsiOptional tileMode, + JsiOptional> matrix, + JsiOptional flag, JsiOptional start, + JsiOptional end) { + auto colors = toColors(jsiColors); + auto positions = toPositions(jsiPositions, colors.size()); + auto startAngle = + start.has_value() ? static_cast(*start) : 0.0f; + auto endAngle = end.has_value() ? static_cast(*end) : 360.0f; + SkGradient::Colors gradColors(SkSpan(colors), toSpan(positions), + toTileMode(tileMode)); + SkGradient grad(gradColors, + SkGradient::Interpolation::FromFlags(toFlag(flag))); + sk_sp gradient = + SkShaders::SweepGradient(SkPoint::Make(x, y), startAngle, endAngle, + grad, toLocalMatrix(matrix)); + return std::make_shared(getContext(), std::move(gradient)); } - JSI_HOST_FUNCTION(MakeTwoPointConicalGradient) { - auto start = - *JsiSkPoint::fromValue(runtime, arguments[0].asObject(runtime)).get(); - auto startRadius = arguments[1].asNumber(); - - auto end = - *JsiSkPoint::fromValue(runtime, arguments[2].asObject(runtime)).get(); - auto endRadius = arguments[3].asNumber(); - - std::vector colors = getColors(runtime, arguments[4]); - auto colorsSize = colors.size(); - if (colorsSize < 2) { - throw std::invalid_argument("colors must have at least 2 colors"); - } - std::vector positions = getPositions(runtime, arguments[5]); - if (!positions.empty() && positions.size() != colorsSize) { - throw std::invalid_argument( - "positions must be empty or the same size as colors"); - } - auto tileMode = getTileMode(arguments, 6, count); - auto localMatrix = getLocalMatrix(runtime, arguments, 7, count); - auto flag = getFlag(arguments, 8, count); - - SkGradient::Colors gradColors( - SkSpan(colors), - !positions.empty() - ? SkSpan(positions.data(), positions.size()) - : SkSpan(), - tileMode); - SkGradient grad(gradColors, SkGradient::Interpolation::FromFlags(flag)); + std::shared_ptr + MakeTwoPointConicalGradient(SkPoint start, double startRadius, SkPoint end, + double endRadius, + JsiOptional> jsiColors, + JsiOptional> jsiPositions, + JsiOptional tileMode, + JsiOptional> matrix, + JsiOptional flag) { + auto colors = toColors(jsiColors); + auto positions = toPositions(jsiPositions, colors.size()); + SkGradient::Colors gradColors(SkSpan(colors), toSpan(positions), + toTileMode(tileMode)); + SkGradient grad(gradColors, + SkGradient::Interpolation::FromFlags(toFlag(flag))); sk_sp gradient = SkShaders::TwoPointConicalGradient( - start, startRadius, end, endRadius, grad, localMatrix); - - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(gradient))); + start, startRadius, end, endRadius, grad, toLocalMatrix(matrix)); + return std::make_shared(getContext(), std::move(gradient)); } - JSI_HOST_FUNCTION(MakeTurbulence) { - auto baseFreqX = arguments[0].asNumber(); - auto baseFreqY = arguments[1].asNumber(); - auto octaves = arguments[2].asNumber(); - auto seed = arguments[3].asNumber(); - auto tileW = arguments[4].asNumber(); - auto tileH = arguments[5].asNumber(); + std::shared_ptr MakeTurbulence(double baseFreqX, + double baseFreqY, double octaves, + double seed, double tileW, + double tileH) { SkISize size = SkISize::Make(tileW, tileH); sk_sp gradient = SkShaders::MakeTurbulence(baseFreqX, baseFreqY, octaves, seed, &size); - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(gradient))); + return std::make_shared(getContext(), std::move(gradient)); } - JSI_HOST_FUNCTION(MakeFractalNoise) { - auto baseFreqX = arguments[0].asNumber(); - auto baseFreqY = arguments[1].asNumber(); - auto octaves = arguments[2].asNumber(); - auto seed = arguments[3].asNumber(); - auto tileW = arguments[4].asNumber(); - auto tileH = arguments[5].asNumber(); + std::shared_ptr MakeFractalNoise(double baseFreqX, + double baseFreqY, + double octaves, double seed, + double tileW, double tileH) { SkISize size = SkISize::Make(tileW, tileH); sk_sp gradient = SkShaders::MakeFractalNoise(baseFreqX, baseFreqY, octaves, seed, &size); - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(gradient))); + return std::make_shared(getContext(), std::move(gradient)); } - JSI_HOST_FUNCTION(MakeBlend) { - auto blendMode = (SkBlendMode)arguments[0].asNumber(); - auto one = JsiSkShader::fromValue(runtime, arguments[1]); - auto two = JsiSkShader::fromValue(runtime, arguments[2]); - sk_sp gradient = SkShaders::Blend(blendMode, one, two); - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(gradient))); + std::shared_ptr MakeBlend(double blendMode, sk_sp one, + sk_sp two) { + sk_sp gradient = + SkShaders::Blend(static_cast(blendMode), one, two); + return std::make_shared(getContext(), std::move(gradient)); } - JSI_HOST_FUNCTION(MakeColor) { - auto color = JsiSkColor::fromValue(runtime, arguments[0]); + std::shared_ptr MakeColor(JsiColor color) { sk_sp gradient = SkShaders::Color(color); - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(gradient))); + return std::make_shared(getContext(), std::move(gradient)); } size_t getMemoryPressure() override { return 1024; } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { - installHostMethod(runtime, prototype, "MakeLinearGradient", - &JsiSkShaderFactory::MakeLinearGradient); - installHostMethod(runtime, prototype, "MakeRadialGradient", - &JsiSkShaderFactory::MakeRadialGradient); - installHostMethod(runtime, prototype, "MakeSweepGradient", - &JsiSkShaderFactory::MakeSweepGradient); - installHostMethod(runtime, prototype, "MakeTwoPointConicalGradient", - &JsiSkShaderFactory::MakeTwoPointConicalGradient); - installHostMethod(runtime, prototype, "MakeTurbulence", - &JsiSkShaderFactory::MakeTurbulence); - installHostMethod(runtime, prototype, "MakeFractalNoise", - &JsiSkShaderFactory::MakeFractalNoise); - installHostMethod(runtime, prototype, "MakeBlend", - &JsiSkShaderFactory::MakeBlend); - installHostMethod(runtime, prototype, "MakeColor", - &JsiSkShaderFactory::MakeColor); + installMethod(runtime, prototype, "MakeLinearGradient", + &JsiSkShaderFactory::MakeLinearGradient); + installMethod(runtime, prototype, "MakeRadialGradient", + &JsiSkShaderFactory::MakeRadialGradient); + installMethod(runtime, prototype, "MakeSweepGradient", + &JsiSkShaderFactory::MakeSweepGradient); + installMethod(runtime, prototype, "MakeTwoPointConicalGradient", + &JsiSkShaderFactory::MakeTwoPointConicalGradient); + installMethod(runtime, prototype, "MakeTurbulence", + &JsiSkShaderFactory::MakeTurbulence); + installMethod(runtime, prototype, "MakeFractalNoise", + &JsiSkShaderFactory::MakeFractalNoise); + installMethod(runtime, prototype, "MakeBlend", + &JsiSkShaderFactory::MakeBlend); + installMethod(runtime, prototype, "MakeColor", + &JsiSkShaderFactory::MakeColor); } explicit JsiSkShaderFactory(std::shared_ptr context) : JsiSkNativeObject(std::move(context)) {} + +private: + static std::vector + toColors(const JsiOptional> &jsiColors) { + std::vector colors; + if (jsiColors.has_value()) { + colors.reserve(jsiColors->size()); + for (const auto &color : *jsiColors) { + colors.push_back(SkColor4f::FromColor(color)); + } + } + if (colors.size() < 2) { + throw std::invalid_argument("colors must have at least 2 colors"); + } + return colors; + } + + static std::vector + toPositions(const JsiOptional> &jsiPositions, + size_t colorsSize) { + std::vector positions = + jsiPositions.has_value() ? *jsiPositions : std::vector{}; + if (!positions.empty() && positions.size() != colorsSize) { + throw std::invalid_argument( + "positions must be empty or have the same size as colors"); + } + return positions; + } + + static SkSpan toSpan(const std::vector &positions) { + return !positions.empty() + ? SkSpan(positions.data(), positions.size()) + : SkSpan(); + } + + static SkTileMode toTileMode(const JsiOptional &tileMode) { + return tileMode.has_value() ? static_cast(*tileMode) + : SkTileMode::kClamp; + } + + static int toFlag(const JsiOptional &flag) { + return flag.has_value() ? static_cast(*flag) : 0; + } + + static SkMatrix * + toLocalMatrix(const JsiOptional> &matrix) { + return matrix.has_value() ? matrix->get() : nullptr; + } }; } // namespace RNSkia diff --git a/packages/skia/cpp/api/JsiSkSurfaceFactory.h b/packages/skia/cpp/api/JsiSkSurfaceFactory.h index 28b7531094..7d84c9fa72 100644 --- a/packages/skia/cpp/api/JsiSkSurfaceFactory.h +++ b/packages/skia/cpp/api/JsiSkSurfaceFactory.h @@ -2,6 +2,7 @@ #include #include +#include #include @@ -24,16 +25,14 @@ class JsiSkSurfaceFactory : public JsiSkNativeObject { public: static constexpr const char *CLASS_NAME = "SurfaceFactory"; - JSI_HOST_FUNCTION(Make) { - auto width = static_cast(arguments[0].asNumber()); - auto height = static_cast(arguments[1].asNumber()); + std::variant> Make(int width, + int height) { auto imageInfo = SkImageInfo::MakeN32Premul(width, height); auto surface = SkSurfaces::Raster(imageInfo); if (surface == nullptr) { - return jsi::Value::null(); + return nullptr; } - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(surface))); + return std::make_shared(getContext(), std::move(surface)); } JSI_HOST_FUNCTION(MakeOffscreen) { @@ -63,7 +62,7 @@ class JsiSkSurfaceFactory : public JsiSkNativeObject { size_t getMemoryPressure() override { return 2048; } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { - installHostMethod(runtime, prototype, "Make", &JsiSkSurfaceFactory::Make); + installMethod(runtime, prototype, "Make", &JsiSkSurfaceFactory::Make); installHostMethod(runtime, prototype, "MakeOffscreen", &JsiSkSurfaceFactory::MakeOffscreen); } diff --git a/packages/skia/cpp/api/JsiSkTextBlobFactory.h b/packages/skia/cpp/api/JsiSkTextBlobFactory.h index 44d4abdc28..05434943d9 100644 --- a/packages/skia/cpp/api/JsiSkTextBlobFactory.h +++ b/packages/skia/cpp/api/JsiSkTextBlobFactory.h @@ -1,11 +1,13 @@ #pragma once #include +#include #include #include #include +#include "JsiSkConverters.h" #include "JsiSkFont.h" #include "JsiSkNativeObjects.h" #include "JsiSkRSXform.h" @@ -26,88 +28,63 @@ class JsiSkTextBlobFactory : public JsiSkNativeObject { public: static constexpr const char *CLASS_NAME = "TextBlobFactory"; - JSI_HOST_FUNCTION(MakeFromText) { - auto str = arguments[0].asString(runtime).utf8(runtime); - auto font = JsiSkFont::fromValue(runtime, arguments[1]); + std::shared_ptr MakeFromText(std::string str, + std::shared_ptr font) { auto textBlob = SkTextBlob::MakeFromString(str.c_str(), *font); - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(textBlob))); + return std::make_shared(getContext(), std::move(textBlob)); } - JSI_HOST_FUNCTION(MakeFromGlyphs) { - auto jsiGlyphs = arguments[0].asObject(runtime).asArray(runtime); - auto font = JsiSkFont::fromValue(runtime, arguments[1]); + std::shared_ptr MakeFromGlyphs(std::vector glyphIds, + std::shared_ptr font) { int bytesPerGlyph = 2; std::vector glyphs; - int glyphsSize = static_cast(jsiGlyphs.size(runtime)); - glyphs.reserve(glyphsSize); - for (int i = 0; i < glyphsSize; i++) { - glyphs.push_back(jsiGlyphs.getValueAtIndex(runtime, i).asNumber()); + glyphs.reserve(glyphIds.size()); + for (auto glyph : glyphIds) { + glyphs.push_back(static_cast(glyph)); } auto textBlob = SkTextBlob::MakeFromText(glyphs.data(), glyphs.size() * bytesPerGlyph, *font, SkTextEncoding::kGlyphID); - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(textBlob))); + return std::make_shared(getContext(), std::move(textBlob)); } - JSI_HOST_FUNCTION(MakeFromRSXform) { - auto str = arguments[0].asString(runtime).utf8(runtime); - auto jsiRsxforms = arguments[1].asObject(runtime).asArray(runtime); - auto font = JsiSkFont::fromValue(runtime, arguments[2]); - std::vector rsxforms; - int rsxformsSize = static_cast(jsiRsxforms.size(runtime)); - rsxforms.reserve(rsxformsSize); - for (int i = 0; i < rsxformsSize; i++) { - auto rsxform = JsiSkRSXform::fromValue( - runtime, jsiRsxforms.getValueAtIndex(runtime, i)); - rsxforms.push_back(*rsxform); - } + std::shared_ptr + MakeFromRSXform(std::string str, std::vector rsxforms, + std::shared_ptr font) { auto x = SkSpan(rsxforms.data(), rsxforms.size()); auto textBlob = SkTextBlob::MakeFromRSXform(str.c_str(), str.length(), x, *font); - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(textBlob))); + return std::make_shared(getContext(), std::move(textBlob)); } - JSI_HOST_FUNCTION(MakeFromRSXformGlyphs) { - auto jsiGlyphs = arguments[0].asObject(runtime).asArray(runtime); - auto jsiRsxforms = arguments[1].asObject(runtime).asArray(runtime); - auto font = JsiSkFont::fromValue(runtime, arguments[2]); + std::shared_ptr + MakeFromRSXformGlyphs(std::vector glyphIds, + std::vector rsxforms, + std::shared_ptr font) { int bytesPerGlyph = 2; std::vector glyphs; - int glyphsSize = static_cast(jsiGlyphs.size(runtime)); - glyphs.reserve(glyphsSize); - for (int i = 0; i < glyphsSize; i++) { - glyphs.push_back(jsiGlyphs.getValueAtIndex(runtime, i).asNumber()); - } - std::vector rsxforms; - int rsxformsSize = static_cast(jsiRsxforms.size(runtime)); - rsxforms.reserve(rsxformsSize); - for (int i = 0; i < rsxformsSize; i++) { - auto rsxform = JsiSkRSXform::fromValue( - runtime, jsiRsxforms.getValueAtIndex(runtime, i)); - rsxforms.push_back(*rsxform); + glyphs.reserve(glyphIds.size()); + for (auto glyph : glyphIds) { + glyphs.push_back(static_cast(glyph)); } auto x = SkSpan(rsxforms.data(), rsxforms.size()); auto textBlob = SkTextBlob::MakeFromRSXform( glyphs.data(), glyphs.size() * bytesPerGlyph, x, *font, SkTextEncoding::kGlyphID); - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(textBlob))); + return std::make_shared(getContext(), std::move(textBlob)); } size_t getMemoryPressure() override { return 2048; } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { - installHostMethod(runtime, prototype, "MakeFromText", - &JsiSkTextBlobFactory::MakeFromText); - installHostMethod(runtime, prototype, "MakeFromGlyphs", - &JsiSkTextBlobFactory::MakeFromGlyphs); - installHostMethod(runtime, prototype, "MakeFromRSXform", - &JsiSkTextBlobFactory::MakeFromRSXform); - installHostMethod(runtime, prototype, "MakeFromRSXformGlyphs", - &JsiSkTextBlobFactory::MakeFromRSXformGlyphs); + installMethod(runtime, prototype, "MakeFromText", + &JsiSkTextBlobFactory::MakeFromText); + installMethod(runtime, prototype, "MakeFromGlyphs", + &JsiSkTextBlobFactory::MakeFromGlyphs); + installMethod(runtime, prototype, "MakeFromRSXform", + &JsiSkTextBlobFactory::MakeFromRSXform); + installMethod(runtime, prototype, "MakeFromRSXformGlyphs", + &JsiSkTextBlobFactory::MakeFromRSXformGlyphs); } explicit JsiSkTextBlobFactory(std::shared_ptr context) diff --git a/packages/skia/cpp/api/JsiSkTypeface.h b/packages/skia/cpp/api/JsiSkTypeface.h index 667c978fd1..64bf9df6da 100644 --- a/packages/skia/cpp/api/JsiSkTypeface.h +++ b/packages/skia/cpp/api/JsiSkTypeface.h @@ -1,11 +1,13 @@ #pragma once #include +#include #include #include #include +#include "JsiSkConverters.h" #include "JsiSkNativeObjects.h" #include "utils/RNSkLog.h" @@ -32,11 +34,10 @@ class JsiSkTypeface : JsiSkWrappingSkPtrNativeObject( std::move(context), std::move(typeface)) {} - JSI_HOST_FUNCTION(getGlyphIDs) { - auto str = arguments[0].asString(runtime).utf8(runtime); + std::vector getGlyphIDs(std::string str, JsiOptional numGlyphs) { int numGlyphIDs = - count > 1 && !arguments[1].isNull() && !arguments[1].isUndefined() - ? static_cast(arguments[1].asNumber()) + numGlyphs.has_value() + ? *numGlyphs : getObject()->textToGlyphs(str.c_str(), str.length(), SkTextEncoding::kUTF8, SkSpan(nullptr, 0)); @@ -45,12 +46,7 @@ class JsiSkTypeface getObject()->textToGlyphs( str.c_str(), str.length(), SkTextEncoding::kUTF8, SkSpan(static_cast(glyphIDs.data()), numGlyphIDs)); - auto jsiGlyphIDs = jsi::Array(runtime, numGlyphIDs); - for (int i = 0; i < numGlyphIDs; i++) { - jsiGlyphIDs.setValueAtIndex(runtime, i, - jsi::Value(static_cast(glyphIDs[i]))); - } - return jsiGlyphIDs; + return std::vector(glyphIDs.begin(), glyphIDs.end()); } size_t getMemoryPressure() override { @@ -85,8 +81,8 @@ class JsiSkTypeface static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostMethod(runtime, prototype, "getGlyphIDs", - &JsiSkTypeface::getGlyphIDs); + installMethod(runtime, prototype, "getGlyphIDs", + &JsiSkTypeface::getGlyphIDs); } }; diff --git a/packages/skia/cpp/api/JsiSkTypefaceFactory.h b/packages/skia/cpp/api/JsiSkTypefaceFactory.h index d5d130a6d9..f5c3624b6e 100644 --- a/packages/skia/cpp/api/JsiSkTypefaceFactory.h +++ b/packages/skia/cpp/api/JsiSkTypefaceFactory.h @@ -2,10 +2,13 @@ #include #include +#include #include +#include "JsiSkConverters.h" #include "JsiSkData.h" +#include "JsiSkFontMgrFactory.h" #include "JsiSkNativeObjects.h" #include "JsiSkTypeface.h" @@ -17,22 +20,21 @@ class JsiSkTypefaceFactory : public JsiSkNativeObject { public: static constexpr const char *CLASS_NAME = "TypefaceFactory"; - JSI_HOST_FUNCTION(MakeFreeTypeFaceFromData) { - auto data = JsiSkData::fromValue(runtime, arguments[0]); + std::variant> + MakeFreeTypeFaceFromData(sk_sp data) { auto fontMgr = JsiSkFontMgrFactory::getFontMgr(getContext()); auto typeface = fontMgr->makeFromData(std::move(data)); if (typeface == nullptr) { - return jsi::Value::null(); + return nullptr; } - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(typeface))); + return std::make_shared(getContext(), std::move(typeface)); } size_t getMemoryPressure() override { return 1024; } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { - installHostMethod(runtime, prototype, "MakeFreeTypeFaceFromData", - &JsiSkTypefaceFactory::MakeFreeTypeFaceFromData); + installMethod(runtime, prototype, "MakeFreeTypeFaceFromData", + &JsiSkTypefaceFactory::MakeFreeTypeFaceFromData); } explicit JsiSkTypefaceFactory(std::shared_ptr context) diff --git a/packages/skia/cpp/api/JsiSkTypefaceFontProvider.h b/packages/skia/cpp/api/JsiSkTypefaceFontProvider.h index 8174182fb3..01a8f2cccd 100644 --- a/packages/skia/cpp/api/JsiSkTypefaceFontProvider.h +++ b/packages/skia/cpp/api/JsiSkTypefaceFontProvider.h @@ -1,10 +1,13 @@ #pragma once #include +#include +#include #include #include +#include "JsiSkConverters.h" #include "JsiSkFontStyle.h" #include "JsiSkNativeObjects.h" #include "JsiSkTypeface.h" @@ -30,18 +33,16 @@ class JsiSkTypefaceFontProvider public: static constexpr const char *CLASS_NAME = "TypefaceFontProvider"; - JSI_HOST_FUNCTION(registerFont) { - sk_sp typeface = - JsiSkTypeface::fromValue(runtime, arguments[0]); - SkString familyName(arguments[1].asString(runtime).utf8(runtime).c_str()); + void registerFont(sk_sp typeface, std::string familyNameStr) { + SkString familyName(familyNameStr.c_str()); getObject()->registerTypeface(typeface, familyName); - return jsi::Value::undefined(); } - JSI_HOST_FUNCTION(matchFamilyStyle) { - auto name = count > 0 ? arguments[0].asString(runtime).utf8(runtime) : ""; - auto fontStyle = - count > 1 ? JsiSkFontStyle::fromValue(runtime, arguments[1]) : nullptr; + std::shared_ptr + matchFamilyStyle(std::optional nameParam, + std::optional> fontStyleParam) { + auto name = nameParam.value_or(""); + auto fontStyle = fontStyleParam.value_or(nullptr); if (name == "" || fontStyle == nullptr) { throw std::runtime_error("matchFamilyStyle requires a name and a style"); } @@ -53,17 +54,15 @@ class JsiSkTypefaceFontProvider if (!typeface) { throw std::runtime_error("Could not find font style for " + name); } - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(typeface))); + return std::make_shared(getContext(), std::move(typeface)); } - JSI_HOST_FUNCTION(countFamilies) { return getObject()->countFamilies(); } + int countFamilies() { return getObject()->countFamilies(); } - JSI_HOST_FUNCTION(getFamilyName) { - auto i = static_cast(arguments[0].asNumber()); + std::string getFamilyName(int i) { SkString name; getObject()->getFamilyName(i, &name); - return jsi::String::createFromUtf8(runtime, name.c_str()); + return std::string(name.c_str()); } JsiSkTypefaceFontProvider(std::shared_ptr context, @@ -92,14 +91,14 @@ class JsiSkTypefaceFontProvider static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostMethod(runtime, prototype, "registerFont", - &JsiSkTypefaceFontProvider::registerFont); - installHostMethod(runtime, prototype, "matchFamilyStyle", - &JsiSkTypefaceFontProvider::matchFamilyStyle); - installHostMethod(runtime, prototype, "countFamilies", - &JsiSkTypefaceFontProvider::countFamilies); - installHostMethod(runtime, prototype, "getFamilyName", - &JsiSkTypefaceFontProvider::getFamilyName); + installMethod(runtime, prototype, "registerFont", + &JsiSkTypefaceFontProvider::registerFont); + installMethod(runtime, prototype, "matchFamilyStyle", + &JsiSkTypefaceFontProvider::matchFamilyStyle); + installMethod(runtime, prototype, "countFamilies", + &JsiSkTypefaceFontProvider::countFamilies); + installMethod(runtime, prototype, "getFamilyName", + &JsiSkTypefaceFontProvider::getFamilyName); } }; diff --git a/packages/skia/cpp/api/JsiSkTypefaceFontProviderFactory.h b/packages/skia/cpp/api/JsiSkTypefaceFontProviderFactory.h index d2a7713676..2b455e6ea3 100644 --- a/packages/skia/cpp/api/JsiSkTypefaceFontProviderFactory.h +++ b/packages/skia/cpp/api/JsiSkTypefaceFontProviderFactory.h @@ -19,17 +19,16 @@ class JsiSkTypefaceFontProviderFactory public: static constexpr const char *CLASS_NAME = "TypefaceFontProviderFactory"; - JSI_HOST_FUNCTION(Make) { - return makeJsiObject( - runtime, std::make_shared( - getContext(), sk_make_sp())); + std::shared_ptr Make() { + return std::make_shared( + getContext(), sk_make_sp()); } size_t getMemoryPressure() override { return 2048; } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { - installHostMethod(runtime, prototype, "Make", - &JsiSkTypefaceFontProviderFactory::Make); + installMethod(runtime, prototype, "Make", + &JsiSkTypefaceFontProviderFactory::Make); } explicit JsiSkTypefaceFontProviderFactory( diff --git a/packages/skia/cpp/api/JsiSkVertices.h b/packages/skia/cpp/api/JsiSkVertices.h index 43366edb13..3d6abda72d 100644 --- a/packages/skia/cpp/api/JsiSkVertices.h +++ b/packages/skia/cpp/api/JsiSkVertices.h @@ -6,6 +6,7 @@ #include #include "JsiSkNativeObjects.h" +#include "JsiSkRect.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdocumentation" @@ -28,15 +29,12 @@ class JsiSkVertices : JsiSkWrappingSkPtrNativeObject( std::move(context), std::move(vertices)) {} - JSI_HOST_FUNCTION(bounds) { + std::shared_ptr bounds() { const auto &result = getObject()->bounds(); - return makeJsiObject(runtime, - std::make_shared(getContext(), result)); + return std::make_shared(getContext(), result); } - JSI_HOST_FUNCTION(uniqueID) { - return static_cast(getObject()->uniqueID()); - } + double uniqueID() { return static_cast(getObject()->uniqueID()); } size_t getMemoryPressure() override { auto vertices = getObject(); @@ -54,8 +52,8 @@ class JsiSkVertices static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostMethod(runtime, prototype, "bounds", &JsiSkVertices::bounds); - installHostMethod(runtime, prototype, "uniqueID", &JsiSkVertices::uniqueID); + installMethod(runtime, prototype, "bounds", &JsiSkVertices::bounds); + installMethod(runtime, prototype, "uniqueID", &JsiSkVertices::uniqueID); } /** diff --git a/packages/skia/cpp/api/JsiVideo.h b/packages/skia/cpp/api/JsiVideo.h index 896300d5c2..83b056e915 100644 --- a/packages/skia/cpp/api/JsiVideo.h +++ b/packages/skia/cpp/api/JsiVideo.h @@ -1,29 +1,17 @@ #pragma once #include -#include #include -#include +#include #include "JsiSkNativeObjects.h" -#include "utils/RNSkLog.h" #include -#include "JsiSkPaint.h" -#include "JsiSkPoint.h" -#include "JsiSkRect.h" -#include "JsiSkTypeface.h" +#include "JsiSkConverters.h" +#include "JsiSkImage.h" #include "rnskia/RNSkVideo.h" -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdocumentation" - -#include "include/core/SkFont.h" -#include "include/core/SkFontMetrics.h" - -#pragma clang diagnostic pop - namespace RNSkia { namespace jsi = facebook::jsi; @@ -33,83 +21,55 @@ class JsiVideo public: static constexpr const char *CLASS_NAME = "Video"; - JSI_HOST_FUNCTION(nextImage) { + std::variant> nextImage() { double timestamp = 0; - auto video = getObject(); - auto image = video->nextImage(×tamp); + auto image = getObject()->nextImage(×tamp); if (!image) { - return jsi::Value::null(); + return nullptr; } - return makeJsiObject( - runtime, std::make_shared(getContext(), std::move(image))); + return std::make_shared(getContext(), std::move(image)); } - JSI_HOST_FUNCTION(duration) { return getObject()->duration(); } + double duration() { return getObject()->duration(); } - JSI_HOST_FUNCTION(framerate) { return getObject()->framerate(); } + double framerate() { return getObject()->framerate(); } - JSI_HOST_FUNCTION(currentTime) { return getObject()->currentTime(); } + double currentTime() { return getObject()->currentTime(); } - JSI_HOST_FUNCTION(isPlaying) { return getObject()->isPlaying(); } + bool isPlaying() { return getObject()->isPlaying(); } - JSI_HOST_FUNCTION(seek) { - double timestamp = arguments[0].asNumber(); - getObject()->seek(timestamp); - return jsi::Value::undefined(); - } + void seek(double timestamp) { getObject()->seek(timestamp); } - JSI_HOST_FUNCTION(rotation) { - auto context = getContext(); - auto rot = getObject()->getRotationInDegrees(); - return jsi::Value(static_cast(rot)); + double rotation() { + return static_cast(getObject()->getRotationInDegrees()); } - JSI_HOST_FUNCTION(size) { - auto context = getContext(); - auto size = getObject()->getSize(); - auto result = jsi::Object(runtime); - result.setProperty(runtime, "width", static_cast(size.width())); - result.setProperty(runtime, "height", static_cast(size.height())); - return result; - } + SkISize size() { return getObject()->getSize(); } - JSI_HOST_FUNCTION(play) { - getObject()->play(); - return jsi::Value::undefined(); - } + void play() { getObject()->play(); } - JSI_HOST_FUNCTION(pause) { - getObject()->pause(); - return jsi::Value::undefined(); - } + void pause() { getObject()->pause(); } - JSI_HOST_FUNCTION(setVolume) { - auto volume = arguments[0].asNumber(); + void setVolume(double volume) { getObject()->setVolume(static_cast(volume)); - return jsi::Value::undefined(); } - JSI_HOST_FUNCTION(setLooping) { - auto looping = arguments[0].asBool(); - getObject()->setLooping(looping); - return jsi::Value::undefined(); - } + void setLooping(bool looping) { getObject()->setLooping(looping); } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostMethod(runtime, prototype, "nextImage", &JsiVideo::nextImage); - installHostMethod(runtime, prototype, "duration", &JsiVideo::duration); - installHostMethod(runtime, prototype, "framerate", &JsiVideo::framerate); - installHostMethod(runtime, prototype, "currentTime", - &JsiVideo::currentTime); - installHostMethod(runtime, prototype, "isPlaying", &JsiVideo::isPlaying); - installHostMethod(runtime, prototype, "seek", &JsiVideo::seek); - installHostMethod(runtime, prototype, "rotation", &JsiVideo::rotation); - installHostMethod(runtime, prototype, "size", &JsiVideo::size); - installHostMethod(runtime, prototype, "play", &JsiVideo::play); - installHostMethod(runtime, prototype, "pause", &JsiVideo::pause); - installHostMethod(runtime, prototype, "setVolume", &JsiVideo::setVolume); - installHostMethod(runtime, prototype, "setLooping", &JsiVideo::setLooping); + installMethod(runtime, prototype, "nextImage", &JsiVideo::nextImage); + installMethod(runtime, prototype, "duration", &JsiVideo::duration); + installMethod(runtime, prototype, "framerate", &JsiVideo::framerate); + installMethod(runtime, prototype, "currentTime", &JsiVideo::currentTime); + installMethod(runtime, prototype, "isPlaying", &JsiVideo::isPlaying); + installMethod(runtime, prototype, "seek", &JsiVideo::seek); + installMethod(runtime, prototype, "rotation", &JsiVideo::rotation); + installMethod(runtime, prototype, "size", &JsiVideo::size); + installMethod(runtime, prototype, "play", &JsiVideo::play); + installMethod(runtime, prototype, "pause", &JsiVideo::pause); + installMethod(runtime, prototype, "setVolume", &JsiVideo::setVolume); + installMethod(runtime, prototype, "setLooping", &JsiVideo::setLooping); } JsiVideo(std::shared_ptr context, diff --git a/packages/skia/cpp/jsi/NativeObject.h b/packages/skia/cpp/jsi/NativeObject.h index a2f8b8b536..b3f5726ede 100644 --- a/packages/skia/cpp/jsi/NativeObject.h +++ b/packages/skia/cpp/jsi/NativeObject.h @@ -392,6 +392,11 @@ class NativeObject : public jsi::NativeState, /** * Install a method on the prototype. + * + * The installers below resolve `this` with NativeObject::fromValue + * (explicitly qualified): derived classes may shadow fromValue with a + * public static of the same name that returns the wrapped inner object + * (the RNSkia wrappers do), which must not be picked up here. */ template static void installMethod(jsi::Runtime &runtime, jsi::Object &prototype, @@ -401,7 +406,7 @@ class NativeObject : public jsi::NativeState, runtime, jsi::PropNameID::forUtf8(runtime, name), sizeof...(Args), [method](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { - auto native = Derived::fromValue(rt, thisVal); + auto native = NativeObject::fromValue(rt, thisVal); return callMethod(native.get(), method, rt, args, std::index_sequence_for{}, count); }); @@ -421,7 +426,7 @@ class NativeObject : public jsi::NativeState, runtime, jsi::PropNameID::forUtf8(runtime, name), sizeof...(Args), [method](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { - auto native = Derived::fromValue(rt, thisVal); + auto native = NativeObject::fromValue(rt, thisVal); return callMethodWithRuntime(native.get(), method, rt, args, std::index_sequence_for{}, count); @@ -441,7 +446,7 @@ class NativeObject : public jsi::NativeState, 0, [getter](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { - auto native = Derived::fromValue(rt, thisVal); + auto native = NativeObject::fromValue(rt, thisVal); if constexpr (std::is_same_v) { (native.get()->*getter)(); return jsi::Value::undefined(); @@ -481,7 +486,7 @@ class NativeObject : public jsi::NativeState, if (count < 1) { throw jsi::JSError(rt, "Setter requires a value argument"); } - auto native = Derived::fromValue(rt, thisVal); + auto native = NativeObject::fromValue(rt, thisVal); auto value = rnwgpu::JSIConverter>::fromJSI( rt, args[0], false); (native.get()->*setter)(std::move(value)); @@ -528,7 +533,7 @@ class NativeObject : public jsi::NativeState, 0, [getter](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { - auto native = Derived::fromValue(rt, thisVal); + auto native = NativeObject::fromValue(rt, thisVal); ReturnType result = (native.get()->*getter)(); return rnwgpu::JSIConverter>::toJSI( rt, std::move(result)); @@ -542,7 +547,7 @@ class NativeObject : public jsi::NativeState, if (count < 1) { throw jsi::JSError(rt, "Setter requires a value argument"); } - auto native = Derived::fromValue(rt, thisVal); + auto native = NativeObject::fromValue(rt, thisVal); auto value = rnwgpu::JSIConverter>::fromJSI( rt, args[0], false); (native.get()->*setter)(std::move(value)); From 7efc0ed78d67da10ab7708a749ea2be8651dcabb Mon Sep 17 00:00:00 2001 From: William Candillon Date: Thu, 23 Jul 2026 19:44:58 +0200 Subject: [PATCH 2/3] =?UTF-8?q?chore(=F0=9F=90=99):=20Typed=20bindings=20i?= =?UTF-8?q?nstances=20(#3966)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: typed JSI bindings infra + JsiVideo/JsiSkFont exemplars Phase-2 wave 1 of the NativeObject migration: convert raw JSI_HOST_FUNCTION method bodies to typed C++ signatures converted through rnwgpu::JSIConverter. - Add cpp/api/JsiSkConverters.h: JSIConverter specializations for wrapped Skia pointer types (sk_sp, std::shared_ptr) keyed on a JsiSkWrapperFor trait, delegating to each wrapper's fromValue so plain-object fallbacks keep working. Value converters for SkRect, SkPoint, SkMatrix (fromJSI only) and SkISize. All converters are partial specializations so bodies instantiate lazily and headers stay cycle-free. - Add installChainableMethod to JsiSkNativeObject for methods returning this. - Qualify fromValue lookup in NativeObject typed installers as NativeObject::fromValue: RNSkia wrappers shadow fromValue with a public static returning the wrapped inner object. - Convert JsiVideo (all methods typed) and JsiSkFont (typed except getMetrics, which keeps a conditionally-present bounds property) as the reference pattern. * feat: typed bindings for simple factories Wave 2: convert JsiSkColorFilterFactory, JsiSkMaskFilterFactory, JsiSkPathEffectFactory, JsiSkTextBlobFactory, JsiSkTypefaceFactory, JsiSkFontMgrFactory, JsiSkRuntimeEffectFactory and JsiSkDataFactory::fromBase64 to typed signatures. Converter additions: JsiColor strong typedef (SkColor is a uint32_t typedef, so colors need their own argument type), SkRSXform value converter, and JsiOptional (omitted | undefined | null all treated as absent, matching the hasOptionalArgument pattern of the raw bindings). JsiSkFont::getGlyphIDs and MakeDash switch to JsiOptional accordingly. Still raw (cannot be typed): JsiSkDataFactory::fromURI (returns a promise built from the runtime), fromBytes (typed-array introspection), JsiSkPictureFactory::MakePicture (heterogeneous argument dispatch). * feat: typed bindings for image filter and shader factories Wave 3: convert JsiSkImageFilterFactory and JsiSkShaderFactory to typed signatures using JsiOptional for the pervasive optional input-filter/crop-rect/local-matrix arguments, plus a SkPoint3 converter for lighting filters. This also fixes a latent dangling pointer in the gradient factories: getLocalMatrix() returned the raw pointer of a temporary std::shared_ptr, which dangled when the matrix came from the plain-array fallback; the typed signatures keep the shared_ptr alive for the whole call. * feat: typed bindings for path, image and surface factories Wave 4: convert JsiSkPathFactory (all methods except Stroke, whose options object is read leniently property by property), JsiSkPathBuilderFactory, JsiSkAnimatedImageFactory, JsiSkTypefaceFontProviderFactory, and the typeable parts of JsiSkImageFactory (MakeNull, MakeImageFromEncoded, MakeImageFromNativeBuffer, MakeImage) and JsiSkSurfaceFactory (Make). Still raw: image factory promise/texture methods (MakeImageFromViewTag, MakeImageFromNativeTextureUnstable, MakeImageFromTexture, MakeTextureFromImage), SurfaceFactory::MakeOffscreen (options object), and JsiSkSVGFactory (lenient fontMgr/asset-map parsing). * feat: typed bindings for small instance classes Wave 5: convert JsiSkRect, JsiSkPoint and JsiSkRRect (typed getters via installGetter, which keeps them enumerable so the generic toJSON keeps working), JsiSkPicture::makeShader, JsiSkAnimatedImage, JsiSkRuntimeShaderBuilder, JsiSkSVG, JsiSkFontMgr, JsiSkTypefaceFontProvider, JsiSkContourMeasure, JsiSkContourMeasureIter (next returns std::optional so the JS result stays undefined, not null), JsiSkVertices and JsiSkTypeface. Still raw: JsiSkPicture::serialize (constructs a Uint8Array). * feat: typed bindings for JsiSkMatrix and JsiSkPaint Wave 6: JsiSkMatrix converts to typed chainable methods via the new installChainableMethod (the JS functions still return this); concat keeps accepting both Matrix wrappers and plain 9/16-element arrays through the shared_ptr converter. JsiSkPaint converts fully: colors go through the JsiColor strong typedef, and the nullable setShader/setColorFilter/setImageFilter/setMaskFilter/setPathEffect arguments use JsiOptional (null | undefined clear the effect, as before). * feat: typed bindings for image, surface, runtime effect and skottie Convert the remaining instance classes to typed signatures: - JsiSkImage: width/height, getImageInfo, makeShaderOptions/Cubic, makeNonTextureImage, isTextureBacked, encodeToBase64 (nullable string via a new variant converter). encodeImageData takes typed optional format/quality; encodeToBytes stays raw (Uint8Array) and builds the optionals itself; readPixels and getNativeTextureUnstable stay raw. - JsiSkImageInfo and JsiSkRSXform: typed getters (and RSXform::set). - JsiSkSurface: width/height, getCanvas, flush; makeImageSnapshot stays raw (may return the recycled image argument itself). - JsiSkRuntimeEffect: fully typed; castUniforms now takes the converted vector, getUniform returns RuntimeEffectUniform via a dedicated toJSI-only converter. - JsiSkSkottie: typed except the prop/slot getters that build heterogeneous objects (getSlotInfo, getColorSlot, getVec2Slot, getTextSlot, get*Props). The count-guard methods keep returning false on missing arguments via JsiOptional parameters. New SkSize toJSI converter preserves the fractional size() result. JsiSkData has no methods and needs no change. Behavior notes: error strings for wrong argument types change (std::runtime_error instead of jsi::JSError), and encode quality now throws on non-number values instead of silently using 100. --- packages/skia/cpp/api/JsiSkConverters.h | 28 +++ packages/skia/cpp/api/JsiSkImage.h | 132 ++++++------ packages/skia/cpp/api/JsiSkImageInfo.h | 23 +-- packages/skia/cpp/api/JsiSkRSXform.h | 33 +-- packages/skia/cpp/api/JsiSkRuntimeEffect.h | 150 ++++++-------- packages/skia/cpp/api/JsiSkSkottie.h | 230 ++++++++++----------- packages/skia/cpp/api/JsiSkSurface.h | 25 +-- 7 files changed, 296 insertions(+), 325 deletions(-) diff --git a/packages/skia/cpp/api/JsiSkConverters.h b/packages/skia/cpp/api/JsiSkConverters.h index f130c4b53b..8afc24032b 100644 --- a/packages/skia/cpp/api/JsiSkConverters.h +++ b/packages/skia/cpp/api/JsiSkConverters.h @@ -312,6 +312,34 @@ template <> struct JSIConverter { } }; +// std::string | null (nullable string results) +template <> struct JSIConverter> { + using Target = std::variant; + static Target fromJSI(jsi::Runtime &runtime, const jsi::Value &arg, + bool outOfBound) { + if (arg.isNull()) { + return Target(nullptr); + } + return Target(JSIConverter::fromJSI(runtime, arg, outOfBound)); + } + static jsi::Value toJSI(jsi::Runtime &runtime, const Target &arg) { + if (std::holds_alternative(arg)) { + return jsi::Value::null(); + } + return JSIConverter::toJSI(runtime, std::get(arg)); + } +}; + +// SkSize -> {width, height} (fractional) +template <> struct JSIConverter { + static jsi::Value toJSI(jsi::Runtime &runtime, const SkSize &size) { + jsi::Object result(runtime); + result.setProperty(runtime, "width", static_cast(size.width())); + result.setProperty(runtime, "height", static_cast(size.height())); + return result; + } +}; + // SkISize <> {width, height} template <> struct JSIConverter { static SkISize fromJSI(jsi::Runtime &runtime, const jsi::Value &arg, diff --git a/packages/skia/cpp/api/JsiSkImage.h b/packages/skia/cpp/api/JsiSkImage.h index 8551a58371..bfd9cf8ee3 100644 --- a/packages/skia/cpp/api/JsiSkImage.h +++ b/packages/skia/cpp/api/JsiSkImage.h @@ -1,9 +1,12 @@ #pragma once #include +#include #include #include +#include +#include "JsiSkConverters.h" #include "JsiSkDispatcher.h" #include "JsiSkImageInfo.h" #include "JsiSkMatrix.h" @@ -148,53 +151,42 @@ class JsiSkImage : public JsiSkWrappingSkPtrNativeObject { static constexpr const char *CLASS_NAME = "Image"; // TODO-API: Properties? - JSI_HOST_FUNCTION(width) { return static_cast(getObject()->width()); } - JSI_HOST_FUNCTION(height) { - return static_cast(getObject()->height()); - } + double width() { return static_cast(getObject()->width()); } + double height() { return static_cast(getObject()->height()); } - JSI_HOST_FUNCTION(getImageInfo) { - return JsiSkImageInfo::toValue(runtime, getContext(), - getObject()->imageInfo()); + std::shared_ptr getImageInfo() { + return std::make_shared(getContext(), + getObject()->imageInfo()); } - JSI_HOST_FUNCTION(makeShaderOptions) { - auto tmx = (SkTileMode)arguments[0].asNumber(); - auto tmy = (SkTileMode)arguments[1].asNumber(); - auto fm = (SkFilterMode)arguments[2].asNumber(); - auto mm = (SkMipmapMode)arguments[3].asNumber(); - auto m = count > 4 && !arguments[4].isUndefined() - ? JsiSkMatrix::fromValue(runtime, arguments[4]).get() - : nullptr; - auto shader = - getObject()->makeShader(tmx, tmy, SkSamplingOptions(fm, mm), m); - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(shader))); + std::shared_ptr + makeShaderOptions(double tmx, double tmy, double fm, double mm, + std::optional> m) { + auto shader = getObject()->makeShader( + static_cast(tmx), static_cast(tmy), + SkSamplingOptions(static_cast(fm), + static_cast(mm)), + m.has_value() ? m->get() : nullptr); + return std::make_shared(getContext(), std::move(shader)); } - JSI_HOST_FUNCTION(makeShaderCubic) { - auto tmx = (SkTileMode)arguments[0].asNumber(); - auto tmy = (SkTileMode)arguments[1].asNumber(); - auto B = SkDoubleToScalar(arguments[2].asNumber()); - auto C = SkDoubleToScalar(arguments[3].asNumber()); - auto m = count > 4 && !arguments[4].isUndefined() - ? JsiSkMatrix::fromValue(runtime, arguments[4]).get() - : nullptr; - auto shader = - getObject()->makeShader(tmx, tmy, SkSamplingOptions({B, C}), m); - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(shader))); + std::shared_ptr + makeShaderCubic(double tmx, double tmy, double B, double C, + std::optional> m) { + auto shader = getObject()->makeShader( + static_cast(tmx), static_cast(tmy), + SkSamplingOptions({SkDoubleToScalar(B), SkDoubleToScalar(C)}), + m.has_value() ? m->get() : nullptr); + return std::make_shared(getContext(), std::move(shader)); } - sk_sp encodeImageData(const jsi::Value *arguments, size_t count) { + sk_sp encodeImageData(JsiOptional formatParam, + JsiOptional qualityParam) { // Get optional parameters - auto format = - count >= 1 ? static_cast(arguments[0].asNumber()) - : SkEncodedImageFormat::kPNG; - - auto quality = (count >= 2 && arguments[1].isNumber()) - ? arguments[1].asNumber() - : 100.0; + auto format = formatParam.has_value() + ? static_cast(*formatParam) + : SkEncodedImageFormat::kPNG; + auto quality = qualityParam.has_value() ? *qualityParam : 100.0; auto image = getObject(); #if defined(SK_GRAPHITE) image = DawnContext::getInstance().MakeRasterImage(image); @@ -238,8 +230,17 @@ class JsiSkImage : public JsiSkWrappingSkPtrNativeObject { return data; } + // Stays raw: constructs a Uint8Array result. JSI_HOST_FUNCTION(encodeToBytes) { - auto data = encodeImageData(arguments, count); + JsiOptional format = + count >= 1 && !arguments[0].isUndefined() && !arguments[0].isNull() + ? JsiOptional(arguments[0].asNumber()) + : JsiOptional(); + JsiOptional quality = count >= 2 && arguments[1].isNumber() + ? JsiOptional( + arguments[1].asNumber()) + : JsiOptional(); + auto data = encodeImageData(format, quality); if (!data) { return jsi::Value::null(); } @@ -261,17 +262,18 @@ class JsiSkImage : public JsiSkWrappingSkPtrNativeObject { return array; } - JSI_HOST_FUNCTION(encodeToBase64) { - auto data = encodeImageData(arguments, count); + std::variant + encodeToBase64(JsiOptional format, JsiOptional quality) { + auto data = encodeImageData(format, quality); if (!data) { - return jsi::Value::null(); + return nullptr; } auto len = Base64::Encode(data->bytes(), data->size(), nullptr); auto buffer = std::string(len, 0); Base64::Encode(data->bytes(), data->size(), reinterpret_cast(&buffer[0])); - return jsi::String::createFromAscii(runtime, buffer); + return buffer; } JSI_HOST_FUNCTION(readPixels) { @@ -328,22 +330,22 @@ class JsiSkImage : public JsiSkWrappingSkPtrNativeObject { return dest; } - JSI_HOST_FUNCTION(makeNonTextureImage) { + std::variant> + makeNonTextureImage() { #if defined(SK_GRAPHITE) auto rasterImage = DawnContext::getInstance().MakeRasterImage(getObject()); #else auto grContext = getContext()->getDirectContext(); auto image = getObject(); if (!grContext) { - throw jsi::JSError(runtime, "No GPU context available."); + throw std::runtime_error("No GPU context available."); } auto rasterImage = image->makeRasterImage(grContext); #endif if (!rasterImage) { - return jsi::Value::null(); + return nullptr; } - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(rasterImage))); + return std::make_shared(getContext(), std::move(rasterImage)); } JSI_HOST_FUNCTION(getNativeTextureUnstable) { @@ -355,9 +357,7 @@ class JsiSkImage : public JsiSkWrappingSkPtrNativeObject { return JsiTextureInfo::toValue(runtime, texInfo); } - JSI_HOST_FUNCTION(isTextureBacked) { - return jsi::Value(getObject()->isTextureBacked()); - } + bool isTextureBacked() { return getObject()->isTextureBacked(); } /** Returns the underlying object from a host object of this type @@ -369,26 +369,26 @@ class JsiSkImage : public JsiSkWrappingSkPtrNativeObject { static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostMethod(runtime, prototype, "width", &JsiSkImage::width); - installHostMethod(runtime, prototype, "height", &JsiSkImage::height); - installHostMethod(runtime, prototype, "getImageInfo", - &JsiSkImage::getImageInfo); - installHostMethod(runtime, prototype, "makeShaderOptions", - &JsiSkImage::makeShaderOptions); - installHostMethod(runtime, prototype, "makeShaderCubic", - &JsiSkImage::makeShaderCubic); + installMethod(runtime, prototype, "width", &JsiSkImage::width); + installMethod(runtime, prototype, "height", &JsiSkImage::height); + installMethod(runtime, prototype, "getImageInfo", + &JsiSkImage::getImageInfo); + installMethod(runtime, prototype, "makeShaderOptions", + &JsiSkImage::makeShaderOptions); + installMethod(runtime, prototype, "makeShaderCubic", + &JsiSkImage::makeShaderCubic); installHostMethod(runtime, prototype, "encodeToBytes", &JsiSkImage::encodeToBytes); - installHostMethod(runtime, prototype, "encodeToBase64", - &JsiSkImage::encodeToBase64); + installMethod(runtime, prototype, "encodeToBase64", + &JsiSkImage::encodeToBase64); installHostMethod(runtime, prototype, "readPixels", &JsiSkImage::readPixels); - installHostMethod(runtime, prototype, "makeNonTextureImage", - &JsiSkImage::makeNonTextureImage); + installMethod(runtime, prototype, "makeNonTextureImage", + &JsiSkImage::makeNonTextureImage); installHostMethod(runtime, prototype, "getNativeTextureUnstable", &JsiSkImage::getNativeTextureUnstable); - installHostMethod(runtime, prototype, "isTextureBacked", - &JsiSkImage::isTextureBacked); + installMethod(runtime, prototype, "isTextureBacked", + &JsiSkImage::isTextureBacked); } JsiSkImage(std::shared_ptr context, diff --git a/packages/skia/cpp/api/JsiSkImageInfo.h b/packages/skia/cpp/api/JsiSkImageInfo.h index d1c23eb0f8..4956b9c95d 100644 --- a/packages/skia/cpp/api/JsiSkImageInfo.h +++ b/packages/skia/cpp/api/JsiSkImageInfo.h @@ -60,14 +60,12 @@ class JsiSkImageInfo std::move(context), imageInfo)); } - JSI_PROPERTY_GET(width) { return static_cast(getObject()->width()); } - JSI_PROPERTY_GET(height) { - return static_cast(getObject()->height()); - } - JSI_PROPERTY_GET(colorType) { + double getWidth() { return static_cast(getObject()->width()); } + double getHeight() { return static_cast(getObject()->height()); } + double getColorType() { return static_cast(getObject()->colorType()); } - JSI_PROPERTY_GET(alphaType) { + double getAlphaType() { return static_cast(getObject()->alphaType()); } @@ -77,13 +75,12 @@ class JsiSkImageInfo static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostGetter(runtime, prototype, "width", &JsiSkImageInfo::get_width); - installHostGetter(runtime, prototype, "height", - &JsiSkImageInfo::get_height); - installHostGetter(runtime, prototype, "colorType", - &JsiSkImageInfo::get_colorType); - installHostGetter(runtime, prototype, "alphaType", - &JsiSkImageInfo::get_alphaType); + installGetter(runtime, prototype, "width", &JsiSkImageInfo::getWidth); + installGetter(runtime, prototype, "height", &JsiSkImageInfo::getHeight); + installGetter(runtime, prototype, "colorType", + &JsiSkImageInfo::getColorType); + installGetter(runtime, prototype, "alphaType", + &JsiSkImageInfo::getAlphaType); } }; } // namespace RNSkia diff --git a/packages/skia/cpp/api/JsiSkRSXform.h b/packages/skia/cpp/api/JsiSkRSXform.h index f83f978554..7965a27b20 100644 --- a/packages/skia/cpp/api/JsiSkRSXform.h +++ b/packages/skia/cpp/api/JsiSkRSXform.h @@ -28,38 +28,25 @@ class JsiSkRSXform : JsiSkWrappingSharedPtrNativeObject( std::move(context), std::make_shared(rsxform)) {} - JSI_PROPERTY_GET(scos) { - return jsi::Value(SkScalarToDouble(getObject()->fSCos)); - } - JSI_PROPERTY_GET(ssin) { - return jsi::Value(SkScalarToDouble(getObject()->fSSin)); - } - JSI_PROPERTY_GET(tx) { - return jsi::Value(SkScalarToDouble(getObject()->fTx)); - } - JSI_PROPERTY_GET(ty) { - return jsi::Value(SkScalarToDouble(getObject()->fTy)); - } + double getScos() { return SkScalarToDouble(getObject()->fSCos); } + double getSsin() { return SkScalarToDouble(getObject()->fSSin); } + double getTx() { return SkScalarToDouble(getObject()->fTx); } + double getTy() { return SkScalarToDouble(getObject()->fTy); } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Woverloaded-virtual" - JSI_HOST_FUNCTION(set) { - auto scos = arguments[0].asNumber(); - auto ssin = arguments[1].asNumber(); - auto tx = arguments[2].asNumber(); - auto ty = arguments[3].asNumber(); + void set(double scos, double ssin, double tx, double ty) { getObject()->set(scos, ssin, tx, ty); - return jsi::Value::undefined(); } #pragma clang diagnostic pop static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostGetter(runtime, prototype, "scos", &JsiSkRSXform::get_scos); - installHostGetter(runtime, prototype, "ssin", &JsiSkRSXform::get_ssin); - installHostGetter(runtime, prototype, "tx", &JsiSkRSXform::get_tx); - installHostGetter(runtime, prototype, "ty", &JsiSkRSXform::get_ty); - installHostMethod(runtime, prototype, "set", &JsiSkRSXform::set); + installGetter(runtime, prototype, "scos", &JsiSkRSXform::getScos); + installGetter(runtime, prototype, "ssin", &JsiSkRSXform::getSsin); + installGetter(runtime, prototype, "tx", &JsiSkRSXform::getTx); + installGetter(runtime, prototype, "ty", &JsiSkRSXform::getTy); + installMethod(runtime, prototype, "set", &JsiSkRSXform::set); } /** diff --git a/packages/skia/cpp/api/JsiSkRuntimeEffect.h b/packages/skia/cpp/api/JsiSkRuntimeEffect.h index ad77d168e0..8d3db04f03 100644 --- a/packages/skia/cpp/api/JsiSkRuntimeEffect.h +++ b/packages/skia/cpp/api/JsiSkRuntimeEffect.h @@ -5,6 +5,7 @@ #include #include +#include "JsiSkConverters.h" #include "JsiSkMatrix.h" #include "JsiSkNativeObjects.h" #include "JsiSkShader.h" @@ -36,108 +37,95 @@ struct RuntimeEffectUniform { bool isInteger; }; +} // namespace RNSkia + +namespace rnwgpu { + +// RuntimeEffectUniform -> {columns, rows, slot, isInteger} +template <> struct JSIConverter { + static jsi::Value toJSI(jsi::Runtime &runtime, + const RNSkia::RuntimeEffectUniform &u) { + jsi::Object result(runtime); + result.setProperty(runtime, "columns", u.columns); + result.setProperty(runtime, "rows", u.rows); + result.setProperty(runtime, "slot", u.slot); + result.setProperty(runtime, "isInteger", u.isInteger); + return result; + } +}; + +} // namespace rnwgpu + +namespace RNSkia { + class JsiSkRuntimeEffect : public JsiSkWrappingSkPtrNativeObject { public: static constexpr const char *CLASS_NAME = "RuntimeEffect"; - JSI_HOST_FUNCTION(makeShader) { - auto uniforms = castUniforms(runtime, arguments[0]); - - auto matrix = - count >= 2 && !arguments[1].isUndefined() && !arguments[1].isNull() - ? JsiSkMatrix::fromValue(runtime, arguments[1]).get() - : nullptr; - - // Create and return shader as host object - auto shader = - getObject()->makeShader(std::move(uniforms), nullptr, 0, matrix); - - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(shader))); + std::shared_ptr + makeShader(std::vector uniformValues, + JsiOptional> matrix) { + auto uniforms = castUniforms(uniformValues); + auto shader = getObject()->makeShader( + std::move(uniforms), nullptr, 0, + matrix.has_value() ? matrix->get() : nullptr); + return std::make_shared(getContext(), std::move(shader)); } - JSI_HOST_FUNCTION(makeShaderWithChildren) { - auto uniforms = castUniforms(runtime, arguments[0]); - - // Children - std::vector> children; - auto jsiChildren = arguments[1].asObject(runtime).asArray(runtime); - auto jsiChildCount = jsiChildren.size(runtime); - children.reserve(jsiChildCount); - for (int i = 0; i < jsiChildCount; i++) { - auto shader = getJsiObject( - runtime, jsiChildren.getValueAtIndex(runtime, i)) - ->getObject(); - children.push_back(shader); - } - - auto matrix = - count >= 3 && !arguments[2].isUndefined() && !arguments[2].isNull() - ? JsiSkMatrix::fromValue(runtime, arguments[2]).get() - : nullptr; - - // Create and return shader as host object - auto shader = getObject()->makeShader(std::move(uniforms), children.data(), - children.size(), matrix); - - return makeJsiObject(runtime, std::make_shared( - getContext(), std::move(shader))); + std::shared_ptr + makeShaderWithChildren(std::vector uniformValues, + std::vector> children, + JsiOptional> matrix) { + auto uniforms = castUniforms(uniformValues); + auto shader = getObject()->makeShader( + std::move(uniforms), children.data(), children.size(), + matrix.has_value() ? matrix->get() : nullptr); + return std::make_shared(getContext(), std::move(shader)); } - JSI_HOST_FUNCTION(getUniformCount) { + int getUniformCount() { return static_cast(getObject()->uniforms().size()); } - JSI_HOST_FUNCTION(getUniformFloatCount) { + int getUniformFloatCount() { return static_cast(getObject()->uniformSize() / sizeof(float)); } - JSI_HOST_FUNCTION(getUniformName) { - auto i = static_cast(arguments[0].asNumber()); + std::string getUniformName(int i) { if (i < 0 || i >= getObject()->uniforms().size()) { - throw jsi::JSError(runtime, "invalid uniform index"); + throw std::runtime_error("invalid uniform index"); } auto it = getObject()->uniforms().begin() + i; - return jsi::String::createFromAscii(runtime, std::string(it->name)); + return std::string(it->name); } - JSI_HOST_FUNCTION(getUniform) { - auto i = static_cast(arguments[0].asNumber()); + RuntimeEffectUniform getUniform(int i) { if (i < 0 || i >= getObject()->uniforms().size()) { - throw jsi::JSError(runtime, "invalid uniform index"); + throw std::runtime_error("invalid uniform index"); } auto it = getObject()->uniforms().begin() + i; - auto result = jsi::Object(runtime); - RuntimeEffectUniform su = fromUniform(*it); - result.setProperty(runtime, "columns", su.columns); - result.setProperty(runtime, "rows", su.rows); - result.setProperty(runtime, "slot", su.slot); - result.setProperty(runtime, "isInteger", su.isInteger); - return result; + return fromUniform(*it); } - JSI_HOST_FUNCTION(source) { - return jsi::String::createFromAscii(runtime, getObject()->source()); - } + std::string source() { return std::string(getObject()->source()); } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostMethod(runtime, prototype, "makeShader", - &JsiSkRuntimeEffect::makeShader); - installHostMethod(runtime, prototype, "makeShaderWithChildren", - &JsiSkRuntimeEffect::makeShaderWithChildren); - installHostMethod(runtime, prototype, "getUniformCount", - &JsiSkRuntimeEffect::getUniformCount); - installHostMethod(runtime, prototype, "getUniformFloatCount", - &JsiSkRuntimeEffect::getUniformFloatCount); - installHostMethod(runtime, prototype, "getUniformName", - &JsiSkRuntimeEffect::getUniformName); - installHostMethod(runtime, prototype, "getUniform", - &JsiSkRuntimeEffect::getUniform); - installHostMethod(runtime, prototype, "source", - &JsiSkRuntimeEffect::source); + installMethod(runtime, prototype, "makeShader", + &JsiSkRuntimeEffect::makeShader); + installMethod(runtime, prototype, "makeShaderWithChildren", + &JsiSkRuntimeEffect::makeShaderWithChildren); + installMethod(runtime, prototype, "getUniformCount", + &JsiSkRuntimeEffect::getUniformCount); + installMethod(runtime, prototype, "getUniformFloatCount", + &JsiSkRuntimeEffect::getUniformFloatCount); + installMethod(runtime, prototype, "getUniformName", + &JsiSkRuntimeEffect::getUniformName); + installMethod(runtime, prototype, "getUniform", + &JsiSkRuntimeEffect::getUniform); + installMethod(runtime, prototype, "source", &JsiSkRuntimeEffect::source); } JsiSkRuntimeEffect(std::shared_ptr context, @@ -206,17 +194,13 @@ class JsiSkRuntimeEffect } private: - sk_sp castUniforms(jsi::Runtime &runtime, const jsi::Value &value) { - auto jsiUniforms = value.asObject(runtime).asArray(runtime); - auto jsiUniformsSize = jsiUniforms.size(runtime); - + sk_sp castUniforms(const std::vector &values) { // verify size of input uniforms - if (jsiUniformsSize * sizeof(float) != getObject()->uniformSize()) { - std::string msg = + if (values.size() * sizeof(float) != getObject()->uniformSize()) { + throw std::runtime_error( "Uniforms size differs from effect's uniform size. Received " + - std::to_string(jsiUniformsSize) + " expected " + - std::to_string(getObject()->uniformSize() / sizeof(float)); - throw jsi::JSError(runtime, msg.c_str()); + std::to_string(values.size()) + " expected " + + std::to_string(getObject()->uniformSize() / sizeof(float))); } auto uniforms = SkData::MakeUninitialized(getObject()->uniformSize()); @@ -228,7 +212,7 @@ class JsiSkRuntimeEffect RuntimeEffectUniform reu = fromUniform(*it); for (std::size_t j = 0; j < reu.columns * reu.rows; ++j) { const std::size_t offset = reu.slot + j; - float fValue = jsiUniforms.getValueAtIndex(runtime, offset).asNumber(); + float fValue = static_cast(values[offset]); int iValue = static_cast(fValue); auto value = reu.isInteger ? SkBits2Float(iValue) : fValue; memcpy(SkTAddOffset(uniforms->writable_data(), diff --git a/packages/skia/cpp/api/JsiSkSkottie.h b/packages/skia/cpp/api/JsiSkSkottie.h index 5bbb2f7dce..39ee6a2c88 100644 --- a/packages/skia/cpp/api/JsiSkSkottie.h +++ b/packages/skia/cpp/api/JsiSkSkottie.h @@ -1,9 +1,16 @@ #pragma once +#include +#include +#include +#include +#include + #include #include "JsiSkCanvas.h" #include "JsiSkColor.h" +#include "JsiSkConverters.h" #include "JsiSkNativeObjects.h" #include "JsiSkPoint.h" #include "JsiSkRect.h" @@ -153,84 +160,64 @@ class JsiSkSkottie static constexpr const char *CLASS_NAME = "Skottie"; // #region Properties - JSI_HOST_FUNCTION(duration) { + double duration() { return static_cast(getObject()->_animation->duration()); } - JSI_HOST_FUNCTION(fps) { - return static_cast(getObject()->_animation->fps()); - } + double fps() { return static_cast(getObject()->_animation->fps()); } // #endregion // #region Methods - JSI_HOST_FUNCTION(seekFrame) { + void seekFrame(double frame, + std::optional> rectParam) { sksg::InvalidationController ic; - getObject()->_animation->seekFrame(arguments[0].asNumber(), &ic); + getObject()->_animation->seekFrame(frame, &ic); auto bounds = ic.bounds(); - if (count >= 2) { - auto rect = JsiSkRect::fromValue(runtime, arguments[1]); - if (rect != nullptr) { - rect->setXYWH(bounds.x(), bounds.y(), bounds.width(), bounds.height()); - } + if (rectParam.has_value() && *rectParam != nullptr) { + auto rect = *rectParam; + rect->setXYWH(bounds.x(), bounds.y(), bounds.width(), bounds.height()); } - return jsi::Value::undefined(); } - JSI_HOST_FUNCTION(size) { - auto size = getObject()->_animation->size(); - jsi::Object jsiSize(runtime); - jsiSize.setProperty(runtime, "width", size.width()); - jsiSize.setProperty(runtime, "height", size.height()); - return jsiSize; - } + SkSize size() { return getObject()->_animation->size(); } - JSI_HOST_FUNCTION(render) { - auto canvas = getJsiObject(runtime, arguments[0])->getCanvas(); - if (count > 1) { - auto rect = JsiSkRect::fromValue(runtime, arguments[1]); - getObject()->_animation->render(canvas, rect.get()); + void render(std::shared_ptr jsiCanvas, + std::optional> rect) { + auto canvas = jsiCanvas->getCanvas(); + if (rect.has_value()) { + getObject()->_animation->render(canvas, rect->get()); } else { getObject()->_animation->render(canvas); } - - return jsi::Value::undefined(); } - JSI_HOST_FUNCTION(version) { - return jsi::String::createFromUtf8( - runtime, getObject()->_animation->version().c_str()); + std::string version() { + return std::string(getObject()->_animation->version().c_str()); } - JSI_HOST_FUNCTION(setColor) { - if (count < 2) { - return jsi::Value(false); + bool setColor(JsiOptional key, JsiOptional color) { + if (!key.has_value() || !color.has_value()) { + return false; } - auto key = arguments[0].asString(runtime).utf8(runtime); - auto color = JsiSkColor::fromValue(runtime, arguments[1]); - return getObject()->_propManager->setColor(key, color); + return getObject()->_propManager->setColor(*key, *color); } - JSI_HOST_FUNCTION(setOpacity) { - if (count < 2) { - return jsi::Value(false); + bool setOpacity(JsiOptional key, JsiOptional opacity) { + if (!key.has_value() || !opacity.has_value()) { + return false; } - - auto key = arguments[0].asString(runtime).utf8(runtime); - auto opacity = arguments[1].asNumber(); - return getObject()->_propManager->setOpacity(key, opacity); + return getObject()->_propManager->setOpacity(*key, *opacity); } - JSI_HOST_FUNCTION(setText) { - if (count < 3) { - return jsi::Value(false); + bool setText(JsiOptional key, JsiOptional text, + JsiOptional size) { + if (!key.has_value() || !text.has_value() || !size.has_value()) { + return false; } - auto key = arguments[0].asString(runtime).utf8(runtime); - auto text = arguments[1].asString(runtime).utf8(runtime); - auto size = arguments[2].asNumber(); // preserve all other text fields - auto t = getObject()->_propManager->getText(key); - t.fText = SkString(text); - t.fTextSize = size; - return getObject()->_propManager->setText(key, t); + auto t = getObject()->_propManager->getText(*key); + t.fText = SkString(*text); + t.fTextSize = *size; + return getObject()->_propManager->setText(*key, t); } JSI_HOST_FUNCTION(getTextProps) { @@ -251,27 +238,24 @@ class JsiSkSkottie return props; } - JSI_HOST_FUNCTION(setTransform) { - if (count < 7) { - return jsi::Value(false); + bool setTransform(JsiOptional key, JsiOptional anchor, + JsiOptional position, JsiOptional scale, + JsiOptional rotation, JsiOptional skew, + JsiOptional skewAxis) { + if (!key.has_value() || !anchor.has_value() || !position.has_value() || + !scale.has_value() || !rotation.has_value() || !skew.has_value() || + !skewAxis.has_value()) { + return false; } - auto key = arguments[0].asString(runtime).utf8(runtime); - auto anchor = JsiSkPoint::fromValue(runtime, arguments[1]); - auto position = JsiSkPoint::fromValue(runtime, arguments[2]); - auto scale = JsiSkPoint::fromValue(runtime, arguments[3]); - auto rotation = arguments[4].asNumber(); - auto skew = arguments[5].asNumber(); - auto skewAxis = arguments[6].asNumber(); - skottie::TransformPropertyValue transform; transform.fAnchorPoint = {anchor->x(), anchor->y()}; transform.fPosition = {position->x(), position->y()}; transform.fScale = {scale->x(), scale->y()}; - transform.fRotation = rotation; - transform.fSkew = skew; - transform.fSkewAxis = skewAxis; - return getObject()->_propManager->setTransform(key, transform); + transform.fRotation = *rotation; + transform.fSkew = *skew; + transform.fSkewAxis = *skewAxis; + return getObject()->_propManager->setTransform(*key, transform); } JSI_HOST_FUNCTION(getSlotInfo) { @@ -325,52 +309,47 @@ class JsiSkSkottie return slotInfoJS; } - JSI_HOST_FUNCTION(setColorSlot) { - if (count < 2) { - return jsi::Value(false); + bool setColorSlot(JsiOptional slotID, + JsiOptional color) { + if (!slotID.has_value() || !color.has_value()) { + return false; } - auto slotID = arguments[0].asString(runtime).utf8(runtime); - auto color = JsiSkColor::fromValue(runtime, arguments[1]); - return getObject()->_slotManager->setColorSlot(SkString(slotID), color); + return getObject()->_slotManager->setColorSlot(SkString(*slotID), *color); } - JSI_HOST_FUNCTION(setScalarSlot) { - if (count < 2) { - return jsi::Value(false); + bool setScalarSlot(JsiOptional slotID, + JsiOptional scalar) { + if (!slotID.has_value() || !scalar.has_value()) { + return false; } - auto slotID = arguments[0].asString(runtime).utf8(runtime); - auto scalar = arguments[1].asNumber(); - return getObject()->_slotManager->setScalarSlot(SkString(slotID), scalar); + return getObject()->_slotManager->setScalarSlot(SkString(*slotID), + *scalar); } - JSI_HOST_FUNCTION(setVec2Slot) { - if (count < 2) { - return jsi::Value(false); + bool setVec2Slot(JsiOptional slotID, + JsiOptional point) { + if (!slotID.has_value() || !point.has_value()) { + return false; } - auto slotID = arguments[0].asString(runtime).utf8(runtime); - auto point = JsiSkPoint::fromValue(runtime, arguments[1]); SkV2 vec2{point->x(), point->y()}; - return getObject()->_slotManager->setVec2Slot(SkString(slotID), vec2); + return getObject()->_slotManager->setVec2Slot(SkString(*slotID), vec2); } - JSI_HOST_FUNCTION(setTextSlot) { - if (count < 2) { - return jsi::Value(false); - } - auto key = arguments[0].asString(runtime).utf8(runtime); + // The text value argument is intentionally not declared: the raw binding + // never read it (the method is not implemented yet). + bool setTextSlot(JsiOptional key) { // TODO: Implement proper text slot setting - return jsi::Value(false); + return false; } - JSI_HOST_FUNCTION(setImageSlot) { - if (count < 2) { - return jsi::Value(false); + bool setImageSlot(JsiOptional slotID, + JsiOptional assetName) { + if (!slotID.has_value() || !assetName.has_value()) { + return false; } - auto slotID = arguments[0].asString(runtime).utf8(runtime); - auto assetName = arguments[1].asString(runtime).utf8(runtime); return getObject()->_slotManager->setImageSlot( - SkString(slotID), getObject()->_resourceProvider->loadImageAsset( - nullptr, assetName.data(), nullptr)); + SkString(*slotID), getObject()->_resourceProvider->loadImageAsset( + nullptr, assetName->data(), nullptr)); } JSI_HOST_FUNCTION(getColorSlot) { @@ -384,15 +363,15 @@ class JsiSkSkottie return jsi::Value::null(); } - JSI_HOST_FUNCTION(getScalarSlot) { - if (count < 1) { - return jsi::Value::null(); + std::variant + getScalarSlot(JsiOptional slotID) { + if (!slotID.has_value()) { + return nullptr; } - auto slotID = arguments[0].asString(runtime).utf8(runtime); - if (auto v = getObject()->_slotManager->getScalarSlot(SkString(slotID))) { - return jsi::Value(v.value()); + if (auto v = getObject()->_slotManager->getScalarSlot(SkString(*slotID))) { + return static_cast(v.value()); } - return jsi::Value::null(); + return nullptr; } JSI_HOST_FUNCTION(getVec2Slot) { @@ -551,29 +530,28 @@ class JsiSkSkottie static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostMethod(runtime, prototype, "duration", &JsiSkSkottie::duration); - installHostMethod(runtime, prototype, "fps", &JsiSkSkottie::fps); - installHostMethod(runtime, prototype, "seekFrame", - &JsiSkSkottie::seekFrame); - installHostMethod(runtime, prototype, "render", &JsiSkSkottie::render); - installHostMethod(runtime, prototype, "size", &JsiSkSkottie::size); - installHostMethod(runtime, prototype, "version", &JsiSkSkottie::version); + installMethod(runtime, prototype, "duration", &JsiSkSkottie::duration); + installMethod(runtime, prototype, "fps", &JsiSkSkottie::fps); + installMethod(runtime, prototype, "seekFrame", &JsiSkSkottie::seekFrame); + installMethod(runtime, prototype, "render", &JsiSkSkottie::render); + installMethod(runtime, prototype, "size", &JsiSkSkottie::size); + installMethod(runtime, prototype, "version", &JsiSkSkottie::version); installHostMethod(runtime, prototype, "getSlotInfo", &JsiSkSkottie::getSlotInfo); - installHostMethod(runtime, prototype, "setColorSlot", - &JsiSkSkottie::setColorSlot); - installHostMethod(runtime, prototype, "setScalarSlot", - &JsiSkSkottie::setScalarSlot); - installHostMethod(runtime, prototype, "setVec2Slot", - &JsiSkSkottie::setVec2Slot); - installHostMethod(runtime, prototype, "setTextSlot", - &JsiSkSkottie::setTextSlot); - installHostMethod(runtime, prototype, "setImageSlot", - &JsiSkSkottie::setImageSlot); + installMethod(runtime, prototype, "setColorSlot", + &JsiSkSkottie::setColorSlot); + installMethod(runtime, prototype, "setScalarSlot", + &JsiSkSkottie::setScalarSlot); + installMethod(runtime, prototype, "setVec2Slot", + &JsiSkSkottie::setVec2Slot); + installMethod(runtime, prototype, "setTextSlot", + &JsiSkSkottie::setTextSlot); + installMethod(runtime, prototype, "setImageSlot", + &JsiSkSkottie::setImageSlot); installHostMethod(runtime, prototype, "getColorSlot", &JsiSkSkottie::getColorSlot); - installHostMethod(runtime, prototype, "getScalarSlot", - &JsiSkSkottie::getScalarSlot); + installMethod(runtime, prototype, "getScalarSlot", + &JsiSkSkottie::getScalarSlot); installHostMethod(runtime, prototype, "getVec2Slot", &JsiSkSkottie::getVec2Slot); installHostMethod(runtime, prototype, "getTextSlot", @@ -586,8 +564,8 @@ class JsiSkSkottie &JsiSkSkottie::getTransformProps); installHostMethod(runtime, prototype, "getTextProps", &JsiSkSkottie::getTextProps); - installHostMethod(runtime, prototype, "setColor", &JsiSkSkottie::setColor); - installHostMethod(runtime, prototype, "setText", &JsiSkSkottie::setText); + installMethod(runtime, prototype, "setColor", &JsiSkSkottie::setColor); + installMethod(runtime, prototype, "setText", &JsiSkSkottie::setText); } // #endregion diff --git a/packages/skia/cpp/api/JsiSkSurface.h b/packages/skia/cpp/api/JsiSkSurface.h index fe7211bc44..189af001b7 100644 --- a/packages/skia/cpp/api/JsiSkSurface.h +++ b/packages/skia/cpp/api/JsiSkSurface.h @@ -7,6 +7,7 @@ #include +#include "JsiSkConverters.h" #include "JsiSkDispatcher.h" #include "JsiSkNativeObjects.h" #include "JsiTextureInfo.h" @@ -67,27 +68,25 @@ class JsiSkSurface } // TODO-API: Properties? - JSI_HOST_FUNCTION(width) { return static_cast(getObject()->width()); } - JSI_HOST_FUNCTION(height) { - return static_cast(getObject()->height()); - } + double width() { return static_cast(getObject()->width()); } + double height() { return static_cast(getObject()->height()); } - JSI_HOST_FUNCTION(getCanvas) { + std::shared_ptr getCanvas() { auto surface = getObject(); auto canvas = std::make_shared(getContext(), surface->getCanvas()); // Keep a reference to the owning surface so the canvas can read pixels back // through a snapshot on Graphite (which lacks synchronous canvas readback). canvas->setSurface(surface); - return makeJsiObject(runtime, std::move(canvas)); + return canvas; } - JSI_HOST_FUNCTION(flush) { + void flush(JsiOptional syncParam) { auto surface = getObject(); // When `sync` is true, block until the GPU has finished executing the // submitted work. Required before a native consumer on a different command // queue reads this surface's texture via getNativeTextureUnstable(). #3916 - bool sync = count > 0 && arguments[0].isBool() && arguments[0].getBool(); + bool sync = syncParam.has_value() && *syncParam; #if defined(SK_GRAPHITE) // A raster surface (e.g. Skia.Surface.Make) has no Graphite recorder; // only Graphite-backed surfaces need to snap and submit a recording. @@ -102,7 +101,6 @@ class JsiSkSurface dContext->flushAndSubmit(sync ? GrSyncCpu::kYes : GrSyncCpu::kNo); } #endif - return jsi::Value::undefined(); } JSI_HOST_FUNCTION(makeImageSnapshot) { @@ -195,13 +193,12 @@ class JsiSkSurface static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); - installHostMethod(runtime, prototype, "width", &JsiSkSurface::width); - installHostMethod(runtime, prototype, "height", &JsiSkSurface::height); - installHostMethod(runtime, prototype, "getCanvas", - &JsiSkSurface::getCanvas); + installMethod(runtime, prototype, "width", &JsiSkSurface::width); + installMethod(runtime, prototype, "height", &JsiSkSurface::height); + installMethod(runtime, prototype, "getCanvas", &JsiSkSurface::getCanvas); installHostMethod(runtime, prototype, "makeImageSnapshot", &JsiSkSurface::makeImageSnapshot); - installHostMethod(runtime, prototype, "flush", &JsiSkSurface::flush); + installMethod(runtime, prototype, "flush", &JsiSkSurface::flush); installHostMethod(runtime, prototype, "getNativeTextureUnstable", &JsiSkSurface::getNativeTextureUnstable); } From e5dbd522f8659d8c07564d5c716f7aba95db571c Mon Sep 17 00:00:00 2001 From: William Candillon Date: Thu, 23 Jul 2026 21:29:16 +0200 Subject: [PATCH 3/3] =?UTF-8?q?chore(=F0=9F=90=99):=20Typed=20JSI=20bindin?= =?UTF-8?q?gs=20(#3967)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: typed JSI bindings infra + JsiVideo/JsiSkFont exemplars Phase-2 wave 1 of the NativeObject migration: convert raw JSI_HOST_FUNCTION method bodies to typed C++ signatures converted through rnwgpu::JSIConverter. - Add cpp/api/JsiSkConverters.h: JSIConverter specializations for wrapped Skia pointer types (sk_sp, std::shared_ptr) keyed on a JsiSkWrapperFor trait, delegating to each wrapper's fromValue so plain-object fallbacks keep working. Value converters for SkRect, SkPoint, SkMatrix (fromJSI only) and SkISize. All converters are partial specializations so bodies instantiate lazily and headers stay cycle-free. - Add installChainableMethod to JsiSkNativeObject for methods returning this. - Qualify fromValue lookup in NativeObject typed installers as NativeObject::fromValue: RNSkia wrappers shadow fromValue with a public static returning the wrapped inner object. - Convert JsiVideo (all methods typed) and JsiSkFont (typed except getMetrics, which keeps a conditionally-present bounds property) as the reference pattern. * feat: typed bindings for simple factories Wave 2: convert JsiSkColorFilterFactory, JsiSkMaskFilterFactory, JsiSkPathEffectFactory, JsiSkTextBlobFactory, JsiSkTypefaceFactory, JsiSkFontMgrFactory, JsiSkRuntimeEffectFactory and JsiSkDataFactory::fromBase64 to typed signatures. Converter additions: JsiColor strong typedef (SkColor is a uint32_t typedef, so colors need their own argument type), SkRSXform value converter, and JsiOptional (omitted | undefined | null all treated as absent, matching the hasOptionalArgument pattern of the raw bindings). JsiSkFont::getGlyphIDs and MakeDash switch to JsiOptional accordingly. Still raw (cannot be typed): JsiSkDataFactory::fromURI (returns a promise built from the runtime), fromBytes (typed-array introspection), JsiSkPictureFactory::MakePicture (heterogeneous argument dispatch). * feat: typed bindings for image filter and shader factories Wave 3: convert JsiSkImageFilterFactory and JsiSkShaderFactory to typed signatures using JsiOptional for the pervasive optional input-filter/crop-rect/local-matrix arguments, plus a SkPoint3 converter for lighting filters. This also fixes a latent dangling pointer in the gradient factories: getLocalMatrix() returned the raw pointer of a temporary std::shared_ptr, which dangled when the matrix came from the plain-array fallback; the typed signatures keep the shared_ptr alive for the whole call. * feat: typed bindings for path, image and surface factories Wave 4: convert JsiSkPathFactory (all methods except Stroke, whose options object is read leniently property by property), JsiSkPathBuilderFactory, JsiSkAnimatedImageFactory, JsiSkTypefaceFontProviderFactory, and the typeable parts of JsiSkImageFactory (MakeNull, MakeImageFromEncoded, MakeImageFromNativeBuffer, MakeImage) and JsiSkSurfaceFactory (Make). Still raw: image factory promise/texture methods (MakeImageFromViewTag, MakeImageFromNativeTextureUnstable, MakeImageFromTexture, MakeTextureFromImage), SurfaceFactory::MakeOffscreen (options object), and JsiSkSVGFactory (lenient fontMgr/asset-map parsing). * feat: typed bindings for small instance classes Wave 5: convert JsiSkRect, JsiSkPoint and JsiSkRRect (typed getters via installGetter, which keeps them enumerable so the generic toJSON keeps working), JsiSkPicture::makeShader, JsiSkAnimatedImage, JsiSkRuntimeShaderBuilder, JsiSkSVG, JsiSkFontMgr, JsiSkTypefaceFontProvider, JsiSkContourMeasure, JsiSkContourMeasureIter (next returns std::optional so the JS result stays undefined, not null), JsiSkVertices and JsiSkTypeface. Still raw: JsiSkPicture::serialize (constructs a Uint8Array). * feat: typed bindings for JsiSkMatrix and JsiSkPaint Wave 6: JsiSkMatrix converts to typed chainable methods via the new installChainableMethod (the JS functions still return this); concat keeps accepting both Matrix wrappers and plain 9/16-element arrays through the shared_ptr converter. JsiSkPaint converts fully: colors go through the JsiColor strong typedef, and the nullable setShader/setColorFilter/setImageFilter/setMaskFilter/setPathEffect arguments use JsiOptional (null | undefined clear the effect, as before). * feat: typed bindings for image, surface, runtime effect and skottie Convert the remaining instance classes to typed signatures: - JsiSkImage: width/height, getImageInfo, makeShaderOptions/Cubic, makeNonTextureImage, isTextureBacked, encodeToBase64 (nullable string via a new variant converter). encodeImageData takes typed optional format/quality; encodeToBytes stays raw (Uint8Array) and builds the optionals itself; readPixels and getNativeTextureUnstable stay raw. - JsiSkImageInfo and JsiSkRSXform: typed getters (and RSXform::set). - JsiSkSurface: width/height, getCanvas, flush; makeImageSnapshot stays raw (may return the recycled image argument itself). - JsiSkRuntimeEffect: fully typed; castUniforms now takes the converted vector, getUniform returns RuntimeEffectUniform via a dedicated toJSI-only converter. - JsiSkSkottie: typed except the prop/slot getters that build heterogeneous objects (getSlotInfo, getColorSlot, getVec2Slot, getTextSlot, get*Props). The count-guard methods keep returning false on missing arguments via JsiOptional parameters. New SkSize toJSI converter preserves the fractional size() result. JsiSkData has no methods and needs no change. Behavior notes: error strings for wrong argument types change (std::runtime_error instead of jsi::JSError), and encode quality now throws on non-number values instead of silently using 100. * feat: typed bindings for JsiSkPath and JsiSkPathBuilder Convert the two chainable-heavy path classes: - Add installChainableMethodWithRuntime for chainable methods that need the calling runtime; the deprecated JsiSkPath mutators use it so they keep logging their JS-console deprecation warning while returning this for chaining. - JsiSkPathBuilder converts fully via installChainableMethod; every query and build method is typed. - JsiSkPath converts fully except stroke(), whose options object is read leniently property by property. - SkPoint gets its own converter with a toJSI producing a plain {x, y} object (what the raw getLastPt bindings returned, not a Point wrapper); toCmds returns its command list as vector>. Note: Path.equals keeps the pre-existing quirk of comparing its two arguments rather than this against one argument (the TS interface declares equals(other), so the documented one-argument call was already broken in the native binding before this change). --- packages/skia/cpp/api/JsiSkConverters.h | 19 +- packages/skia/cpp/api/JsiSkNativeObjects.h | 31 ++ packages/skia/cpp/api/JsiSkPath.h | 565 ++++++++------------- packages/skia/cpp/api/JsiSkPathBuilder.h | 402 +++++---------- 4 files changed, 398 insertions(+), 619 deletions(-) diff --git a/packages/skia/cpp/api/JsiSkConverters.h b/packages/skia/cpp/api/JsiSkConverters.h index 8afc24032b..39ac425f77 100644 --- a/packages/skia/cpp/api/JsiSkConverters.h +++ b/packages/skia/cpp/api/JsiSkConverters.h @@ -260,7 +260,6 @@ struct JSIConverter>, */ template struct JSIConverter || - std::is_same_v || std::is_same_v || std::is_same_v>> { static T fromJSI(jsi::Runtime &runtime, const jsi::Value &arg, @@ -269,6 +268,24 @@ struct JSIConverter || } }; +// SkPoint additionally converts back to a plain {x, y} object — this is what +// the raw bindings returned for point results (e.g. getLastPt), NOT a Point +// wrapper. Methods that should return a wrapper use +// std::shared_ptr instead. +template +struct JSIConverter>> { + static T fromJSI(jsi::Runtime &runtime, const jsi::Value &arg, + bool outOfBound) { + return *RNSkia::JsiSkWrapperFor_t::fromValue(runtime, arg); + } + static jsi::Value toJSI(jsi::Runtime &runtime, const T &point) { + jsi::Object result(runtime); + result.setProperty(runtime, "x", static_cast(point.x())); + result.setProperty(runtime, "y", static_cast(point.y())); + return result; + } +}; + // Colors via the JsiColor strong typedef (see above). template struct JSIConverter>> { diff --git a/packages/skia/cpp/api/JsiSkNativeObjects.h b/packages/skia/cpp/api/JsiSkNativeObjects.h index 5955911dab..413d80fe5a 100644 --- a/packages/skia/cpp/api/JsiSkNativeObjects.h +++ b/packages/skia/cpp/api/JsiSkNativeObjects.h @@ -302,6 +302,27 @@ class JsiSkNativeObject : public rnwgpu::NativeObject { prototype.setProperty(runtime, name, func); } + /** + * Chainable variant for methods that need the calling jsi::Runtime as + * their first parameter (e.g. the deprecated SkPath mutators, which log a + * warning to the JS console). + */ + template + static void installChainableMethodWithRuntime( + jsi::Runtime &runtime, jsi::Object &prototype, const char *name, + ReturnType (Derived::*method)(jsi::Runtime &, Args...)) { + auto func = jsi::Function::createFromHostFunction( + runtime, jsi::PropNameID::forUtf8(runtime, name), sizeof...(Args), + [method](jsi::Runtime &rt, const jsi::Value &thisValue, + const jsi::Value *args, size_t count) -> jsi::Value { + auto native = fromThis(rt, thisValue); + invokeChainableWithRuntime(native.get(), method, rt, args, + std::index_sequence_for{}, count); + return jsi::Value(rt, thisValue); + }); + prototype.setProperty(runtime, name, func); + } + private: // Invokes a typed member function with JSI argument conversion, discarding // the result. Used by installChainableMethod, which returns thisValue. @@ -314,6 +335,16 @@ class JsiSkNativeObject : public rnwgpu::NativeObject { runtime, args[Is], Is >= count)...); } + template + static void invokeChainableWithRuntime( + Derived *obj, ReturnType (Derived::*method)(jsi::Runtime &, Args...), + jsi::Runtime &runtime, const jsi::Value *args, std::index_sequence, + size_t count) { + (obj->*method)(runtime, + rnwgpu::JSIConverter>::fromJSI( + runtime, args[Is], Is >= count)...); + } + static void defineProperty(jsi::Runtime &runtime, jsi::Object &prototype, const char *name, jsi::Function *getter, jsi::Function *setter) { diff --git a/packages/skia/cpp/api/JsiSkPath.h b/packages/skia/cpp/api/JsiSkPath.h index 469c1f59f0..7bcedea8ef 100644 --- a/packages/skia/cpp/api/JsiSkPath.h +++ b/packages/skia/cpp/api/JsiSkPath.h @@ -2,13 +2,16 @@ #include #include +#include #include #include #include +#include #include #include +#include "JsiSkConverters.h" #include "JsiSkMatrix.h" #include "JsiSkNativeObjects.h" #include "JsiSkPoint.h" @@ -77,338 +80,233 @@ class JsiSkPath SkPath asPath() const { return getObject()->snapshot(); } public: - // Mutable building methods (deprecated) + // Mutable building methods (deprecated) — chainable, and they take the + // runtime for the JS console deprecation warning. - JSI_HOST_FUNCTION(moveTo) { + void moveTo(jsi::Runtime &runtime, double x, double y) { warnDeprecatedPathMethod(runtime, "moveTo", "Use Skia.PathBuilder.Make().moveTo() instead."); - SkScalar x = arguments[0].asNumber(); - SkScalar y = arguments[1].asNumber(); getObject()->moveTo(x, y); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(rMoveTo) { + void rMoveTo(jsi::Runtime &runtime, double x, double y) { warnDeprecatedPathMethod(runtime, "rMoveTo", "Use Skia.PathBuilder.Make().rMoveTo() instead."); - SkScalar x = arguments[0].asNumber(); - SkScalar y = arguments[1].asNumber(); - getObject()->rMoveTo({x, y}); - return thisValue.getObject(runtime); + getObject()->rMoveTo({static_cast(x), static_cast(y)}); } - JSI_HOST_FUNCTION(lineTo) { + void lineTo(jsi::Runtime &runtime, double x, double y) { warnDeprecatedPathMethod(runtime, "lineTo", "Use Skia.PathBuilder.Make().lineTo() instead."); - SkScalar x = arguments[0].asNumber(); - SkScalar y = arguments[1].asNumber(); getObject()->lineTo(x, y); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(rLineTo) { + void rLineTo(jsi::Runtime &runtime, double x, double y) { warnDeprecatedPathMethod(runtime, "rLineTo", "Use Skia.PathBuilder.Make().rLineTo() instead."); - SkScalar x = arguments[0].asNumber(); - SkScalar y = arguments[1].asNumber(); getObject()->rLineTo(x, y); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(quadTo) { + void quadTo(jsi::Runtime &runtime, double x1, double y1, double x2, + double y2) { warnDeprecatedPathMethod(runtime, "quadTo", "Use Skia.PathBuilder.Make().quadTo() instead."); - SkScalar x1 = arguments[0].asNumber(); - SkScalar y1 = arguments[1].asNumber(); - SkScalar x2 = arguments[2].asNumber(); - SkScalar y2 = arguments[3].asNumber(); getObject()->quadTo(x1, y1, x2, y2); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(rQuadTo) { + void rQuadTo(jsi::Runtime &runtime, double x1, double y1, double x2, + double y2) { warnDeprecatedPathMethod(runtime, "rQuadTo", "Use Skia.PathBuilder.Make().rQuadTo() instead."); - SkScalar x1 = arguments[0].asNumber(); - SkScalar y1 = arguments[1].asNumber(); - SkScalar x2 = arguments[2].asNumber(); - SkScalar y2 = arguments[3].asNumber(); getObject()->rQuadTo(x1, y1, x2, y2); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(conicTo) { + void conicTo(jsi::Runtime &runtime, double x1, double y1, double x2, + double y2, double w) { warnDeprecatedPathMethod(runtime, "conicTo", "Use Skia.PathBuilder.Make().conicTo() instead."); - SkScalar x1 = arguments[0].asNumber(); - SkScalar y1 = arguments[1].asNumber(); - SkScalar x2 = arguments[2].asNumber(); - SkScalar y2 = arguments[3].asNumber(); - SkScalar w = arguments[4].asNumber(); getObject()->conicTo(x1, y1, x2, y2, w); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(rConicTo) { + void rConicTo(jsi::Runtime &runtime, double x1, double y1, double x2, + double y2, double w) { warnDeprecatedPathMethod(runtime, "rConicTo", "Use Skia.PathBuilder.Make().rConicTo() instead."); - SkScalar x1 = arguments[0].asNumber(); - SkScalar y1 = arguments[1].asNumber(); - SkScalar x2 = arguments[2].asNumber(); - SkScalar y2 = arguments[3].asNumber(); - SkScalar w = arguments[4].asNumber(); getObject()->rConicTo(x1, y1, x2, y2, w); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(cubicTo) { + void cubicTo(jsi::Runtime &runtime, double x1, double y1, double x2, + double y2, double x3, double y3) { warnDeprecatedPathMethod(runtime, "cubicTo", "Use Skia.PathBuilder.Make().cubicTo() instead."); - SkScalar x1 = arguments[0].asNumber(); - SkScalar y1 = arguments[1].asNumber(); - SkScalar x2 = arguments[2].asNumber(); - SkScalar y2 = arguments[3].asNumber(); - SkScalar x3 = arguments[4].asNumber(); - SkScalar y3 = arguments[5].asNumber(); getObject()->cubicTo(x1, y1, x2, y2, x3, y3); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(rCubicTo) { + void rCubicTo(jsi::Runtime &runtime, double x1, double y1, double x2, + double y2, double x3, double y3) { warnDeprecatedPathMethod(runtime, "rCubicTo", "Use Skia.PathBuilder.Make().rCubicTo() instead."); - SkScalar x1 = arguments[0].asNumber(); - SkScalar y1 = arguments[1].asNumber(); - SkScalar x2 = arguments[2].asNumber(); - SkScalar y2 = arguments[3].asNumber(); - SkScalar x3 = arguments[4].asNumber(); - SkScalar y3 = arguments[5].asNumber(); getObject()->rCubicTo(x1, y1, x2, y2, x3, y3); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(close) { + void close(jsi::Runtime &runtime) { warnDeprecatedPathMethod(runtime, "close", "Use Skia.PathBuilder.Make().close() instead."); getObject()->close(); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(reset) { + void reset(jsi::Runtime &runtime) { warnDeprecatedPathMethod(runtime, "reset", "Use Skia.PathBuilder.Make().reset() instead."); getObject()->reset(); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(rewind) { + void rewind(jsi::Runtime &runtime) { warnDeprecatedPathMethod(runtime, "rewind", "Use Skia.PathBuilder.Make().reset() instead."); getObject()->reset(); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(addPath) { + void addPath(jsi::Runtime &runtime, std::shared_ptr src, + JsiOptional> matrix, + JsiOptional extend) { warnDeprecatedPathMethod(runtime, "addPath", "Use Skia.PathBuilder.Make().addPath() instead."); - auto src = JsiSkPath::fromValue(runtime, arguments[0]); auto srcPath = src->snapshot(); - auto matrix = - count > 1 && !arguments[1].isUndefined() && !arguments[1].isNull() - ? JsiSkMatrix::fromValue(runtime, arguments[1]) - : nullptr; - auto extend = count > 2 && arguments[2].getBool(); - auto mode = - extend ? SkPath::kExtend_AddPathMode : SkPath::kAppend_AddPathMode; - if (matrix) { - getObject()->addPath(srcPath, *matrix, mode); + auto mode = extend.has_value() && *extend ? SkPath::kExtend_AddPathMode + : SkPath::kAppend_AddPathMode; + if (matrix.has_value()) { + getObject()->addPath(srcPath, **matrix, mode); } else { getObject()->addPath(srcPath, mode); } - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(addArc) { + void addArc(jsi::Runtime &runtime, std::shared_ptr rect, + double start, double sweep) { warnDeprecatedPathMethod(runtime, "addArc", "Use Skia.PathBuilder.Make().addArc() instead."); - auto rect = JsiSkRect::fromValue(runtime, arguments[0]); - auto start = arguments[1].asNumber(); - auto sweep = arguments[2].asNumber(); getObject()->addArc(*rect, start, sweep); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(addOval) { + void addOval(jsi::Runtime &runtime, std::shared_ptr rect, + JsiOptional isCCW, JsiOptional startIndex) { warnDeprecatedPathMethod( runtime, "addOval", "Use Skia.Path.Oval() or Skia.PathBuilder.Make().addOval() instead."); - auto rect = JsiSkRect::fromValue(runtime, arguments[0]); - auto isCCW = count > 1 && arguments[1].getBool(); - auto startIndex = - count > 2 ? static_cast(arguments[2].asNumber()) : 1; - auto direction = isCCW ? SkPathDirection::kCCW : SkPathDirection::kCW; - getObject()->addOval(*rect, direction, startIndex); - return thisValue.getObject(runtime); + getObject()->addOval(*rect, toDirection(isCCW), + startIndex.has_value() + ? static_cast(*startIndex) + : 1); } - JSI_HOST_FUNCTION(addRect) { + void addRect(jsi::Runtime &runtime, std::shared_ptr rect, + JsiOptional isCCW) { warnDeprecatedPathMethod( runtime, "addRect", "Use Skia.Path.Rect() or Skia.PathBuilder.Make().addRect() instead."); - auto rect = JsiSkRect::fromValue(runtime, arguments[0]); - auto isCCW = count > 1 && arguments[1].getBool(); - auto direction = isCCW ? SkPathDirection::kCCW : SkPathDirection::kCW; - getObject()->addRect(*rect, direction); - return thisValue.getObject(runtime); + getObject()->addRect(*rect, toDirection(isCCW)); } - JSI_HOST_FUNCTION(addRRect) { + void addRRect(jsi::Runtime &runtime, std::shared_ptr rrect, + JsiOptional isCCW) { warnDeprecatedPathMethod( runtime, "addRRect", "Use Skia.Path.RRect() or Skia.PathBuilder.Make().addRRect() instead."); - auto rrect = JsiSkRRect::fromValue(runtime, arguments[0]); - auto isCCW = count > 1 && arguments[1].getBool(); - auto direction = isCCW ? SkPathDirection::kCCW : SkPathDirection::kCW; - getObject()->addRRect(*rrect, direction); - return thisValue.getObject(runtime); + getObject()->addRRect(*rrect, toDirection(isCCW)); } - JSI_HOST_FUNCTION(addCircle) { + void addCircle(jsi::Runtime &runtime, double x, double y, double r) { warnDeprecatedPathMethod( runtime, "addCircle", "Use Skia.Path.Circle() or Skia.PathBuilder.Make().addCircle() " "instead."); - SkScalar x = arguments[0].asNumber(); - SkScalar y = arguments[1].asNumber(); - SkScalar r = arguments[2].asNumber(); getObject()->addCircle(x, y, r); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(addPoly) { + void addPoly(jsi::Runtime &runtime, std::vector points, + bool close) { warnDeprecatedPathMethod( runtime, "addPoly", "Use Skia.Path.Polygon() or Skia.PathBuilder.Make().addPoly() " "instead."); - auto jsiPoints = arguments[0].asObject(runtime).asArray(runtime); - auto close = arguments[1].getBool(); - auto pointsSize = static_cast(jsiPoints.size(runtime)); - std::vector points; - points.reserve(pointsSize); - for (int i = 0; i < pointsSize; i++) { - auto pt = - JsiSkPoint::fromValue(runtime, jsiPoints.getValueAtIndex(runtime, i)); - points.push_back(*pt); - } getObject()->addPolygon(SkSpan(points.data(), points.size()), close); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(arcToOval) { + void arcToOval(jsi::Runtime &runtime, std::shared_ptr rect, + double start, double sweep, bool forceMoveTo) { warnDeprecatedPathMethod( runtime, "arcToOval", "Use Skia.PathBuilder.Make().arcToOval() instead."); - auto rect = JsiSkRect::fromValue(runtime, arguments[0]); - auto start = arguments[1].asNumber(); - auto sweep = arguments[2].asNumber(); - auto forceMoveTo = arguments[3].getBool(); getObject()->arcTo(*rect, start, sweep, forceMoveTo); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(arcToRotated) { + void arcToRotated(jsi::Runtime &runtime, double rx, double ry, + double xAxisRotate, bool useSmallArc, bool isCCW, double x, + double y) { warnDeprecatedPathMethod( runtime, "arcToRotated", "Use Skia.PathBuilder.Make().arcToRotated() instead."); - SkScalar rx = arguments[0].asNumber(); - SkScalar ry = arguments[1].asNumber(); - SkScalar xAxisRotate = arguments[2].asNumber(); - auto useSmallArc = arguments[3].getBool(); - auto isCCW = arguments[4].getBool(); - SkScalar x = arguments[5].asNumber(); - SkScalar y = arguments[6].asNumber(); auto arcSize = useSmallArc ? SkPathBuilder::kSmall_ArcSize : SkPathBuilder::kLarge_ArcSize; auto sweep = isCCW ? SkPathDirection::kCCW : SkPathDirection::kCW; getObject()->arcTo(SkPoint::Make(rx, ry), xAxisRotate, arcSize, sweep, SkPoint::Make(x, y)); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(rArcTo) { + void rArcTo(jsi::Runtime &runtime, double rx, double ry, double xAxisRotate, + bool useSmallArc, bool isCCW, double dx, double dy) { warnDeprecatedPathMethod(runtime, "rArcTo", "Use Skia.PathBuilder.Make().rArcTo() instead."); - SkScalar rx = arguments[0].asNumber(); - SkScalar ry = arguments[1].asNumber(); - SkScalar xAxisRotate = arguments[2].asNumber(); - auto useSmallArc = arguments[3].getBool(); - auto isCCW = arguments[4].getBool(); - SkScalar dx = arguments[5].asNumber(); - SkScalar dy = arguments[6].asNumber(); auto arcSize = useSmallArc ? SkPathBuilder::kSmall_ArcSize : SkPathBuilder::kLarge_ArcSize; auto sweep = isCCW ? SkPathDirection::kCCW : SkPathDirection::kCW; SkVector dxdy(dx, dy); SkPoint r(rx, ry); getObject()->rArcTo(r, xAxisRotate, arcSize, sweep, dxdy); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(arcToTangent) { + void arcToTangent(jsi::Runtime &runtime, double x1, double y1, double x2, + double y2, double r) { warnDeprecatedPathMethod( runtime, "arcToTangent", "Use Skia.PathBuilder.Make().arcToTangent() instead."); - SkScalar x1 = arguments[0].asNumber(); - SkScalar y1 = arguments[1].asNumber(); - SkScalar x2 = arguments[2].asNumber(); - SkScalar y2 = arguments[3].asNumber(); - SkScalar r = arguments[4].asNumber(); getObject()->arcTo(SkPoint::Make(x1, y1), SkPoint::Make(x2, y2), r); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(setFillType) { + void setFillType(jsi::Runtime &runtime, double ft) { warnDeprecatedPathMethod( runtime, "setFillType", "Use Skia.PathBuilder.Make().setFillType() instead."); - auto ft = arguments[0].asNumber(); getObject()->setFillType(static_cast(static_cast(ft))); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(setIsVolatile) { + void setIsVolatile(jsi::Runtime &runtime, bool v) { warnDeprecatedPathMethod( runtime, "setIsVolatile", "Use Skia.PathBuilder.Make().setIsVolatile() instead."); - auto v = arguments[0].getBool(); getObject()->setIsVolatile(v); - return thisValue.getObject(runtime); } // Mutable transform methods (deprecated) - JSI_HOST_FUNCTION(transform) { + void transform(jsi::Runtime &runtime, std::shared_ptr m3) { warnDeprecatedPathMethod( runtime, "transform", "Use Skia.PathBuilder.Make().transform() instead."); - auto m3 = *JsiSkMatrix::fromValue(runtime, arguments[0]); - getObject()->transform(m3); - return thisValue.getObject(runtime); + getObject()->transform(*m3); } - JSI_HOST_FUNCTION(offset) { + void offset(jsi::Runtime &runtime, double dx, double dy) { warnDeprecatedPathMethod(runtime, "offset", "Use Skia.PathBuilder.Make().offset() instead."); - SkScalar dx = arguments[0].asNumber(); - SkScalar dy = arguments[1].asNumber(); getObject()->offset(dx, dy); - return thisValue.getObject(runtime); } // Mutable path operations (deprecated) - JSI_HOST_FUNCTION(simplify) { + void simplify(jsi::Runtime &runtime) { warnDeprecatedPathMethod(runtime, "simplify", "Use Skia.Path.Simplify(path) instead."); auto path = asPath(); @@ -416,25 +314,22 @@ class JsiSkPath if (result.has_value()) { *getObject() = SkPathBuilder(result.value()); } - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(op) { + void op(jsi::Runtime &runtime, std::shared_ptr path2, + double pathOp) { warnDeprecatedPathMethod(runtime, "op", "Use Skia.Path.MakeFromOp() instead."); - auto path2 = JsiSkPath::fromValue(runtime, arguments[0]); - auto pathOp = - static_cast(static_cast(arguments[1].asNumber())); auto p1 = asPath(); auto p2 = path2->snapshot(); - auto result = ::Op(p1, p2, pathOp); + auto result = + ::Op(p1, p2, static_cast(static_cast(pathOp))); if (result.has_value()) { *getObject() = SkPathBuilder(result.value()); } - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(makeAsWinding) { + void makeAsWinding(jsi::Runtime &runtime) { warnDeprecatedPathMethod(runtime, "makeAsWinding", "Use Skia.Path.AsWinding(path) instead."); auto path = asPath(); @@ -442,17 +337,14 @@ class JsiSkPath if (result.has_value()) { *getObject() = SkPathBuilder(result.value()); } - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(dash) { + void dash(jsi::Runtime &runtime, double on, double off, double phase) { warnDeprecatedPathMethod( runtime, "dash", "Use Skia.Path.Dash(path, on, off, phase) instead."); auto path = asPath(); - SkScalar on = arguments[0].asNumber(); - SkScalar off = arguments[1].asNumber(); - auto phase = arguments[2].asNumber(); - SkScalar intervals[] = {on, off}; + SkScalar intervals[] = {static_cast(on), + static_cast(off)}; auto pe = SkDashPathEffect::Make(SkSpan(intervals, 2), phase); if (pe) { SkStrokeRec rec(SkStrokeRec::InitStyle::kHairline_InitStyle); @@ -461,9 +353,9 @@ class JsiSkPath *getObject() = std::move(resultBuilder); } } - return thisValue.getObject(runtime); } + // Stays raw: the options object is read leniently property by property. JSI_HOST_FUNCTION(stroke) { warnDeprecatedPathMethod(runtime, "stroke", "Use Skia.Path.Stroke(path, opts) instead."); @@ -505,16 +397,14 @@ class JsiSkPath return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(trim) { + void trim(jsi::Runtime &runtime, double startValue, double endValue, + bool isComplement) { warnDeprecatedPathMethod( runtime, "trim", "Use Skia.Path.Trim(path, start, end, isComplement) instead."); auto path = asPath(); - float start = - std::clamp(static_cast(arguments[0].asNumber()), 0.0f, 1.0f); - float end = - std::clamp(static_cast(arguments[1].asNumber()), 0.0f, 1.0f); - auto isComplement = arguments[2].getBool(); + float start = std::clamp(static_cast(startValue), 0.0f, 1.0f); + float end = std::clamp(static_cast(endValue), 0.0f, 1.0f); auto mode = isComplement ? SkTrimPathEffect::Mode::kInverted : SkTrimPathEffect::Mode::kNormal; auto pe = SkTrimPathEffect::Make(start, end, mode); @@ -525,106 +415,77 @@ class JsiSkPath *getObject() = std::move(resultBuilder); } } - return thisValue.getObject(runtime); } // Query methods - JSI_HOST_FUNCTION(computeTightBounds) { + std::shared_ptr computeTightBounds() { auto path = asPath(); - auto result = path.computeTightBounds(); - return makeJsiObject(runtime, - std::make_shared(getContext(), result)); + return std::make_shared(getContext(), + path.computeTightBounds()); } - JSI_HOST_FUNCTION(getBounds) { - auto result = getObject()->computeBounds(); - return makeJsiObject(runtime, - std::make_shared(getContext(), result)); + std::shared_ptr getBounds() { + return std::make_shared(getContext(), + getObject()->computeBounds()); } - JSI_HOST_FUNCTION(contains) { - auto x = arguments[0].asNumber(); - auto y = arguments[1].asNumber(); - return jsi::Value(asPath().contains(x, y)); - } + bool contains(double x, double y) { return asPath().contains(x, y); } - JSI_HOST_FUNCTION(getFillType) { - auto fillType = getObject()->fillType(); - return jsi::Value(static_cast(fillType)); - } + int getFillType() { return static_cast(getObject()->fillType()); } - JSI_HOST_FUNCTION(isVolatile) { return jsi::Value(asPath().isVolatile()); } + bool isVolatile() { return asPath().isVolatile(); } - JSI_HOST_FUNCTION(getPoint) { - auto index = arguments[0].asNumber(); + std::shared_ptr getPoint(int index) { auto point = asPath().getPoint(index); - return makeJsiObject(runtime, - std::make_shared(getContext(), point)); + return std::make_shared(getContext(), point); } - JSI_HOST_FUNCTION(isEmpty) { return jsi::Value(getObject()->isEmpty()); } + bool isEmpty() { return getObject()->isEmpty(); } - JSI_HOST_FUNCTION(countPoints) { - auto points = asPath().countPoints(); - return jsi::Value(points); - } + int countPoints() { return asPath().countPoints(); } - JSI_HOST_FUNCTION(getLastPt) { + SkPoint getLastPt() { auto last = getObject()->getLastPt(); - auto point = jsi::Object(runtime); - if (last.has_value()) { - point.setProperty(runtime, "x", static_cast(last->fX)); - point.setProperty(runtime, "y", static_cast(last->fY)); - } else { - point.setProperty(runtime, "x", 0.0); - point.setProperty(runtime, "y", 0.0); - } - return point; + return last.value_or(SkPoint::Make(0, 0)); } - JSI_HOST_FUNCTION(toSVGString) { + std::string toSVGString() { auto path = asPath(); auto s = SkParsePath::ToSVGString(path); - return jsi::String::createFromUtf8(runtime, s.c_str()); + return std::string(s.c_str()); } - JSI_HOST_FUNCTION(equals) { - auto p1 = JsiSkPath::fromValue(runtime, arguments[0]); - auto p2 = JsiSkPath::fromValue(runtime, arguments[1]); - return jsi::Value(p1->snapshot() == p2->snapshot()); + bool equals(std::shared_ptr p1, + std::shared_ptr p2) { + return p1->snapshot() == p2->snapshot(); } - JSI_HOST_FUNCTION(copy) { - auto path = asPath(); - return makeJsiObject(runtime, - std::make_shared(getContext(), path)); + std::shared_ptr copy() { + return std::make_shared(getContext(), asPath()); } - JSI_HOST_FUNCTION(isInterpolatable) { - auto path2 = JsiSkPath::fromValue(runtime, arguments[0]); + bool isInterpolatable(std::shared_ptr path2) { auto p1 = asPath(); auto p2 = path2->snapshot(); return p1.isInterpolatable(p2); } - JSI_HOST_FUNCTION(interpolate) { - auto path2 = JsiSkPath::fromValue(runtime, arguments[0]); - auto weight = arguments[1].asNumber(); + std::variant> + interpolate(std::shared_ptr path2, double weight) { auto p1 = asPath(); auto p2 = path2->snapshot(); SkPath result; auto succeed = p1.interpolate(p2, weight, &result); if (!succeed) { - return jsi::Value::null(); + return nullptr; } - return makeJsiObject( - runtime, std::make_shared(getContext(), std::move(result))); + return std::make_shared(getContext(), std::move(result)); } - JSI_HOST_FUNCTION(toCmds) { + std::vector> toCmds() { auto path = asPath(); - std::vector cmdList; + std::vector> cmds; SkPoint pts[4]; SkPath::Iter iter(path, false); SkPath::Verb verb; @@ -632,141 +493,141 @@ class JsiSkPath while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { switch (verb) { case SkPath::kMove_Verb: { - auto cmd = jsi::Array(runtime, 3); - cmd.setValueAtIndex(runtime, 0, static_cast(MOVE)); - cmd.setValueAtIndex(runtime, 1, static_cast(pts[0].x())); - cmd.setValueAtIndex(runtime, 2, static_cast(pts[0].y())); - cmdList.push_back(std::move(cmd)); + cmds.push_back({static_cast(MOVE), + static_cast(pts[0].x()), + static_cast(pts[0].y())}); break; } case SkPath::kLine_Verb: { - auto cmd = jsi::Array(runtime, 3); - cmd.setValueAtIndex(runtime, 0, static_cast(LINE)); - cmd.setValueAtIndex(runtime, 1, static_cast(pts[1].x())); - cmd.setValueAtIndex(runtime, 2, static_cast(pts[1].y())); - cmdList.push_back(std::move(cmd)); + cmds.push_back({static_cast(LINE), + static_cast(pts[1].x()), + static_cast(pts[1].y())}); break; } case SkPath::kQuad_Verb: { - auto cmd = jsi::Array(runtime, 5); - cmd.setValueAtIndex(runtime, 0, static_cast(QUAD)); - cmd.setValueAtIndex(runtime, 1, static_cast(pts[1].x())); - cmd.setValueAtIndex(runtime, 2, static_cast(pts[1].y())); - cmd.setValueAtIndex(runtime, 3, static_cast(pts[2].x())); - cmd.setValueAtIndex(runtime, 4, static_cast(pts[2].y())); - cmdList.push_back(std::move(cmd)); + cmds.push_back( + {static_cast(QUAD), static_cast(pts[1].x()), + static_cast(pts[1].y()), static_cast(pts[2].x()), + static_cast(pts[2].y())}); break; } case SkPath::kConic_Verb: { - auto cmd = jsi::Array(runtime, 6); - cmd.setValueAtIndex(runtime, 0, static_cast(CONIC)); - cmd.setValueAtIndex(runtime, 1, static_cast(pts[1].x())); - cmd.setValueAtIndex(runtime, 2, static_cast(pts[1].y())); - cmd.setValueAtIndex(runtime, 3, static_cast(pts[2].x())); - cmd.setValueAtIndex(runtime, 4, static_cast(pts[2].y())); - cmd.setValueAtIndex(runtime, 5, - static_cast(iter.conicWeight())); - cmdList.push_back(std::move(cmd)); + cmds.push_back( + {static_cast(CONIC), static_cast(pts[1].x()), + static_cast(pts[1].y()), static_cast(pts[2].x()), + static_cast(pts[2].y()), + static_cast(iter.conicWeight())}); break; } case SkPath::kCubic_Verb: { - auto cmd = jsi::Array(runtime, 7); - cmd.setValueAtIndex(runtime, 0, static_cast(CUBIC)); - cmd.setValueAtIndex(runtime, 1, static_cast(pts[1].x())); - cmd.setValueAtIndex(runtime, 2, static_cast(pts[1].y())); - cmd.setValueAtIndex(runtime, 3, static_cast(pts[2].x())); - cmd.setValueAtIndex(runtime, 4, static_cast(pts[2].y())); - cmd.setValueAtIndex(runtime, 5, static_cast(pts[3].x())); - cmd.setValueAtIndex(runtime, 6, static_cast(pts[3].y())); - cmdList.push_back(std::move(cmd)); + cmds.push_back( + {static_cast(CUBIC), static_cast(pts[1].x()), + static_cast(pts[1].y()), static_cast(pts[2].x()), + static_cast(pts[2].y()), static_cast(pts[3].x()), + static_cast(pts[3].y())}); break; } case SkPath::kClose_Verb: { - auto cmd = jsi::Array(runtime, 1); - cmd.setValueAtIndex(runtime, 0, static_cast(CLOSE)); - cmdList.push_back(std::move(cmd)); + cmds.push_back({static_cast(CLOSE)}); break; } default: break; } } - - // Create the jsi::Array with the exact size - auto cmds = jsi::Array(runtime, cmdList.size()); - for (size_t i = 0; i < cmdList.size(); ++i) { - cmds.setValueAtIndex(runtime, i, cmdList[i]); - } - return cmds; } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); // Mutable building methods - installHostMethod(runtime, prototype, "moveTo", &JsiSkPath::moveTo); - installHostMethod(runtime, prototype, "rMoveTo", &JsiSkPath::rMoveTo); - installHostMethod(runtime, prototype, "lineTo", &JsiSkPath::lineTo); - installHostMethod(runtime, prototype, "rLineTo", &JsiSkPath::rLineTo); - installHostMethod(runtime, prototype, "quadTo", &JsiSkPath::quadTo); - installHostMethod(runtime, prototype, "rQuadTo", &JsiSkPath::rQuadTo); - installHostMethod(runtime, prototype, "conicTo", &JsiSkPath::conicTo); - installHostMethod(runtime, prototype, "rConicTo", &JsiSkPath::rConicTo); - installHostMethod(runtime, prototype, "cubicTo", &JsiSkPath::cubicTo); - installHostMethod(runtime, prototype, "rCubicTo", &JsiSkPath::rCubicTo); - installHostMethod(runtime, prototype, "close", &JsiSkPath::close); - installHostMethod(runtime, prototype, "reset", &JsiSkPath::reset); - installHostMethod(runtime, prototype, "rewind", &JsiSkPath::rewind); - installHostMethod(runtime, prototype, "addPath", &JsiSkPath::addPath); - installHostMethod(runtime, prototype, "addArc", &JsiSkPath::addArc); - installHostMethod(runtime, prototype, "addOval", &JsiSkPath::addOval); - installHostMethod(runtime, prototype, "addRect", &JsiSkPath::addRect); - installHostMethod(runtime, prototype, "addRRect", &JsiSkPath::addRRect); - installHostMethod(runtime, prototype, "addCircle", &JsiSkPath::addCircle); - installHostMethod(runtime, prototype, "addPoly", &JsiSkPath::addPoly); - installHostMethod(runtime, prototype, "arcToOval", &JsiSkPath::arcToOval); - installHostMethod(runtime, prototype, "arcToRotated", - &JsiSkPath::arcToRotated); - installHostMethod(runtime, prototype, "rArcTo", &JsiSkPath::rArcTo); - installHostMethod(runtime, prototype, "arcToTangent", - &JsiSkPath::arcToTangent); - installHostMethod(runtime, prototype, "setFillType", - &JsiSkPath::setFillType); - installHostMethod(runtime, prototype, "setIsVolatile", - &JsiSkPath::setIsVolatile); + installChainableMethodWithRuntime(runtime, prototype, "moveTo", + &JsiSkPath::moveTo); + installChainableMethodWithRuntime(runtime, prototype, "rMoveTo", + &JsiSkPath::rMoveTo); + installChainableMethodWithRuntime(runtime, prototype, "lineTo", + &JsiSkPath::lineTo); + installChainableMethodWithRuntime(runtime, prototype, "rLineTo", + &JsiSkPath::rLineTo); + installChainableMethodWithRuntime(runtime, prototype, "quadTo", + &JsiSkPath::quadTo); + installChainableMethodWithRuntime(runtime, prototype, "rQuadTo", + &JsiSkPath::rQuadTo); + installChainableMethodWithRuntime(runtime, prototype, "conicTo", + &JsiSkPath::conicTo); + installChainableMethodWithRuntime(runtime, prototype, "rConicTo", + &JsiSkPath::rConicTo); + installChainableMethodWithRuntime(runtime, prototype, "cubicTo", + &JsiSkPath::cubicTo); + installChainableMethodWithRuntime(runtime, prototype, "rCubicTo", + &JsiSkPath::rCubicTo); + installChainableMethodWithRuntime(runtime, prototype, "close", + &JsiSkPath::close); + installChainableMethodWithRuntime(runtime, prototype, "reset", + &JsiSkPath::reset); + installChainableMethodWithRuntime(runtime, prototype, "rewind", + &JsiSkPath::rewind); + installChainableMethodWithRuntime(runtime, prototype, "addPath", + &JsiSkPath::addPath); + installChainableMethodWithRuntime(runtime, prototype, "addArc", + &JsiSkPath::addArc); + installChainableMethodWithRuntime(runtime, prototype, "addOval", + &JsiSkPath::addOval); + installChainableMethodWithRuntime(runtime, prototype, "addRect", + &JsiSkPath::addRect); + installChainableMethodWithRuntime(runtime, prototype, "addRRect", + &JsiSkPath::addRRect); + installChainableMethodWithRuntime(runtime, prototype, "addCircle", + &JsiSkPath::addCircle); + installChainableMethodWithRuntime(runtime, prototype, "addPoly", + &JsiSkPath::addPoly); + installChainableMethodWithRuntime(runtime, prototype, "arcToOval", + &JsiSkPath::arcToOval); + installChainableMethodWithRuntime(runtime, prototype, "arcToRotated", + &JsiSkPath::arcToRotated); + installChainableMethodWithRuntime(runtime, prototype, "rArcTo", + &JsiSkPath::rArcTo); + installChainableMethodWithRuntime(runtime, prototype, "arcToTangent", + &JsiSkPath::arcToTangent); + installChainableMethodWithRuntime(runtime, prototype, "setFillType", + &JsiSkPath::setFillType); + installChainableMethodWithRuntime(runtime, prototype, "setIsVolatile", + &JsiSkPath::setIsVolatile); // Mutable transform methods - installHostMethod(runtime, prototype, "transform", &JsiSkPath::transform); - installHostMethod(runtime, prototype, "offset", &JsiSkPath::offset); + installChainableMethodWithRuntime(runtime, prototype, "transform", + &JsiSkPath::transform); + installChainableMethodWithRuntime(runtime, prototype, "offset", + &JsiSkPath::offset); // Mutable path operations - installHostMethod(runtime, prototype, "simplify", &JsiSkPath::simplify); - installHostMethod(runtime, prototype, "op", &JsiSkPath::op); - installHostMethod(runtime, prototype, "makeAsWinding", - &JsiSkPath::makeAsWinding); - installHostMethod(runtime, prototype, "dash", &JsiSkPath::dash); + installChainableMethodWithRuntime(runtime, prototype, "simplify", + &JsiSkPath::simplify); + installChainableMethodWithRuntime(runtime, prototype, "op", + &JsiSkPath::op); + installChainableMethodWithRuntime(runtime, prototype, "makeAsWinding", + &JsiSkPath::makeAsWinding); + installChainableMethodWithRuntime(runtime, prototype, "dash", + &JsiSkPath::dash); installHostMethod(runtime, prototype, "stroke", &JsiSkPath::stroke); - installHostMethod(runtime, prototype, "trim", &JsiSkPath::trim); + installChainableMethodWithRuntime(runtime, prototype, "trim", + &JsiSkPath::trim); // Query methods - installHostMethod(runtime, prototype, "computeTightBounds", - &JsiSkPath::computeTightBounds); - installHostMethod(runtime, prototype, "getBounds", &JsiSkPath::getBounds); - installHostMethod(runtime, prototype, "contains", &JsiSkPath::contains); - installHostMethod(runtime, prototype, "getFillType", - &JsiSkPath::getFillType); - installHostMethod(runtime, prototype, "isVolatile", &JsiSkPath::isVolatile); - installHostMethod(runtime, prototype, "getPoint", &JsiSkPath::getPoint); - installHostMethod(runtime, prototype, "isEmpty", &JsiSkPath::isEmpty); - installHostMethod(runtime, prototype, "countPoints", - &JsiSkPath::countPoints); - installHostMethod(runtime, prototype, "getLastPt", &JsiSkPath::getLastPt); - installHostMethod(runtime, prototype, "toSVGString", - &JsiSkPath::toSVGString); - installHostMethod(runtime, prototype, "equals", &JsiSkPath::equals); - installHostMethod(runtime, prototype, "copy", &JsiSkPath::copy); - installHostMethod(runtime, prototype, "isInterpolatable", - &JsiSkPath::isInterpolatable); - installHostMethod(runtime, prototype, "interpolate", - &JsiSkPath::interpolate); - installHostMethod(runtime, prototype, "toCmds", &JsiSkPath::toCmds); + installMethod(runtime, prototype, "computeTightBounds", + &JsiSkPath::computeTightBounds); + installMethod(runtime, prototype, "getBounds", &JsiSkPath::getBounds); + installMethod(runtime, prototype, "contains", &JsiSkPath::contains); + installMethod(runtime, prototype, "getFillType", &JsiSkPath::getFillType); + installMethod(runtime, prototype, "isVolatile", &JsiSkPath::isVolatile); + installMethod(runtime, prototype, "getPoint", &JsiSkPath::getPoint); + installMethod(runtime, prototype, "isEmpty", &JsiSkPath::isEmpty); + installMethod(runtime, prototype, "countPoints", &JsiSkPath::countPoints); + installMethod(runtime, prototype, "getLastPt", &JsiSkPath::getLastPt); + installMethod(runtime, prototype, "toSVGString", &JsiSkPath::toSVGString); + installMethod(runtime, prototype, "equals", &JsiSkPath::equals); + installMethod(runtime, prototype, "copy", &JsiSkPath::copy); + installMethod(runtime, prototype, "isInterpolatable", + &JsiSkPath::isInterpolatable); + installMethod(runtime, prototype, "interpolate", &JsiSkPath::interpolate); + installMethod(runtime, prototype, "toCmds", &JsiSkPath::toCmds); } JsiSkPath(std::shared_ptr context, SkPathBuilder builder) @@ -810,6 +671,12 @@ class JsiSkPath return makeJsiObject(runtime, std::make_shared(context, std::move(path))); } + +private: + static SkPathDirection toDirection(const JsiOptional &isCCW) { + return isCCW.has_value() && *isCCW ? SkPathDirection::kCCW + : SkPathDirection::kCW; + } }; } // namespace RNSkia diff --git a/packages/skia/cpp/api/JsiSkPathBuilder.h b/packages/skia/cpp/api/JsiSkPathBuilder.h index 2999501005..c7565e0ebe 100644 --- a/packages/skia/cpp/api/JsiSkPathBuilder.h +++ b/packages/skia/cpp/api/JsiSkPathBuilder.h @@ -6,6 +6,7 @@ #include +#include "JsiSkConverters.h" #include "JsiSkMatrix.h" #include "JsiSkNativeObjects.h" #include "JsiSkPath.h" @@ -33,377 +34,234 @@ class JsiSkPathBuilder static constexpr const char *CLASS_NAME = "PathBuilder"; // Movement methods - JSI_HOST_FUNCTION(moveTo) { - SkScalar x = arguments[0].asNumber(); - SkScalar y = arguments[1].asNumber(); - getObject()->moveTo(x, y); - return thisValue.getObject(runtime); - } + void moveTo(double x, double y) { getObject()->moveTo(x, y); } - JSI_HOST_FUNCTION(rMoveTo) { - SkScalar x = arguments[0].asNumber(); - SkScalar y = arguments[1].asNumber(); - getObject()->rMoveTo({x, y}); - return thisValue.getObject(runtime); + void rMoveTo(double x, double y) { + getObject()->rMoveTo({static_cast(x), static_cast(y)}); } - JSI_HOST_FUNCTION(lineTo) { - SkScalar x = arguments[0].asNumber(); - SkScalar y = arguments[1].asNumber(); - getObject()->lineTo(x, y); - return thisValue.getObject(runtime); - } + void lineTo(double x, double y) { getObject()->lineTo(x, y); } - JSI_HOST_FUNCTION(rLineTo) { - SkScalar x = arguments[0].asNumber(); - SkScalar y = arguments[1].asNumber(); - getObject()->rLineTo(x, y); - return thisValue.getObject(runtime); - } + void rLineTo(double x, double y) { getObject()->rLineTo(x, y); } // Curve methods - JSI_HOST_FUNCTION(quadTo) { - auto x1 = arguments[0].asNumber(); - auto y1 = arguments[1].asNumber(); - auto x2 = arguments[2].asNumber(); - auto y2 = arguments[3].asNumber(); + void quadTo(double x1, double y1, double x2, double y2) { getObject()->quadTo(x1, y1, x2, y2); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(rQuadTo) { - auto x1 = arguments[0].asNumber(); - auto y1 = arguments[1].asNumber(); - auto x2 = arguments[2].asNumber(); - auto y2 = arguments[3].asNumber(); + void rQuadTo(double x1, double y1, double x2, double y2) { getObject()->rQuadTo(x1, y1, x2, y2); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(conicTo) { - auto x1 = arguments[0].asNumber(); - auto y1 = arguments[1].asNumber(); - auto x2 = arguments[2].asNumber(); - auto y2 = arguments[3].asNumber(); - auto w = arguments[4].asNumber(); + void conicTo(double x1, double y1, double x2, double y2, double w) { getObject()->conicTo(x1, y1, x2, y2, w); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(rConicTo) { - auto x1 = arguments[0].asNumber(); - auto y1 = arguments[1].asNumber(); - auto x2 = arguments[2].asNumber(); - auto y2 = arguments[3].asNumber(); - auto w = arguments[4].asNumber(); + void rConicTo(double x1, double y1, double x2, double y2, double w) { getObject()->rConicTo(x1, y1, x2, y2, w); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(cubicTo) { - auto x1 = arguments[0].asNumber(); - auto y1 = arguments[1].asNumber(); - auto x2 = arguments[2].asNumber(); - auto y2 = arguments[3].asNumber(); - auto x3 = arguments[4].asNumber(); - auto y3 = arguments[5].asNumber(); + void cubicTo(double x1, double y1, double x2, double y2, double x3, + double y3) { getObject()->cubicTo(x1, y1, x2, y2, x3, y3); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(rCubicTo) { - auto x1 = arguments[0].asNumber(); - auto y1 = arguments[1].asNumber(); - auto x2 = arguments[2].asNumber(); - auto y2 = arguments[3].asNumber(); - auto x3 = arguments[4].asNumber(); - auto y3 = arguments[5].asNumber(); + void rCubicTo(double x1, double y1, double x2, double y2, double x3, + double y3) { getObject()->rCubicTo(x1, y1, x2, y2, x3, y3); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(close) { - getObject()->close(); - return thisValue.getObject(runtime); - } + void close() { getObject()->close(); } // Arc methods - JSI_HOST_FUNCTION(arcToOval) { - auto rect = JsiSkRect::fromValue(runtime, arguments[0]); - auto start = arguments[1].asNumber(); - auto sweep = arguments[2].asNumber(); - auto forceMoveTo = arguments[3].getBool(); + void arcToOval(std::shared_ptr rect, double start, double sweep, + bool forceMoveTo) { getObject()->arcTo(*rect, start, sweep, forceMoveTo); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(arcToRotated) { - auto rx = arguments[0].asNumber(); - auto ry = arguments[1].asNumber(); - auto xAxisRotate = arguments[2].asNumber(); - auto useSmallArc = arguments[3].getBool(); + void arcToRotated(double rx, double ry, double xAxisRotate, bool useSmallArc, + bool isCCW, double x, double y) { auto arcSize = useSmallArc ? SkPathBuilder::ArcSize::kSmall_ArcSize : SkPathBuilder::ArcSize::kLarge_ArcSize; - auto sweep = - arguments[4].getBool() ? SkPathDirection::kCCW : SkPathDirection::kCW; - auto x = arguments[5].asNumber(); - auto y = arguments[6].asNumber(); + auto sweep = isCCW ? SkPathDirection::kCCW : SkPathDirection::kCW; getObject()->arcTo(SkPoint::Make(rx, ry), xAxisRotate, arcSize, sweep, SkPoint::Make(x, y)); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(rArcTo) { - auto rx = arguments[0].asNumber(); - auto ry = arguments[1].asNumber(); - auto xAxisRotate = arguments[2].asNumber(); - auto useSmallArc = arguments[3].getBool(); + void rArcTo(double rx, double ry, double xAxisRotate, bool useSmallArc, + bool isCCW, double dx, double dy) { auto arcSize = useSmallArc ? SkPathBuilder::ArcSize::kSmall_ArcSize : SkPathBuilder::ArcSize::kLarge_ArcSize; - auto sweep = - arguments[4].getBool() ? SkPathDirection::kCCW : SkPathDirection::kCW; - auto dx = arguments[5].asNumber(); - auto dy = arguments[6].asNumber(); + auto sweep = isCCW ? SkPathDirection::kCCW : SkPathDirection::kCW; SkPoint r(rx, ry); SkVector d(dx, dy); getObject()->rArcTo(r, xAxisRotate, arcSize, sweep, d); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(arcToTangent) { - auto x1 = arguments[0].asNumber(); - auto y1 = arguments[1].asNumber(); - auto x2 = arguments[2].asNumber(); - auto y2 = arguments[3].asNumber(); - auto r = arguments[4].asNumber(); + void arcToTangent(double x1, double y1, double x2, double y2, double r) { getObject()->arcTo(SkPoint::Make(x1, y1), SkPoint::Make(x2, y2), r); - return thisValue.getObject(runtime); } // Shape methods - JSI_HOST_FUNCTION(addRect) { - auto rect = JsiSkRect::fromValue(runtime, arguments[0]); - auto direction = SkPathDirection::kCW; - if (count >= 2 && arguments[1].getBool()) { - direction = SkPathDirection::kCCW; - } - getObject()->addRect(*rect, direction); - return thisValue.getObject(runtime); + void addRect(std::shared_ptr rect, JsiOptional isCCW) { + getObject()->addRect(*rect, toDirection(isCCW)); } - JSI_HOST_FUNCTION(addOval) { - auto rect = JsiSkRect::fromValue(runtime, arguments[0]); - auto direction = SkPathDirection::kCW; - if (count >= 2 && arguments[1].getBool()) { - direction = SkPathDirection::kCCW; - } - unsigned startIndex = count < 3 ? 0 : arguments[2].asNumber(); - getObject()->addOval(*rect, direction, startIndex); - return thisValue.getObject(runtime); + void addOval(std::shared_ptr rect, JsiOptional isCCW, + JsiOptional startIndex) { + getObject()->addOval(*rect, toDirection(isCCW), + startIndex.has_value() + ? static_cast(*startIndex) + : 0); } - JSI_HOST_FUNCTION(addArc) { - auto rect = JsiSkRect::fromValue(runtime, arguments[0]); - auto start = arguments[1].asNumber(); - auto sweep = arguments[2].asNumber(); + void addArc(std::shared_ptr rect, double start, double sweep) { getObject()->addArc(*rect, start, sweep); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(addRRect) { - auto rrect = JsiSkRRect::fromValue(runtime, arguments[0]); - auto direction = SkPathDirection::kCW; - if (count >= 2 && arguments[1].getBool()) { - direction = SkPathDirection::kCCW; - } - getObject()->addRRect(*rrect, direction); - return thisValue.getObject(runtime); + void addRRect(std::shared_ptr rrect, JsiOptional isCCW) { + getObject()->addRRect(*rrect, toDirection(isCCW)); } - JSI_HOST_FUNCTION(addCircle) { - auto x = arguments[0].asNumber(); - auto y = arguments[1].asNumber(); - auto r = arguments[2].asNumber(); - auto direction = SkPathDirection::kCW; - if (count >= 4 && arguments[3].getBool()) { - direction = SkPathDirection::kCCW; - } - getObject()->addCircle(x, y, r, direction); - return thisValue.getObject(runtime); + void addCircle(double x, double y, double r, JsiOptional isCCW) { + getObject()->addCircle(x, y, r, toDirection(isCCW)); } - JSI_HOST_FUNCTION(addPoly) { - std::vector points; - auto jsiPoints = arguments[0].asObject(runtime).asArray(runtime); - auto close = arguments[1].getBool(); - auto pointsSize = jsiPoints.size(runtime); - points.reserve(pointsSize); - for (int i = 0; i < pointsSize; i++) { - std::shared_ptr point = JsiSkPoint::fromValue( - runtime, jsiPoints.getValueAtIndex(runtime, i).asObject(runtime)); - points.push_back(*point.get()); - } + void addPoly(std::vector points, bool close) { getObject()->addPolygon(SkSpan(points.data(), points.size()), close); - return thisValue.getObject(runtime); } - JSI_HOST_FUNCTION(addPath) { - auto src = JsiSkPath::fromValue(runtime, arguments[0]); + void addPath(std::shared_ptr src, + JsiOptional> matrix, + JsiOptional extend) { auto srcPath = src->snapshot(); - auto matrix = - count > 1 && !arguments[1].isUndefined() && !arguments[1].isNull() - ? JsiSkMatrix::fromValue(runtime, arguments[1]) - : nullptr; - auto mode = count > 2 && arguments[2].isBool() && arguments[2].getBool() - ? SkPath::kExtend_AddPathMode - : SkPath::kAppend_AddPathMode; - if (matrix == nullptr) { - getObject()->addPath(srcPath, mode); + auto mode = extend.has_value() && *extend ? SkPath::kExtend_AddPathMode + : SkPath::kAppend_AddPathMode; + if (matrix.has_value()) { + getObject()->addPath(srcPath, **matrix, mode); } else { - getObject()->addPath(srcPath, *matrix, mode); + getObject()->addPath(srcPath, mode); } - return thisValue.getObject(runtime); } // Configuration methods - JSI_HOST_FUNCTION(setFillType) { - auto ft = (SkPathFillType)arguments[0].asNumber(); - getObject()->setFillType(ft); - return thisValue.getObject(runtime); + void setFillType(double ft) { + getObject()->setFillType(static_cast(ft)); } - JSI_HOST_FUNCTION(setIsVolatile) { - auto v = arguments[0].getBool(); - getObject()->setIsVolatile(v); - return thisValue.getObject(runtime); - } + void setIsVolatile(bool v) { getObject()->setIsVolatile(v); } - JSI_HOST_FUNCTION(reset) { - getObject()->reset(); - return thisValue.getObject(runtime); - } + void reset() { getObject()->reset(); } - JSI_HOST_FUNCTION(offset) { - SkScalar dx = arguments[0].asNumber(); - SkScalar dy = arguments[1].asNumber(); - getObject()->offset(dx, dy); - return thisValue.getObject(runtime); - } + void offset(double dx, double dy) { getObject()->offset(dx, dy); } - JSI_HOST_FUNCTION(transform) { - auto m3 = *JsiSkMatrix::fromValue(runtime, arguments[0]); + void transform(std::shared_ptr m3) { // Create a path from current state, transform, then rebuild - auto path = getObject()->snapshot().makeTransform(m3); + auto path = getObject()->snapshot().makeTransform(*m3); *getObject() = SkPathBuilder(path); - return thisValue.getObject(runtime); } // Query methods - JSI_HOST_FUNCTION(computeBounds) { + std::shared_ptr computeBounds() { auto path = getObject()->snapshot(); - auto result = path.getBounds(); - return makeJsiObject(runtime, - std::make_shared(getContext(), result)); + return std::make_shared(getContext(), path.getBounds()); } - JSI_HOST_FUNCTION(isEmpty) { - return jsi::Value(getObject()->snapshot().isEmpty()); - } + bool isEmpty() { return getObject()->snapshot().isEmpty(); } - JSI_HOST_FUNCTION(getLastPt) { - SkPoint last; + SkPoint getLastPt() { + SkPoint last = SkPoint::Make(0, 0); getObject()->snapshot().getLastPt(&last); - auto point = jsi::Object(runtime); - point.setProperty(runtime, "x", static_cast(last.fX)); - point.setProperty(runtime, "y", static_cast(last.fY)); - return point; + return last; } - JSI_HOST_FUNCTION(countPoints) { - auto points = getObject()->snapshot().countPoints(); - return jsi::Value(points); - } + int countPoints() { return getObject()->snapshot().countPoints(); } // Build methods - JSI_HOST_FUNCTION(build) { + std::shared_ptr build() { // snapshot() returns a copy without resetting the builder auto path = getObject()->snapshot(); - return makeJsiObject( - runtime, std::make_shared(getContext(), std::move(path))); + return std::make_shared(getContext(), std::move(path)); } - JSI_HOST_FUNCTION(detach) { + std::shared_ptr detach() { // detach() returns the path and resets the builder auto path = getObject()->detach(); - return makeJsiObject( - runtime, std::make_shared(getContext(), std::move(path))); + return std::make_shared(getContext(), std::move(path)); } static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) { installCommon(runtime, prototype); // Movement - installHostMethod(runtime, prototype, "moveTo", &JsiSkPathBuilder::moveTo); - installHostMethod(runtime, prototype, "rMoveTo", - &JsiSkPathBuilder::rMoveTo); - installHostMethod(runtime, prototype, "lineTo", &JsiSkPathBuilder::lineTo); - installHostMethod(runtime, prototype, "rLineTo", - &JsiSkPathBuilder::rLineTo); + installChainableMethod(runtime, prototype, "moveTo", + &JsiSkPathBuilder::moveTo); + installChainableMethod(runtime, prototype, "rMoveTo", + &JsiSkPathBuilder::rMoveTo); + installChainableMethod(runtime, prototype, "lineTo", + &JsiSkPathBuilder::lineTo); + installChainableMethod(runtime, prototype, "rLineTo", + &JsiSkPathBuilder::rLineTo); // Curves - installHostMethod(runtime, prototype, "quadTo", &JsiSkPathBuilder::quadTo); - installHostMethod(runtime, prototype, "rQuadTo", - &JsiSkPathBuilder::rQuadTo); - installHostMethod(runtime, prototype, "conicTo", - &JsiSkPathBuilder::conicTo); - installHostMethod(runtime, prototype, "rConicTo", - &JsiSkPathBuilder::rConicTo); - installHostMethod(runtime, prototype, "cubicTo", - &JsiSkPathBuilder::cubicTo); - installHostMethod(runtime, prototype, "rCubicTo", - &JsiSkPathBuilder::rCubicTo); - installHostMethod(runtime, prototype, "close", &JsiSkPathBuilder::close); + installChainableMethod(runtime, prototype, "quadTo", + &JsiSkPathBuilder::quadTo); + installChainableMethod(runtime, prototype, "rQuadTo", + &JsiSkPathBuilder::rQuadTo); + installChainableMethod(runtime, prototype, "conicTo", + &JsiSkPathBuilder::conicTo); + installChainableMethod(runtime, prototype, "rConicTo", + &JsiSkPathBuilder::rConicTo); + installChainableMethod(runtime, prototype, "cubicTo", + &JsiSkPathBuilder::cubicTo); + installChainableMethod(runtime, prototype, "rCubicTo", + &JsiSkPathBuilder::rCubicTo); + installChainableMethod(runtime, prototype, "close", + &JsiSkPathBuilder::close); // Arcs - installHostMethod(runtime, prototype, "arcToOval", - &JsiSkPathBuilder::arcToOval); - installHostMethod(runtime, prototype, "arcToRotated", - &JsiSkPathBuilder::arcToRotated); - installHostMethod(runtime, prototype, "rArcTo", &JsiSkPathBuilder::rArcTo); - installHostMethod(runtime, prototype, "arcToTangent", - &JsiSkPathBuilder::arcToTangent); + installChainableMethod(runtime, prototype, "arcToOval", + &JsiSkPathBuilder::arcToOval); + installChainableMethod(runtime, prototype, "arcToRotated", + &JsiSkPathBuilder::arcToRotated); + installChainableMethod(runtime, prototype, "rArcTo", + &JsiSkPathBuilder::rArcTo); + installChainableMethod(runtime, prototype, "arcToTangent", + &JsiSkPathBuilder::arcToTangent); // Shapes - installHostMethod(runtime, prototype, "addRect", - &JsiSkPathBuilder::addRect); - installHostMethod(runtime, prototype, "addOval", - &JsiSkPathBuilder::addOval); - installHostMethod(runtime, prototype, "addArc", &JsiSkPathBuilder::addArc); - installHostMethod(runtime, prototype, "addRRect", - &JsiSkPathBuilder::addRRect); - installHostMethod(runtime, prototype, "addCircle", - &JsiSkPathBuilder::addCircle); - installHostMethod(runtime, prototype, "addPoly", - &JsiSkPathBuilder::addPoly); - installHostMethod(runtime, prototype, "addPath", - &JsiSkPathBuilder::addPath); + installChainableMethod(runtime, prototype, "addRect", + &JsiSkPathBuilder::addRect); + installChainableMethod(runtime, prototype, "addOval", + &JsiSkPathBuilder::addOval); + installChainableMethod(runtime, prototype, "addArc", + &JsiSkPathBuilder::addArc); + installChainableMethod(runtime, prototype, "addRRect", + &JsiSkPathBuilder::addRRect); + installChainableMethod(runtime, prototype, "addCircle", + &JsiSkPathBuilder::addCircle); + installChainableMethod(runtime, prototype, "addPoly", + &JsiSkPathBuilder::addPoly); + installChainableMethod(runtime, prototype, "addPath", + &JsiSkPathBuilder::addPath); // Config - installHostMethod(runtime, prototype, "setFillType", - &JsiSkPathBuilder::setFillType); - installHostMethod(runtime, prototype, "setIsVolatile", - &JsiSkPathBuilder::setIsVolatile); - installHostMethod(runtime, prototype, "reset", &JsiSkPathBuilder::reset); - installHostMethod(runtime, prototype, "offset", &JsiSkPathBuilder::offset); - installHostMethod(runtime, prototype, "transform", - &JsiSkPathBuilder::transform); + installChainableMethod(runtime, prototype, "setFillType", + &JsiSkPathBuilder::setFillType); + installChainableMethod(runtime, prototype, "setIsVolatile", + &JsiSkPathBuilder::setIsVolatile); + installChainableMethod(runtime, prototype, "reset", + &JsiSkPathBuilder::reset); + installChainableMethod(runtime, prototype, "offset", + &JsiSkPathBuilder::offset); + installChainableMethod(runtime, prototype, "transform", + &JsiSkPathBuilder::transform); // Query - installHostMethod(runtime, prototype, "computeBounds", - &JsiSkPathBuilder::computeBounds); - installHostMethod(runtime, prototype, "isEmpty", - &JsiSkPathBuilder::isEmpty); - installHostMethod(runtime, prototype, "getLastPt", - &JsiSkPathBuilder::getLastPt); - installHostMethod(runtime, prototype, "countPoints", - &JsiSkPathBuilder::countPoints); + installMethod(runtime, prototype, "computeBounds", + &JsiSkPathBuilder::computeBounds); + installMethod(runtime, prototype, "isEmpty", &JsiSkPathBuilder::isEmpty); + installMethod(runtime, prototype, "getLastPt", + &JsiSkPathBuilder::getLastPt); + installMethod(runtime, prototype, "countPoints", + &JsiSkPathBuilder::countPoints); // Build - installHostMethod(runtime, prototype, "build", &JsiSkPathBuilder::build); - installHostMethod(runtime, prototype, "detach", &JsiSkPathBuilder::detach); + installMethod(runtime, prototype, "build", &JsiSkPathBuilder::build); + installMethod(runtime, prototype, "detach", &JsiSkPathBuilder::detach); } JsiSkPathBuilder(std::shared_ptr context, @@ -426,6 +284,12 @@ class JsiSkPathBuilder return makeJsiObject(runtime, std::make_shared(context, builder)); } + +private: + static SkPathDirection toDirection(const JsiOptional &isCCW) { + return isCCW.has_value() && *isCCW ? SkPathDirection::kCCW + : SkPathDirection::kCW; + } }; } // namespace RNSkia