diff --git a/packages/skia/cpp/api/JsiSkImageFilterFactory.h b/packages/skia/cpp/api/JsiSkImageFilterFactory.h index 3488401b04..7a496d2663 100644 --- a/packages/skia/cpp/api/JsiSkImageFilterFactory.h +++ b/packages/skia/cpp/api/JsiSkImageFilterFactory.h @@ -7,12 +7,14 @@ #include "JsiSkHostObjects.h" #include "JsiSkImageFilter.h" +#include "JsiSkPicture.h" #include "JsiSkRuntimeShaderBuilder.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdocumentation" #include "include/core/SkImageFilter.h" +#include "include/core/SkPoint3.h" #pragma clang diagnostic pop @@ -20,46 +22,127 @@ 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 JsiSkHostObject { public: + 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]); + } + return jsi::Object::createFromHostObject( + runtime, std::make_shared( + getContext(), + SkImageFilters::Arithmetic( + k1, k2, k3, k4, enforcePMColor, std::move(background), + std::move(foreground), cropRect))); + } + + 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]); + } + + SkImageFilters::CropRect cropRect = {}; + if (hasOptionalArgument(arguments, count, 3)) { + cropRect = *JsiSkRect::fromValue(runtime, arguments[3]); + } + + return jsi::Object::createFromHostObject( + runtime, std::make_shared( + getContext(), SkImageFilters::Blend( + std::move(mode), std::move(background), + std::move(foreground), cropRect))); + } + JSI_HOST_FUNCTION(MakeBlur) { float sigmaX = arguments[0].asNumber(); float sigmaY = arguments[1].asNumber(); int tileMode = arguments[2].asNumber(); sk_sp imageFilter; - if (!arguments[3].isNull()) { + 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]); + } return jsi::Object::createFromHostObject( runtime, std::make_shared( getContext(), SkImageFilters::Blur(sigmaX, sigmaY, (SkTileMode)tileMode, - imageFilter))); + std::move(imageFilter), cropRect))); } JSI_HOST_FUNCTION(MakeColorFilter) { auto cf = JsiSkColorFilter::fromValue(runtime, arguments[0]); - sk_sp input; - if (!arguments[1].isNull()) { + 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]); + } + return jsi::Object::createFromHostObject( + runtime, + std::make_shared( + getContext(), SkImageFilters::ColorFilter( + std::move(cf), std::move(input), cropRect))); + } + + 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]); + } return jsi::Object::createFromHostObject( runtime, std::make_shared( - getContext(), SkImageFilters::ColorFilter( - std::move(cf), std::move(input)))); + getContext(), SkImageFilters::Compose(std::move(outer), + std::move(inner)))); } - JSI_HOST_FUNCTION(MakeOffset) { - auto x = arguments[0].asNumber(); - auto y = arguments[1].asNumber(); - sk_sp input; - if (!arguments[2].isNull()) { - input = JsiSkImageFilter::fromValue(runtime, arguments[2]); + 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]); } return jsi::Object::createFromHostObject( runtime, std::make_shared( - getContext(), SkImageFilters::Offset(x, y, std::move(input)))); + getContext(), + SkImageFilters::Crop(rect, tileMode, std::move(imageFilter)))); } JSI_HOST_FUNCTION(MakeDisplacementMap) { @@ -68,61 +151,21 @@ class JsiSkImageFilterFactory : public JsiSkHostObject { auto fYChannelSelector = static_cast(arguments[1].asNumber()); auto scale = arguments[2].asNumber(); - auto in2 = JsiSkImageFilter::fromValue(runtime, arguments[3]); - sk_sp input; - if (!arguments[4].isNull()) { + auto in1 = 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]); + } return jsi::Object::createFromHostObject( runtime, std::make_shared( getContext(), SkImageFilters::DisplacementMap( fXChannelSelector, fYChannelSelector, scale, - std::move(in2), std::move(input)))); - } - - JSI_HOST_FUNCTION(MakeShader) { - auto shader = JsiSkShader::fromValue(runtime, arguments[0]); - return jsi::Object::createFromHostObject( - runtime, std::make_shared( - getContext(), SkImageFilters::Shader(std::move(shader)))); - } - - JSI_HOST_FUNCTION(MakeCompose) { - sk_sp outer; - if (!arguments[0].isNull() && !arguments[0].isUndefined()) { - outer = JsiSkImageFilter::fromValue(runtime, arguments[0]); - } - sk_sp inner; - if (!arguments[1].isNull() && !arguments[1].isUndefined()) { - inner = JsiSkImageFilter::fromValue(runtime, arguments[1]); - } - return jsi::Object::createFromHostObject( - runtime, std::make_shared( - getContext(), SkImageFilters::Compose(std::move(outer), - std::move(inner)))); - } - - JSI_HOST_FUNCTION(MakeBlend) { - auto mode = static_cast(arguments[0].asNumber()); - sk_sp background = - JsiSkImageFilter::fromValue(runtime, arguments[1]); - sk_sp foreground = nullptr; - - if (count > 2 && !arguments[2].isNull()) { - foreground = JsiSkImageFilter::fromValue(runtime, arguments[2]); - } - - SkImageFilters::CropRect cropRect = {}; - if (count > 3 && !arguments[3].isUndefined()) { - cropRect = *JsiSkRect::fromValue(runtime, arguments[3]); - } - - return jsi::Object::createFromHostObject( - runtime, std::make_shared( - getContext(), SkImageFilters::Blend( - std::move(mode), std::move(background), - std::move(foreground), cropRect))); + std::move(in1), std::move(input), cropRect))); } JSI_HOST_FUNCTION(MakeDropShadow) { @@ -131,12 +174,12 @@ class JsiSkImageFilterFactory : public JsiSkHostObject { auto sigmaX = arguments[2].asNumber(); auto sigmaY = arguments[3].asNumber(); auto color = JsiSkColor::fromValue(runtime, arguments[4]); - sk_sp input; - if (!arguments[5].isNull() && !arguments[5].isUndefined()) { + sk_sp input = nullptr; + if (hasOptionalArgument(arguments, count, 5)) { input = JsiSkImageFilter::fromValue(runtime, arguments[5]); } SkImageFilters::CropRect cropRect = {}; - if (count > 6 && !arguments[6].isUndefined()) { + if (hasOptionalArgument(arguments, count, 6)) { cropRect = *JsiSkRect::fromValue(runtime, arguments[6]); } return jsi::Object::createFromHostObject( @@ -152,12 +195,12 @@ class JsiSkImageFilterFactory : public JsiSkHostObject { auto sigmaX = arguments[2].asNumber(); auto sigmaY = arguments[3].asNumber(); auto color = JsiSkColor::fromValue(runtime, arguments[4]); - sk_sp input; - if (!arguments[5].isNull() && !arguments[5].isUndefined()) { + sk_sp input = nullptr; + if (hasOptionalArgument(arguments, count, 5)) { input = JsiSkImageFilter::fromValue(runtime, arguments[5]); } SkImageFilters::CropRect cropRect = {}; - if (count > 6 && !arguments[6].isUndefined()) { + if (hasOptionalArgument(arguments, count, 6)) { cropRect = *JsiSkRect::fromValue(runtime, arguments[6]); } return jsi::Object::createFromHostObject( @@ -167,10 +210,263 @@ class JsiSkImageFilterFactory : public JsiSkHostObject { std::move(input), cropRect))); } - JSI_HOST_FUNCTION(MakeErode) { + JSI_HOST_FUNCTION(MakeEmpty) { + return jsi::Object::createFromHostObject( + runtime, std::make_shared(getContext(), + SkImageFilters::Empty())); + } + + 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(); + } + return jsi::Object::createFromHostObject( + runtime, std::make_shared( + getContext(), SkImageFilters::Image( + std::move(image), srcRect, dstRect, + SkSamplingOptions(filterMode, mipmap)))); + } + + 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]); + } + return jsi::Object::createFromHostObject( + runtime, std::make_shared( + getContext(), SkImageFilters::Magnifier( + lensBounds, zoomAmount, inset, + SkSamplingOptions(filterMode, mipmap), + input, cropRect))); + } + + JSI_HOST_FUNCTION(MakeMatrixConvolution) { + SkISize kernelSize = + SkISize(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(arguments[5].asNumber(), arguments[6].asNumber()); + SkTileMode tileMode = (SkTileMode)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]); + } + return jsi::Object::createFromHostObject( + runtime, std::make_shared( + getContext(), + SkImageFilters::MatrixConvolution( + kernelSize, kernel.data(), gain, bias, kernelOffset, + tileMode, convolveAlpha, std::move(input), cropRect))); + } + + 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]); + } + return jsi::Object::createFromHostObject( + runtime, + std::make_shared( + getContext(), SkImageFilters::MatrixTransform( + matrix, SkSamplingOptions(filterMode, mipmap), + std::move(input)))); + } + + JSI_HOST_FUNCTION(MakeMerge) { + 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)); + } + } + SkImageFilters::CropRect cropRect = {}; + if (hasOptionalArgument(arguments, count, 1)) { + cropRect = *JsiSkRect::fromValue(runtime, arguments[1]); + } + return jsi::Object::createFromHostObject( + runtime, + std::make_shared( + getContext(), + SkImageFilters::Merge(filters.data(), filtersCount, cropRect))); + } + + 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]); + } + return jsi::Object::createFromHostObject( + runtime, std::make_shared( + getContext(), + SkImageFilters::Offset(x, y, std::move(input), cropRect))); + } + + 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(); + } + return jsi::Object::createFromHostObject( + runtime, std::make_shared( + getContext(), + SkImageFilters::Picture(std::move(picture), targetRect))); + } + + JSI_HOST_FUNCTION(MakeRuntimeShader) { + auto rtb = JsiSkRuntimeShaderBuilder::fromValue(runtime, arguments[0]); + const char *childName = ""; + if (hasOptionalArgument(arguments, count, 1)) { + childName = arguments[1].asString(runtime).utf8(runtime).c_str(); + } + sk_sp input = nullptr; + if (hasOptionalArgument(arguments, count, 2)) { + input = JsiSkImageFilter::fromValue(runtime, arguments[2]); + } + return jsi::Object::createFromHostObject( + runtime, std::make_shared( + getContext(), SkImageFilters::RuntimeShader( + *rtb, childName, std::move(input)))); + } + + 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::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)); + } + } + return jsi::Object::createFromHostObject( + runtime, std::make_shared( + getContext(), + SkImageFilters::RuntimeShader(*rtb, maxSampleRadius, + childNamesStringView.data(), + inputs.data(), length))); + } + + 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]); + } + return jsi::Object::createFromHostObject( + runtime, std::make_shared( + getContext(), SkImageFilters::Shader(std::move(shader), + dither, cropRect))); + } + + 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]); + } + return jsi::Object::createFromHostObject( + runtime, + std::make_shared( + getContext(), SkImageFilters::Tile(src, dst, std::move(input)))); + } + + JSI_HOST_FUNCTION(MakeDilate) { auto rx = arguments[0].asNumber(); auto ry = arguments[1].asNumber(); - sk_sp input; + sk_sp input = nullptr; if (!arguments[2].isNull() && !arguments[2].isUndefined()) { input = JsiSkImageFilter::fromValue(runtime, arguments[2]); } @@ -180,58 +476,198 @@ class JsiSkImageFilterFactory : public JsiSkHostObject { } return jsi::Object::createFromHostObject( runtime, std::make_shared( - getContext(), SkImageFilters::Erode( + getContext(), SkImageFilters::Dilate( rx, ry, std::move(input), cropRect))); } - JSI_HOST_FUNCTION(MakeDilate) { + JSI_HOST_FUNCTION(MakeErode) { auto rx = arguments[0].asNumber(); auto ry = arguments[1].asNumber(); - sk_sp input; + sk_sp input = nullptr; if (!arguments[2].isNull() && !arguments[2].isUndefined()) { input = JsiSkImageFilter::fromValue(runtime, arguments[2]); } SkImageFilters::CropRect cropRect = {}; - if (count > 3 && !arguments[3].isUndefined()) { + if (count > 3 && !arguments[3].isUndefined() && !arguments[3].isNull()) { cropRect = *JsiSkRect::fromValue(runtime, arguments[3]); } return jsi::Object::createFromHostObject( runtime, std::make_shared( - getContext(), SkImageFilters::Dilate( + getContext(), SkImageFilters::Erode( rx, ry, std::move(input), cropRect))); } - JSI_HOST_FUNCTION(MakeRuntimeShader) { - auto rtb = JsiSkRuntimeShaderBuilder::fromValue(runtime, arguments[0]); + 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); + } - const char *childName = ""; - if (!arguments[1].isNull() && !arguments[1].isUndefined()) { - childName = arguments[1].asString(runtime).utf8(runtime).c_str(); + 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]); + } + return jsi::Object::createFromHostObject( + runtime, std::make_shared( + getContext(), SkImageFilters::DistantLitDiffuse( + direction, lightColor, surfaceScale, kd, + std::move(input), cropRect))); + } - sk_sp input; - if (!arguments[2].isNull() && !arguments[2].isUndefined()) { - input = JsiSkImageFilter::fromValue(runtime, arguments[2]); + 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]); } return jsi::Object::createFromHostObject( runtime, std::make_shared( - getContext(), SkImageFilters::RuntimeShader( - *rtb, childName, std::move(input)))); + getContext(), SkImageFilters::PointLitDiffuse( + location, lightColor, surfaceScale, kd, + std::move(input), cropRect))); + } + + 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]); + } + return jsi::Object::createFromHostObject( + runtime, std::make_shared( + getContext(), SkImageFilters::SpotLitDiffuse( + location, target, falloffExponent, + cutoffAngle, lightColor, surfaceScale, + kd, std::move(input), cropRect))); + } + + 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]); + } + return jsi::Object::createFromHostObject( + runtime, std::make_shared( + getContext(), SkImageFilters::DistantLitSpecular( + direction, lightColor, surfaceScale, ks, + shininess, std::move(input), cropRect))); + } + + 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]); + } + return jsi::Object::createFromHostObject( + runtime, std::make_shared( + getContext(), SkImageFilters::PointLitSpecular( + location, lightColor, surfaceScale, ks, + shininess, std::move(input), cropRect))); + } + + 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]); + } + return jsi::Object::createFromHostObject( + runtime, + std::make_shared( + getContext(), + SkImageFilters::SpotLitSpecular( + location, target, falloffExponent, cutoffAngle, lightColor, + surfaceScale, ks, shininess, std::move(input), cropRect))); } JSI_EXPORT_FUNCTIONS( + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeArithmetic), + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeBlend), JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeBlur), - JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeOffset), JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeColorFilter), - JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeShader), - JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeDisplacementMap), JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeCompose), - JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeErode), - JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeDilate), - JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeBlend), + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeCrop), + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeDisplacementMap), JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeDropShadow), JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeDropShadowOnly), - JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeRuntimeShader)) + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeEmpty), + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeImage), + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeMagnifier), + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeMatrixConvolution), + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeMatrixTransform), + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeMerge), + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeOffset), + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakePicture), + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeRuntimeShader), + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeRuntimeShaderWithChildren), + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeShader), + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeTile), + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeDilate), + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeErode), + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeDistantLitDiffuse), + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakePointLitDiffuse), + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeSpotLitDiffuse), + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeDistantLitSpecular), + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakePointLitSpecular), + JSI_EXPORT_FUNC(JsiSkImageFilterFactory, MakeSpotLitSpecular)) explicit JsiSkImageFilterFactory(std::shared_ptr context) : JsiSkHostObject(std::move(context)) {} diff --git a/packages/skia/src/dom/nodes/paint/ImageFilters.ts b/packages/skia/src/dom/nodes/paint/ImageFilters.ts index c3636fc016..5e154e4061 100644 --- a/packages/skia/src/dom/nodes/paint/ImageFilters.ts +++ b/packages/skia/src/dom/nodes/paint/ImageFilters.ts @@ -109,7 +109,7 @@ export class DisplacementMapImageFilterNode extends ImageFilterDeclaration SkImageFilter; /** * Create a filter that blurs its input by the separate X and Y sigmas. The provided tile mode * is used when the blur kernel goes outside the input image. @@ -58,24 +60,27 @@ export interface ImageFilterFactory { * @param sigmaY - The Gaussian sigma value for blurring along the Y axis. * @param mode * @param input - if null, it will use the dynamic source image (e.g. a saved layer) + * @param cropRect Optional rectangle to crop input and output. */ MakeBlur( sigmaX: number, sigmaY: number, mode: TileMode, - input: SkImageFilter | null + input?: SkImageFilter | null, + cropRect?: SkRect | null ): SkImageFilter; - /** * Create a filter that applies the color filter to the input filter results. - * @param cf + * + * @param colorFilter - The color filter to apply * @param input - if null, it will use the dynamic source image (e.g. a saved layer) + * @param cropRect Optional rectangle to crop input and output. */ MakeColorFilter( - cf: SkColorFilter, - input: SkImageFilter | null + colorFilter: SkColorFilter, + input?: SkImageFilter | null, + cropRect?: SkRect | null ): SkImageFilter; - /** * Create a filter that composes 'inner' with 'outer', such that the results of 'inner' are * treated as the source bitmap passed to 'outer'. @@ -87,7 +92,43 @@ export interface ImageFilterFactory { outer: SkImageFilter | null, inner: SkImageFilter | null ): SkImageFilter; - + /** + * Create a filter that applies a crop to the result of the 'input' filter. Pixels within the + * crop rectangle are unmodified from what 'input' produced. Pixels outside of crop match the + * provided SkTileMode (defaulting to kDecal). + * + * NOTE: The optional CropRect argument for many of the factories is equivalent to creating the + * filter without a CropRect and then wrapping it in ::Crop(rect, kDecal). Explicitly adding + * Crop filters lets you control their tiling and use different geometry for the input and the + * output of another filter. + * + * @param rect The cropping rect + * @param tileMode The TileMode applied to pixels *outside* of 'crop' @default TileMode.Decal + * @param input The input filter that is cropped, uses source image if this is null + */ + MakeCrop( + rect: SkRect, + tileMode?: TileMode | null, + input?: SkImageFilter | null + ): SkImageFilter; + /** + * Spatially displace pixel values of the filtered image + * + * @param channelX - Color channel to be used along the X axis + * @param channelY - Color channel to be used along the Y axis + * @param scale - Scale factor to be used in the displacement + * @param in1 - Source image filter to use for the displacement + * @param input - if null, it will use the dynamic source image + * @param cropRect Optional rectangle to crop input and output. + */ + MakeDisplacementMap( + channelX: ColorChannel, + channelY: ColorChannel, + scale: number, + in1: SkImageFilter, + input?: SkImageFilter | null, + cropRect?: SkRect | null + ): SkImageFilter; /** * Create a filter that draws a drop shadow under the input content. * This filter produces an image that includes the inputs' content. @@ -105,9 +146,16 @@ export interface ImageFilterFactory { sigmaX: number, sigmaY: number, color: SkColor, - input: SkImageFilter | null, - cropRect?: SkRect + input?: SkImageFilter | null, + cropRect?: SkRect | null ) => SkImageFilter; + /** + * Offsets the input image + * + * @param dx - Offset along the X axis + * @param dy - Offset along the X axis + * @param input - if null, it will use the dynamic source image + */ /** * Create a filter that renders a drop shadow, in exactly the same manner as ::DropShadow, except * that the resulting image does not include the input content. @@ -126,50 +174,134 @@ export interface ImageFilterFactory { sigmaX: number, sigmaY: number, color: SkColor, - input: SkImageFilter | null, - cropRect?: SkRect + input?: SkImageFilter | null, + cropRect?: SkRect | null ) => SkImageFilter; /** - * Create a filter that erodes each input pixel's channel values to the minimum channel value - * within the given radii along the x and y axes. - * @param radiusX The distance to erode along the x axis to either side of each pixel. - * @param radiusY The distance to erode along the y axis to either side of each pixel. - * @param input The image filter that is eroded, using source bitmap if this is null. - * @param cropRect Optional rectangle that crops the input and output. + * Create a filter that always produces transparent black. */ - MakeErode: ( - rx: number, - ry: number, - input: SkImageFilter | null, - cropRect?: SkRect - ) => SkImageFilter; + MakeEmpty(): SkImageFilter; /** - * Create a filter that dilates each input pixel's channel values to the max value within the - * given radii along the x and y axes. - * @param radiusX The distance to dilate along the x axis to either side of each pixel. - * @param radiusY The distance to dilate along the y axis to either side of each pixel. - * @param input The image filter that is dilated, using source bitmap if this is null. - * @param cropRect Optional rectangle that crops the input and output. + * Create a filter that draws the 'srcRect' portion of image into 'dstRect' using the given + * filter quality. Similar to SkCanvas::drawImageRect. The returned image filter evaluates + * to transparent black if 'image' is null. + * + * @param image The image that is output by the filter, subset by 'srcRect'. + * @param srcRect The source pixels sampled into 'dstRect', if null the image bounds are used. + * @param dstRect The local rectangle to draw the image into, if null the srcRect is used. + * @param filterMode The filter mode to use when sampling the image @default FilterMode.Nearest + * @param mipmap The mipmap mode to use when sampling the image @default MipmapMode.None */ - MakeDilate: ( - rx: number, - ry: number, - input: SkImageFilter | null, - cropRect?: SkRect - ) => SkImageFilter; + MakeImage( + image: SkImage, + srcRect?: SkRect | null, + dstRect?: SkRect | null, + filterMode?: FilterMode, + mipmap?: MipmapMode + ): SkImageFilter; /** - * This filter takes an SkBlendMode and uses it to composite the two filters together. - * @param mode The blend mode that defines the compositing operation - * @param background The Dst pixels used in blending, if null the source bitmap is used. - * @param foreground The Src pixels used in blending, if null the source bitmap is used. - * @cropRect Optional rectangle to crop input and output. + * Create a filter that fills 'lensBounds' with a magnification of the input. + * + * @param lensBounds The outer bounds of the magnifier effect + * @param zoomAmount The amount of magnification applied to the input image + * @param inset The size or width of the fish-eye distortion around the magnified content + * @param filterMode The filter mode to use when sampling the image @default FilterMode.Nearest + * @param mipmap The mipmap mode to use when sampling the image @default MipmapMode.None + * @param input The input filter that is magnified; if null the source bitmap is used + * @param cropRect Optional rectangle that crops the input and output. */ - MakeBlend: ( - mode: BlendMode, - background: SkImageFilter, - foreground: SkImageFilter | null, - cropRect?: SkRect - ) => SkImageFilter; + MakeMagnifier( + lensBounds: SkRect, + zoomAmount: number, + inset: number, + filterMode?: FilterMode, + mipmap?: MipmapMode, + input?: SkImageFilter | null, + cropRect?: SkRect | null + ): SkImageFilter; + /** + * Create a filter that applies an NxM image processing kernel to the input image. This can be + * used to produce effects such as sharpening, blurring, edge detection, etc. + * @param kernelSizeX The width of the kernel. Must be greater than zero. + * @param kernelSizeY The height of the kernel. Must be greater than zero. + * @param kernel The image processing kernel. Must contain kernelSizeX * kernelSizeY elements, in row order. + * @param gain A scale factor applied to each pixel after convolution. This can be + * used to normalize the kernel, if it does not already sum to 1. + * @param bias A bias factor added to each pixel after convolution. + * @param kernelOffsetX An offset applied to each pixel coordinate before convolution. + * This can be used to center the kernel over the image + * (e.g., a 3x3 kernel should have an offset of {1, 1}). + * @param kernelOffsetY An offset applied to each pixel coordinate before convolution. + * This can be used to center the kernel over the image + * (e.g., a 3x3 kernel should have an offset of {1, 1}). + * @param tileMode How accesses outside the image are treated. TileMode.Mirror is not supported. + * @param convolveAlpha If true, all channels are convolved. If false, only the RGB channels + * are convolved, and alpha is copied from the source image. + * @param input The input image filter, if null the source bitmap is used instead. + * @param cropRect Optional rectangle to which the output processing will be limited. + */ + MakeMatrixConvolution( + kernelSizeX: number, + kernelSizeY: number, + kernel: number[], + gain: number, + bias: number, + kernelOffsetX: number, + kernelOffsetY: number, + tileMode: TileMode, + convolveAlpha: boolean, + input?: SkImageFilter | null, + cropRect?: SkRect | null + ): SkImageFilter; + /** + * Create a filter that transforms the input image by 'matrix'. This matrix transforms the + * local space, which means it effectively happens prior to any transformation coming from the + * SkCanvas initiating the filtering. + * @param matrix The matrix to apply to the original content. + * @param filterMode The filter mode to use when sampling the image @default FilterMode.Nearest + * @param mipmap The mipmap mode to use when sampling the image @default MipmapMode.None + * @param input The image filter to transform, or null to use the source image. + */ + MakeMatrixTransform( + matrix: SkMatrix, + filterMode?: FilterMode, + mipmap?: MipmapMode, + input?: SkImageFilter | null + ): SkImageFilter; + /** + * Create a filter that merges filters together by drawing their results in order + * with src-over blending. + * @param filters The input filter array to merge. Any null + * filter pointers will use the source bitmap instead. + * @param cropRect Optional rectangle that crops all input filters and the output. + */ + MakeMerge( + filters: Array, + cropRect?: SkRect | null + ): SkImageFilter; + /** + * Create a filter that offsets the input filter by the given vector. + * @param dx The x offset in local space that the image is shifted. + * @param dy The y offset in local space that the image is shifted. + * @param input The input that will be moved, if null the source bitmap is used instead. + * @param cropRect Optional rectangle to crop the input and output. + */ + MakeOffset( + dx: number, + dy: number, + input?: SkImageFilter | null, + cropRect?: SkRect | null + ): SkImageFilter; + /** + * Create a filter that produces the SkPicture as its output, clipped to both 'targetRect' and + * the picture's internal cull rect. + * + * If 'pic' is null, the returned image filter produces transparent black. + * + * @param picture The picture that is drawn for the filter output. + * @param targetRect The drawing region for the picture. If null, the picture's bounds are used. + */ + MakePicture(picture: SkPicture, targetRect?: SkRect | null): SkImageFilter; /** * Create a filter that fills the output with the per-pixel evaluation of the SkShader produced * by the SkRuntimeShaderBuilder. The shader is defined in the image filter's local coordinate @@ -189,4 +321,228 @@ export interface ImageFilterFactory { childShaderName: string | null, input: SkImageFilter | null ) => SkImageFilter; + /** + * Create a filter that fills the output with the per-pixel evaluation of the SkShader produced + * by the SkRuntimeEffectBuilder. The shader is defined in the image filter's local coordinate + * system, so it will automatically be affected by SkCanvas' transform. + * + * This requires a GPU backend or SkSL to be compiled in. + * + * @param builder The builder used to produce the runtime shader, that will in turn + * fill the result image + * @param childShaderNames The names of the child shaders defined in the builder that will be + * bound to the input params (or the source image if the input param + * is null). If any name is null, or appears more than once, factory + * fails and returns nullptr. + * @param sampleRadius defines the sampling radius of 'childShaderName' relative to + * the runtime shader produced by 'builder'. + * If greater than 0, the coordinate passed to childShader.eval() will + * be up to 'sampleRadius' away (maximum absolute offset in 'x' or 'y') + * from the coordinate passed into the runtime shader. + * @param inputs The image filters that will be provided as input to the runtime + * shader. If any are null, the implicit source image is used instead. + */ + MakeRuntimeShaderWithChildren: ( + builder: SkRuntimeShaderBuilder, + sampleRadius: number, + childShaderNames: string[], + inputs: Array + ) => SkImageFilter; + /** + * Transforms a shader into an impage filter + * + * @param shader - The Shader to be transformed + * @param dither + * @param cropRect - Optional rectangle to crop input and output. + */ + MakeShader( + shader: SkShader, + dither?: boolean, + cropRect?: SkRect | null + ): SkImageFilter; + /** + * Create a tile image filter. + * @param src Defines the pixels to tile + * @param dst Defines the pixel region that the tiles will be drawn to + * @param input The input that will be tiled, if null the source bitmap is used instead. + */ + MakeTile( + src: SkRect, + dst: SkRect, + input?: SkImageFilter | null + ): SkImageFilter; + /** + * Create a filter that dilates each input pixel's channel values to the max value within the + * given radii along the x and y axes. + * @param radiusX The distance to dilate along the x axis to either side of each pixel. + * @param radiusY The distance to dilate along the y axis to either side of each pixel. + * @param input The image filter that is dilated, using source bitmap if this is null. + * @param cropRect Optional rectangle that crops the input and output. + */ + MakeDilate: ( + rx: number, + ry: number, + input?: SkImageFilter | null, + cropRect?: SkRect | null + ) => SkImageFilter; + /** + * Create a filter that erodes each input pixel's channel values to the minimum channel value + * within the given radii along the x and y axes. + * @param radiusX The distance to erode along the x axis to either side of each pixel. + * @param radiusY The distance to erode along the y axis to either side of each pixel. + * @param input The image filter that is eroded, using source bitmap if this is null. + * @param cropRect Optional rectangle that crops the input and output. + */ + MakeErode: ( + rx: number, + ry: number, + input: SkImageFilter | null, + cropRect?: SkRect | null + ) => SkImageFilter; + /** + * Create a filter that calculates the diffuse illumination from a distant light source, + * interpreting the alpha channel of the input as the height profile of the surface (to + * approximate normal vectors). + * @param direction The direction to the distance light. + * @param lightColor The color of the diffuse light source. + * @param surfaceScale Scale factor to transform from alpha values to physical height. + * @param kd Diffuse reflectance coefficient. + * @param input The input filter that defines surface normals (as alpha), or uses the + * source bitmap when null. + * @param cropRect Optional rectangle that crops the input and output. + */ + MakeDistantLitDiffuse( + direction: SkPoint3, + lightColor: SkColor, + surfaceScale: number, + kd: number, + input?: SkImageFilter | null, + cropRect?: SkRect | null + ): SkImageFilter; + /** + * Create a filter that calculates the diffuse illumination from a point light source, using + * alpha channel of the input as the height profile of the surface (to approximate normal + * vectors). + * @param location The location of the point light. + * @param lightColor The color of the diffuse light source. + * @param surfaceScale Scale factor to transform from alpha values to physical height. + * @param kd Diffuse reflectance coefficient. + * @param input The input filter that defines surface normals (as alpha), or uses the + * source bitmap when null. + * @param cropRect Optional rectangle that crops the input and output. + */ + MakePointLitDiffuse( + location: SkPoint3, + lightColor: SkColor, + surfaceScale: number, + kd: number, + input?: SkImageFilter | null, + cropRect?: SkRect | null + ): SkImageFilter; + /** + * Create a filter that calculates the diffuse illumination from a spot light source, using + * alpha channel of the input as the height profile of the surface (to approximate normal + * vectors). The spot light is restricted to be within 'cutoffAngle' of the vector between + * the location and target. + * @param location The location of the spot light. + * @param target The location that the spot light is point towards + * @param falloffExponent Exponential falloff parameter for illumination outside of cutoffAngle + * @param cutoffAngle Maximum angle from lighting direction that receives full light + * @param lightColor The color of the diffuse light source. + * @param surfaceScale Scale factor to transform from alpha values to physical height. + * @param kd Diffuse reflectance coefficient. + * @param input The input filter that defines surface normals (as alpha), or uses the + * source bitmap when null. + * @param cropRect Optional rectangle that crops the input and output. + */ + MakeSpotLitDiffuse( + location: SkPoint3, + target: SkPoint3, + falloffExponent: number, + cutoffAngle: number, + lightColor: SkColor, + surfaceScale: number, + kd: number, + input?: SkImageFilter | null, + cropRect?: SkRect | null + ): SkImageFilter; + /** + * Create a filter that calculates the specular illumination from a distant light source, + * interpreting the alpha channel of the input as the height profile of the surface (to + * approximate normal vectors). + * @param direction The direction to the distance light. + * @param lightColor The color of the specular light source. + * @param surfaceScale Scale factor to transform from alpha values to physical height. + * @param ks Specular reflectance coefficient. + * @param shininess The specular exponent determining how shiny the surface is. + * @param input The input filter that defines surface normals (as alpha), or uses the + * source bitmap when null. + * @param cropRect Optional rectangle that crops the input and output. + */ + MakeDistantLitSpecular( + direction: SkPoint3, + lightColor: SkColor, + surfaceScale: number, + ks: number, + shininess: number, + input?: SkImageFilter | null, + cropRect?: SkRect | null + ): SkImageFilter; + /** + * Create a filter that calculates the specular illumination from a point light source, using + * alpha channel of the input as the height profile of the surface (to approximate normal + * vectors). + * @param location The location of the point light. + * @param lightColor The color of the specular light source. + * @param surfaceScale Scale factor to transform from alpha values to physical height. + * @param ks Specular reflectance coefficient. + * @param shininess The specular exponent determining how shiny the surface is. + * @param input The input filter that defines surface normals (as alpha), or uses the + * source bitmap when null. + * @param cropRect Optional rectangle that crops the input and output. + */ + MakePointLitSpecular( + location: SkPoint3, + lightColor: SkColor, + surfaceScale: number, + ks: number, + shininess: number, + input?: SkImageFilter | null, + cropRect?: SkRect | null + ): SkImageFilter; + /** + * Create a filter that calculates the specular illumination from a spot light source, using + * alpha channel of the input as the height profile of the surface (to approximate normal + * vectors). The spot light is restricted to be within 'cutoffAngle' of the vector between + * the location and target. + * @param location The location of the spot light. + * @param target The location that the spot light is point towards + * @param falloffExponent Exponential falloff parameter for illumination outside of cutoffAngle + * @param cutoffAngle Maximum angle from lighting direction that receives full light + * @param lightColor The color of the specular light source. + * @param surfaceScale Scale factor to transform from alpha values to physical height. + * @param ks Specular reflectance coefficient. + * @param shininess The specular exponent determining how shiny the surface is. + * @param input The input filter that defines surface normals (as alpha), or uses the + * source bitmap when null. + * @param cropRect Optional rectangle that crops the input and output. + */ + MakeSpotLitSpecular( + location: SkPoint3, + target: SkPoint3, + falloffExponent: number, + cutoffAngle: number, + lightColor: SkColor, + surfaceScale: number, + ks: number, + shininess: number, + input?: SkImageFilter | null, + cropRect?: SkRect | null + ): SkImageFilter; } + +export type SkPoint3 = { + x: number; + y: number; + z: number; +}; diff --git a/packages/skia/src/skia/web/JsiSkImageFilterFactory.ts b/packages/skia/src/skia/web/JsiSkImageFilterFactory.ts index 79c203049d..87e36eccef 100644 --- a/packages/skia/src/skia/web/JsiSkImageFilterFactory.ts +++ b/packages/skia/src/skia/web/JsiSkImageFilterFactory.ts @@ -1,6 +1,7 @@ -import type { CanvasKit, ImageFilter } from "canvaskit-wasm"; +import type { CanvasKit, Image, ImageFilter } from "canvaskit-wasm"; import type { + SkMatrix, ColorChannel, ImageFilterFactory, SkColor, @@ -11,11 +12,16 @@ import type { SkRuntimeShaderBuilder, SkShader, TileMode, + SkImage, + FilterMode, + MipmapMode, } from "../types"; import { Host, NotImplementedOnRNWeb, getEnum } from "./Host"; import { JsiSkImageFilter } from "./JsiSkImageFilter"; import { JsiSkColorFilter } from "./JsiSkColorFilter"; +import { JsiSkImage } from "./JsiSkImage"; +import { JsiSkRect } from "./JsiSkRect"; export class JsiSkImageFilterFactory extends Host @@ -25,62 +31,71 @@ export class JsiSkImageFilterFactory super(CanvasKit); } - MakeOffset(dx: number, dy: number, input: SkImageFilter | null) { - const inputFilter = - input === null ? null : JsiSkImageFilter.fromValue(input); - const filter = this.CanvasKit.ImageFilter.MakeOffset(dx, dy, inputFilter); - return new JsiSkImageFilter(this.CanvasKit, filter); + MakeArithmetic(): SkImageFilter { + throw new NotImplementedOnRNWeb(); } - MakeDisplacementMap( - channelX: ColorChannel, - channelY: ColorChannel, - scale: number, - in1: SkImageFilter, - input: SkImageFilter | null + MakeBlend( + mode: BlendMode, + background: SkImageFilter, + foreground?: SkImageFilter | null, + cropRect?: SkRect | null ): SkImageFilter { const inputFilter = - input === null ? null : JsiSkImageFilter.fromValue(input); - const filter = this.CanvasKit.ImageFilter.MakeDisplacementMap( - getEnum(this.CanvasKit.ColorChannel, channelX), - getEnum(this.CanvasKit.ColorChannel, channelY), - scale, - JsiSkImageFilter.fromValue(in1), + foreground == null + ? null + : JsiSkImageFilter.fromValue(foreground); + if (cropRect) { + throw new NotImplementedOnRNWeb( + "The cropRect argument is not yet supported on React Native Web." + ); + } + const filter = this.CanvasKit.ImageFilter.MakeBlend( + getEnum(this.CanvasKit.BlendMode, mode), + JsiSkImageFilter.fromValue(background), inputFilter ); return new JsiSkImageFilter(this.CanvasKit, filter); } - MakeShader(shader: SkShader, _input: SkImageFilter | null): SkImageFilter { - const filter = this.CanvasKit.ImageFilter.MakeShader( - JsiSkImageFilter.fromValue(shader) - ); - return new JsiSkImageFilter(this.CanvasKit, filter); - } - MakeBlur( sigmaX: number, sigmaY: number, mode: TileMode, - input: SkImageFilter | null + input?: SkImageFilter | null, + cropRect?: SkRect | null ) { + if (cropRect) { + throw new NotImplementedOnRNWeb( + "The cropRect argument is not yet supported on React Native Web." + ); + } return new JsiSkImageFilter( this.CanvasKit, this.CanvasKit.ImageFilter.MakeBlur( sigmaX, sigmaY, getEnum(this.CanvasKit.TileMode, mode), - input === null ? null : JsiSkImageFilter.fromValue(input) + input == null ? null : JsiSkImageFilter.fromValue(input) ) ); } - MakeColorFilter(cf: SkColorFilter, input: SkImageFilter | null) { + MakeColorFilter( + cf: SkColorFilter, + input?: SkImageFilter | null, + cropRect?: SkRect | null + ) { + if (cropRect) { + throw new NotImplementedOnRNWeb( + "The cropRect argument is not yet supported on React Native Web." + ); + } return new JsiSkImageFilter( this.CanvasKit, this.CanvasKit.ImageFilter.MakeColorFilter( JsiSkColorFilter.fromValue(cf), - input === null ? null : JsiSkImageFilter.fromValue(input) + input == null ? null : JsiSkImageFilter.fromValue(input) ) ); } @@ -95,17 +110,46 @@ export class JsiSkImageFilterFactory ); } + MakeCrop(): SkImageFilter { + throw new NotImplementedOnRNWeb(); + } + + MakeDisplacementMap( + channelX: ColorChannel, + channelY: ColorChannel, + scale: number, + in1: SkImageFilter, + input?: SkImageFilter | null, + cropRect?: SkRect | null + ): SkImageFilter { + if (cropRect) { + throw new NotImplementedOnRNWeb( + "The cropRect argument is not yet supported on React Native Web." + ); + } + const inputFilter = + input == null ? null : JsiSkImageFilter.fromValue(input); + const filter = this.CanvasKit.ImageFilter.MakeDisplacementMap( + getEnum(this.CanvasKit.ColorChannel, channelX), + getEnum(this.CanvasKit.ColorChannel, channelY), + scale, + JsiSkImageFilter.fromValue(in1), + inputFilter + ); + return new JsiSkImageFilter(this.CanvasKit, filter); + } + MakeDropShadow( dx: number, dy: number, sigmaX: number, sigmaY: number, color: SkColor, - input: SkImageFilter | null, - cropRect?: SkRect + input?: SkImageFilter | null, + cropRect?: SkRect | null ): SkImageFilter { const inputFilter = - input === null ? null : JsiSkImageFilter.fromValue(input); + input == null ? null : JsiSkImageFilter.fromValue(input); if (cropRect) { throw new NotImplementedOnRNWeb( "The cropRect argument is not yet supported on React Native Web." @@ -128,11 +172,11 @@ export class JsiSkImageFilterFactory sigmaX: number, sigmaY: number, color: SkColor, - input: SkImageFilter | null, - cropRect?: SkRect + input?: SkImageFilter | null, + cropRect?: SkRect | null ): SkImageFilter { const inputFilter = - input === null ? null : JsiSkImageFilter.fromValue(input); + input == null ? null : JsiSkImageFilter.fromValue(input); if (cropRect) { throw new NotImplementedOnRNWeb( "The cropRect argument is not yet supported on React Native Web." @@ -149,31 +193,138 @@ export class JsiSkImageFilterFactory return new JsiSkImageFilter(this.CanvasKit, filter); } - MakeErode( - rx: number, - ry: number, - input: SkImageFilter | null, - cropRect?: SkRect + MakeEmpty(): SkImageFilter { + throw new NotImplementedOnRNWeb(); + } + + MakeImage( + image: SkImage, + srcRect?: SkRect | null, + dstRect?: SkRect | null, + filterMode?: FilterMode, + mipmap?: MipmapMode ): SkImageFilter { + const skImage = JsiSkImage.fromValue(image); + const sampling = { + filter: filterMode + ? getEnum(this.CanvasKit.FilterMode, filterMode) + : this.CanvasKit.FilterMode.Nearest, + mipmap: mipmap + ? getEnum(this.CanvasKit.MipmapMode, mipmap) + : this.CanvasKit.MipmapMode.None, + }; + let filter: ImageFilter | null; + if (srcRect) { + if (!dstRect) { + dstRect = srcRect; + } + filter = this.CanvasKit.ImageFilter.MakeImage( + skImage, + sampling, + JsiSkRect.fromValue(this.CanvasKit, srcRect), + JsiSkRect.fromValue(this.CanvasKit, dstRect) + ); + } else { + filter = this.CanvasKit.ImageFilter.MakeImage(skImage, sampling); + } + if (!filter) { + throw new Error("Failed to create image filter"); + } + return new JsiSkImageFilter(this.CanvasKit, filter); + } + + MakeMagnifier(): SkImageFilter { + throw new NotImplementedOnRNWeb(); + } + + MakeMatrixConvolution(): SkImageFilter { + throw new NotImplementedOnRNWeb(); + } + + MakeMatrixTransform( + matrix: SkMatrix, + filterMode?: FilterMode, + mipmap?: MipmapMode, + input?: SkImageFilter | null + ): SkImageFilter { + const sampling = { + filter: filterMode + ? getEnum(this.CanvasKit.FilterMode, filterMode) + : this.CanvasKit.FilterMode.Nearest, + mipmap: mipmap + ? getEnum(this.CanvasKit.MipmapMode, mipmap) + : this.CanvasKit.MipmapMode.None, + }; + const inputFilter = + input == null ? null : JsiSkImageFilter.fromValue(input); + const filter = this.CanvasKit.ImageFilter.MakeMatrixTransform( + matrix.get(), + sampling, + inputFilter + ); + return new JsiSkImageFilter(this.CanvasKit, filter); + } + + MakeMerge(): SkImageFilter { + throw new NotImplementedOnRNWeb(); + } + + MakeOffset(dx: number, dy: number, input: SkImageFilter | null) { const inputFilter = input === null ? null : JsiSkImageFilter.fromValue(input); + const filter = this.CanvasKit.ImageFilter.MakeOffset(dx, dy, inputFilter); + return new JsiSkImageFilter(this.CanvasKit, filter); + } + + MakePicture(): SkImageFilter { + throw new NotImplementedOnRNWeb(); + } + + MakeRuntimeShader( + _builder: SkRuntimeShaderBuilder, + _childShaderName: string | null, + _input: SkImageFilter | null + ): SkImageFilter { + throw new NotImplementedOnRNWeb(); + } + + MakeRuntimeShaderWithChildren(): SkImageFilter { + throw new NotImplementedOnRNWeb(); + } + + MakeShader( + shader: SkShader, + dither?: boolean, + cropRect?: SkRect | null + ): SkImageFilter { if (cropRect) { throw new NotImplementedOnRNWeb( "The cropRect argument is not yet supported on React Native Web." ); } - const filter = this.CanvasKit.ImageFilter.MakeErode(rx, ry, inputFilter); + if (dither != null) { + throw new NotImplementedOnRNWeb( + "The dither argument is not yet supported on React Native Web." + ); + } + const filter = this.CanvasKit.ImageFilter.MakeShader( + JsiSkImageFilter.fromValue(shader) + ); return new JsiSkImageFilter(this.CanvasKit, filter); } + MakeTile(): SkImageFilter { + throw new NotImplementedOnRNWeb(); + } + MakeDilate( rx: number, ry: number, - input: SkImageFilter | null, - cropRect?: SkRect + input?: SkImageFilter | null, + cropRect?: SkRect | null ): SkImageFilter { const inputFilter = - input === null ? null : JsiSkImageFilter.fromValue(input); + input == null ? null : JsiSkImageFilter.fromValue(input); if (cropRect) { throw new NotImplementedOnRNWeb( "The cropRect argument is not yet supported on React Native Web." @@ -183,34 +334,44 @@ export class JsiSkImageFilterFactory return new JsiSkImageFilter(this.CanvasKit, filter); } - MakeBlend( - mode: BlendMode, - background: SkImageFilter, - foreground: SkImageFilter | null, - cropRect?: SkRect + MakeErode( + rx: number, + ry: number, + input?: SkImageFilter | null, + cropRect?: SkRect | null ): SkImageFilter { const inputFilter = - foreground === null - ? null - : JsiSkImageFilter.fromValue(foreground); + input == null ? null : JsiSkImageFilter.fromValue(input); if (cropRect) { throw new NotImplementedOnRNWeb( "The cropRect argument is not yet supported on React Native Web." ); } - const filter = this.CanvasKit.ImageFilter.MakeBlend( - getEnum(this.CanvasKit.BlendMode, mode), - JsiSkImageFilter.fromValue(background), - inputFilter - ); + const filter = this.CanvasKit.ImageFilter.MakeErode(rx, ry, inputFilter); return new JsiSkImageFilter(this.CanvasKit, filter); } - MakeRuntimeShader( - _builder: SkRuntimeShaderBuilder, - _childShaderName: string | null, - _input: SkImageFilter | null - ): SkImageFilter { + MakeDistantLitDiffuse(): SkImageFilter { + throw new NotImplementedOnRNWeb(); + } + + MakePointLitDiffuse(): SkImageFilter { + throw new NotImplementedOnRNWeb(); + } + + MakeSpotLitDiffuse(): SkImageFilter { + throw new NotImplementedOnRNWeb(); + } + + MakeDistantLitSpecular(): SkImageFilter { + throw new NotImplementedOnRNWeb(); + } + + MakePointLitSpecular(): SkImageFilter { + throw new NotImplementedOnRNWeb(); + } + + MakeSpotLitSpecular(): SkImageFilter { throw new NotImplementedOnRNWeb(); } }