diff --git a/USAGE.md b/USAGE.md index 6e9b21557..490d32db5 100644 --- a/USAGE.md +++ b/USAGE.md @@ -1308,6 +1308,7 @@ The following filters have been implemented: - FeGaussianBlur - FeMerge - FeOffset +- FeTurbulence Not supported yet: @@ -1325,7 +1326,6 @@ Not supported yet: - FeSpecularLighting - FeSpotLight - FeTile -- FeTurbulence Exmaple use of filters: diff --git a/android/src/main/java/com/horcrux/svg/FeTurbulenceView.java b/android/src/main/java/com/horcrux/svg/FeTurbulenceView.java new file mode 100644 index 000000000..7e4abc866 --- /dev/null +++ b/android/src/main/java/com/horcrux/svg/FeTurbulenceView.java @@ -0,0 +1,298 @@ +package com.horcrux.svg; + +import android.annotation.SuppressLint; +import android.graphics.Bitmap; +import android.graphics.Color; +import com.facebook.react.bridge.Dynamic; +import com.facebook.react.bridge.ReactContext; +import java.util.ArrayList; + +@SuppressLint("ViewConstructor") +class FeTurbulenceView extends FilterPrimitiveView { + ArrayList mBaseFrequency; + SVGLength mNumOctaves; + SVGLength mSeed; + FilterProperties.FeTurbulenceStitchTile mStitchTiles; + FilterProperties.FeTurbulenceType mType; + + public FeTurbulenceView(ReactContext reactContext) { + super(reactContext); + } + + public void setBaseFrequency(Dynamic baseFrequency) { + this.mBaseFrequency = SVGLength.arrayFrom(baseFrequency); + invalidate(); + } + + public void setNumOctaves(Dynamic numOctaves) { + this.mNumOctaves = SVGLength.from(numOctaves); + invalidate(); + } + + public void setSeed(Dynamic seed) { + this.mSeed = SVGLength.from(seed); + invalidate(); + } + + public void setStitchTiles(String stitchTiles) { + this.mStitchTiles = FilterProperties.FeTurbulenceStitchTile.getEnum(stitchTiles); + invalidate(); + } + + public void setType(String type) { + this.mType = FilterProperties.FeTurbulenceType.getEnum(type); + invalidate(); + } + + private static final int RAND_m = 2147483647; + private static final int RAND_a = 16807; + private static final int RAND_q = 127773; + private static final int RAND_r = 2836; + + private static final int BSize = 0x100; + private static final int BM = 0xff; + private static final int PerlinN = 0x1000; + + private final int[] uLatticeSelector = new int[BSize + BSize + 2]; + private final double[][][] fGradient = new double[4][BSize + BSize + 2][2]; + + private long setupSeed(long lSeed) { + if (lSeed <= 0) lSeed = -(lSeed % (RAND_m - 1)) + 1; + if (lSeed > RAND_m - 1) lSeed = RAND_m - 1; + return lSeed; + } + + private long randomNext(long lSeed) { + long result = RAND_a * (lSeed % RAND_q) - RAND_r * (lSeed / RAND_q); + if (result <= 0) result += RAND_m; + return result; + } + + private void init(long lSeed) { + lSeed = setupSeed(lSeed); + for (int k = 0; k < 4; k++) { + for (int i = 0; i < BSize; i++) { + uLatticeSelector[i] = i; + while (true) { + lSeed = randomNext(lSeed); + double gx = (double) ((lSeed % (BSize + BSize)) - BSize) / BSize; + + lSeed = randomNext(lSeed); + double gy = (double) ((lSeed % (BSize + BSize)) - BSize) / BSize; + + if (gx == 0.0 && gy == 0.0) { + continue; + } + double s = Math.sqrt(gx * gx + gy * gy); + if (s > 1.0) { + continue; + } + fGradient[k][i][0] = gx / s; + fGradient[k][i][1] = gy / s; + break; + } + } + } + + int i = BSize; + while (--i > 0) { + lSeed = randomNext(lSeed); + int j = (int) (lSeed % BSize); + int tmp = uLatticeSelector[i]; + uLatticeSelector[i] = uLatticeSelector[j]; + uLatticeSelector[j] = tmp; + } + for (i = 0; i < BSize + 2; i++) { + uLatticeSelector[BSize + i] = uLatticeSelector[i]; + for (int k = 0; k < 4; k++) { + fGradient[k][BSize + i][0] = fGradient[k][i][0]; + fGradient[k][BSize + i][1] = fGradient[k][i][1]; + } + } + } + + private double sCurve(double t) { + return t * t * (3.0 - 2.0 * t); + } + + private double lerp(double t, double a, double b) { + return a + t * (b - a); + } + + private static class StitchInfo { + int nWidth; + int nHeight; + int nWrapX; + int nWrapY; + } + + private double noise2(int nColorChannel, double[] vec, StitchInfo pStitchInfo) { + int bx0, bx1, by0, by1; + int b00, b10, b01, b11; + double rx0, rx1, ry0, ry1, sx, sy, a, b, u, v; + int i, j; + + double t = vec[0] + PerlinN; + bx0 = (int) t; + bx1 = bx0 + 1; + rx0 = t - (int) t; + rx1 = rx0 - 1.0; + + t = vec[1] + PerlinN; + by0 = (int) t; + by1 = by0 + 1; + ry0 = t - (int) t; + ry1 = ry0 - 1.0; + + if (pStitchInfo != null) { + if (bx0 >= pStitchInfo.nWrapX) bx0 -= pStitchInfo.nWidth; + if (bx1 >= pStitchInfo.nWrapX) bx1 -= pStitchInfo.nWidth; + if (by0 >= pStitchInfo.nWrapY) by0 -= pStitchInfo.nHeight; + if (by1 >= pStitchInfo.nWrapY) by1 -= pStitchInfo.nHeight; + } + + bx0 &= BM; + bx1 &= BM; + by0 &= BM; + by1 &= BM; + + i = uLatticeSelector[bx0]; + j = uLatticeSelector[bx1]; + + b00 = uLatticeSelector[i + by0]; + b10 = uLatticeSelector[j + by0]; + b01 = uLatticeSelector[i + by1]; + b11 = uLatticeSelector[j + by1]; + + sx = sCurve(rx0); + sy = sCurve(ry0); + + double[] q; + q = fGradient[nColorChannel][b00]; + u = rx0 * q[0] + ry0 * q[1]; + q = fGradient[nColorChannel][b10]; + v = rx1 * q[0] + ry0 * q[1]; + a = lerp(sx, u, v); + q = fGradient[nColorChannel][b01]; + u = rx0 * q[0] + ry1 * q[1]; + q = fGradient[nColorChannel][b11]; + v = rx1 * q[0] + ry1 * q[1]; + b = lerp(sx, u, v); + return lerp(sy, a, b); + } + + private double turbulence(int nColorChannel, double[] point, double width, double height) { + double baseFreqX = this.getBaseFrequencyX(); + double baseFreqY = this.getBaseFrequencyY(); + StitchInfo stitch = new StitchInfo(); + StitchInfo pStitchInfo = null; + + if (mStitchTiles == FilterProperties.FeTurbulenceStitchTile.STITCH) { + if (baseFreqX != 0.0) { + double fLoFreq = Math.floor(width * baseFreqX) / width; + double fHiFreq = Math.ceil(width * baseFreqX) / width; + if (baseFreqX / fLoFreq < fHiFreq / baseFreqX) baseFreqX = fLoFreq; + else baseFreqX = fHiFreq; + } + if (baseFreqY != 0.0) { + double fLoFreq = Math.floor(height * baseFreqY) / height; + double fHiFreq = Math.ceil(height * baseFreqY) / height; + if (baseFreqY / fLoFreq < fHiFreq / baseFreqY) baseFreqY = fLoFreq; + else baseFreqY = fHiFreq; + } + + pStitchInfo = stitch; + stitch.nWidth = (int) (width * baseFreqX + 0.5); + stitch.nWrapX = PerlinN + stitch.nWidth; + stitch.nHeight = (int) (height * baseFreqY + 0.5); + stitch.nWrapY = PerlinN + stitch.nHeight; + } + + double fSum = 0.0; + double[] vec = new double[2]; + vec[0] = (point[0] / mScale) * baseFreqX; + vec[1] = (point[1] / mScale) * baseFreqY; + double ratio = 1.0; + + for (int nOctave = 0; nOctave < (int) mNumOctaves.value; nOctave++) { + double n = noise2(nColorChannel, vec, pStitchInfo); + if (mType == FilterProperties.FeTurbulenceType.FRACTAL_NOISE) { + fSum += (n / ratio); + } else { + fSum += (Math.abs(n) / ratio); + } + vec[0] *= 2.0; + vec[1] *= 2.0; + ratio *= 2.0; + + if (pStitchInfo != null) { + stitch.nWidth *= 2; + stitch.nWrapX = 2 * stitch.nWrapX - PerlinN; + stitch.nHeight *= 2; + stitch.nWrapY = 2 * stitch.nWrapY - PerlinN; + } + } + return fSum; + } + + public Bitmap applyFilter(java.util.HashMap resultsMap, Bitmap prevResult) { + int width = prevResult.getWidth(); + int height = prevResult.getHeight(); + + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + + init((long) mSeed.value); + + int[] pixels = new int[width * height]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + double[] point = new double[] {x, y}; + + double rSum = turbulence(0, point, width, height); + double gSum = turbulence(1, point, width, height); + double bSum = turbulence(2, point, width, height); + double aSum = turbulence(3, point, width, height); + + if (mType == FilterProperties.FeTurbulenceType.FRACTAL_NOISE) { + rSum = (rSum + 1.0) / 2.0; + gSum = (gSum + 1.0) / 2.0; + bSum = (bSum + 1.0) / 2.0; + aSum = (aSum + 1.0) / 2.0; + } + + rSum = Math.max(0.0, Math.min(1.0, rSum)); + gSum = Math.max(0.0, Math.min(1.0, gSum)); + bSum = Math.max(0.0, Math.min(1.0, bSum)); + aSum = Math.max(0.0, Math.min(1.0, aSum)); + + int r = (int) Math.round(rSum * 255.0); + int g = (int) Math.round(gSum * 255.0); + int b = (int) Math.round(bSum * 255.0); + int a = (int) Math.round(aSum * 255.0); + + int color = Color.argb(a, r, g, b); + pixels[y * width + x] = color; + } + } + + bitmap.setPixels(pixels, 0, width, 0, 0, width, height); + return bitmap; + } + + private float getBaseFrequencyX() { + if (mBaseFrequency != null && !mBaseFrequency.isEmpty()) { + return (float) mBaseFrequency.get(0).value; + } + + return 0; + } + + private float getBaseFrequencyY() { + if (mBaseFrequency != null && mBaseFrequency.size() > 1) { + return (float) mBaseFrequency.get(1).value; + } + + return this.getBaseFrequencyX(); + } +} diff --git a/android/src/main/java/com/horcrux/svg/FilterProperties.java b/android/src/main/java/com/horcrux/svg/FilterProperties.java index fbd17c059..67c88a18e 100644 --- a/android/src/main/java/com/horcrux/svg/FilterProperties.java +++ b/android/src/main/java/com/horcrux/svg/FilterProperties.java @@ -181,4 +181,68 @@ public String toString() { return type; } } + + enum FeTurbulenceType { + FRACTAL_NOISE("fractalNoise"), + TURBULENCE("turbulence"); + + private final String type; + + FeTurbulenceType(String type) { + this.type = type; + } + + static FeTurbulenceType getEnum(String strVal) { + if (!typeToEnum.containsKey(strVal)) { + throw new IllegalArgumentException("Unknown String Value: " + strVal); + } + return typeToEnum.get(strVal); + } + + private static final Map typeToEnum = new HashMap<>(); + + static { + for (final FeTurbulenceType en : FeTurbulenceType.values()) { + typeToEnum.put(en.type, en); + } + } + + @Nonnull + @Override + public String toString() { + return type; + } + } + + enum FeTurbulenceStitchTile { + STITCH("stitch"), + NO_STITCH("noStitch"); + + private final String stitchTile; + + FeTurbulenceStitchTile(String stitchTile) { + this.stitchTile = stitchTile; + } + + static FeTurbulenceStitchTile getEnum(String strVal) { + if (!stitchTileToEnum.containsKey(strVal)) { + throw new IllegalArgumentException("Unknown String Value: " + strVal); + } + return stitchTileToEnum.get(strVal); + } + + private static final Map stitchTileToEnum = new HashMap<>(); + + static { + for (final FeTurbulenceStitchTile en : FeTurbulenceStitchTile.values()) { + stitchTileToEnum.put(en.stitchTile, en); + } + } + + @Nonnull + @Override + public String toString() { + return stitchTile; + } + } } diff --git a/android/src/main/java/com/horcrux/svg/RenderableViewManager.java b/android/src/main/java/com/horcrux/svg/RenderableViewManager.java index 06095e45f..8ca0bde48 100644 --- a/android/src/main/java/com/horcrux/svg/RenderableViewManager.java +++ b/android/src/main/java/com/horcrux/svg/RenderableViewManager.java @@ -107,6 +107,8 @@ import com.facebook.react.viewmanagers.RNSVGFeMergeManagerInterface; import com.facebook.react.viewmanagers.RNSVGFeOffsetManagerDelegate; import com.facebook.react.viewmanagers.RNSVGFeOffsetManagerInterface; +import com.facebook.react.viewmanagers.RNSVGFeTurbulenceManagerDelegate; +import com.facebook.react.viewmanagers.RNSVGFeTurbulenceManagerInterface; import com.facebook.react.viewmanagers.RNSVGFilterManagerDelegate; import com.facebook.react.viewmanagers.RNSVGFilterManagerInterface; import com.facebook.react.viewmanagers.RNSVGForeignObjectManagerDelegate; @@ -488,6 +490,7 @@ protected enum SVGClass { RNSVGFeGaussianBlur, RNSVGFeMerge, RNSVGFeOffset, + RNSVGFeTurbulence, RNSVGMarker, RNSVGForeignObject, } @@ -548,6 +551,8 @@ protected VirtualView createViewInstance(@Nonnull ThemedReactContext reactContex return new FeMergeView(reactContext); case RNSVGFeOffset: return new FeOffsetView(reactContext); + case RNSVGFeTurbulence: + return new FeTurbulenceView(reactContext); case RNSVGMarker: return new MarkerView(reactContext); case RNSVGForeignObject: @@ -1676,6 +1681,41 @@ public void setDy(FeOffsetView node, Dynamic dy) { } } + static class FeTurbulenceManager extends FilterPrimitiveManager + implements RNSVGFeTurbulenceManagerInterface { + FeTurbulenceManager() { + super(SVGClass.RNSVGFeTurbulence); + mDelegate = new RNSVGFeTurbulenceManagerDelegate(this); + } + + public static final String REACT_CLASS = "RNSVGFeTurbulence"; + + @ReactProp(name = "baseFrequency") + public void setBaseFrequency(FeTurbulenceView node, Dynamic baseFrequency) { + node.setBaseFrequency(baseFrequency); + } + + @ReactProp(name = "numOctaves") + public void setNumOctaves(FeTurbulenceView node, Dynamic numOctaves) { + node.setNumOctaves(numOctaves); + } + + @ReactProp(name = "seed") + public void setSeed(FeTurbulenceView node, Dynamic seed) { + node.setSeed(seed); + } + + @ReactProp(name = "stitchTile") + public void setStitchTiles(FeTurbulenceView node, String stitchTiles) { + node.setStitchTiles(stitchTiles); + } + + @ReactProp(name = "type") + public void setType(FeTurbulenceView node, String type) { + node.setType(type); + } + } + @ReactProp(name = "filter") public void setFilter(T node, String filter) { node.setFilter(filter); diff --git a/android/src/main/java/com/horcrux/svg/SvgPackage.java b/android/src/main/java/com/horcrux/svg/SvgPackage.java index fe81f02c3..3c640c55b 100644 --- a/android/src/main/java/com/horcrux/svg/SvgPackage.java +++ b/android/src/main/java/com/horcrux/svg/SvgPackage.java @@ -277,6 +277,15 @@ public NativeModule get() { return new FeOffsetManager(); } })); + specs.put( + FeTurbulenceManager.REACT_CLASS, + ModuleSpec.viewManagerSpec( + new Provider() { + @Override + public NativeModule get() { + return new FeTurbulenceManager(); + } + })); specs.put( ForeignObjectManager.REACT_CLASS, ModuleSpec.viewManagerSpec( diff --git a/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGFeTurbulenceManagerDelegate.java b/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGFeTurbulenceManagerDelegate.java new file mode 100644 index 000000000..3813ac392 --- /dev/null +++ b/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGFeTurbulenceManagerDelegate.java @@ -0,0 +1,60 @@ +/** +* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). +* +* Do not edit this file as changes may cause incorrect behavior and will be lost +* once the code is regenerated. +* +* @generated by codegen project: GeneratePropsJavaDelegate.js +*/ + +package com.facebook.react.viewmanagers; + +import android.view.View; +import androidx.annotation.Nullable; +import com.facebook.react.bridge.DynamicFromObject; +import com.facebook.react.uimanager.BaseViewManager; +import com.facebook.react.uimanager.BaseViewManagerDelegate; +import com.facebook.react.uimanager.LayoutShadowNode; + +public class RNSVGFeTurbulenceManagerDelegate & RNSVGFeTurbulenceManagerInterface> extends BaseViewManagerDelegate { + public RNSVGFeTurbulenceManagerDelegate(U viewManager) { + super(viewManager); + } + @Override + public void setProperty(T view, String propName, @Nullable Object value) { + switch (propName) { + case "x": + mViewManager.setX(view, new DynamicFromObject(value)); + break; + case "y": + mViewManager.setY(view, new DynamicFromObject(value)); + break; + case "width": + mViewManager.setWidth(view, new DynamicFromObject(value)); + break; + case "height": + mViewManager.setHeight(view, new DynamicFromObject(value)); + break; + case "result": + mViewManager.setResult(view, value == null ? null : (String) value); + break; + case "baseFrequency": + mViewManager.setBaseFrequency(view, new DynamicFromObject(value)); + break; + case "numOctaves": + mViewManager.setNumOctaves(view, new DynamicFromObject(value)); + break; + case "seed": + mViewManager.setSeed(view, new DynamicFromObject(value)); + break; + case "stitchTiles": + mViewManager.setStitchTiles(view, (String) value); + break; + case "type": + mViewManager.setType(view, (String) value); + break; + default: + super.setProperty(view, propName, value); + } + } +} diff --git a/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGFeTurbulenceManagerInterface.java b/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGFeTurbulenceManagerInterface.java new file mode 100644 index 000000000..fbc360c7e --- /dev/null +++ b/android/src/paper/java/com/facebook/react/viewmanagers/RNSVGFeTurbulenceManagerInterface.java @@ -0,0 +1,27 @@ +/** +* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). +* +* Do not edit this file as changes may cause incorrect behavior and will be lost +* once the code is regenerated. +* +* @generated by codegen project: GeneratePropsJavaInterface.js +*/ + +package com.facebook.react.viewmanagers; + +import android.view.View; +import androidx.annotation.Nullable; +import com.facebook.react.bridge.Dynamic; + +public interface RNSVGFeTurbulenceManagerInterface { + void setX(T view, Dynamic value); + void setY(T view, Dynamic value); + void setWidth(T view, Dynamic value); + void setHeight(T view, Dynamic value); + void setResult(T view, @Nullable String value); + void setBaseFrequency(T view, Dynamic value); + void setNumOctaves(T view, Dynamic value); + void setSeed(T view, Dynamic value); + void setStitchTiles(T view, @Nullable String value); + void setType(T view, @Nullable String value); +} diff --git a/apple/Filters/RNSVGFeTurbulence.h b/apple/Filters/RNSVGFeTurbulence.h new file mode 100644 index 000000000..b05478ef9 --- /dev/null +++ b/apple/Filters/RNSVGFeTurbulence.h @@ -0,0 +1,13 @@ +#import "RNSVGTurbulenceType.h" +#import "RNSVGTurbulenceStitchTile.h" +#import "RNSVGFilterPrimitive.h" + +@interface RNSVGFeTurbulence : RNSVGFilterPrimitive + +@property (nonatomic, strong) NSArray *baseFrequency; +@property (nonatomic, strong) RNSVGLength *numOctaves; +@property (nonatomic, strong) RNSVGLength *seed; +@property (nonatomic, assign) RNSVGTurbulenceType type; +@property (nonatomic, assign) RNSVGTurbulenceStitchTile stitchTiles; + +@end diff --git a/apple/Filters/RNSVGFeTurbulence.mm b/apple/Filters/RNSVGFeTurbulence.mm new file mode 100644 index 000000000..fe4a6e8e8 --- /dev/null +++ b/apple/Filters/RNSVGFeTurbulence.mm @@ -0,0 +1,401 @@ +#import "RNSVGFeTurbulence.h" + +#ifdef RCT_NEW_ARCH_ENABLED +#import +#import +#import +#import +#import "RNSVGConvert.h" +#import "RNSVGFabricConversions.h" +#endif // RCT_NEW_ARCH_ENABLED + +#define RAND_m 2147483647 +#define RAND_a 16807 +#define RAND_q 127773 +#define RAND_r 2836 + +#define BM 0xff +#define PerlinN 0x1000 +#define NM 0xfff +#define BSize 0x100 + +typedef struct { + int nWidth; + int nHeight; + int nWrapX; + int nWrapY; +} StitchInfo; + +@implementation RNSVGFeTurbulence + +static int uLatticeSelector[BSize + BSize + 2]; +static double fGradient[4][BSize + BSize + 2][2]; + +#ifdef RCT_NEW_ARCH_ENABLED +using namespace facebook::react; + +// Needed because of this: https://github.com/facebook/react-native/pull/37274 ++ (void)load +{ + [super load]; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + static const auto defaultProps = std::make_shared(); + _props = defaultProps; + } + return self; +} + +#pragma mark - RCTComponentViewProtocol + ++ (ComponentDescriptorProvider)componentDescriptorProvider +{ + return concreteComponentDescriptorProvider(); +} + +- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps +{ + const auto &newProps = static_cast(*props); + + id baseFrequency = RNSVGConvertFollyDynamicToId(newProps.baseFrequency); + if (baseFrequency != nil) { + self.baseFrequency = [RCTConvert RNSVGLengthArray:baseFrequency]; + } + + id numOctaves = RNSVGConvertFollyDynamicToId(newProps.numOctaves); + if (numOctaves != nil) { + self.numOctaves = [RCTConvert RNSVGLength:numOctaves]; + } + + id seed = RNSVGConvertFollyDynamicToId(newProps.seed); + if (seed != nil) { + self.seed = [RCTConvert RNSVGLength:seed]; + } + + self.type = [RNSVGConvert RNSVGTurbulenceTypeFromCppEquivalent:newProps.type]; + self.stitchTiles = [RNSVGConvert RNSVGTurbulenceStitchTileFromCppEquivalent:newProps.stitchTiles]; + + setCommonFilterProps(newProps, self); + _props = std::static_pointer_cast(props); +} + +- (void)prepareForRecycle +{ + [super prepareForRecycle]; + _baseFrequency = nil; + _numOctaves = [RNSVGLength lengthWithNumber:1]; + _seed = [RNSVGLength lengthWithNumber:0]; + _stitchTiles = RNSVGTurbulenceStitchTile::SVG_FETURBULENCE_STITCH_TILE_NO_STITCH; + _type = RNSVGTurbulenceType::SVG_FETURBULENCE_TYPE_TURBULENCE; +} +#endif // RCT_NEW_ARCH_ENABLED + +- (void)setBaseFrequency:(NSArray *)baseFrequency +{ + if (baseFrequency == _baseFrequency) { + return; + } + + _baseFrequency = baseFrequency; + [self invalidate]; +} + +- (void)setNumOctaves:(RNSVGLength *)numOctaves +{ + if ([numOctaves isEqualTo:_numOctaves]) { + return; + } + + _numOctaves = numOctaves; + [self invalidate]; +} + +- (void)setSeed:(RNSVGLength *)seed +{ + if ([seed isEqualTo:_seed]) { + return; + } + + _seed = seed; + [self invalidate]; +} + +- (void)setStitchTiles:(RNSVGTurbulenceStitchTile)stitchTiles +{ + if (stitchTiles == _stitchTiles) { + return; + } + _stitchTiles = stitchTiles; + [self invalidate]; +} + +- (void)setType:(RNSVGTurbulenceType)type +{ + if (type == _type) { + return; + } + _type = type; + [self invalidate]; +} + +- (CIImage *)applyFilter:(NSMutableDictionary *)results + previousFilterResult:(CIImage *)previous +{ + CGRect extent = previous.extent; + size_t width = CGRectGetWidth(extent); + size_t height = CGRectGetHeight(extent); + + [self init:(long)_seed]; + + unsigned char *rawData = (unsigned char *)calloc(height * width * 4, sizeof(unsigned char)); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + double point[2] = { (double)x, (double)y }; + + double rSum = [self turbulence:0 point:point width:width height:height]; + double gSum = [self turbulence:1 point:point width:width height:height]; + double bSum = [self turbulence:2 point:point width:width height:height]; + double aSum = [self turbulence:3 point:point width:width height:height]; + + if (_type == RNSVGTurbulenceType::SVG_FETURBULENCE_TYPE_FRACTAL_NOISE) { + rSum = (rSum + 1.0) / 2.0; + gSum = (gSum + 1.0) / 2.0; + bSum = (bSum + 1.0) / 2.0; + aSum = (aSum + 1.0) / 2.0; + } + + rSum = fmin(fmax(rSum, 0.0), 1.0); + gSum = fmin(fmax(gSum, 0.0), 1.0); + bSum = fmin(fmax(bSum, 0.0), 1.0); + aSum = fmin(fmax(aSum, 0.0), 1.0); + + int offset = (int)(y * width + x) * 4; + rawData[offset] = (unsigned char)round(rSum * 255.0); + rawData[offset + 1] = (unsigned char)round(gSum * 255.0); + rawData[offset + 2] = (unsigned char)round(bSum * 255.0); + rawData[offset + 3] = (unsigned char)round(aSum * 255.0); + } + } + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, rawData, width * height * 4, NULL); + CGImageRef cgImage = CGImageCreate(width, height, 8, 32, width * 4, colorSpace, + (CGBitmapInfo)kCGImageAlphaLast | kCGBitmapByteOrder32Big, + provider, NULL, NO, kCGRenderingIntentDefault); + + CIImage *output = [CIImage imageWithCGImage:cgImage]; + + CGImageRelease(cgImage); + CGDataProviderRelease(provider); + CGColorSpaceRelease(colorSpace); + free(rawData); + + return output; +} + +- (void)init:(long)lSeed { + double s; + int i, j, k; + lSeed = [self setupSeed:lSeed]; + + for (k = 0; k < 4; k++) { + for (i = 0; i < BSize; i++) { + uLatticeSelector[i] = i; + do { + for (j = 0; j < 2; j++) { + lSeed = [self random_val:lSeed]; + fGradient[k][i][j] = ((double)((lSeed % (BSize + BSize)) - BSize)) / (double)BSize; + } + } while (fGradient[k][i][0] == 0.0 && fGradient[k][i][1] == 0.0); + + s = sqrt(fGradient[k][i][0] * fGradient[k][i][0] + fGradient[k][i][1] * fGradient[k][i][1]); + if (s > 1.0) { + i--; + continue; + } + fGradient[k][i][0] /= s; + fGradient[k][i][1] /= s; + } + } + + while (--i) { + k = uLatticeSelector[i]; + lSeed = [self random_val:lSeed]; + j = (int)(lSeed % BSize); + uLatticeSelector[i] = uLatticeSelector[j]; + uLatticeSelector[j] = k; + } + + for (i = 0; i < BSize + 2; i++) { + uLatticeSelector[BSize + i] = uLatticeSelector[i]; + for (k = 0; k < 4; k++) { + for (j = 0; j < 2; j++) fGradient[k][BSize + i][j] = fGradient[k][i][j]; + } + } +} + +- (double)noise2:(int)nColorChannel vec:(double *)vec pStitchInfo:(StitchInfo *)pStitchInfo +{ + int bx0, bx1, by0, by1, b00, b10, b01, b11; + double rx0, rx1, ry0, ry1, *q, sx, sy, a, b, t, u, v; + int i, j; + + t = vec[0] + PerlinN; + bx0 = (int)t; + bx1 = bx0 + 1; + rx0 = t - (int)t; + rx1 = rx0 - 1.0; + + t = vec[1] + PerlinN; + by0 = (int)t; + by1 = by0 + 1; + ry0 = t - (int)t; + ry1 = ry0 - 1.0; + + if (pStitchInfo != NULL) { + if (bx0 >= pStitchInfo->nWrapX) bx0 -= pStitchInfo->nWidth; + if (bx1 >= pStitchInfo->nWrapX) bx1 -= pStitchInfo->nWidth; + if (by0 >= pStitchInfo->nWrapY) by0 -= pStitchInfo->nHeight; + if (by1 >= pStitchInfo->nWrapY) by1 -= pStitchInfo->nHeight; + } + + bx0 &= BM; + bx1 &= BM; + by0 &= BM; + by1 &= BM; + + i = uLatticeSelector[bx0]; + j = uLatticeSelector[bx1]; + + b00 = uLatticeSelector[i + by0]; + b10 = uLatticeSelector[j + by0]; + b01 = uLatticeSelector[i + by1]; + b11 = uLatticeSelector[j + by1]; + + sx = [self sCurve:rx0]; + sy = [self sCurve:ry0]; + + q = fGradient[nColorChannel][b00]; u = rx0 * q[0] + ry0 * q[1]; + q = fGradient[nColorChannel][b10]; v = rx1 * q[0] + ry0 * q[1]; + a = [self lerp:sx a:u b:v]; + + q = fGradient[nColorChannel][b01]; u = rx0 * q[0] + ry1 * q[1]; + q = fGradient[nColorChannel][b11]; v = rx1 * q[0] + ry1 * q[1]; + b = [self lerp:sx a:u b:v]; + + return [self lerp:sy a:a b:b]; +} + +- (double)turbulence:(int)nColorChannel point:(double *)point width:(double)width height:(double)height +{ + StitchInfo stitch; + StitchInfo *pStitchInfo = NULL; + double baseFreqX = [self getBaseFrequencyX]; + double baseFreqY = [self getBaseFrequencyY]; + + if (_stitchTiles == RNSVGTurbulenceStitchTile::SVG_FETURBULENCE_STITCH_TILE_STITCH) { + if (baseFreqX != 0.0) { + double fLoFreq = floor(width * baseFreqX) / width; + double fHiFreq = ceil(width * baseFreqX) / width; + baseFreqX = (baseFreqX / fLoFreq < fHiFreq / baseFreqX) ? fLoFreq : fHiFreq; + } + if (baseFreqY != 0.0) { + double fLoFreq = floor(height * baseFreqY) / height; + double fHiFreq = ceil(height * baseFreqY) / height; + baseFreqY = (baseFreqY / fLoFreq < fHiFreq / baseFreqY) ? fLoFreq : fHiFreq; + } + + pStitchInfo = &stitch; + stitch.nWidth = (int)(width * baseFreqX + 0.5); + stitch.nWrapX = (int)(PerlinN + stitch.nWidth); + stitch.nHeight = (int)(height * baseFreqY + 0.5); + stitch.nWrapY = (int)(PerlinN + stitch.nHeight); + } + + double fSum = 0.0; + double vec[2]; +#if TARGET_OS_OSX + double screenScale = [[NSScreen mainScreen] backingScaleFactor]; +#else + double screenScale = [UIScreen mainScreen].scale; +#endif + vec[0] = point[0] / screenScale * baseFreqX; + vec[1] = point[1] / screenScale * baseFreqY; + double ratio = 1.0; + + for (int nOctave = 0; nOctave < _numOctaves.value; nOctave++) { + double n = [self noise2:nColorChannel vec:vec pStitchInfo:pStitchInfo]; + + if (_type == RNSVGTurbulenceType::SVG_FETURBULENCE_TYPE_FRACTAL_NOISE) { + fSum += n / ratio; + } else { + fSum += fabs(n) / ratio; + } + + vec[0] *= 2.0; + vec[1] *= 2.0; + ratio *= 2.0; + + if (pStitchInfo != NULL) { + stitch.nWidth *= 2; + stitch.nWrapX = 2 * stitch.nWrapX - PerlinN; + stitch.nHeight *= 2; + stitch.nWrapY = 2 * stitch.nWrapY - PerlinN; + } + } + + return fSum; +} + +- (long)setupSeed:(long)lSeed +{ + if (lSeed <= 0) lSeed = -(lSeed % (RAND_m - 1)) + 1; + if (lSeed > RAND_m - 1) lSeed = RAND_m - 1; + return lSeed; +} + +- (long)random_val:(long)lSeed +{ + long result; + result = RAND_a * (lSeed % RAND_q) - RAND_r * (lSeed / RAND_q); + if (result <= 0) result += RAND_m; + return result; +} + + +- (double)sCurve:(double)t +{ + return t * t * (3.0 - 2.0 * t); +} + +- (double)lerp:(double)t a:(double)a b:(double)b +{ + return a + t * (b - a); +} + +- (CGFloat)getBaseFrequencyX { + if (_baseFrequency != nil && _baseFrequency.count > 0) { + return _baseFrequency[0].value; + } + return 0.0f; +} + +- (CGFloat)getBaseFrequencyY { + if (_baseFrequency != nil && _baseFrequency.count > 1) { + return _baseFrequency[1].value; + } + return [self getBaseFrequencyX]; +} + +#ifdef RCT_NEW_ARCH_ENABLED +Class RNSVGFeTurbulenceCls(void) +{ + return RNSVGFeTurbulence.class; +} +#endif // RCT_NEW_ARCH_ENABLED + +@end diff --git a/apple/Filters/RNSVGTurbulenceStitchTile.h b/apple/Filters/RNSVGTurbulenceStitchTile.h new file mode 100644 index 000000000..7bf2e7e0d --- /dev/null +++ b/apple/Filters/RNSVGTurbulenceStitchTile.h @@ -0,0 +1,4 @@ +typedef CF_ENUM(int32_t, RNSVGTurbulenceStitchTile) { + SVG_FETURBULENCE_STITCH_TILE_STITCH, + SVG_FETURBULENCE_STITCH_TILE_NO_STITCH, +}; diff --git a/apple/Filters/RNSVGTurbulenceType.h b/apple/Filters/RNSVGTurbulenceType.h new file mode 100644 index 000000000..5abcee82e --- /dev/null +++ b/apple/Filters/RNSVGTurbulenceType.h @@ -0,0 +1,4 @@ +typedef CF_ENUM(int32_t, RNSVGTurbulenceType) { + SVG_FETURBULENCE_TYPE_TURBULENCE, + SVG_FETURBULENCE_TYPE_FRACTAL_NOISE, +}; diff --git a/apple/Utils/RCTConvert+RNSVG.h b/apple/Utils/RCTConvert+RNSVG.h index 5f835292f..dcab8ed3a 100644 --- a/apple/Utils/RCTConvert+RNSVG.h +++ b/apple/Utils/RCTConvert+RNSVG.h @@ -18,6 +18,8 @@ #import "RNSVGLength.h" #import "RNSVGMaskType.h" #import "RNSVGPathParser.h" +#import "RNSVGTurbulenceStitchTile.h" +#import "RNSVGTurbulenceType.h" #import "RNSVGUnits.h" #import "RNSVGVBMOS.h" diff --git a/apple/Utils/RCTConvert+RNSVG.mm b/apple/Utils/RCTConvert+RNSVG.mm index b65cae7d6..e529abf03 100644 --- a/apple/Utils/RCTConvert+RNSVG.mm +++ b/apple/Utils/RCTConvert+RNSVG.mm @@ -98,6 +98,24 @@ @implementation RCTConvert (RNSVG) SVG_FECOMPOSITE_OPERATOR_UNKNOWN, intValue) +RCT_ENUM_CONVERTER( + RNSVGTurbulenceType, + (@{ + @"turbulence" : @(SVG_FETURBULENCE_TYPE_TURBULENCE), + @"fractalNoise" : @(SVG_FETURBULENCE_TYPE_FRACTAL_NOISE), + }), + SVG_FETURBULENCE_TYPE_TURBULENCE, + intValue) + +RCT_ENUM_CONVERTER( + RNSVGTurbulenceStitchTile, + (@{ + @"stitch" : @(SVG_FETURBULENCE_STITCH_TILE_STITCH), + @"noStitch" : @(SVG_FETURBULENCE_STITCH_TILE_NO_STITCH), + }), + SVG_FETURBULENCE_STITCH_TILE_NO_STITCH, + intValue) + + (RNSVGBrush *)RNSVGBrush:(id)json { if (json == nil) { diff --git a/apple/Utils/RNSVGConvert.h b/apple/Utils/RNSVGConvert.h index 320007c43..e58bcdaf7 100644 --- a/apple/Utils/RNSVGConvert.h +++ b/apple/Utils/RNSVGConvert.h @@ -5,6 +5,8 @@ #import "RNSVGCompositeOperator.h" #import "RNSVGEdgeMode.h" #import "RNSVGUnits.h" +#import "RNSVGTurbulenceStitchTile.h" +#import "RNSVGTurbulenceType.h" namespace react = facebook::react; @@ -16,6 +18,8 @@ namespace react = facebook::react; + (RNSVGColorMatrixType)RNSVGColorMatrixTypeFromCppEquivalent:(react::RNSVGFeColorMatrixType)type; + (RNSVGCompositeOperator)RNSVGRNSVGCompositeOperatorFromCppEquivalent:(react::RNSVGFeCompositeOperator1)operator1; + (RNSVGEdgeMode)RNSVGEdgeModeFromCppEquivalent:(react::RNSVGFeGaussianBlurEdgeMode)edgeMode; ++ (RNSVGTurbulenceStitchTile)RNSVGTurbulenceStitchTileFromCppEquivalent:(react::RNSVGFeTurbulenceStitchTiles)stitchTiles; ++ (RNSVGTurbulenceType)RNSVGTurbulenceTypeFromCppEquivalent:(react::RNSVGFeTurbulenceType)type; @end diff --git a/apple/Utils/RNSVGConvert.mm b/apple/Utils/RNSVGConvert.mm index 967521491..5743a968b 100644 --- a/apple/Utils/RNSVGConvert.mm +++ b/apple/Utils/RNSVGConvert.mm @@ -87,6 +87,30 @@ + (RNSVGEdgeMode)RNSVGEdgeModeFromCppEquivalent:(react::RNSVGFeGaussianBlurEdgeM } } ++ (RNSVGTurbulenceStitchTile)RNSVGTurbulenceStitchTileFromCppEquivalent:(react::RNSVGFeTurbulenceStitchTiles)stitchTiles +{ + switch (stitchTiles) { + case react::RNSVGFeTurbulenceStitchTiles::Stitch: + return SVG_FETURBULENCE_STITCH_TILE_STITCH; + case react::RNSVGFeTurbulenceStitchTiles::NoStitch: + return SVG_FETURBULENCE_STITCH_TILE_NO_STITCH; + default: + return SVG_FETURBULENCE_STITCH_TILE_NO_STITCH; + } +} + ++ (RNSVGTurbulenceType)RNSVGTurbulenceTypeFromCppEquivalent:(react::RNSVGFeTurbulenceType)type +{ + switch (type) { + case react::RNSVGFeTurbulenceType::Turbulence: + return SVG_FETURBULENCE_TYPE_TURBULENCE; + case react::RNSVGFeTurbulenceType::FractalNoise: + return SVG_FETURBULENCE_TYPE_FRACTAL_NOISE; + default: + return SVG_FETURBULENCE_TYPE_TURBULENCE; + } +} + @end #endif // RCT_NEW_ARCH_ENABLED diff --git a/apple/ViewManagers/RNSVGFeTurbulenceManager.h b/apple/ViewManagers/RNSVGFeTurbulenceManager.h new file mode 100644 index 000000000..83d33f25e --- /dev/null +++ b/apple/ViewManagers/RNSVGFeTurbulenceManager.h @@ -0,0 +1,5 @@ +#import "RNSVGFilterPrimitiveManager.h" + +@interface RNSVGFeTurbulenceManager : RNSVGFilterPrimitiveManager + +@end diff --git a/apple/ViewManagers/RNSVGFeTurbulenceManager.mm b/apple/ViewManagers/RNSVGFeTurbulenceManager.mm new file mode 100644 index 000000000..d6d987b3f --- /dev/null +++ b/apple/ViewManagers/RNSVGFeTurbulenceManager.mm @@ -0,0 +1,21 @@ +#import "RNSVGFeTurbulenceManager.h" +#import "RNSVGFeTurbulence.h" +#import "RNSVGTurbulenceType.h" +#import "RNSVGTurbulenceStitchTile.h" + +@implementation RNSVGFeTurbulenceManager + +RCT_EXPORT_MODULE() + +- (RNSVGFeTurbulence *)node +{ + return [RNSVGFeTurbulence new]; +} + +RCT_EXPORT_VIEW_PROPERTY(baseFrequency, NSArray) +RCT_EXPORT_VIEW_PROPERTY(numOctaves, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(seed, RNSVGLength *) +RCT_EXPORT_VIEW_PROPERTY(type, RNSVGTurbulenceType) +RCT_EXPORT_VIEW_PROPERTY(stitchTiles, RNSVGTurbulenceStitchTile) + +@end diff --git a/apps/common/example/examples/Filters/FeTurbulence.tsx b/apps/common/example/examples/Filters/FeTurbulence.tsx new file mode 100644 index 000000000..c3645f55e --- /dev/null +++ b/apps/common/example/examples/Filters/FeTurbulence.tsx @@ -0,0 +1,72 @@ +import React from 'react'; +import {Defs, FeTurbulence, Filter, Rect, Svg, Text} from 'react-native-svg'; + +function W3CExample() { + return ( + + + + + + + + + + + + + + + + (type=turbulence | baseFrequency="0.01 0.1" | numOctaves=1) + + + + + (type=turbulence | baseFrequency="0.1 0.01" | numOctaves=1) + + + + + (type=turbulence | baseFrequency="0.001 1" | numOctaves=1) + + + ); +} +W3CExample.title = 'W3C example'; + +const icon = ( + + + + + + +); +const samples = [W3CExample]; + +export {icon, samples}; diff --git a/apps/common/example/examples/Filters/index.tsx b/apps/common/example/examples/Filters/index.tsx index 36069573c..abffe7de3 100644 --- a/apps/common/example/examples/Filters/index.tsx +++ b/apps/common/example/examples/Filters/index.tsx @@ -8,6 +8,7 @@ import * as FeFlood from './FeFlood'; import * as FeGaussianBlur from './FeGaussianBlur'; import * as FeMerge from './FeMerge'; import * as FeOffset from './FeOffset'; +import * as FeTurbulence from './FeTurbulence'; import * as ReanimatedFeColorMatrix from './ReanimatedFeColorMatrix'; const samples = { @@ -19,6 +20,7 @@ const samples = { FeGaussianBlur, FeMerge, FeOffset, + FeTurbulence, ReanimatedFeColorMatrix, }; diff --git a/common/cpp/react/renderer/components/rnsvg/RNSVGComponentDescriptors.h b/common/cpp/react/renderer/components/rnsvg/RNSVGComponentDescriptors.h index 61c44e7a6..c5a10cea3 100644 --- a/common/cpp/react/renderer/components/rnsvg/RNSVGComponentDescriptors.h +++ b/common/cpp/react/renderer/components/rnsvg/RNSVGComponentDescriptors.h @@ -28,6 +28,8 @@ using RNSVGFeMergeComponentDescriptor = ConcreteComponentDescriptor; using RNSVGFeOffsetComponentDescriptor = ConcreteComponentDescriptor; +using RNSVGFeTurbulenceComponentDescriptor = + ConcreteComponentDescriptor; using RNSVGFilterComponentDescriptor = ConcreteComponentDescriptor; using RNSVGForeignObjectComponentDescriptor = diff --git a/common/cpp/react/renderer/components/rnsvg/RNSVGShadowNodes.cpp b/common/cpp/react/renderer/components/rnsvg/RNSVGShadowNodes.cpp index 70f1abd43..f7dc33712 100644 --- a/common/cpp/react/renderer/components/rnsvg/RNSVGShadowNodes.cpp +++ b/common/cpp/react/renderer/components/rnsvg/RNSVGShadowNodes.cpp @@ -13,6 +13,7 @@ extern const char RNSVGFeFloodComponentName[] = "RNSVGFeFlood"; extern const char RNSVGFeGaussianBlurComponentName[] = "RNSVGFeGaussianBlur"; extern const char RNSVGFeMergeComponentName[] = "RNSVGFeMerge"; extern const char RNSVGFeOffsetComponentName[] = "RNSVGFeOffset"; +extern const char RNSVGFeTurbulenceComponentName[] = "RNSVGFeTurbulence"; extern const char RNSVGFilterComponentName[] = "RNSVGFilter"; extern const char RNSVGForeignObjectComponentName[] = "RNSVGForeignObject"; extern const char RNSVGGroupComponentName[] = "RNSVGGroup"; diff --git a/common/cpp/react/renderer/components/rnsvg/RNSVGShadowNodes.h b/common/cpp/react/renderer/components/rnsvg/RNSVGShadowNodes.h index 71addc842..fb3a118bc 100644 --- a/common/cpp/react/renderer/components/rnsvg/RNSVGShadowNodes.h +++ b/common/cpp/react/renderer/components/rnsvg/RNSVGShadowNodes.h @@ -99,6 +99,14 @@ JSI_EXPORT extern const char RNSVGFeOffsetComponentName[]; using RNSVGFeOffsetShadowNode = RNSVGConcreteShadowNode; +JSI_EXPORT extern const char RNSVGFeTurbulenceComponentName[]; + +/* + * `ShadowNode` for component. + */ +using RNSVGFeTurbulenceShadowNode = + RNSVGConcreteShadowNode; + JSI_EXPORT extern const char RNSVGFilterComponentName[]; /* diff --git a/package.json b/package.json index fe86447c3..e249295a9 100644 --- a/package.json +++ b/package.json @@ -147,6 +147,7 @@ "RNSVGFeGaussianBlur": "RNSVGFeGaussianBlur", "RNSVGFeMerge": "RNSVGFeMerge", "RNSVGFeOffset": "RNSVGFeOffset", + "RNSVGFeTurbulence": "RNSVGFeTurbulence", "RNSVGFilter": "RNSVGFilter", "RNSVGForeignObject": "RNSVGForeignObject", "RNSVGGroup": "RNSVGGroup", diff --git a/react-native.config.js b/react-native.config.js index 96657a69c..f7f2f7b88 100644 --- a/react-native.config.js +++ b/react-native.config.js @@ -13,6 +13,7 @@ module.exports = { 'RNSVGFeGaussianBlurComponentDescriptor', 'RNSVGFeMergeComponentDescriptor', 'RNSVGFeOffsetComponentDescriptor', + 'RNSVGFeTurbulenceComponentDescriptor', 'RNSVGFilterComponentDescriptor', 'RNSVGEllipseComponentDescriptor', 'RNSVGForeignObjectComponentDescriptor', diff --git a/src/elements/filters/FeTurbulence.tsx b/src/elements/filters/FeTurbulence.tsx index a83afdd9a..08ed7cd95 100644 --- a/src/elements/filters/FeTurbulence.tsx +++ b/src/elements/filters/FeTurbulence.tsx @@ -1,6 +1,11 @@ +import { RNSVGFeTurbulence } from '../../fabric'; import { NumberArray, NumberProp } from '../../lib/extract/types'; -import { warnUnimplementedFilter } from '../../lib/util'; import FilterPrimitive from './FilterPrimitive'; +import { NativeMethods } from 'react-native'; +import { + extractFeTurbulence, + extractFilter, +} from '../../lib/extract/extractFilter'; export interface FeTurbulenceProps { baseFrequency?: NumberArray; @@ -15,10 +20,23 @@ export default class FeTurbulence extends FilterPrimitive { static defaultProps = { ...this.defaultPrimitiveProps, + baseFrequency: 0, + numOctaves: 1, + seed: 0, + stitchTiles: 'noStitch', + type: 'turbulence', }; render() { - warnUnimplementedFilter(); - return null; + return ( + + this.refMethod(ref as (FeTurbulence & NativeMethods) | null) + } + {...this.props} + {...extractFilter(this.props)} + {...extractFeTurbulence(this.props)} + /> + ); } } diff --git a/src/fabric/FeTurbulenceNativeComponent.ts b/src/fabric/FeTurbulenceNativeComponent.ts new file mode 100644 index 000000000..aa6eda9bb --- /dev/null +++ b/src/fabric/FeTurbulenceNativeComponent.ts @@ -0,0 +1,29 @@ +import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; +import type { ViewProps } from './utils'; + +import { NumberArray, NumberProp } from '../lib/extract/types'; +import type { UnsafeMixed } from './codegenUtils'; +import { WithDefault } from 'react-native/Libraries/Types/CodegenTypes'; + +interface FilterPrimitiveCommonProps { + x?: UnsafeMixed; + y?: UnsafeMixed; + width?: UnsafeMixed; + height?: UnsafeMixed; + result?: string; +} + +type StitchTile = 'stitch' | 'noStitch'; +type Type = 'fractalNoise' | 'turbulence'; + +export interface NativeProps extends ViewProps, FilterPrimitiveCommonProps { + baseFrequency?: UnsafeMixed; + numOctaves?: WithDefault, 1>; + seed?: WithDefault, 0>; + stitchTiles?: WithDefault; + type?: WithDefault; +} + +export default codegenNativeComponent('RNSVGFeTurbulence', { + interfaceOnly: true, +}); diff --git a/src/fabric/index.ts b/src/fabric/index.ts index 71147c7da..d014ecaeb 100644 --- a/src/fabric/index.ts +++ b/src/fabric/index.ts @@ -28,6 +28,7 @@ import RNSVGFeFlood from './FeFloodNativeComponent'; import RNSVGFeGaussianBlur from './FeGaussianBlurNativeComponent'; import RNSVGFeMerge from './FeMergeNativeComponent'; import RNSVGFeOffset from './FeOffsetNativeComponent'; +import RNSVGFeTurbulence from './FeTurbulenceNativeComponent'; export { RNSVGCircle, @@ -60,4 +61,5 @@ export { RNSVGFeGaussianBlur, RNSVGFeMerge, RNSVGFeOffset, + RNSVGFeTurbulence, }; diff --git a/src/lib/extract/extractFilter.ts b/src/lib/extract/extractFilter.ts index e921f6c50..5ee02c077 100644 --- a/src/lib/extract/extractFilter.ts +++ b/src/lib/extract/extractFilter.ts @@ -6,12 +6,14 @@ import { FeCompositeProps as FeCompositeComponentProps } from '../../elements/fi import { FeFloodProps as FeFloodComponentProps } from '../../elements/filters/FeFlood'; import { FeGaussianBlurProps as FeGaussianBlurComponentProps } from '../../elements/filters/FeGaussianBlur'; import { FeMergeProps as FeMergeComponentProps } from '../../elements/filters/FeMerge'; +import { FeTurbulenceProps as FeTurbulenceComponentProps } from '../../elements/filters/FeTurbulence'; import { NativeProps as FeBlendNativeProps } from '../../fabric/FeBlendNativeComponent'; import { NativeProps as FeColorMatrixNativeProps } from '../../fabric/FeColorMatrixNativeComponent'; import { NativeProps as FeCompositeNativeProps } from '../../fabric/FeCompositeNativeComponent'; import { NativeProps as FeFloodNativeProps } from '../../fabric/FeFloodNativeComponent'; import { NativeProps as FeGaussianBlurNativeProps } from '../../fabric/FeGaussianBlurNativeComponent'; import { NativeProps as FeMergeNativeProps } from '../../fabric/FeMergeNativeComponent'; +import { NativeProps as FeTurbulenceNativeProps } from '../../fabric/FeTurbulenceNativeComponent'; import extractBrush from './extractBrush'; import extractOpacity from './extractOpacity'; import { NumberProp } from './types'; @@ -180,3 +182,25 @@ export const extractFeMerge = ( return { nodes }; }; + +export const extractFeTurbulence = (props: FeTurbulenceComponentProps) => { + const extracted: FeTurbulenceNativeProps = {}; + + if (props.baseFrequency !== undefined) { + extracted.baseFrequency = props.baseFrequency; + } + if (props.numOctaves !== undefined) { + extracted.numOctaves = props.numOctaves; + } + if (props.seed !== undefined) { + extracted.seed = props.seed; + } + if (props.stitchTiles) { + extracted.stitchTiles = props.stitchTiles; + } + if (props.type) { + extracted.type = props.type; + } + + return extracted; +};