-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
# Summary Continuation of #2362 implementing `FeBlend` filter https://www.w3.org/TR/SVG11/filters.html#feBlendElement ## Test Plan Example app → Filters → `FeBlend` ## Compatibility | OS | Implemented | | ------- | :---------: | | iOS | ✅ | | macOS | ✅ _*_ | | Android | ✅ | | Web | ✅ |
- Loading branch information
Showing
24 changed files
with
736 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package com.horcrux.svg; | ||
|
||
import android.graphics.Bitmap; | ||
|
||
interface CustomFilterFunction { | ||
float[] execute(float[] src, float[] dst); | ||
} | ||
|
||
public class CustomFilter { | ||
public static Bitmap apply(Bitmap srcBmp, Bitmap dstBmp, CustomFilterFunction func) { | ||
int width = srcBmp.getWidth(); | ||
int height = srcBmp.getHeight(); | ||
int[] srcPixels = new int[width * height]; | ||
int[] dstPixels = new int[width * height]; | ||
int[] resPixels = new int[width * height]; | ||
int srcArgb = 0; | ||
float[] src = new float[] {0, 0, 0, 0}; | ||
int dstArgb = 0; | ||
float[] dst = new float[] {0, 0, 0, 0}; | ||
|
||
try { | ||
srcBmp.getPixels(srcPixels, 0, width, 0, 0, width, height); | ||
dstBmp.getPixels(dstPixels, 0, width, 0, 0, width, height); | ||
} catch (IllegalArgumentException | ArrayIndexOutOfBoundsException ignored) { | ||
} | ||
|
||
for (int y = 0; y < height; y++) { | ||
for (int x = 0; x < width; x++) { | ||
srcArgb = srcPixels[y * width + x]; | ||
src[0] = ((srcArgb >> 24) & 0xff) / 255f; | ||
src[1] = ((srcArgb >> 16) & 0xff) / 255f; | ||
src[2] = ((srcArgb >> 8) & 0xff) / 255f; | ||
src[3] = (srcArgb & 0xff) / 255f; | ||
|
||
dstArgb = dstPixels[y * width + x]; | ||
dst[0] = ((dstArgb >> 24) & 0xff) / 255f; | ||
dst[1] = ((dstArgb >> 16) & 0xff) / 255f; | ||
dst[2] = ((dstArgb >> 8) & 0xff) / 255f; | ||
dst[3] = (dstArgb & 0xff) / 255f; | ||
|
||
resPixels[y * width + x] = normalizeFromFloats(func.execute(src, dst)); | ||
} | ||
} | ||
|
||
return Bitmap.createBitmap(resPixels, width, height, Bitmap.Config.ARGB_8888); | ||
} | ||
|
||
public static int normalizeFromFloat(float c) { | ||
return Math.min(255, Math.max(0, Math.round(c * 255))); | ||
} | ||
|
||
public static int normalizeFromFloats(float[] res) { | ||
if (res.length < 4 || normalizeFromFloat(res[0]) <= 0) { | ||
return 0; | ||
} | ||
return (normalizeFromFloat(res[0]) << 24) | ||
| (normalizeFromFloat(res[1] / res[0]) << 16) | ||
| (normalizeFromFloat(res[2] / res[0]) << 8) | ||
| normalizeFromFloat(res[3] / res[0]); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
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 FeBlendView extends FilterPrimitiveView { | ||
String mIn1; | ||
String mIn2; | ||
FilterProperties.FeBlendMode mMode; | ||
|
||
public FeBlendView(ReactContext reactContext) { | ||
super(reactContext); | ||
super.mFilterSubregion.mX = new SVGLength(0); | ||
super.mFilterSubregion.mY = new SVGLength(0); | ||
super.mFilterSubregion.mW = new SVGLength("100%"); | ||
super.mFilterSubregion.mH = new SVGLength("100%"); | ||
} | ||
|
||
public void setIn1(String in1) { | ||
this.mIn1 = in1; | ||
invalidate(); | ||
} | ||
|
||
public void setIn2(String in2) { | ||
this.mIn2 = in2; | ||
invalidate(); | ||
} | ||
|
||
public void setMode(String mode) { | ||
this.mMode = FilterProperties.FeBlendMode.getEnum(mode); | ||
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); | ||
|
||
if (this.mMode == FilterProperties.FeBlendMode.MULTIPLY) { | ||
CustomFilterFunction multiply = | ||
(src, dst) -> { | ||
float[] res = new float[4]; | ||
res[0] = 1f - (1f - src[0]) * (1f - dst[0]); | ||
res[1] = | ||
src[1] * src[0] * (1f - dst[0]) | ||
+ dst[1] * dst[0] * (1f - src[0]) | ||
+ src[1] * src[0] * dst[1] * dst[0]; | ||
res[2] = | ||
src[2] * src[0] * (1f - dst[0]) | ||
+ dst[2] * dst[0] * (1f - src[0]) | ||
+ src[2] * src[0] * dst[2] * dst[0]; | ||
res[3] = | ||
src[3] * src[0] * (1f - dst[0]) | ||
+ dst[3] * dst[0] * (1f - src[0]) | ||
+ src[3] * src[0] * dst[3] * dst[0]; | ||
return res; | ||
}; | ||
return CustomFilter.apply(in1, in2, multiply); | ||
} | ||
|
||
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.mMode) { | ||
case NORMAL -> { | ||
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OVER)); | ||
} | ||
case SCREEN -> { | ||
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN)); | ||
} | ||
case LIGHTEN -> { | ||
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN)); | ||
} | ||
case DARKEN -> { | ||
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN)); | ||
} | ||
case MULTIPLY -> {} | ||
} | ||
canvas.drawBitmap(in2, 0, 0, paint); | ||
|
||
return result; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
53 changes: 53 additions & 0 deletions
53
android/src/paper/java/com/facebook/react/viewmanagers/RNSVGFeBlendManagerDelegate.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
/** | ||
* 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 RNSVGFeBlendManagerDelegate<T extends View, U extends BaseViewManagerInterface<T> & RNSVGFeBlendManagerInterface<T>> extends BaseViewManagerDelegate<T, U> { | ||
public RNSVGFeBlendManagerDelegate(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 "mode": | ||
mViewManager.setMode(view, (String) value); | ||
break; | ||
default: | ||
super.setProperty(view, propName, value); | ||
} | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
android/src/paper/java/com/facebook/react/viewmanagers/RNSVGFeBlendManagerInterface.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/** | ||
* 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 RNSVGFeBlendManagerInterface<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 setMode(T view, @Nullable String value); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
typedef CF_ENUM(int32_t, RNSVGBlendMode) { | ||
SVG_FEBLEND_MODE_UNKNOWN, | ||
SVG_FEBLEND_MODE_NORMAL, | ||
SVG_FEBLEND_MODE_MULTIPLY, | ||
SVG_FEBLEND_MODE_SCREEN, | ||
SVG_FEBLEND_MODE_DARKEN, | ||
SVG_FEBLEND_MODE_LIGHTEN, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#import "RNSVGBlendMode.h" | ||
#import "RNSVGFilterPrimitive.h" | ||
|
||
@interface RNSVGFeBlend : RNSVGFilterPrimitive | ||
|
||
@property (nonatomic, strong) NSString *in1; | ||
@property (nonatomic, strong) NSString *in2; | ||
@property (nonatomic, assign) RNSVGBlendMode mode; | ||
|
||
@end |
Oops, something went wrong.