-
-
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 `FeFlood` filter https://www.w3.org/TR/SVG11/filters.html#feFloodElement ## Test Plan Example app → Filters → `FeFlood` ## Compatibility | OS | Implemented | | ------- | :---------: | | iOS | ✅ | | macOS | ✅ _*_ | | Android | ✅ | | Web | ✅ | _* `canvasWidth/canvasHeight` is incorrect on macOS, so there might be some problems_
- Loading branch information
Showing
17 changed files
with
581 additions
and
6 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,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 | ||
} | ||
} | ||
} |
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
50 changes: 50 additions & 0 deletions
50
android/src/paper/java/com/facebook/react/viewmanagers/RNSVGFeFloodManagerDelegate.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,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); | ||
} | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
android/src/paper/java/com/facebook/react/viewmanagers/RNSVGFeFloodManagerInterface.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,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); | ||
} |
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,9 @@ | ||
#import "RNSVGBrush.h" | ||
#import "RNSVGFilterPrimitive.h" | ||
|
||
@interface RNSVGFeFlood : RNSVGFilterPrimitive | ||
|
||
@property (nonatomic, strong) RNSVGBrush *floodColor; | ||
@property (nonatomic, assign) CGFloat floodOpacity; | ||
|
||
@end |
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,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 |
Oops, something went wrong.