mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-06 07:06:11 +00:00
feat: implement filter region lengths directly on RNSVGFilterRegion (#2485)
# Summary [apple] Use filter region directly instead of creating new one on every rerender. [android] rename some variables and add temporary fix for null lengths
This commit is contained in:
@@ -9,30 +9,30 @@ import java.util.HashMap;
|
||||
@SuppressLint("ViewConstructor")
|
||||
class FilterPrimitiveView extends DefinitionView {
|
||||
private String mResult;
|
||||
public final FilterRegion mFilterRegion;
|
||||
public final FilterRegion mFilterSubregion;
|
||||
|
||||
public FilterPrimitiveView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
mFilterRegion = new FilterRegion();
|
||||
mFilterSubregion = new FilterRegion();
|
||||
}
|
||||
|
||||
public void setX(Dynamic x) {
|
||||
mFilterRegion.setX(x);
|
||||
mFilterSubregion.setX(x);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setY(Dynamic y) {
|
||||
mFilterRegion.setY(y);
|
||||
mFilterSubregion.setY(y);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setWidth(Dynamic width) {
|
||||
mFilterRegion.setWidth(width);
|
||||
mFilterSubregion.setWidth(width);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setHeight(Dynamic height) {
|
||||
mFilterRegion.setHeight(height);
|
||||
mFilterSubregion.setHeight(height);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
@@ -26,24 +26,24 @@ public class FilterRegion {
|
||||
mH = SVGLength.from(height);
|
||||
}
|
||||
|
||||
public Rect getCropRect(VirtualView view, FilterProperties.Units units, RectF renderableBounds) {
|
||||
public Rect getCropRect(VirtualView view, FilterProperties.Units units, RectF bounds) {
|
||||
double x, y, width, height;
|
||||
if (units == FilterProperties.Units.USER_SPACE_ON_USE) {
|
||||
x = view.relativeOn(this.mX, view.getSvgView().getCanvasWidth());
|
||||
y = view.relativeOn(this.mY, view.getSvgView().getCanvasHeight());
|
||||
width = view.relativeOn(this.mW, view.getSvgView().getCanvasWidth());
|
||||
height = view.relativeOn(this.mH, view.getSvgView().getCanvasHeight());
|
||||
x = this.mX == null ? 0 : view.relativeOn(this.mX, view.getSvgView().getCanvasWidth());
|
||||
y = this.mY == null ? 0 : view.relativeOn(this.mY, view.getSvgView().getCanvasHeight());
|
||||
width = this.mW == null ? 0 : view.relativeOn(this.mW, view.getSvgView().getCanvasWidth());
|
||||
height = this.mH == null ? 0 : view.relativeOn(this.mH, view.getSvgView().getCanvasHeight());
|
||||
return new Rect((int) x, (int) y, (int) (x + width), (int) (y + height));
|
||||
} else { // FilterProperties.Units.OBJECT_BOUNDING_BOX
|
||||
x = view.relativeOnFraction(this.mX, renderableBounds.width());
|
||||
y = view.relativeOnFraction(this.mY, renderableBounds.height());
|
||||
width = view.relativeOnFraction(this.mW, renderableBounds.width());
|
||||
height = view.relativeOnFraction(this.mH, renderableBounds.height());
|
||||
x = view.relativeOnFraction(this.mX, bounds.width());
|
||||
y = view.relativeOnFraction(this.mY, bounds.height());
|
||||
width = view.relativeOnFraction(this.mW, bounds.width());
|
||||
height = view.relativeOnFraction(this.mH, bounds.height());
|
||||
return new Rect(
|
||||
(int) (renderableBounds.left + x),
|
||||
(int) (renderableBounds.top + y),
|
||||
(int) (renderableBounds.left + x + width),
|
||||
(int) (renderableBounds.top + y + height));
|
||||
(int) (bounds.left + x),
|
||||
(int) (bounds.top + y),
|
||||
(int) (bounds.left + x + width),
|
||||
(int) (bounds.top + y + height));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,10 @@ class FilterView extends DefinitionView {
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public FilterRegion getFilterRegion() {
|
||||
return mFilterRegion;
|
||||
}
|
||||
|
||||
@Override
|
||||
void saveDefinition() {
|
||||
if (mName != null) {
|
||||
@@ -75,6 +79,8 @@ class FilterView extends DefinitionView {
|
||||
Bitmap res = source;
|
||||
Bitmap resultBitmap = Bitmap.createBitmap(res.getWidth(), res.getHeight(), res.getConfig());
|
||||
Canvas canvas = new Canvas(resultBitmap);
|
||||
Rect filterRegionRect =
|
||||
this.mFilterRegion.getCropRect(this, this.mFilterUnits, renderableBounds);
|
||||
Rect cropRect;
|
||||
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
@@ -83,7 +89,7 @@ class FilterView extends DefinitionView {
|
||||
FilterPrimitiveView currentFilter = (FilterPrimitiveView) node;
|
||||
resultBitmap.eraseColor(Color.TRANSPARENT);
|
||||
cropRect =
|
||||
currentFilter.mFilterRegion.getCropRect(
|
||||
currentFilter.mFilterSubregion.getCropRect(
|
||||
currentFilter, this.mPrimitiveUnits, renderableBounds);
|
||||
canvas.drawBitmap(currentFilter.applyFilter(mResultsMap, res), cropRect, cropRect, null);
|
||||
res = resultBitmap.copy(Bitmap.Config.ARGB_8888, true);
|
||||
@@ -98,8 +104,8 @@ class FilterView extends DefinitionView {
|
||||
|
||||
// crop Bitmap to filter coordinates
|
||||
resultBitmap.eraseColor(Color.TRANSPARENT);
|
||||
cropRect = this.mFilterRegion.getCropRect(this, this.mFilterUnits, renderableBounds);
|
||||
canvas.drawBitmap(res, cropRect, cropRect, null);
|
||||
|
||||
canvas.drawBitmap(res, filterRegionRect, filterRegionRect, null);
|
||||
return resultBitmap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
|
||||
@interface RNSVGFilter : RNSVGNode
|
||||
|
||||
@property (nonatomic, strong) RNSVGLength *x;
|
||||
@property (nonatomic, strong) RNSVGLength *y;
|
||||
@property (nonatomic, strong) RNSVGLength *width;
|
||||
@property (nonatomic, strong) RNSVGLength *height;
|
||||
@property (nonatomic, assign) RNSVGUnits filterUnits;
|
||||
@property (nonatomic, assign) RNSVGUnits primitiveUnits;
|
||||
@property (nonatomic, strong) RNSVGFilterRegion *filterRegion;
|
||||
|
||||
- (CIImage *)applyFilter:(CIImage *)img
|
||||
backgroundImg:(CIImage *)backgroundImg
|
||||
@@ -18,5 +15,9 @@
|
||||
- (CGContext *)openContext:(CGSize)size;
|
||||
- (void)endContext:(CGContext *)context;
|
||||
- (CIImage *)getMaskFromRect:(CGContext *)context rect:(CGRect)rect ctm:(CGAffineTransform)ctm;
|
||||
- (void)setX:(RNSVGLength *)x;
|
||||
- (void)setY:(RNSVGLength *)y;
|
||||
- (void)setWidth:(RNSVGLength *)width;
|
||||
- (void)setHeight:(RNSVGLength *)height;
|
||||
|
||||
@end
|
||||
|
||||
@@ -70,12 +70,9 @@ using namespace facebook::react;
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
_x = nil;
|
||||
_y = nil;
|
||||
_width = nil;
|
||||
_height = nil;
|
||||
_filterUnits = kRNSVGUnitsObjectBoundingBox;
|
||||
_primitiveUnits = kRNSVGUnitsUserSpaceOnUse;
|
||||
_filterRegion = nil;
|
||||
}
|
||||
#endif // RCT_NEW_ARCH_ENABLED
|
||||
|
||||
@@ -83,6 +80,7 @@ using namespace facebook::react;
|
||||
{
|
||||
if (self = [super init]) {
|
||||
resultsMap = [NSMutableDictionary dictionary];
|
||||
_filterRegion = [[RNSVGFilterRegion alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -112,12 +110,9 @@ using namespace facebook::react;
|
||||
for (RNSVGNode *node in self.subviews) {
|
||||
if ([node isKindOfClass:[RNSVGFilterPrimitive class]]) {
|
||||
currentFilter = (RNSVGFilterPrimitive *)node;
|
||||
cropRect = [[RNSVGFilterRegion regionWithX:currentFilter.x
|
||||
y:currentFilter.y
|
||||
width:currentFilter.width
|
||||
height:currentFilter.height] getCropRect:currentFilter
|
||||
units:self.primitiveUnits
|
||||
renderableBounds:renderableBounds];
|
||||
cropRect = [currentFilter.filterSubregion getCropRect:currentFilter
|
||||
units:self.primitiveUnits
|
||||
bounds:renderableBounds];
|
||||
mask = [self getMaskFromRect:cropContext rect:cropRect ctm:ctm];
|
||||
[cropFilter setValue:[currentFilter applyFilter:resultsMap previousFilterResult:result ctm:ctm]
|
||||
forKey:@"inputImage"];
|
||||
@@ -136,10 +131,7 @@ using namespace facebook::react;
|
||||
}
|
||||
}
|
||||
|
||||
cropRect = [[RNSVGFilterRegion regionWithX:self.x y:self.y width:self.width
|
||||
height:self.height] getCropRect:self
|
||||
units:self.filterUnits
|
||||
renderableBounds:renderableBounds];
|
||||
cropRect = [currentFilter.filterSubregion getCropRect:self units:self.filterUnits bounds:renderableBounds];
|
||||
mask = [self getMaskFromRect:cropContext rect:cropRect ctm:ctm];
|
||||
[cropFilter setValue:result forKey:@"inputImage"];
|
||||
[cropFilter setValue:mask forKey:@"inputMaskImage"];
|
||||
@@ -212,41 +204,41 @@ static CIImage *applySourceAlphaFilter(CIImage *inputImage)
|
||||
|
||||
- (void)setX:(RNSVGLength *)x
|
||||
{
|
||||
if ([x isEqualTo:_x]) {
|
||||
if ([x isEqualTo:_filterRegion.x]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_x = x;
|
||||
[_filterRegion setX:x];
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setY:(RNSVGLength *)y
|
||||
{
|
||||
if ([y isEqualTo:_y]) {
|
||||
if ([y isEqualTo:_filterRegion.y]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_y = y;
|
||||
[_filterRegion setY:y];
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setWidth:(RNSVGLength *)width
|
||||
{
|
||||
if ([width isEqualTo:_width]) {
|
||||
if ([width isEqualTo:_filterRegion.width]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_width = width;
|
||||
[_filterRegion setWidth:width];
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setHeight:(RNSVGLength *)height
|
||||
{
|
||||
if ([height isEqualTo:_height]) {
|
||||
if ([height isEqualTo:_filterRegion.height]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_height = height;
|
||||
[_filterRegion setHeight:height];
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
|
||||
@@ -3,15 +3,16 @@
|
||||
|
||||
@interface RNSVGFilterPrimitive : RNSVGNode
|
||||
|
||||
@property (nonatomic, strong) RNSVGLength *x;
|
||||
@property (nonatomic, strong) RNSVGLength *y;
|
||||
@property (nonatomic, strong) RNSVGLength *width;
|
||||
@property (nonatomic, strong) RNSVGLength *height;
|
||||
@property (nonatomic, strong) NSString *result;
|
||||
@property (nonatomic, strong) RNSVGFilterRegion *filterSubregion;
|
||||
|
||||
- (CIImage *)applyFilter:(NSMutableDictionary<NSString *, CIImage *> *)results previousFilterResult:(CIImage *)previous;
|
||||
- (CIImage *)applyFilter:(NSMutableDictionary<NSString *, CIImage *> *)results
|
||||
previousFilterResult:(CIImage *)previous
|
||||
ctm:(CGAffineTransform)ctm;
|
||||
- (void)setX:(RNSVGLength *)x;
|
||||
- (void)setY:(RNSVGLength *)y;
|
||||
- (void)setWidth:(RNSVGLength *)width;
|
||||
- (void)setHeight:(RNSVGLength *)height;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#import <RNSVGFilterPrimitive.h>
|
||||
#import <RNSVGNode.h>
|
||||
#import "RNSVGFilter.h"
|
||||
|
||||
#ifdef RCT_NEW_ARCH_ENABLED
|
||||
#import <React/RCTConversions.h>
|
||||
@@ -15,14 +16,20 @@
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
_x = nil;
|
||||
_y = nil;
|
||||
_width = nil;
|
||||
_height = nil;
|
||||
_filterSubregion = nil;
|
||||
_result = nil;
|
||||
}
|
||||
#endif // RCT_NEW_ARCH_ENABLED
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_filterSubregion = [[RNSVGFilterRegion alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
return nil;
|
||||
@@ -40,41 +47,41 @@
|
||||
|
||||
- (void)setX:(RNSVGLength *)x
|
||||
{
|
||||
if ([x isEqualTo:_x]) {
|
||||
if ([x isEqualTo:_filterSubregion.x]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_x = x;
|
||||
[_filterSubregion setX:x];
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setY:(RNSVGLength *)y
|
||||
{
|
||||
if ([y isEqualTo:_y]) {
|
||||
if ([y isEqualTo:_filterSubregion.y]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_y = y;
|
||||
[_filterSubregion setY:y];
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setWidth:(RNSVGLength *)width
|
||||
{
|
||||
if ([width isEqualTo:_width]) {
|
||||
if ([width isEqualTo:_filterSubregion.width]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_width = width;
|
||||
[_filterSubregion setWidth:width];
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setHeight:(RNSVGLength *)height
|
||||
{
|
||||
if ([height isEqualTo:_height]) {
|
||||
if ([height isEqualTo:_filterSubregion.height]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_height = height;
|
||||
[_filterSubregion setHeight:height];
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
@property (nonatomic, strong) RNSVGLength *height;
|
||||
|
||||
+ (instancetype)regionWithX:(RNSVGLength *)x y:(RNSVGLength *)y width:(RNSVGLength *)width height:(RNSVGLength *)height;
|
||||
- (CGRect)getCropRect:(RNSVGNode *)node units:(RNSVGUnits)units renderableBounds:(CGRect)renderableBounds;
|
||||
- (CGRect)getCropRect:(RNSVGNode *)node units:(RNSVGUnits)units bounds:(CGRect)bounds;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -5,12 +5,6 @@
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_x = [RNSVGLength lengthWithNumber:0];
|
||||
_y = [RNSVGLength lengthWithNumber:0];
|
||||
_width = [RNSVGLength lengthWithNumber:0];
|
||||
_height = [RNSVGLength lengthWithNumber:0];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -44,15 +38,15 @@
|
||||
_height = height;
|
||||
}
|
||||
|
||||
- (CGRect)getCropRect:(RNSVGNode *)node units:(RNSVGUnits)units renderableBounds:(CGRect)renderableBounds
|
||||
- (CGRect)getCropRect:(RNSVGNode *)node units:(RNSVGUnits)units bounds:(CGRect)bounds
|
||||
{
|
||||
CGFloat x, y, width, height;
|
||||
if (units == kRNSVGUnitsObjectBoundingBox) {
|
||||
x = [node relativeOnFraction:self.x relative:renderableBounds.size.width];
|
||||
y = [node relativeOnFraction:self.y relative:renderableBounds.size.height];
|
||||
width = [node relativeOnFraction:self.width relative:renderableBounds.size.width];
|
||||
height = [node relativeOnFraction:self.height relative:renderableBounds.size.height];
|
||||
return CGRectMake(renderableBounds.origin.x + x, renderableBounds.origin.y + y, width, height);
|
||||
x = [node relativeOnFraction:self.x relative:bounds.size.width];
|
||||
y = [node relativeOnFraction:self.y relative:bounds.size.height];
|
||||
width = [node relativeOnFraction:self.width relative:bounds.size.width];
|
||||
height = [node relativeOnFraction:self.height relative:bounds.size.height];
|
||||
return CGRectMake(bounds.origin.x + x, bounds.origin.y + y, width, height);
|
||||
} else { // kRNSVGUnitsUserSpaceOnUse
|
||||
x = [node relativeOnWidth:self.x];
|
||||
y = [node relativeOnHeight:self.y];
|
||||
|
||||
@@ -141,4 +141,11 @@ extern CGFloat const RNSVG_DEFAULT_FONT_SIZE;
|
||||
|
||||
- (void)clearPath;
|
||||
|
||||
/**
|
||||
* get canvas dimensions
|
||||
*/
|
||||
- (CGFloat)getCanvasWidth;
|
||||
|
||||
- (CGFloat)getCanvasHeight;
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user