Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1301,8 +1301,9 @@ Filter effects are a way of processing an element’s rendering before it is dis
The following filters have been implemented:

- FeBlend
- FeComposite
- FeColorMatrix
- FeComposite
- FeConvolveMatrix
- FeDropShadow
- FeFlood
- FeGaussianBlur
Expand All @@ -1312,7 +1313,6 @@ The following filters have been implemented:
Not supported yet:

- FeComponentTransfer
- FeConvolveMatrix
- FeDiffuseLighting
- FeDisplacementMap
- FeFuncA
Expand Down
203 changes: 203 additions & 0 deletions android/src/main/java/com/horcrux/svg/FeConvolveMatrixView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
package com.horcrux.svg;

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext;
import java.util.ArrayList;
import java.util.HashMap;

@SuppressLint("ViewConstructor")
class FeConvolveMatrixView extends FilterPrimitiveView {
String mIn1;
ArrayList<SVGLength> mOrder;
ArrayList<SVGLength> mKernelMatrix;
SVGLength mDivisor;
SVGLength mBias;
SVGLength mTargetX;
SVGLength mTargetY;
FilterProperties.EdgeMode mEdgeMode;
boolean mPreserveAlpha;

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

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

public void setOrder(Dynamic order) {
this.mOrder = SVGLength.arrayFrom(order);
invalidate();
}

public void setKernelMatrix(Dynamic kernelMatrix) {
this.mKernelMatrix = SVGLength.arrayFrom(kernelMatrix);
invalidate();
}

public void setDivisor(Dynamic divisor) {
this.mDivisor = SVGLength.from(divisor);
invalidate();
}

public void setBias(Dynamic bias) {
this.mBias = SVGLength.from(bias);
invalidate();
}

public void setTargetX(Dynamic targetX) {
this.mTargetX = SVGLength.from(targetX);
invalidate();
}

public void setTargetY(Dynamic targetY) {
this.mTargetY = SVGLength.from(targetY);
invalidate();
}

public void setEdgeMode(String edgeMode) {
this.mEdgeMode = FilterProperties.EdgeMode.getEnum(edgeMode);
System.out.println(this.mEdgeMode);
invalidate();
}

public void setPreserveAlpha(boolean preserveAlpha) {
this.mPreserveAlpha = preserveAlpha;
invalidate();
}

@Override
public Bitmap applyFilter(HashMap<String, Bitmap> resultsMap, Bitmap prevResult) {
Bitmap in1 = getSource(resultsMap, prevResult, this.mIn1);

return performConvolution(in1);
}

private Bitmap performConvolution(Bitmap src) {
int width = src.getWidth();
int height = src.getHeight();
int orderX = this.getOrderX();
int orderY = this.getOrderY();
float[] kernel = this.getKernelMatrixArray(orderX, orderY);
float divisor = this.getDivisor(kernel);
int targetX = this.getTargetX(orderX);
int targetY = this.getTargetY(orderY);

Bitmap out = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

int[] inPixels = new int[width * height];
src.getPixels(inPixels, 0, width, 0, 0, width, height);
int[] outPixels = new int[inPixels.length];

for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
float r = 0, g = 0, b = 0, a = 0;

for (int ky = 0; ky < orderY; ky++) {
for (int kx = 0; kx < orderX; kx++) {
int ix = x + (kx - targetX);
int iy = y + (ky - targetY);

int sample;
switch (this.mEdgeMode) {
case DUPLICATE:
ix = Math.max(0, Math.min(ix, width - 1));
iy = Math.max(0, Math.min(iy, height - 1));
sample = inPixels[iy * width + ix];
break;
case WRAP:
ix = (ix + width) % width;
iy = (iy + height) % height;
sample = inPixels[iy * width + ix];
break;
case NONE:
default:
if (ix < 0 || iy < 0 || ix >= width || iy >= height) {
sample = 0;
} else {
sample = inPixels[iy * width + ix];
}
break;
}

float kval = kernel[(orderY - 1 - ky) * orderX + (orderX - 1 - kx)];

a += ((sample >> 24) & 0xFF) * kval;
r += ((sample >> 16) & 0xFF) * kval;
g += ((sample >> 8) & 0xFF) * kval;
b += (sample & 0xFF) * kval;
}
}

r = r / divisor + (int) mBias.value;
g = g / divisor + (int) mBias.value;
b = b / divisor + (int) mBias.value;

if (mPreserveAlpha) {
a = (inPixels[y * width + x] >> 24) & 0xFF;
} else {
a = a / divisor;
}

int ir = Math.min(255, Math.max(0, Math.round(r)));
int ig = Math.min(255, Math.max(0, Math.round(g)));
int ib = Math.min(255, Math.max(0, Math.round(b)));
int ia = Math.min(255, Math.max(0, Math.round(a)));

outPixels[y * width + x] = (ia << 24) | (ir << 16) | (ig << 8) | ib;
}
}

out.setPixels(outPixels, 0, width, 0, 0, width, height);
return out;
}

private int getOrderX() {
if (this.mOrder == null || this.mOrder.isEmpty()) {
return 3;
}

return (int) this.mOrder.get(0).value;
}

private int getOrderY() {
if (this.mOrder == null || this.mOrder.size() <= 1) {
return this.getOrderX();
}

return (int) this.mOrder.get(1).value;
}

private float getDivisor(float[] kernel) {
float divisor = (mDivisor != null) ? (float) mDivisor.value : 0f;

if (divisor == 0f) {
float sum = 0;
for (float v : kernel) sum += v;
divisor = (sum == 0f) ? 1f : sum;
}

return divisor;
}

private float[] getKernelMatrixArray(int orderX, int orderY) {
float[] kernel = new float[orderX * orderY];

for (int i = 0; i < kernel.length; i++) {
kernel[i] = (float) mKernelMatrix.get(i).value;
}

return kernel;
}

private int getTargetX(int orderX) {
return (mTargetX != null) ? (int) mTargetX.value : orderX / 2;
}

private int getTargetY(int orderY) {
return (mTargetY != null) ? (int) mTargetY.value : orderY / 2;
}
}
60 changes: 60 additions & 0 deletions android/src/main/java/com/horcrux/svg/RenderableViewManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@
import com.facebook.react.viewmanagers.RNSVGFeColorMatrixManagerInterface;
import com.facebook.react.viewmanagers.RNSVGFeCompositeManagerDelegate;
import com.facebook.react.viewmanagers.RNSVGFeCompositeManagerInterface;
import com.facebook.react.viewmanagers.RNSVGFeConvolveMatrixManagerDelegate;
import com.facebook.react.viewmanagers.RNSVGFeConvolveMatrixManagerInterface;
import com.facebook.react.viewmanagers.RNSVGFeFloodManagerDelegate;
import com.facebook.react.viewmanagers.RNSVGFeFloodManagerInterface;
import com.facebook.react.viewmanagers.RNSVGFeGaussianBlurManagerDelegate;
Expand Down Expand Up @@ -484,6 +486,7 @@ protected enum SVGClass {
RNSVGFeBlend,
RNSVGFeColorMatrix,
RNSVGFeComposite,
RNSVGFeConvolveMatrix,
RNSVGFeFlood,
RNSVGFeGaussianBlur,
RNSVGFeMerge,
Expand Down Expand Up @@ -540,6 +543,8 @@ protected VirtualView createViewInstance(@Nonnull ThemedReactContext reactContex
return new FeColorMatrixView(reactContext);
case RNSVGFeComposite:
return new FeCompositeView(reactContext);
case RNSVGFeConvolveMatrix:
return new FeConvolveMatrixView(reactContext);
case RNSVGFeFlood:
return new FeFloodView(reactContext);
case RNSVGFeGaussianBlur:
Expand Down Expand Up @@ -1582,6 +1587,61 @@ public void setK4(FeCompositeView node, float value) {
}
}

static class FeConvolveMatrixManager extends FilterPrimitiveManager<FeConvolveMatrixView>
implements RNSVGFeConvolveMatrixManagerInterface<FeConvolveMatrixView> {
FeConvolveMatrixManager() {
super(SVGClass.RNSVGFeConvolveMatrix);
mDelegate = new RNSVGFeConvolveMatrixManagerDelegate(this);
}

public static final String REACT_CLASS = "RNSVGFeConvolveMatrix";

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

@ReactProp(name = "order")
public void setOrder(FeConvolveMatrixView node, Dynamic order) {
node.setOrder(order);
}

@ReactProp(name = "kernelMatrix")
public void setKernelMatrix(FeConvolveMatrixView node, Dynamic kernelMatrix) {
node.setKernelMatrix(kernelMatrix);
}

@ReactProp(name = "divisor")
public void setDivisor(FeConvolveMatrixView node, Dynamic divisor) {
node.setDivisor(divisor);
}

@ReactProp(name = "bias")
public void setBias(FeConvolveMatrixView node, Dynamic bias) {
node.setBias(bias);
}

@ReactProp(name = "targetX")
public void setTargetX(FeConvolveMatrixView node, Dynamic targetX) {
node.setTargetX(targetX);
}

@ReactProp(name = "targetY")
public void setTargetY(FeConvolveMatrixView node, Dynamic targetY) {
node.setTargetY(targetY);
}

@ReactProp(name = "edgeMode")
public void setEdgeMode(FeConvolveMatrixView node, String edgeMode) {
node.setEdgeMode(edgeMode);
}

@ReactProp(name = "preserveAlpha")
public void setPreserveAlpha(FeConvolveMatrixView node, boolean preserveAlpha) {
node.setPreserveAlpha(preserveAlpha);
}
}

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 @@ -241,6 +241,15 @@ public NativeModule get() {
return new FeCompositeManager();
}
}));
specs.put(
FeConvolveMatrixManager.REACT_CLASS,
ModuleSpec.viewManagerSpec(
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new FeConvolveMatrixManager();
}
}));
specs.put(
FeFloodManager.REACT_CLASS,
ModuleSpec.viewManagerSpec(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* 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 RNSVGFeConvolveMatrixManagerDelegate<T extends View, U extends BaseViewManager<T, ? extends LayoutShadowNode> & RNSVGFeConvolveMatrixManagerInterface<T>> extends BaseViewManagerDelegate<T, U> {
public RNSVGFeConvolveMatrixManagerDelegate(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 "order":
mViewManager.setOrder(view, new DynamicFromObject(value));
break;
case "kernelMatrix":
mViewManager.setKernelMatrix(view, new DynamicFromObject(value));
break;
case "divisor":
mViewManager.setDivisor(view, new DynamicFromObject(value));
break;
case "bias":
mViewManager.setBias(view, new DynamicFromObject(value));
break;
case "targetX":
mViewManager.setTargetX(view, new DynamicFromObject(value));
break;
case "targetY":
mViewManager.setTargetY(view, new DynamicFromObject(value));
break;
case "edgeMode":
mViewManager.setEdgeMode(view, (String) value);
break;
case "preserveAlpha":
mViewManager.setPreserveAlpha(view, value == null ? false : (boolean) value);
break;
default:
super.setProperty(view, propName, value);
}
}
}
Loading
Loading