Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
430175b
feat: create FeComposite fabric
jakex7 Aug 28, 2024
21ef4c0
feat: implement ios composite filters
jakex7 Aug 28, 2024
da1a6ff
feat: setup custom CoreImage Kernels using Metal
jakex7 Aug 28, 2024
73a46e0
feat: create composite Xor and Arithmetic kernels
jakex7 Aug 28, 2024
a622190
feat: implement FeComposite on Android
jakex7 Aug 30, 2024
16d648e
fix: filter bitmap sizes
jakex7 Aug 30, 2024
e27af89
docs: add examples for FeComposite
jakex7 Aug 30, 2024
708b545
fix: filters size in example app
jakex7 Aug 30, 2024
8c905dc
feat: add CoreImage header to comply with macosx
jakex7 Aug 30, 2024
cde51c6
chore: add paper view managers
jakex7 Aug 30, 2024
56c1680
fix: ios paper
jakex7 Aug 30, 2024
ef68130
Merge branch 'main' into @jakex7/filtersFeComposite
jakex7 Sep 12, 2024
d3e2510
Merge branch 'main' into @jakex7/filtersFeComposite
jakex7 Sep 23, 2024
0c24561
Merge branch 'main' into @jakex7/filtersFeComposite
jakex7 Sep 23, 2024
27e4d41
Merge branch 'main' into @jakex7/filtersFeComposite
jakex7 Oct 9, 2024
0e37716
Merge branch 'main' into @jakex7/filtersFeComposite
jakex7 Oct 23, 2024
e356ddb
chore: generate metal
jakex7 Oct 23, 2024
c08a72f
feat: compile separate metallib for every device types
jakex7 Oct 24, 2024
349bf55
feat: extract custom metal filter
jakex7 Oct 24, 2024
fc0387b
feat: update reference image
jakex7 Oct 24, 2024
b5abade
docs: add feCompostie to examples
jakex7 Oct 24, 2024
892af70
docs: revert unnecessary changes
jakex7 Oct 24, 2024
09e4420
Merge branch 'main' into @jakex7/filtersFeComposite
jakex7 Oct 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions RNSVG.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,18 @@ Pod::Spec.new do |s|
s.homepage = package['homepage']
s.authors = 'Horcrux Chen'
s.source = { :git => 'https://github.com/react-native-community/react-native-svg.git', :tag => "v#{s.version}" }
s.source_files = 'apple/**/*.{h,m,mm}'
s.source_files = 'apple/**/*.{h,m,mm,metal}'
s.ios.exclude_files = '**/*.macos.{h,m,mm}'
s.tvos.exclude_files = '**/*.macos.{h,m,mm}'
s.visionos.exclude_files = '**/*.macos.{h,m,mm}' if s.respond_to?(:visionos)
s.osx.exclude_files = '**/*.ios.{h,m,mm}'
s.requires_arc = true
s.requires_arc = true
s.platforms = { :osx => "10.14", :ios => "12.4", :tvos => "12.4", :visionos => "1.0" }

s.osx.resource_bundles = {'RNSVGFilters' => ['apple/**/*.macosx.metallib']}
s.ios.resource_bundles = {'RNSVGFilters' => ['apple/**/*.iphoneos.metallib']}
s.tvos.resource_bundles = {'RNSVGFilters' => ['apple/**/*.appletvos.metallib']}
s.visionos.resource_bundles = {'RNSVGFilters' => ['apple/**/*.xros.metallib']}

if fabric_enabled
install_modules_dependencies(s)
Expand Down
132 changes: 132 additions & 0 deletions android/src/main/java/com/horcrux/svg/FeCompositeView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package com.horcrux.svg;

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import com.facebook.react.bridge.ReactContext;
import java.util.HashMap;

@SuppressLint("ViewConstructor")
class FeCompositeView extends FilterPrimitiveView {
String mIn1;
String mIn2;
float mK1;
float mK2;
float mK3;
float mK4;
FilterProperties.FeCompositeOperator mOperator;

public FeCompositeView(ReactContext reactContext) {
super(reactContext);
}

public void setIn1(String in1) {
this.mIn1 = in1;
invalidate();
}

public void setIn2(String in2) {
this.mIn2 = in2;
invalidate();
}

public void setK1(Float value) {
this.mK1 = value;
invalidate();
}

public void setK2(Float value) {
this.mK2 = value;
invalidate();
}

public void setK3(Float value) {
this.mK3 = value;
invalidate();
}

public void setK4(Float value) {
this.mK4 = value;
invalidate();
}

public void setOperator(String operator) {
this.mOperator = FilterProperties.FeCompositeOperator.getEnum(operator);
invalidate();
}

@Override
public Bitmap applyFilter(HashMap<String, Bitmap> resultsMap, Bitmap prevResult) {
Bitmap in1 = getSource(resultsMap, prevResult, this.mIn1);
Bitmap in2 = getSource(resultsMap, prevResult, this.mIn2);
Bitmap result = Bitmap.createBitmap(in1.getWidth(), in1.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
canvas.drawBitmap(in1, 0, 0, paint);

switch (this.mOperator) {
case OVER -> {
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OVER));
}
case IN -> {
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
}
case OUT -> {
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
}
case ATOP -> {
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
}
case XOR -> {
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
}
case ARITHMETIC -> {
// result = k1*i1*i2 + k2*i1 + k3*i2 + k4
int nPixels = result.getWidth() * result.getHeight();
int[] pixels1 = new int[nPixels];
int[] pixels2 = new int[nPixels];
result.getPixels(
pixels1, 0, result.getWidth(), 0, 0, result.getWidth(), result.getHeight());

for (int i = 0; i < nPixels; i++) {
int color1 = pixels1[i];
int color2 = pixels2[i];

int r1 = (color1 >> 16) & 0xFF;
int g1 = (color1 >> 8) & 0xFF;
int b1 = color1 & 0xFF;
int a1 = (color1 >>> 24);
int r2 = (color2 >> 16) & 0xFF;
int g2 = (color2 >> 8) & 0xFF;
int b2 = color2 & 0xFF;
int a2 = (color2 >>> 24);

int rResult = (int) (mK1 * r1 * r2 + mK2 * r1 + mK3 * r2 + mK4);
int gResult = (int) (mK1 * g1 * g2 + mK2 * g1 + mK3 * g2 + mK4);
int bResult = (int) (mK1 * b1 * b2 + mK2 * b1 + mK3 * b2 + mK4);
int aResult = (int) (mK1 * a1 * a2 + mK2 * a1 + mK3 * a2 + mK4);

rResult = Math.min(255, Math.max(0, rResult));
gResult = Math.min(255, Math.max(0, gResult));
bResult = Math.min(255, Math.max(0, bResult));
aResult = Math.min(255, Math.max(0, aResult));

int pixel = (aResult << 24) | (rResult << 16) | (gResult << 8) | bResult;
pixels1[i] = pixel;
}

result.setPixels(
pixels1, 0, result.getWidth(), 0, 0, result.getWidth(), result.getHeight());
}
}

if (this.mOperator != FilterProperties.FeCompositeOperator.ARITHMETIC) {
canvas.drawBitmap(in2, 0, 0, paint);
}

return result;
}
}
37 changes: 37 additions & 0 deletions android/src/main/java/com/horcrux/svg/FilterProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,41 @@ public String toString() {
return type;
}
}

enum FeCompositeOperator {
OVER("over"),
IN("in"),
OUT("out"),
ATOP("atop"),
XOR("xor"),
ARITHMETIC("arithmetic"),
;

private final String type;

FeCompositeOperator(String type) {
this.type = type;
}

static FeCompositeOperator getEnum(String strVal) {
if (!typeToEnum.containsKey(strVal)) {
throw new IllegalArgumentException("Unknown String Value: " + strVal);
}
return typeToEnum.get(strVal);
}

private static final Map<String, FeCompositeOperator> typeToEnum = new HashMap<>();

static {
for (final FeCompositeOperator en : FeCompositeOperator.values()) {
typeToEnum.put(en.type, en);
}
}

@Nonnull
@Override
public String toString() {
return type;
}
}
}
50 changes: 50 additions & 0 deletions android/src/main/java/com/horcrux/svg/RenderableViewManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@
import com.facebook.react.viewmanagers.RNSVGFeBlendManagerInterface;
import com.facebook.react.viewmanagers.RNSVGFeColorMatrixManagerDelegate;
import com.facebook.react.viewmanagers.RNSVGFeColorMatrixManagerInterface;
import com.facebook.react.viewmanagers.RNSVGFeCompositeManagerDelegate;
import com.facebook.react.viewmanagers.RNSVGFeCompositeManagerInterface;
import com.facebook.react.viewmanagers.RNSVGFeFloodManagerDelegate;
import com.facebook.react.viewmanagers.RNSVGFeFloodManagerInterface;
import com.facebook.react.viewmanagers.RNSVGFeGaussianBlurManagerDelegate;
Expand Down Expand Up @@ -595,6 +597,7 @@ protected enum SVGClass {
RNSVGFilter,
RNSVGFeBlend,
RNSVGFeColorMatrix,
RNSVGFeComposite,
RNSVGFeFlood,
RNSVGFeGaussianBlur,
RNSVGFeMerge,
Expand Down Expand Up @@ -649,6 +652,8 @@ protected VirtualView createViewInstance(@Nonnull ThemedReactContext reactContex
return new FeBlendView(reactContext);
case RNSVGFeColorMatrix:
return new FeColorMatrixView(reactContext);
case RNSVGFeComposite:
return new FeCompositeView(reactContext);
case RNSVGFeFlood:
return new FeFloodView(reactContext);
case RNSVGFeGaussianBlur:
Expand Down Expand Up @@ -1640,6 +1645,51 @@ public void setValues(FeColorMatrixView node, @Nullable ReadableArray values) {
}
}

static class FeCompositeManager extends FilterPrimitiveManager<FeCompositeView>
implements RNSVGFeCompositeManagerInterface<FeCompositeView> {
FeCompositeManager() {
super(SVGClass.RNSVGFeComposite);
mDelegate = new RNSVGFeCompositeManagerDelegate(this);
}

public static final String REACT_CLASS = "RNSVGFeComposite";

@ReactProp(name = "in1")
public void setIn1(FeCompositeView node, String in1) {
node.setIn1(in1);
}

@ReactProp(name = "in2")
public void setIn2(FeCompositeView node, String in2) {
node.setIn2(in2);
}

@ReactProp(name = "operator1")
public void setOperator1(FeCompositeView node, String operator) {
node.setOperator(operator);
}

@ReactProp(name = "k1")
public void setK1(FeCompositeView node, float value) {
node.setK1(value);
}

@ReactProp(name = "k2")
public void setK2(FeCompositeView node, float value) {
node.setK2(value);
}

@ReactProp(name = "k3")
public void setK3(FeCompositeView node, float value) {
node.setK3(value);
}

@ReactProp(name = "k4")
public void setK4(FeCompositeView node, float value) {
node.setK4(value);
}
}

static class FeFloodManager extends FilterPrimitiveManager<FeFloodView>
implements RNSVGFeFloodManagerInterface<FeFloodView> {
FeFloodManager() {
Expand Down
9 changes: 9 additions & 0 deletions android/src/main/java/com/horcrux/svg/SvgPackage.java
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,15 @@ public NativeModule get() {
return new FeColorMatrixManager();
}
}));
specs.put(
FeCompositeManager.REACT_CLASS,
ModuleSpec.viewManagerSpec(
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new FeCompositeManager();
}
}));
specs.put(
FeFloodManager.REACT_CLASS,
ModuleSpec.viewManagerSpec(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* 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.BaseViewManagerDelegate;
import com.facebook.react.uimanager.BaseViewManagerInterface;

public class RNSVGFeCompositeManagerDelegate<T extends View, U extends BaseViewManagerInterface<T> & RNSVGFeCompositeManagerInterface<T>> extends BaseViewManagerDelegate<T, U> {
public RNSVGFeCompositeManagerDelegate(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 "in1":
mViewManager.setIn1(view, value == null ? null : (String) value);
break;
case "in2":
mViewManager.setIn2(view, value == null ? null : (String) value);
break;
case "operator1":
mViewManager.setOperator1(view, (String) value);
break;
case "k1":
mViewManager.setK1(view, value == null ? 0f : ((Double) value).floatValue());
break;
case "k2":
mViewManager.setK2(view, value == null ? 0f : ((Double) value).floatValue());
break;
case "k3":
mViewManager.setK3(view, value == null ? 0f : ((Double) value).floatValue());
break;
case "k4":
mViewManager.setK4(view, value == null ? 0f : ((Double) value).floatValue());
break;
default:
super.setProperty(view, propName, value);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* 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 RNSVGFeCompositeManagerInterface<T extends View> {
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 setIn1(T view, @Nullable String value);
void setIn2(T view, @Nullable String value);
void setOperator1(T view, @Nullable String value);
void setK1(T view, float value);
void setK2(T view, float value);
void setK3(T view, float value);
void setK4(T view, float value);
}
Binary file not shown.
Binary file not shown.
11 changes: 11 additions & 0 deletions apple/Filters/MetalCI/RNSVGArithmeticFilter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#import "RNSVGCustomFilter.h"

@interface RNSVGArithmeticFilter : RNSVGCustomFilter {
CIImage *inputImage2;
NSNumber *inputK1;
NSNumber *inputK2;
NSNumber *inputK3;
NSNumber *inputK4;
}

@end
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Loading