Skip to content

Commit

Permalink
Support PlatformColor (#1561)
Browse files Browse the repository at this point in the history
Since version 0.63 React Native has supported PlatformColor structs that among other features has dark mode support built in. Using those values with RNSVG today throws a warning "[Object object]" is not a valid color or brush and makes the path have a clear color.

This PR adds support for PlatformColor by utilizing the color parsing code shipped with React Native itself. In order to support the dynamic properties I had to retain the reference as a UIColor and convert it to CGColorRef as it's read. This might have a performance penalty.

The Android implementation doesn't yet have support for dynamic values (EDIT: react-native itself doesn't support it, so we're good there I think). Since it relies on the ColorPropConverter class, I think it means minimum supported version of RN will be raised to 0.63.
  • Loading branch information
oblador authored Feb 23, 2022
1 parent 8681303 commit d35878b
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 16 deletions.
8 changes: 7 additions & 1 deletion android/src/main/java/com/horcrux/svg/RenderableView.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableType;
import com.facebook.react.bridge.ColorPropConverter;
import com.facebook.react.uimanager.PointerEvents;
import com.facebook.react.uimanager.annotations.ReactProp;

Expand Down Expand Up @@ -474,7 +475,12 @@ private void setupPaint(Paint paint, float opacity, ReadableArray colors) {
switch (colorType) {
case 0:
if (colors.size() == 2) {
int color = colors.getInt(1);
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));
Expand Down
19 changes: 10 additions & 9 deletions apple/Brushes/RNSVGSolidColorBrush.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,48 +13,49 @@

@implementation RNSVGSolidColorBrush
{
CGColorRef _color;
UIColor *_color;
}

- (instancetype)initWithArray:(NSArray<RNSVGLength *> *)array
{
if ((self = [super initWithArray:array])) {
_color = CGColorRetain([RCTConvert RNSVGCGColor:array offset:1]);
_color = [RCTConvert RNSVGUIColor:array offset:1];
}
return self;
}

- (instancetype)initWithNumber:(NSNumber *)number
{
if ((self = [super init])) {
_color = CGColorRetain([RCTConvert CGColor:number]);
_color = [RCTConvert UIColor:number];
}
return self;
}

- (void)dealloc
{
CGColorRelease(_color);
_color = nil;
}

- (CGColorRef)getColorWithOpacity:(CGFloat)opacity
{
return CGColorCreateCopyWithAlpha(_color, opacity * CGColorGetAlpha(_color));
CGColorRef baseColor = _color.CGColor;
CGColorRef color = CGColorCreateCopyWithAlpha(baseColor, opacity * CGColorGetAlpha(baseColor));
CGColorRelease(baseColor);
return color;
}

- (BOOL)applyFillColor:(CGContextRef)context opacity:(CGFloat)opacity
{
CGColorRef color = CGColorCreateCopyWithAlpha(_color, opacity * CGColorGetAlpha(_color));
CGColorRef color = [self getColorWithOpacity:opacity];
CGContextSetFillColorWithColor(context, color);
CGColorRelease(color);
return YES;
}

- (BOOL)applyStrokeColor:(CGContextRef)context opacity:(CGFloat)opacity
{
CGColorRef color = CGColorCreateCopyWithAlpha(_color, opacity * CGColorGetAlpha(_color));
CGColorRef color = [self getColorWithOpacity:opacity];
CGContextSetStrokeColorWithColor(context, color);
CGColorRelease(color);
return YES;
}

Expand Down
2 changes: 1 addition & 1 deletion apple/Utils/RCTConvert+RNSVG.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
+ (RNSVGBrush *)RNSVGBrush:(id)json;
+ (RNSVGPathParser *)RNSVGCGPath:(NSString *)d;
+ (CGRect)RNSVGCGRect:(id)json offset:(NSUInteger)offset;
+ (CGColorRef)RNSVGCGColor:(id)json offset:(NSUInteger)offset;
+ (UIColor *)RNSVGUIColor:(id)json offset:(NSUInteger)offset;
+ (CGGradientRef)RNSVGCGGradient:(id)json;

@end
6 changes: 3 additions & 3 deletions apple/Utils/RCTConvert+RNSVG.m
Original file line number Diff line number Diff line change
Expand Up @@ -130,17 +130,17 @@ + (CGRect)RNSVGCGRect:(id)json offset:(NSUInteger)offset
};
}

+ (CGColorRef)RNSVGCGColor:(id)json offset:(NSUInteger)offset
+ (UIColor *)RNSVGUIColor:(id)json offset:(NSUInteger)offset
{
NSArray *arr = [self NSArray:json];
if (arr.count == offset + 1) {
return [self CGColor:[arr objectAtIndex:offset]];
return [self UIColor:[arr objectAtIndex:offset]];
}
if (arr.count < offset + 4) {
RCTLogError(@"Too few elements in array (expected at least %zd): %@", (ssize_t)(4 + offset), arr);
return nil;
}
return [self CGColor:[arr subarrayWithRange:(NSRange){offset, 4}]];
return [self UIColor:[arr subarrayWithRange:(NSRange){offset, 4}]];
}

+ (CGGradientRef)RNSVGCGGradient:(id)json
Expand Down
4 changes: 2 additions & 2 deletions src/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';
import * as ReactNative from 'react-native';
import { GestureResponderEvent, TransformsStyle } from 'react-native';
import { GestureResponderEvent, TransformsStyle, OpaqueColorValue } from 'react-native';

// Common props
export type NumberProp = string | number;
Expand Down Expand Up @@ -101,7 +101,7 @@ export type rgbaArray = ReadonlyArray<number>;
// int32ARGBColor = 0xaarrggbb
export type int32ARGBColor = number;

export type Color = int32ARGBColor | rgbaArray | string;
export type Color = int32ARGBColor | rgbaArray | OpaqueColorValue | string;

export interface FillProps {
fill?: Color;
Expand Down
10 changes: 10 additions & 0 deletions src/lib/extract/extractBrush.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ export default function extractBrush(color?: Color) {
return int32ARGBColor;
}

// iOS PlatformColor
if ('semantic' in color) {
return [0, color];
}

// Android PlatformColor
if ('resource_paths' in color) {
return [0, color];
}

console.warn(`"${color}" is not a valid color or brush`);
return null;
}

0 comments on commit d35878b

Please sign in to comment.