Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: FeFlood #2487

Merged
merged 6 commits into from
Oct 15, 2024
Merged
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
2 changes: 1 addition & 1 deletion USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1301,6 +1301,7 @@ Filter effects are a way of processing an element’s rendering before it is dis
The following filters have been implemented:

- FeColorMatrix
- FeFlood
- FeGaussianBlur
- FeMerge
- FeOffset
Expand All @@ -1314,7 +1315,6 @@ Not supported yet:
- FeDiffuseLighting
- FeDisplacementMap
- FeDropShadow
- FeFlood
- FeFuncA
- FeFuncB
- FeFuncG
Expand Down
130 changes: 130 additions & 0 deletions android/src/main/java/com/horcrux/svg/FeFloodView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package com.horcrux.svg;

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import com.facebook.react.bridge.ColorPropConverter;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.JavaOnlyArray;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableType;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nullable;

@SuppressLint("ViewConstructor")
class FeFloodView extends FilterPrimitiveView {
private static final Pattern regex = Pattern.compile("[0-9.-]+");

public @Nullable ReadableArray floodColor;
public float floodOpacity = 1;

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

public void setFloodColor(@Nullable Dynamic color) {
if (color == null || color.isNull()) {
floodColor = null;
invalidate();
return;
}

ReadableType strokeType = color.getType();
if (strokeType.equals(ReadableType.Map)) {
ReadableMap colorMap = color.asMap();
setFloodColor(colorMap);
return;
}

// This code will probably never be reached with current changes
ReadableType type = color.getType();
if (type.equals(ReadableType.Number)) {
floodColor = JavaOnlyArray.of(0, color.asInt());
} else if (type.equals(ReadableType.Array)) {
floodColor = color.asArray();
} else {
JavaOnlyArray arr = new JavaOnlyArray();
arr.pushInt(0);
Matcher m = regex.matcher(color.asString());
int i = 0;
while (m.find()) {
double parsed = Double.parseDouble(m.group());
arr.pushDouble(i++ < 3 ? parsed / 255 : parsed);
}
floodColor = arr;
}
invalidate();
}

public void setFloodColor(@Nullable ReadableMap color) {
if (color == null) {
this.floodColor = null;
invalidate();
return;
}
int type = color.getInt("type");
if (type == 0) {
ReadableType payloadType = color.getType("payload");
if (payloadType.equals(ReadableType.Number)) {
this.floodColor = JavaOnlyArray.of(0, color.getInt("payload"));
} else if (payloadType.equals(ReadableType.Map)) {
this.floodColor = JavaOnlyArray.of(0, color.getMap("payload"));
}
} else if (type == 1) {
this.floodColor = JavaOnlyArray.of(1, color.getString("brushRef"));
} else {
this.floodColor = JavaOnlyArray.of(type);
}
invalidate();
}

public void setFloodOpacity(float opacity) {
this.floodOpacity = opacity;
invalidate();
}

@Override
public Bitmap applyFilter(HashMap<String, Bitmap> resultsMap, Bitmap prevResult) {
Bitmap floodBitmap =
Bitmap.createBitmap(prevResult.getWidth(), prevResult.getHeight(), Bitmap.Config.ARGB_8888);
Canvas floodCanvas = new Canvas(floodBitmap);
Paint paint = new Paint();
paint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.SUBPIXEL_TEXT_FLAG);
paint.setStyle(Paint.Style.FILL);
this.setupPaint(paint, this.floodOpacity, this.floodColor);
floodCanvas.drawPaint(paint);
return floodBitmap;
}

private void setupPaint(Paint paint, float opacity, @Nullable ReadableArray colors) {
int colorType = colors.getInt(0);
switch (colorType) {
case 0:
if (colors.size() == 2) {
int color;
if (colors.getType(1) == ReadableType.Map) {
color = ColorPropConverter.getColor(colors.getMap(1), getContext());
} else {
color = colors.getInt(1);
}
int alpha = color >>> 24;
int combined = Math.round((float) alpha * opacity);
paint.setColor(combined << 24 | (color & 0x00ffffff));
} else {
// solid color
paint.setARGB(
(int) (colors.size() > 4 ? colors.getDouble(4) * opacity * 255 : opacity * 255),
(int) (colors.getDouble(1) * 255),
(int) (colors.getDouble(2) * 255),
(int) (colors.getDouble(3) * 255));
}
break;
// TODO: handle currentColor
}
}
}
29 changes: 29 additions & 0 deletions android/src/main/java/com/horcrux/svg/RenderableViewManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@
import com.facebook.react.viewmanagers.RNSVGEllipseManagerInterface;
import com.facebook.react.viewmanagers.RNSVGFeColorMatrixManagerDelegate;
import com.facebook.react.viewmanagers.RNSVGFeColorMatrixManagerInterface;
import com.facebook.react.viewmanagers.RNSVGFeFloodManagerDelegate;
import com.facebook.react.viewmanagers.RNSVGFeFloodManagerInterface;
import com.facebook.react.viewmanagers.RNSVGFeGaussianBlurManagerDelegate;
import com.facebook.react.viewmanagers.RNSVGFeGaussianBlurManagerInterface;
import com.facebook.react.viewmanagers.RNSVGFeMergeManagerDelegate;
Expand Down Expand Up @@ -590,6 +592,7 @@ protected enum SVGClass {
RNSVGMask,
RNSVGFilter,
RNSVGFeColorMatrix,
RNSVGFeFlood,
RNSVGFeGaussianBlur,
RNSVGFeMerge,
RNSVGFeOffset,
Expand Down Expand Up @@ -641,6 +644,8 @@ protected VirtualView createViewInstance(@Nonnull ThemedReactContext reactContex
return new FilterView(reactContext);
case RNSVGFeColorMatrix:
return new FeColorMatrixView(reactContext);
case RNSVGFeFlood:
return new FeFloodView(reactContext);
case RNSVGFeGaussianBlur:
return new FeGaussianBlurView(reactContext);
case RNSVGFeMerge:
Expand Down Expand Up @@ -1605,6 +1610,30 @@ public void setValues(FeColorMatrixView node, @Nullable ReadableArray values) {
}
}

static class FeFloodManager extends FilterPrimitiveManager<FeFloodView>
implements RNSVGFeFloodManagerInterface<FeFloodView> {
FeFloodManager() {
super(SVGClass.RNSVGFeFlood);
mDelegate = new RNSVGFeFloodManagerDelegate(this);
}

public static final String REACT_CLASS = "RNSVGFeFlood";

@ReactProp(name = "floodColor")
public void setFloodColor(FeFloodView node, @Nullable Dynamic strokeColors) {
node.setFloodColor(strokeColors);
}

public void setFloodColor(FeFloodView view, @Nullable ReadableMap value) {
view.setFloodColor(value);
}

@ReactProp(name = "floodOpacity", defaultFloat = 1f)
public void setFloodOpacity(FeFloodView node, float strokeOpacity) {
node.setFloodOpacity(strokeOpacity);
}
}

static class FeGaussianBlurManager extends FilterPrimitiveManager<FeGaussianBlurView>
implements RNSVGFeGaussianBlurManagerInterface<FeGaussianBlurView> {
FeGaussianBlurManager() {
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 @@ -223,6 +223,15 @@ public NativeModule get() {
return new FeColorMatrixManager();
}
}));
specs.put(
FeFloodManager.REACT_CLASS,
ModuleSpec.viewManagerSpec(
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new FeFloodManager();
}
}));
specs.put(
FeGaussianBlurManager.REACT_CLASS,
ModuleSpec.viewManagerSpec(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* 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 RNSVGFeFloodManagerDelegate<T extends View, U extends BaseViewManagerInterface<T> & RNSVGFeFloodManagerInterface<T>> extends BaseViewManagerDelegate<T, U> {
public RNSVGFeFloodManagerDelegate(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 "floodColor":
mViewManager.setFloodColor(view, new DynamicFromObject(value));
break;
case "floodOpacity":
mViewManager.setFloodOpacity(view, value == null ? 1f : ((Double) value).floatValue());
break;
default:
super.setProperty(view, propName, value);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* 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 RNSVGFeFloodManagerInterface<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 setFloodColor(T view, Dynamic value);
void setFloodOpacity(T view, float value);
}
9 changes: 9 additions & 0 deletions apple/Filters/RNSVGFeFlood.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#import "RNSVGBrush.h"
#import "RNSVGFilterPrimitive.h"

@interface RNSVGFeFlood : RNSVGFilterPrimitive

@property (nonatomic, strong) RNSVGBrush *floodColor;
@property (nonatomic, assign) CGFloat floodOpacity;

@end
91 changes: 91 additions & 0 deletions apple/Filters/RNSVGFeFlood.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#import "RNSVGFeFlood.h"

#ifdef RCT_NEW_ARCH_ENABLED
#import <React/RCTConversions.h>
#import <React/RCTFabricComponentsPlugins.h>
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import <react/renderer/components/view/conversions.h>
#import "RNSVGConvert.h"
#import "RNSVGFabricConversions.h"
#endif // RCT_NEW_ARCH_ENABLED

@implementation RNSVGFeFlood

#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<const RNSVGFeFloodProps>();
_props = defaultProps;
}
return self;
}

#pragma mark - RCTComponentViewProtocol

+ (ComponentDescriptorProvider)componentDescriptorProvider
{
return concreteComponentDescriptorProvider<RNSVGFeFloodComponentDescriptor>();
}

- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &newProps = static_cast<const RNSVGFeFloodProps &>(*props);

id floodColor = RNSVGConvertFollyDynamicToId(newProps.floodColor);
if (floodColor != nil) {
self.floodColor = [RCTConvert RNSVGBrush:floodColor];
}
self.floodOpacity = newProps.floodOpacity;

setCommonFilterProps(newProps, self);
_props = std::static_pointer_cast<RNSVGFeFloodProps const>(props);
}

- (void)prepareForRecycle
{
[super prepareForRecycle];
_floodColor = nil;
_floodOpacity = 1;
}
#endif // RCT_NEW_ARCH_ENABLED

- (void)setFloodColor:(RNSVGBrush *)floodColor
{
if (floodColor == _floodColor) {
return;
}
_floodColor = floodColor;
[self invalidate];
}

- (void)setFloodOpacity:(CGFloat)floodOpacity
{
if (floodOpacity == _floodOpacity) {
return;
}
_floodOpacity = floodOpacity;
[self invalidate];
}

- (CIImage *)applyFilter:(NSMutableDictionary<NSString *, CIImage *> *)results previousFilterResult:(CIImage *)previous
{
return [CIImage imageWithColor:[CIColor colorWithCGColor:[self.floodColor getColorWithOpacity:self.floodOpacity]]];
}

#ifdef RCT_NEW_ARCH_ENABLED
Class<RCTComponentViewProtocol> RNSVGFeFloodCls(void)
{
return RNSVGFeFlood.class;
}
#endif // RCT_NEW_ARCH_ENABLED

@end
Loading
Loading