diff --git a/apps/example/ios/Podfile.lock b/apps/example/ios/Podfile.lock index a736fece8a..2aaa1f6362 100644 --- a/apps/example/ios/Podfile.lock +++ b/apps/example/ios/Podfile.lock @@ -2193,7 +2193,7 @@ SPEC CHECKSUMS: React-Mapbuffer: 0502faf46cab8fb89cfc7bf3e6c6109b6ef9b5de React-microtasksnativemodule: 663bc64e3a96c5fc91081923ae7481adc1359a78 react-native-safe-area-context: 286b3e7b5589795bb85ffc38faf4c0706c48a092 - react-native-skia: 440cfe000ba2f25f03315d727c129cb976db7377 + react-native-skia: 0457c9311947ef4642e3f55d5647220c5e529eb2 react-native-slider: 27263d134d55db948a4706f1e47d0ec88fb354dd React-NativeModulesApple: 16fbd5b040ff6c492dacc361d49e63cba7a6a7a1 React-perflogger: ab51b7592532a0ea45bf6eed7e6cae14a368b678 diff --git a/packages/skia/cpp/api/JsiNativeBuffer.h b/packages/skia/cpp/api/JsiNativeBuffer.h index 367ca8f313..deaf0dbc0b 100644 --- a/packages/skia/cpp/api/JsiNativeBuffer.h +++ b/packages/skia/cpp/api/JsiNativeBuffer.h @@ -6,6 +6,8 @@ #include #include "JsiSkImage.h" +#include "JsiArgParser.h" +#include "JsiArgParserTypes.h" namespace RNSkia { @@ -18,17 +20,17 @@ namespace jsi = facebook::jsi; class JsiNativeBufferFactory : public JsiSkHostObject { public: JSI_HOST_FUNCTION(MakeFromImage) { - auto image = JsiSkImage::fromValue(runtime, arguments[0]); + ArgParser parser(runtime, arguments, count); + auto image = parser.next>(); image->makeNonTextureImage(); uint64_t pointer = getContext()->makeNativeBuffer(image); return jsi::BigInt::fromUint64(runtime, pointer); } JSI_HOST_FUNCTION(Release) { - - jsi::BigInt pointer = arguments[0].asBigInt(runtime); - const uintptr_t nativeBufferPointer = pointer.asUint64(runtime); - + ArgParser parser(runtime, arguments, count); + auto pointer = parser.next(); + const uintptr_t nativeBufferPointer = pointer.asBigInt(runtime).asUint64(runtime); getContext()->releaseNativeBuffer(nativeBufferPointer); return jsi::Value::undefined(); } diff --git a/packages/skia/cpp/api/JsiSkCanvas.h b/packages/skia/cpp/api/JsiSkCanvas.h index 58ecd2d06c..4effb11e0a 100644 --- a/packages/skia/cpp/api/JsiSkCanvas.h +++ b/packages/skia/cpp/api/JsiSkCanvas.h @@ -18,11 +18,16 @@ #include "JsiSkSVG.h" #include "JsiSkTextBlob.h" #include "JsiSkVertices.h" +#include "JsiSkColor.h" +#include "JsiSkImageFilter.h" #include "RNSkTypedArray.h" #include +#include "JsiArgParser.h" +#include "JsiArgParserTypes.h" + #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdocumentation" @@ -41,6 +46,23 @@ #pragma clang diagnostic pop +// ArgParser specializations for types used in JsiSkCanvas +// Must be outside namespace RNSkia since the macros define the namespace +JSI_ARG_PARSER_SK_SP(SkImage, JsiSkImage) +JSI_ARG_PARSER_SHARED_PTR(SkPaint, JsiSkPaint) +JSI_ARG_PARSER_SHARED_PTR(SkRect, JsiSkRect) +JSI_ARG_PARSER_SHARED_PTR(SkRRect, JsiSkRRect) +JSI_ARG_PARSER_SHARED_PTR(SkPath, JsiSkPath) +JSI_ARG_PARSER_SHARED_PTR(SkPoint, JsiSkPoint) +JSI_ARG_PARSER_SK_SP(SkPicture, JsiSkPicture) +JSI_ARG_PARSER_SK_SP(SkTextBlob, JsiSkTextBlob) +JSI_ARG_PARSER_SK_SP(SkVertices, JsiSkVertices) +JSI_ARG_PARSER_SHARED_PTR(SkFont, JsiSkFont) +JSI_ARG_PARSER_SK_SP(SkSVGDOM, JsiSkSVG) +JSI_ARG_PARSER_SHARED_PTR(SkMatrix, JsiSkMatrix) +JSI_ARG_PARSER_SK_SP(SkImageFilter, JsiSkImageFilter) +JSI_ARG_PARSER_SKCOLOR(JsiSkColor) + namespace RNSkia { namespace jsi = facebook::jsi; @@ -48,191 +70,171 @@ namespace jsi = facebook::jsi; class JsiSkCanvas : public JsiSkHostObject { public: JSI_HOST_FUNCTION(drawPaint) { - auto paint = JsiSkPaint::fromValue(runtime, arguments[0]); + ArgParser parser(runtime, arguments, count); + auto paint = parser.next>(); _canvas->drawPaint(*paint); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawLine) { - SkScalar x1 = arguments[0].asNumber(); - SkScalar y1 = arguments[1].asNumber(); - SkScalar x2 = arguments[2].asNumber(); - SkScalar y2 = arguments[3].asNumber(); - auto paint = JsiSkPaint::fromValue(runtime, arguments[4]); + ArgParser parser(runtime, arguments, count); + auto x1 = parser.next(); + auto y1 = parser.next(); + auto x2 = parser.next(); + auto y2 = parser.next(); + auto paint = parser.next>(); _canvas->drawLine(x1, y1, x2, y2, *paint); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawRect) { - auto rect = JsiSkRect::fromValue(runtime, arguments[0]); - auto paint = JsiSkPaint::fromValue(runtime, arguments[1]); + ArgParser parser(runtime, arguments, count); + auto rect = parser.next>(); + auto paint = parser.next>(); _canvas->drawRect(*rect, *paint); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawImage) { - auto image = JsiSkImage::fromValue(runtime, arguments[0]); - auto x = arguments[1].asNumber(); - auto y = arguments[2].asNumber(); - std::shared_ptr paint; - if (count == 4) { - paint = JsiSkPaint::fromValue(runtime, arguments[3]); - } - _canvas->drawImage(image, x, y, SkSamplingOptions(), paint.get()); + ArgParser parser(runtime, arguments, count); + auto image = parser.next>(); + auto x = parser.next(); + auto y = parser.next(); + auto paint = parser.next>>(); + _canvas->drawImage(image, x, y, SkSamplingOptions(), + paint.has_value() ? paint.value().get() : nullptr); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawImageRect) { - auto image = JsiSkImage::fromValue(runtime, arguments[0]); - auto src = JsiSkRect::fromValue(runtime, arguments[1]); - auto dest = JsiSkRect::fromValue(runtime, arguments[2]); - auto paint = JsiSkPaint::fromValue(runtime, arguments[3]); - auto fastSample = count >= 5 && arguments[4].getBool(); + ArgParser parser(runtime, arguments, count); + auto image = parser.next>(); + auto src = parser.next>(); + auto dest = parser.next>(); + auto paint = parser.next>(); + auto fastSample = parser.next>(); _canvas->drawImageRect(image, *src, *dest, SkSamplingOptions(), paint.get(), - fastSample ? SkCanvas::kFast_SrcRectConstraint - : SkCanvas::kStrict_SrcRectConstraint); + fastSample.value_or(false) ? SkCanvas::kFast_SrcRectConstraint + : SkCanvas::kStrict_SrcRectConstraint); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawImageCubic) { - auto image = JsiSkImage::fromValue(runtime, arguments[0]); - auto x = arguments[1].asNumber(); - auto y = arguments[2].asNumber(); - float B = arguments[3].asNumber(); - float C = arguments[4].asNumber(); - std::shared_ptr paint; - if (count == 6) { - if (!arguments[5].isNull()) { - paint = JsiSkPaint::fromValue(runtime, arguments[5]); - } - } - _canvas->drawImage(image, x, y, SkSamplingOptions({B, C}), paint.get()); + ArgParser parser(runtime, arguments, count); + auto image = parser.next>(); + auto x = parser.next(); + auto y = parser.next(); + auto B = parser.next(); + auto C = parser.next(); + auto paint = parser.next>>(); + _canvas->drawImage(image, x, y, SkSamplingOptions({B, C}), + paint.has_value() ? paint.value().get() : nullptr); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawImageOptions) { - auto image = JsiSkImage::fromValue(runtime, arguments[0]); - auto x = arguments[1].asNumber(); - auto y = arguments[2].asNumber(); - auto fm = (SkFilterMode)arguments[3].asNumber(); - auto mm = (SkMipmapMode)arguments[4].asNumber(); - std::shared_ptr paint; - if (count == 6) { - if (!arguments[5].isNull()) { - paint = JsiSkPaint::fromValue(runtime, arguments[5]); - } - } - _canvas->drawImage(image, x, y, SkSamplingOptions(fm, mm), paint.get()); + ArgParser parser(runtime, arguments, count); + auto image = parser.next>(); + auto x = parser.next(); + auto y = parser.next(); + auto fm = parser.next(); + auto mm = parser.next(); + auto paint = parser.next>>(); + _canvas->drawImage(image, x, y, SkSamplingOptions(fm, mm), + paint.has_value() ? paint.value().get() : nullptr); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawImageNine) { - auto image = JsiSkImage::fromValue(runtime, arguments[0]); - auto center = JsiSkRect::fromValue(runtime, arguments[1]); - auto dest = JsiSkRect::fromValue(runtime, arguments[2]); - auto fm = (SkFilterMode)arguments[3].asNumber(); - std::shared_ptr paint; - if (count == 5) { - if (!arguments[4].isNull()) { - paint = JsiSkPaint::fromValue(runtime, arguments[4]); - } - } + ArgParser parser(runtime, arguments, count); + auto image = parser.next>(); + auto center = parser.next>(); + auto dest = parser.next>(); + auto fm = parser.next(); + auto paint = parser.next>>(); _canvas->drawImageNine(image.get(), center->round(), *dest, fm, - paint.get()); + paint.has_value() ? paint.value().get() : nullptr); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawImageRectCubic) { - auto image = JsiSkImage::fromValue(runtime, arguments[0]); - auto src = JsiSkRect::fromValue(runtime, arguments[1]); - auto dest = JsiSkRect::fromValue(runtime, arguments[2]); - float B = arguments[3].asNumber(); - float C = arguments[4].asNumber(); - std::shared_ptr paint; - if (count == 6) { - if (!arguments[5].isNull()) { - paint = JsiSkPaint::fromValue(runtime, arguments[5]); - } - } + ArgParser parser(runtime, arguments, count); + auto image = parser.next>(); + auto src = parser.next>(); + auto dest = parser.next>(); + auto B = parser.next(); + auto C = parser.next(); + auto paint = parser.next>>(); auto constraint = SkCanvas::kStrict_SrcRectConstraint; // TODO: get from caller _canvas->drawImageRect(image.get(), *src, *dest, SkSamplingOptions({B, C}), - paint.get(), constraint); + paint.has_value() ? paint.value().get() : nullptr, constraint); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawImageRectOptions) { - auto image = JsiSkImage::fromValue(runtime, arguments[0]); - auto src = JsiSkRect::fromValue(runtime, arguments[1]); - auto dest = JsiSkRect::fromValue(runtime, arguments[2]); - auto filter = (SkFilterMode)arguments[3].asNumber(); - auto mipmap = (SkMipmapMode)arguments[4].asNumber(); - std::shared_ptr paint; - if (count == 6) { - if (!arguments[5].isNull()) { - paint = JsiSkPaint::fromValue(runtime, arguments[5]); - } - } + ArgParser parser(runtime, arguments, count); + auto image = parser.next>(); + auto src = parser.next>(); + auto dest = parser.next>(); + auto filter = parser.next(); + auto mipmap = parser.next(); + auto paint = parser.next>>(); auto constraint = SkCanvas::kStrict_SrcRectConstraint; _canvas->drawImageRect(image.get(), *src, *dest, {filter, mipmap}, - paint.get(), constraint); + paint.has_value() ? paint.value().get() : nullptr, constraint); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawCircle) { - SkScalar cx = arguments[0].asNumber(); - SkScalar cy = arguments[1].asNumber(); - SkScalar radius = arguments[2].asNumber(); - - auto paint = JsiSkPaint::fromValue(runtime, arguments[3]); + ArgParser parser(runtime, arguments, count); + auto cx = parser.next(); + auto cy = parser.next(); + auto radius = parser.next(); + auto paint = parser.next>(); _canvas->drawCircle(cx, cy, radius, *paint); - return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawArc) { - auto oval = JsiSkRect::fromValue(runtime, arguments[0]); - - SkScalar startAngle = arguments[1].asNumber(); - SkScalar sweepAngle = arguments[2].asNumber(); - bool useCenter = arguments[3].getBool(); - - auto paint = JsiSkPaint::fromValue(runtime, arguments[4]); + ArgParser parser(runtime, arguments, count); + auto oval = parser.next>(); + auto startAngle = parser.next(); + auto sweepAngle = parser.next(); + auto useCenter = parser.next(); + auto paint = parser.next>(); _canvas->drawArc(*oval, startAngle, sweepAngle, useCenter, *paint); - return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawRRect) { - auto rect = JsiSkRRect::fromValue(runtime, arguments[0]); - auto paint = JsiSkPaint::fromValue(runtime, arguments[1]); - + ArgParser parser(runtime, arguments, count); + auto rect = parser.next>(); + auto paint = parser.next>(); _canvas->drawRRect(*rect, *paint); - return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawDRRect) { - auto outer = JsiSkRRect::fromValue(runtime, arguments[0]); - auto inner = JsiSkRRect::fromValue(runtime, arguments[1]); - auto paint = JsiSkPaint::fromValue(runtime, arguments[2]); - + ArgParser parser(runtime, arguments, count); + auto outer = parser.next>(); + auto inner = parser.next>(); + auto paint = parser.next>(); _canvas->drawDRRect(*outer, *inner, *paint); - return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawOval) { - auto rect = JsiSkRect::fromValue(runtime, arguments[0]); - auto paint = JsiSkPaint::fromValue(runtime, arguments[1]); - + ArgParser parser(runtime, arguments, count); + auto rect = parser.next>(); + auto paint = parser.next>(); _canvas->drawOval(*rect, *paint); - return jsi::Value::undefined(); } JSI_HOST_FUNCTION(restoreToCount) { - auto c = arguments[0].asNumber(); + ArgParser parser(runtime, arguments, count); + auto c = parser.next(); _canvas->restoreToCount(c); return jsi::Value::undefined(); } @@ -247,54 +249,54 @@ class JsiSkCanvas : public JsiSkHostObject { } JSI_HOST_FUNCTION(drawPoints) { - auto pointMode = arguments[0].asNumber(); - std::vector points; + ArgParser parser(runtime, arguments, count); + auto pointMode = parser.next(); + auto jsiPoints = parser.next(); + auto paint = parser.next>(); - auto jsiPoints = arguments[1].asObject(runtime).asArray(runtime); auto pointsSize = jsiPoints.size(runtime); - - // Check if we have at least one point if (pointsSize == 0) { throw std::invalid_argument("Points array must not be empty"); } + std::vector points; 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()); } - auto paint = JsiSkPaint::fromValue(runtime, arguments[2]); auto p = SkSpan(points.data(), points.size()); - _canvas->drawPoints((SkCanvas::PointMode)pointMode, p, *paint); - + _canvas->drawPoints(pointMode, p, *paint); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawVertices) { - auto vertices = JsiSkVertices::fromValue(runtime, arguments[0]); - auto blendMode = (SkBlendMode)arguments[1].getNumber(); - auto paint = JsiSkPaint::fromValue(runtime, arguments[2]); + ArgParser parser(runtime, arguments, count); + auto vertices = parser.next>(); + auto blendMode = parser.next(); + auto paint = parser.next>(); _canvas->drawVertices(vertices, blendMode, *paint); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawPatch) { - std::vector cubics; - std::vector colors; - std::vector texs; - - auto jsiCubics = arguments[0].asObject(runtime).asArray(runtime); + ArgParser parser(runtime, arguments, count); + auto jsiCubics = parser.next(); + auto jsiColors = parser.next>(); + auto jsiTexs = parser.next>(); + auto blendMode = parser.next(); + auto paint = parser.next>>(); + + // Parse cubics auto cubicsSize = jsiCubics.size(runtime); - - // Validate cubic points - must be exactly 12 points if (cubicsSize != 12) { throw std::invalid_argument( "Cubic points array must contain exactly 12 points"); } + std::vector cubics; cubics.reserve(cubicsSize); for (int i = 0; i < cubicsSize; i++) { std::shared_ptr point = JsiSkPoint::fromValue( @@ -302,11 +304,10 @@ class JsiSkCanvas : public JsiSkHostObject { cubics.push_back(*point.get()); } - if (count >= 2 && !arguments[1].isNull() && !arguments[1].isUndefined()) { - auto jsiColors = arguments[1].asObject(runtime).asArray(runtime); - auto colorsSize = jsiColors.size(runtime); - - // Validate colors array - must be exactly 4 colors + // Parse colors (optional) + std::vector colors; + if (jsiColors.has_value()) { + auto colorsSize = jsiColors.value().size(runtime); if (colorsSize != 4) { throw std::invalid_argument( "Colors array must contain exactly 4 colors"); @@ -315,16 +316,15 @@ class JsiSkCanvas : public JsiSkHostObject { colors.reserve(colorsSize); for (int i = 0; i < colorsSize; i++) { SkColor color = JsiSkColor::fromValue( - runtime, jsiColors.getValueAtIndex(runtime, i)); + runtime, jsiColors.value().getValueAtIndex(runtime, i)); colors.push_back(color); } } - if (count >= 3 && !arguments[2].isNull() && !arguments[2].isUndefined()) { - auto jsiTexs = arguments[2].asObject(runtime).asArray(runtime); - auto texsSize = jsiTexs.size(runtime); - - // Validate textures array - must be exactly 4 points + // Parse texture coordinates (optional) + std::vector texs; + if (jsiTexs.has_value()) { + auto texsSize = jsiTexs.value().size(runtime); if (texsSize != 4) { throw std::invalid_argument( "Texture coordinates array must contain exactly 4 points"); @@ -333,98 +333,101 @@ class JsiSkCanvas : public JsiSkHostObject { texs.reserve(texsSize); for (int i = 0; i < texsSize; i++) { auto point = JsiSkPoint::fromValue( - runtime, jsiTexs.getValueAtIndex(runtime, i).asObject(runtime)); + runtime, jsiTexs.value().getValueAtIndex(runtime, i).asObject(runtime)); texs.push_back(*point.get()); } } - auto paint = - count >= 4 ? JsiSkPaint::fromValue(runtime, arguments[4]) : nullptr; - auto blendMode = static_cast(arguments[3].asNumber()); - _canvas->drawPatch(cubics.data(), colors.empty() ? nullptr : colors.data(), - texs.empty() ? nullptr : texs.data(), blendMode, *paint); + if (paint.has_value()) { + _canvas->drawPatch(cubics.data(), colors.empty() ? nullptr : colors.data(), + texs.empty() ? nullptr : texs.data(), blendMode, + *paint.value()); + } else { + SkPaint defaultPaint; + _canvas->drawPatch(cubics.data(), colors.empty() ? nullptr : colors.data(), + texs.empty() ? nullptr : texs.data(), blendMode, + defaultPaint); + } return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawPath) { - auto path = JsiSkPath::fromValue(runtime, arguments[0]); - auto paint = JsiSkPaint::fromValue(runtime, arguments[1]); - + ArgParser parser(runtime, arguments, count); + auto path = parser.next>(); + auto paint = parser.next>(); _canvas->drawPath(*path, *paint); - return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawText) { - auto textVal = arguments[0].asString(runtime).utf8(runtime); - auto text = textVal.c_str(); - SkScalar x = arguments[1].asNumber(); - SkScalar y = arguments[2].asNumber(); - - auto paint = JsiSkPaint::fromValue(runtime, arguments[3]); - auto font = JsiSkFont::fromValue(runtime, arguments[4]); - - _canvas->drawSimpleText(text, strlen(text), SkTextEncoding::kUTF8, x, y, + ArgParser parser(runtime, arguments, count); + auto textVal = parser.next(); + auto x = parser.next(); + auto y = parser.next(); + auto paint = parser.next>(); + auto font = parser.next>(); + _canvas->drawSimpleText(textVal.c_str(), textVal.length(), SkTextEncoding::kUTF8, x, y, *font, *paint); - return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawTextBlob) { - auto blob = JsiSkTextBlob::fromValue(runtime, arguments[0]); - SkScalar x = arguments[1].asNumber(); - SkScalar y = arguments[2].asNumber(); - auto paint = JsiSkPaint::fromValue(runtime, arguments[3]); + ArgParser parser(runtime, arguments, count); + auto blob = parser.next>(); + auto x = parser.next(); + auto y = parser.next(); + auto paint = parser.next>(); _canvas->drawTextBlob(blob, x, y, *paint); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawGlyphs) { - auto jsiGlyphs = arguments[0].asObject(runtime).asArray(runtime); - auto jsiPositions = arguments[1].asObject(runtime).asArray(runtime); - auto x = arguments[2].asNumber(); - auto y = arguments[3].asNumber(); - auto font = JsiSkFont::fromValue(runtime, arguments[4]); - auto paint = JsiSkPaint::fromValue(runtime, arguments[5]); - SkPoint origin = SkPoint::Make(x, y); + ArgParser parser(runtime, arguments, count); + auto jsiGlyphs = parser.next(); + auto jsiPositions = parser.next(); + auto x = parser.next(); + auto y = parser.next(); + auto font = parser.next>(); + auto paint = parser.next>(); - 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()); - } + SkPoint origin = SkPoint::Make(x, y); - std::vector glyphs; int glyphsSize = static_cast(jsiGlyphs.size(runtime)); + int pointsSize = static_cast(jsiPositions.size(runtime)); - // Validate that glyphs and positions arrays have the same size if (glyphsSize != pointsSize) { throw std::invalid_argument( "Glyphs and positions arrays must have the same length"); } + std::vector glyphs; glyphs.reserve(glyphsSize); for (int i = 0; i < glyphsSize; i++) { glyphs.push_back(jsiGlyphs.getValueAtIndex(runtime, i).asNumber()); } + std::vector positions; + 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()); + } + auto g = SkSpan(glyphs.data(), glyphs.size()); auto p = SkSpan(positions.data(), positions.size()); _canvas->drawGlyphs(g, p, origin, *font, *paint); - return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawSvg) { - auto svgdom = JsiSkSVG::fromValue(runtime, arguments[0]); - if (count == 3 && arguments[1].isNumber() && arguments[2].isNumber()) { - // read size - auto w = arguments[1].asNumber(); - auto h = arguments[2].asNumber(); - svgdom->setContainerSize(SkSize::Make(w, h)); + ArgParser parser(runtime, arguments, count); + auto svgdom = parser.next>(); + auto w = parser.next>(); + auto h = parser.next>(); + + if (w.has_value() && h.has_value()) { + svgdom->setContainerSize(SkSize::Make(w.value(), h.value())); } else { auto canvasSize = _canvas->getBaseLayerSize(); svgdom->setContainerSize(SkSize::Make(canvasSize)); @@ -434,25 +437,28 @@ class JsiSkCanvas : public JsiSkHostObject { } JSI_HOST_FUNCTION(clipPath) { - auto path = JsiSkPath::fromValue(runtime, arguments[0]); - auto op = (SkClipOp)arguments[1].asNumber(); - auto doAntiAlias = arguments[2].getBool(); + ArgParser parser(runtime, arguments, count); + auto path = parser.next>(); + auto op = parser.next(); + auto doAntiAlias = parser.next(); _canvas->clipPath(*path, op, doAntiAlias); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(clipRect) { - auto rect = JsiSkRect::fromValue(runtime, arguments[0]); - auto op = (SkClipOp)arguments[1].asNumber(); - auto doAntiAlias = arguments[2].getBool(); + ArgParser parser(runtime, arguments, count); + auto rect = parser.next>(); + auto op = parser.next(); + auto doAntiAlias = parser.next(); _canvas->clipRect(*rect, op, doAntiAlias); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(clipRRect) { - auto rrect = JsiSkRRect::fromValue(runtime, arguments[0]); - auto op = (SkClipOp)arguments[1].asNumber(); - auto doAntiAlias = arguments[2].getBool(); + ArgParser parser(runtime, arguments, count); + auto rrect = parser.next>(); + auto op = parser.next(); + auto doAntiAlias = parser.next(); _canvas->clipRRect(*rrect, op, doAntiAlias); return jsi::Value::undefined(); } @@ -460,20 +466,16 @@ class JsiSkCanvas : public JsiSkHostObject { JSI_HOST_FUNCTION(save) { return jsi::Value(_canvas->save()); } JSI_HOST_FUNCTION(saveLayer) { - SkPaint *paint = (count >= 1 && !arguments[0].isUndefined()) - ? JsiSkPaint::fromValue(runtime, arguments[0]).get() - : nullptr; - SkRect *bounds = - count >= 2 && !arguments[1].isNull() && !arguments[1].isUndefined() - ? JsiSkRect::fromValue(runtime, arguments[1]).get() - : nullptr; - SkImageFilter *backdrop = - count >= 3 && !arguments[2].isNull() && !arguments[2].isUndefined() - ? JsiSkImageFilter::fromValue(runtime, arguments[2]).get() - : nullptr; - SkCanvas::SaveLayerFlags flags = count >= 4 ? arguments[3].asNumber() : 0; + ArgParser parser(runtime, arguments, count); + auto paint = parser.next>>(); + auto bounds = parser.next>>(); + auto backdrop = parser.next>>(); + auto flags = parser.next>(); return jsi::Value(_canvas->saveLayer( - SkCanvas::SaveLayerRec(bounds, paint, backdrop, flags))); + SkCanvas::SaveLayerRec(bounds.has_value() ? bounds.value().get() : nullptr, + paint.has_value() ? paint.value().get() : nullptr, + backdrop.has_value() ? backdrop.value().get() : nullptr, + flags.value_or(0)))); } JSI_HOST_FUNCTION(restore) { @@ -482,102 +484,108 @@ class JsiSkCanvas : public JsiSkHostObject { } JSI_HOST_FUNCTION(rotate) { - SkScalar degrees = arguments[0].asNumber(); - SkScalar rx = arguments[1].asNumber(); - SkScalar ry = arguments[2].asNumber(); + ArgParser parser(runtime, arguments, count); + auto degrees = parser.next(); + auto rx = parser.next(); + auto ry = parser.next(); _canvas->rotate(degrees, rx, ry); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(translate) { - SkScalar dx = arguments[0].asNumber(); - SkScalar dy = arguments[1].asNumber(); + ArgParser parser(runtime, arguments, count); + auto dx = parser.next(); + auto dy = parser.next(); _canvas->translate(dx, dy); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(scale) { - SkScalar sx = arguments[0].asNumber(); - SkScalar sy = arguments[1].asNumber(); + ArgParser parser(runtime, arguments, count); + auto sx = parser.next(); + auto sy = parser.next(); _canvas->scale(sx, sy); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(skew) { - SkScalar sx = arguments[0].asNumber(); - SkScalar sy = arguments[1].asNumber(); + ArgParser parser(runtime, arguments, count); + auto sx = parser.next(); + auto sy = parser.next(); _canvas->skew(sx, sy); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawColor) { - SkColor cl = JsiSkColor::fromValue(runtime, arguments[0]); - if (count == 1) { - _canvas->drawColor(cl); + ArgParser parser(runtime, arguments, count); + auto cl = parser.next(); + auto mode = parser.next>(); + if (mode.has_value()) { + _canvas->drawColor(cl, mode.value()); } else { - auto mode = static_cast(arguments[1].asNumber()); - _canvas->drawColor(cl, mode); + _canvas->drawColor(cl); } return jsi::Value::undefined(); } JSI_HOST_FUNCTION(clear) { - SkColor cl = JsiSkColor::fromValue(runtime, arguments[0]); + ArgParser parser(runtime, arguments, count); + auto cl = parser.next(); _canvas->clear(cl); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(concat) { - auto matrix = JsiSkMatrix::fromValue(runtime, arguments[0]); - _canvas->concat(*matrix.get()); + ArgParser parser(runtime, arguments, count); + auto matrix = parser.next>(); + _canvas->concat(*matrix); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawPicture) { - auto picture = JsiSkPicture::fromValue(runtime, arguments[0]); + ArgParser parser(runtime, arguments, count); + auto picture = parser.next>(); _canvas->drawPicture(picture); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(drawAtlas) { - auto atlas = JsiSkImage::fromValue(runtime, arguments[0]); - auto rects = arguments[1].asObject(runtime).asArray(runtime); - auto transforms = arguments[2].asObject(runtime).asArray(runtime); - auto paint = JsiSkPaint::fromValue(runtime, arguments[3]); - auto blendMode = count > 5 && !arguments[4].isUndefined() - ? static_cast(arguments[4].asNumber()) - : SkBlendMode::kDstOver; + ArgParser parser(runtime, arguments, count); + auto atlas = parser.next>(); + auto rects = parser.next(); + auto transforms = parser.next(); + auto paint = parser.next>(); + auto blendMode = parser.next>(); + auto colorsArray = parser.next>(); + auto samplingValue = parser.next>(); - std::vector xforms; + int rectsSize = static_cast(rects.size(runtime)); int xformsSize = static_cast(transforms.size(runtime)); - xforms.reserve(xformsSize); - for (int i = 0; i < xformsSize; i++) { - auto xform = JsiSkRSXform::fromValue( - runtime, transforms.getValueAtIndex(runtime, i).asObject(runtime)); - xforms.push_back(*xform.get()); + + if (xformsSize != rectsSize) { + throw std::invalid_argument( + "Transforms and rects arrays must have the same length"); } std::vector skRects; - int rectsSize = static_cast(rects.size(runtime)); skRects.reserve(rectsSize); for (int i = 0; i < rectsSize; i++) { auto rect = JsiSkRect::fromValue( runtime, rects.getValueAtIndex(runtime, i).asObject(runtime)); - skRects.push_back(*rect.get()); + skRects.push_back(*rect); } - // Validate transforms and rects have the same size - if (xformsSize != rectsSize) { - throw std::invalid_argument( - "Transforms and rects arrays must have the same length"); + std::vector xforms; + xforms.reserve(xformsSize); + for (int i = 0; i < xformsSize; i++) { + auto xform = JsiSkRSXform::fromValue( + runtime, transforms.getValueAtIndex(runtime, i).asObject(runtime)); + xforms.push_back(*xform); } std::vector colors; - if (count > 5 && !arguments[5].isUndefined()) { - auto colorsArray = arguments[5].asObject(runtime).asArray(runtime); - int colorsSize = static_cast(colorsArray.size(runtime)); - - // Validate colors array matches the size of sprites and transforms + if (colorsArray.has_value()) { + int colorsSize = static_cast(colorsArray.value().size(runtime)); if (colorsSize != rectsSize) { throw std::invalid_argument( "Colors array must have the same length as rects/transforms"); @@ -585,14 +593,12 @@ class JsiSkCanvas : public JsiSkHostObject { colors.reserve(colorsSize); for (int i = 0; i < colorsSize; i++) { - // Convert from [r,g,b,a] in [0,1] to SkColor - auto val = colorsArray.getValueAtIndex(runtime, i).asObject(runtime); + auto val = colorsArray.value().getValueAtIndex(runtime, i).asObject(runtime); float r = val.getProperty(runtime, "0").asNumber(); float g = val.getProperty(runtime, "1").asNumber(); float b = val.getProperty(runtime, "2").asNumber(); float a = val.getProperty(runtime, "3").asNumber(); - // Convert to 8-bit color channels and pack into SkColor uint8_t r8 = static_cast(r * 255); uint8_t g8 = static_cast(g * 255); uint8_t b8 = static_cast(b * 255); @@ -602,39 +608,43 @@ class JsiSkCanvas : public JsiSkHostObject { colors.push_back(color); } } + SkSamplingOptions sampling(SkFilterMode::kLinear); - if (count > 6) { - sampling = SamplingOptionsFromValue(runtime, arguments[6]); + if (samplingValue.has_value()) { + sampling = SamplingOptionsFromValue(runtime, samplingValue.value()); } + auto x = SkSpan(xforms.data(), xforms.size()); auto t = SkSpan(skRects.data(), skRects.size()); auto c = SkSpan(colors.data(), colors.size()); - _canvas->drawAtlas(atlas.get(), x, t, c, blendMode, sampling, nullptr, - paint.get()); - + _canvas->drawAtlas(atlas.get(), x, t, c, blendMode.value_or(SkBlendMode::kDstOver), + sampling, nullptr, paint.get()); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(readPixels) { - auto srcX = static_cast(arguments[0].asNumber()); - auto srcY = static_cast(arguments[1].asNumber()); - auto info = JsiSkImageInfo::fromValue(runtime, arguments[2]); + ArgParser parser(runtime, arguments, count); + auto srcX = parser.next(); + auto srcY = parser.next(); + auto infoValue = parser.next(); + auto destValue = parser.next>(); + auto bytesPerRowOpt = parser.next>(); + + auto info = JsiSkImageInfo::fromValue(runtime, infoValue); if (!info) { return jsi::Value::null(); } - size_t bytesPerRow = 0; - if (count > 4 && !arguments[4].isUndefined()) { - bytesPerRow = static_cast(arguments[4].asNumber()); - } else { - bytesPerRow = info->minRowBytes(); - } - auto dest = - count > 3 - ? RNSkTypedArray::getTypedArray(runtime, arguments[3], *info) + + size_t bytesPerRow = bytesPerRowOpt.value_or(info->minRowBytes()); + + auto dest = destValue.has_value() + ? RNSkTypedArray::getTypedArray(runtime, destValue.value(), *info) : RNSkTypedArray::getTypedArray(runtime, jsi::Value::null(), *info); + if (!dest.isObject()) { return jsi::Value::null(); } + jsi::ArrayBuffer buffer = dest.asObject(runtime) .getProperty(runtime, jsi::PropNameID::forAscii(runtime, "buffer")) @@ -707,4 +717,4 @@ class JsiSkCanvas : public JsiSkHostObject { private: SkCanvas *_canvas; }; -} // namespace RNSkia +} // namespace RNSkia \ No newline at end of file diff --git a/packages/skia/cpp/api/JsiSkColor.h b/packages/skia/cpp/api/JsiSkColor.h index 5b4ed2176a..99e7822cd4 100644 --- a/packages/skia/cpp/api/JsiSkColor.h +++ b/packages/skia/cpp/api/JsiSkColor.h @@ -8,6 +8,8 @@ #include "JsiSkHostObjects.h" #include "third_party/CSSColorParser.h" +#include "JsiArgParser.h" +#include "JsiArgParserTypes.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdocumentation" @@ -65,18 +67,21 @@ class JsiSkColor : public RNJsi::JsiHostObject { */ static jsi::HostFunctionType createCtor() { return JSI_HOST_FUNCTION_LAMBDA { - if (arguments[0].isNumber()) { - return JsiSkColor::toValue(runtime, arguments[0].getNumber()); - } else if (arguments[0].isString()) { - auto text = arguments[0].asString(runtime).utf8(runtime); + ArgParser parser(runtime, arguments, count); + auto arg = parser.next(); + + if (arg.isNumber()) { + return JsiSkColor::toValue(runtime, arg.getNumber()); + } else if (arg.isString()) { + auto text = arg.asString(runtime).utf8(runtime); auto color = CSSColorParser::parse(text); if (color.a == -1.0f) { return JsiSkColor::toValue(runtime, SK_ColorBLACK); } return JsiSkColor::toValue( runtime, SkColorSetARGB(color.a * 255, color.r, color.g, color.b)); - } else if (arguments[0].isObject()) { - return arguments[0].getObject(runtime); + } else if (arg.isObject()) { + return arg.getObject(runtime); } return jsi::Value::undefined(); }; diff --git a/packages/skia/cpp/api/JsiSkPaint.h b/packages/skia/cpp/api/JsiSkPaint.h index 76a4cbdcb7..84d11757bc 100644 --- a/packages/skia/cpp/api/JsiSkPaint.h +++ b/packages/skia/cpp/api/JsiSkPaint.h @@ -13,6 +13,9 @@ #include "JsiSkPathEffect.h" #include "JsiSkShader.h" +#include "JsiArgParser.h" +#include "JsiArgParserTypes.h" + #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdocumentation" @@ -20,6 +23,13 @@ #pragma clang diagnostic pop +// ArgParser specializations for types used in JsiSkPaint +JSI_ARG_PARSER_SK_SP(SkMaskFilter, JsiSkMaskFilter) +JSI_ARG_PARSER_SK_SP(SkColorFilter, JsiSkColorFilter) +JSI_ARG_PARSER_SK_SP(SkShader, JsiSkShader) +JSI_ARG_PARSER_SK_SP(SkPathEffect, JsiSkPathEffect) +JSI_ARG_PARSER_SKCOLOR(JsiSkColor) + namespace RNSkia { namespace jsi = facebook::jsi; @@ -28,7 +38,8 @@ class JsiSkPaint : public JsiSkWrappingSharedPtrHostObject { EXPORT_JSI_API_TYPENAME(JsiSkPaint, Paint) JSI_HOST_FUNCTION(assign) { - SkPaint *paint = JsiSkPaint::fromValue(runtime, arguments[0]).get(); + ArgParser parser(runtime, arguments, count); + auto paint = parser.next>(); *getObject() = *paint; return jsi::Value::undefined(); } @@ -72,102 +83,107 @@ class JsiSkPaint : public JsiSkWrappingSharedPtrHostObject { } JSI_HOST_FUNCTION(setColor) { - SkColor color = JsiSkColor::fromValue(runtime, arguments[0]); + ArgParser parser(runtime, arguments, count); + auto color = parser.next(); getObject()->setColor(color); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(setAlphaf) { - SkScalar alpha = arguments[0].asNumber(); + ArgParser parser(runtime, arguments, count); + auto alpha = parser.next(); getObject()->setAlphaf(alpha); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(setAntiAlias) { - bool antiAliased = arguments[0].getBool(); + ArgParser parser(runtime, arguments, count); + auto antiAliased = parser.next(); getObject()->setAntiAlias(antiAliased); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(setDither) { - bool dithered = arguments[0].getBool(); + ArgParser parser(runtime, arguments, count); + auto dithered = parser.next(); getObject()->setDither(dithered); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(setStrokeWidth) { - SkScalar width = arguments[0].asNumber(); + ArgParser parser(runtime, arguments, count); + auto width = parser.next(); getObject()->setStrokeWidth(width); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(setStyle) { - auto style = arguments[0].asNumber(); - getObject()->setStyle(static_cast(style)); + ArgParser parser(runtime, arguments, count); + auto style = parser.next(); + getObject()->setStyle(style); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(setStrokeCap) { - auto cap = arguments[0].asNumber(); - getObject()->setStrokeCap(static_cast(cap)); + ArgParser parser(runtime, arguments, count); + auto cap = parser.next(); + getObject()->setStrokeCap(cap); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(setStrokeJoin) { - int join = arguments[0].asNumber(); - getObject()->setStrokeJoin(static_cast(join)); + ArgParser parser(runtime, arguments, count); + auto join = parser.next(); + getObject()->setStrokeJoin(join); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(setStrokeMiter) { - int limit = arguments[0].asNumber(); + ArgParser parser(runtime, arguments, count); + auto limit = parser.next(); getObject()->setStrokeMiter(limit); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(setBlendMode) { - auto blendMode = (SkBlendMode)arguments[0].asNumber(); + ArgParser parser(runtime, arguments, count); + auto blendMode = parser.next(); getObject()->setBlendMode(blendMode); 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)); + ArgParser parser(runtime, arguments, count); + auto maskFilter = parser.next>>(); + getObject()->setMaskFilter(maskFilter.value_or(nullptr)); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(setImageFilter) { - auto imageFilter = arguments[0].isNull() || arguments[0].isUndefined() - ? nullptr - : JsiSkImageFilter::fromValue(runtime, arguments[0]); - getObject()->setImageFilter(std::move(imageFilter)); + ArgParser parser(runtime, arguments, count); + auto imageFilter = parser.next>>(); + getObject()->setImageFilter(imageFilter.value_or(nullptr)); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(setColorFilter) { - auto colorFilter = arguments[0].isNull() || arguments[0].isUndefined() - ? nullptr - : JsiSkColorFilter::fromValue(runtime, arguments[0]); - getObject()->setColorFilter(std::move(colorFilter)); + ArgParser parser(runtime, arguments, count); + auto colorFilter = parser.next>>(); + getObject()->setColorFilter(colorFilter.value_or(nullptr)); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(setShader) { - auto shader = arguments[0].isNull() || arguments[0].isUndefined() - ? nullptr - : JsiSkShader::fromValue(runtime, arguments[0]); - getObject()->setShader(std::move(shader)); + ArgParser parser(runtime, arguments, count); + auto shader = parser.next>>(); + getObject()->setShader(shader.value_or(nullptr)); return jsi::Value::undefined(); } JSI_HOST_FUNCTION(setPathEffect) { - auto pathEffect = arguments[0].isNull() || arguments[0].isUndefined() - ? nullptr - : JsiSkPathEffect::fromValue(runtime, arguments[0]); - getObject()->setPathEffect(std::move(pathEffect)); + ArgParser parser(runtime, arguments, count); + auto pathEffect = parser.next>>(); + getObject()->setPathEffect(pathEffect.value_or(nullptr)); return jsi::Value::undefined(); } diff --git a/packages/skia/cpp/jsi/JsiArgParser.h b/packages/skia/cpp/jsi/JsiArgParser.h new file mode 100644 index 0000000000..bdcec7b1d3 --- /dev/null +++ b/packages/skia/cpp/jsi/JsiArgParser.h @@ -0,0 +1,283 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace RNSkia { + +namespace jsi = facebook::jsi; + +namespace detail { + +/** + * Helper type trait to detect std::optional + */ +template struct is_optional : std::false_type {}; + +template struct is_optional> : std::true_type {}; + +template +inline constexpr bool is_optional_v = is_optional::value; + +/** + * Helper traits to detect shared_ptr and sk_sp types + */ +template struct is_shared_ptr : std::false_type {}; + +template +struct is_shared_ptr> : std::true_type { + using element_type = T; +}; + +template struct is_sk_sp : std::false_type {}; + +template struct is_sk_sp> : std::true_type { + using element_type = T; +}; + +} // namespace detail + +/** + * Traits class for parsing specific types - specialize this for custom types + */ +template struct ArgParserTraits { + static std::shared_ptr parseSharedPtr(jsi::Runtime &runtime, + const jsi::Value &value) { + // Default implementation - will cause compile error if not specialized + static_assert(sizeof(T) == 0, + "ArgParserTraits not specialized for this type. " + "Use JSI_ARG_PARSER_SHARED_PTR macro."); + return nullptr; + } + + static sk_sp parseSkSp(jsi::Runtime &runtime, const jsi::Value &value) { + // Default implementation - will cause compile error if not specialized + static_assert(sizeof(T) == 0, + "ArgParserTraits not specialized for this type. " + "Use JSI_ARG_PARSER_SK_SP macro."); + return nullptr; + } +}; + +/** + * ArgParser - A utility class for parsing JSI function arguments sequentially + * + * Usage: + * ArgParser parser(runtime, arguments, count); + * auto image = parser.next>(); // required argument + * auto x = parser.next(); // required argument + * auto paint = parser.next>>(); // + * optional + * + * The parser automatically: + * - Validates argument availability for required arguments + * - Returns std::nullopt for missing/null/undefined optional arguments + * - Advances the internal index after each call + * - Provides clear error messages with argument positions + */ +class ArgParser { +public: + ArgParser(jsi::Runtime &runtime, const jsi::Value *arguments, size_t count) + : _runtime(runtime), _arguments(arguments), _count(count), _index(0) {} + + /** + * Parse the next argument. + * - For regular types T: throws if argument is missing, returns T + * - For std::optional: returns std::nullopt if missing/null/undefined, + * otherwise T + */ + template T next() { + if constexpr (detail::is_optional_v) { + // Handle std::optional + using U = typename T::value_type; + + // Check if we're beyond available arguments + if (_index >= _count) { + _index++; + return std::nullopt; + } + + // Check if argument is null or undefined + if (_arguments[_index].isNull() || _arguments[_index].isUndefined()) { + _index++; + return std::nullopt; + } + + // Parse and return the value + return parse(_arguments[_index++]); + } else { + // Handle required argument + if (_index >= _count) { + throw jsi::JSError(_runtime, "Missing required argument at index " + + std::to_string(_index)); + } + + return parse(_arguments[_index++]); + } + } + + /** + * Get the current argument index (useful for error messages) + */ + size_t currentIndex() const { return _index; } + + /** + * Get the total number of arguments + */ + size_t totalCount() const { return _count; } + + /** + * Check if there are more arguments available + */ + bool hasMore() const { return _index < _count; } + + /** + * Reset the parser to start from the beginning + */ + void reset() { _index = 0; } + +private: + jsi::Runtime &_runtime; + const jsi::Value *_arguments; + size_t _count; + size_t _index; + + /** + * Type-specific parsing implementations + * These are ordered by priority - more specific matches first + */ + + // For shared_ptr types - uses ArgParserTraits for customization (highest + // priority for ptr types) + template + typename std::enable_if::value, T>::type + parse(const jsi::Value &value) { + using ElementType = typename detail::is_shared_ptr::element_type; + return ArgParserTraits::parseSharedPtr(_runtime, value); + } + + // For sk_sp types - uses ArgParserTraits for customization + template + typename std::enable_if::value, T>::type + parse(const jsi::Value &value) { + using ElementType = typename detail::is_sk_sp::element_type; + return ArgParserTraits::parseSkSp(_runtime, value); + } + + // Primitive types + template + typename std::enable_if< + std::is_same::value || std::is_same::value, T>::type + parse(const jsi::Value &value) { + if (!value.isNumber()) { + throw jsi::JSError(_runtime, "Expected number for argument at index " + + std::to_string(_index)); + } + return static_cast(value.asNumber()); + } + + template + typename std::enable_if< + std::is_integral::value && !std::is_same::value, T>::type + parse(const jsi::Value &value) { + if (!value.isNumber()) { + throw jsi::JSError(_runtime, "Expected number for argument at index " + + std::to_string(_index)); + } + return static_cast(value.asNumber()); + } + + template + typename std::enable_if::value, T>::type + parse(const jsi::Value &value) { + if (!value.isBool()) { + throw jsi::JSError(_runtime, "Expected boolean for argument at index " + + std::to_string(_index)); + } + return value.getBool(); + } + + template + typename std::enable_if::value, T>::type + parse(const jsi::Value &value) { + if (!value.isString()) { + throw jsi::JSError(_runtime, "Expected string for argument at index " + + std::to_string(_index)); + } + return value.asString(_runtime).utf8(_runtime); + } + + template + typename std::enable_if::value, T>::type + parse(const jsi::Value &value) { + if (!value.isString()) { + throw jsi::JSError(_runtime, "Expected string for argument at index " + + std::to_string(_index)); + } + return value.asString(_runtime); + } + + template + typename std::enable_if::value, T>::type + parse(const jsi::Value &value) { + // For jsi::Value, we need to copy it since we can't just return a reference + return jsi::Value(_runtime, value); + } + + template + typename std::enable_if::value, T>::type + parse(const jsi::Value &value) { + if (!value.isObject()) { + throw jsi::JSError(_runtime, "Expected object for argument at index " + + std::to_string(_index)); + } + return value.asObject(_runtime); + } + + template + typename std::enable_if::value, T>::type + parse(const jsi::Value &value) { + if (!value.isObject()) { + throw jsi::JSError(_runtime, "Expected array for argument at index " + + std::to_string(_index)); + } + auto obj = value.asObject(_runtime); + if (!obj.isArray(_runtime)) { + throw jsi::JSError(_runtime, "Expected array for argument at index " + + std::to_string(_index)); + } + return obj.asArray(_runtime); + } + + template + typename std::enable_if::value, T>::type + parse(const jsi::Value &value) { + if (!value.isObject()) { + throw jsi::JSError(_runtime, "Expected function for argument at index " + + std::to_string(_index)); + } + auto obj = value.asObject(_runtime); + if (!obj.isFunction(_runtime)) { + throw jsi::JSError(_runtime, "Expected function for argument at index " + + std::to_string(_index)); + } + return obj.asFunction(_runtime); + } + + // For enum types (cast from number) + template + typename std::enable_if::value, T>::type + parse(const jsi::Value &value) { + if (!value.isNumber()) { + throw jsi::JSError(_runtime, + "Expected number (enum) for argument at index " + + std::to_string(_index)); + } + return static_cast(static_cast(value.asNumber())); + } +}; + +} // namespace RNSkia diff --git a/packages/skia/cpp/jsi/JsiArgParserTypes.h b/packages/skia/cpp/jsi/JsiArgParserTypes.h new file mode 100644 index 0000000000..5e98fc0e71 --- /dev/null +++ b/packages/skia/cpp/jsi/JsiArgParserTypes.h @@ -0,0 +1,63 @@ +#pragma once + +#include "JsiArgParser.h" + +// Forward declarations for all JsiSk* wrapper classes +// These will be included by files that use ArgParser +namespace RNSkia { + +// Import common forward declarations +class JsiSkPaint; +class JsiSkImage; +class JsiSkRect; +class JsiSkRRect; +class JsiSkPath; +class JsiSkPoint; +class JsiSkMatrix; +class JsiSkFont; +class JsiSkShader; +class JsiSkImageFilter; +class JsiSkColorFilter; +class JsiSkMaskFilter; +class JsiSkPathEffect; +class JsiSkPicture; +class JsiSkTextBlob; +class JsiSkVertices; +class JsiSkSVG; +class JsiSkData; +class JsiSkTypeface; +class JsiSkRSXform; + +} // namespace RNSkia + +// Macro to make it easy to define custom parsers in implementation files +// For shared_ptr types +#define JSI_ARG_PARSER_SHARED_PTR(SkType, JsiType) \ + namespace RNSkia { \ + template <> struct ArgParserTraits { \ + static std::shared_ptr parseSharedPtr(jsi::Runtime &runtime, \ + const jsi::Value &value) { \ + return JsiType::fromValue(runtime, value); \ + } \ + }; \ + } + +// For sk_sp types +#define JSI_ARG_PARSER_SK_SP(SkType, JsiType) \ + namespace RNSkia { \ + template <> struct ArgParserTraits { \ + static sk_sp parseSkSp(jsi::Runtime &runtime, \ + const jsi::Value &value) { \ + return JsiType::fromValue(runtime, value); \ + } \ + }; \ + } + +// For SkColor (special case - it's a typedef, not a class) +#define JSI_ARG_PARSER_SKCOLOR(JsiColorType) \ + namespace RNSkia { \ + template <> \ + inline SkColor ArgParser::parse(const jsi::Value &value) { \ + return JsiColorType::fromValue(_runtime, value); \ + } \ + }