Skip to content

Commit

Permalink
feat: implement filter region lengths directly on RNSVGFilterRegion (#…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
jakex7 authored Oct 14, 2024
1 parent 2b905c4 commit 3aae632
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 77 deletions.
12 changes: 6 additions & 6 deletions android/src/main/java/com/horcrux/svg/FilterPrimitiveView.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,30 @@
@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();
}

Expand Down
26 changes: 13 additions & 13 deletions android/src/main/java/com/horcrux/svg/FilterRegion.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,24 @@ public void setHeight(Dynamic height) {
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));
}
}
}
12 changes: 9 additions & 3 deletions android/src/main/java/com/horcrux/svg/FilterView.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ public void setPrimitiveUnits(String primitiveUnits) {
invalidate();
}

public FilterRegion getFilterRegion() {
return mFilterRegion;
}

@Override
void saveDefinition() {
if (mName != null) {
Expand All @@ -75,6 +79,8 @@ public Bitmap applyFilter(Bitmap source, Bitmap background, RectF renderableBoun
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++) {
Expand All @@ -83,7 +89,7 @@ public Bitmap applyFilter(Bitmap source, Bitmap background, RectF renderableBoun
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);
Expand All @@ -98,8 +104,8 @@ public Bitmap applyFilter(Bitmap source, Bitmap background, RectF renderableBoun

// 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;
}
}
9 changes: 5 additions & 4 deletions apple/Filters/RNSVGFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
36 changes: 14 additions & 22 deletions apple/Filters/RNSVGFilter.mm
Original file line number Diff line number Diff line change
Expand Up @@ -70,19 +70,17 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &
- (void)prepareForRecycle
{
[super prepareForRecycle];
_x = nil;
_y = nil;
_width = nil;
_height = nil;
_filterUnits = kRNSVGUnitsObjectBoundingBox;
_primitiveUnits = kRNSVGUnitsUserSpaceOnUse;
_filterRegion = nil;
}
#endif // RCT_NEW_ARCH_ENABLED

- (id)init
{
if (self = [super init]) {
resultsMap = [NSMutableDictionary dictionary];
_filterRegion = [[RNSVGFilterRegion alloc] init];
}
return self;
}
Expand Down Expand Up @@ -112,12 +110,9 @@ - (CIImage *)applyFilter:(CIImage *)img
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"];
Expand All @@ -136,10 +131,7 @@ - (CIImage *)applyFilter:(CIImage *)img
}
}

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"];
Expand Down Expand Up @@ -212,41 +204,41 @@ - (void)parseReference

- (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];
}

Expand Down
9 changes: 5 additions & 4 deletions apple/Filters/RNSVGFilterPrimitive.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
31 changes: 19 additions & 12 deletions apple/Filters/RNSVGFilterPrimitive.mm
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#import <RNSVGFilterPrimitive.h>
#import <RNSVGNode.h>
#import "RNSVGFilter.h"

#ifdef RCT_NEW_ARCH_ENABLED
#import <React/RCTConversions.h>
Expand All @@ -15,14 +16,20 @@ @implementation RNSVGFilterPrimitive
- (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;
Expand All @@ -40,41 +47,41 @@ - (void)invalidate

- (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];
}

Expand Down
2 changes: 1 addition & 1 deletion apple/Filters/RNSVGFilterRegion.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
18 changes: 6 additions & 12 deletions apple/Filters/RNSVGFilterRegion.mm
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@ @implementation RNSVGFilterRegion
- (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;
}

Expand Down Expand Up @@ -44,15 +38,15 @@ - (void)setHeight:(RNSVGLength *)height
_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];
Expand Down
7 changes: 7 additions & 0 deletions apple/RNSVGNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,11 @@ extern CGFloat const RNSVG_DEFAULT_FONT_SIZE;

- (void)clearPath;

/**
* get canvas dimensions
*/
- (CGFloat)getCanvasWidth;

- (CGFloat)getCanvasHeight;

@end

0 comments on commit 3aae632

Please sign in to comment.