mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-05 22:56:11 +00:00
feat: filters support FeColorMatrix and FilterImage (#2316)
# Summary Introducing the long-awaited **Filters** in `react-native-svg` 🎉 ### Motivation This PR is the beginning of bringing support of SVG Filters into `react-native-svg`. * **related issues**: This PR series will address the following issues: #150, #176, #635, #883, #994, #996, #1216 * **feature overview**: This PR is a boilerplate for Filters * introducing `Filter` component and `FeColorMatrix` as a start. * It also introduces a new subdirectory called `react-native-svg/filter-image` with a `FilterImage` component. # Usage ## Filter and Fe... Filters are compatible with the web familiar standard, so most things should be compatible out-of-the-box and changes will be limited to using a capital letter as it's component. ### Example ```tsx import React from 'react'; import { FeColorMatrix, Filter, Rect, Svg } from 'react-native-svg'; export default () => { return ( <Svg height="300" width="300"> <Filter id="myFilter"> <FeColorMatrix type="saturate" values="0.2" /> </Filter> <Rect x="0" y="0" width="300" height="300" fill="red" filter="url(#myFilter)" /> </Svg> ); }; ```  ## Filter Image `FilterImage` is a new component that is not strictly related to SVG. Its behavior should be the same as a regular `Image` component from React Native with one exception - the additional prop `filters`, which accepts an array of filters to apply to the image. ### Example ```tsx import React from 'react'; import { StyleSheet } from 'react-native'; import { FilterImage } from 'react-native-svg/filter-image'; const myImage = require('./myImage.jpg'); export default () => { return ( <FilterImage style={styles.image} source={myImage} filters={[ { name: 'colorMatrix', type: 'saturate', values: 0.2 }, { name: 'colorMatrix', type: 'matrix', values: [ 0.2, 0.2, 0.2, 0, 0, 0.2, 0.2, 0.2, 0, 0, 0.2, 0.2, 0.2, 0, 0, 0, 0, 0, 1, 0, ], }, ]} /> ); }; const styles = StyleSheet.create({ image: { width: 200, height: 200, }, }); ```  ## Test Plan **Example App**: Updated the example app with various filter effects, showcasing real-world usage. ## Compatibility | OS | Implemented | | ------- | :---------: | | iOS | ✅ | | Android | ✅ | ## Checklist - [x] I have tested this on a device and a simulator - [x] I added documentation in `README.md` and `USAGE.md` - [x] I updated the typed files (typescript)
This commit is contained in:
@@ -7,7 +7,13 @@ module.exports = {
|
||||
'prettier',
|
||||
'plugin:import/typescript',
|
||||
],
|
||||
plugins: ['react', 'react-native', 'import', '@typescript-eslint', 'react-hooks'],
|
||||
plugins: [
|
||||
'react',
|
||||
'react-native',
|
||||
'import',
|
||||
'@typescript-eslint',
|
||||
'react-hooks',
|
||||
],
|
||||
env: {
|
||||
'react-native/react-native': true,
|
||||
},
|
||||
@@ -15,6 +21,7 @@ module.exports = {
|
||||
'import/core-modules': [
|
||||
'react-native-svg',
|
||||
'react-native-svg/css',
|
||||
'react-native-svg/filter-image',
|
||||
],
|
||||
'import/resolver': {
|
||||
'babel-module': {
|
||||
|
||||
@@ -126,10 +126,6 @@ If you suspect that you've found a spec conformance bug, then you can test using
|
||||
|
||||
To check how to use the library, see [USAGE.md](https://github.com/react-native-svg/react-native-svg/blob/main/USAGE.md)
|
||||
|
||||
## TODO:
|
||||
|
||||
1. Filters ([connected PR](https://github.com/react-native-svg/react-native-svg/pull/896))
|
||||
|
||||
## Known issues:
|
||||
|
||||
1. Unable to apply focus point of RadialGradient on Android.
|
||||
|
||||
81
USAGE.md
81
USAGE.md
@@ -1252,3 +1252,84 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Filters
|
||||
|
||||
Filter effects are a way of processing an element’s rendering before it is displayed in the document. Typically, rendering an element via CSS or SVG can conceptually be described as if the element, including its children, are drawn into a buffer (such as a raster image) and then that buffer is composited into the elements parent. Filters apply an effect before the compositing stage. Examples of such effects are blurring, changing color intensity and warping the image.
|
||||
|
||||
Currently supported\* filters are:
|
||||
|
||||
- FeColorMatrix
|
||||
|
||||
\*_More filters are coming soon_
|
||||
|
||||
Exmaple use of filters:
|
||||
|
||||
```jsx
|
||||
import React from 'react';
|
||||
import { FeColorMatrix, Filter, Rect, Svg } from 'react-native-svg';
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<Svg height="300" width="300">
|
||||
<Filter id="myFilter">
|
||||
<FeColorMatrix type="saturate" values="0.2" />
|
||||
</Filter>
|
||||
<Rect
|
||||
x="0"
|
||||
y="0"
|
||||
width="300"
|
||||
height="300"
|
||||
fill="red"
|
||||
filter="url(#myFilter)"
|
||||
/>
|
||||
</Svg>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||

|
||||
|
||||
More info: <https://www.w3.org/TR/SVG11/filters.html>
|
||||
|
||||
## FilterImage
|
||||
|
||||
`FilterImage` is a new component that is not strictly related to SVG. Its behavior should be the same as a regular `Image` component from React Native with one exception - the additional prop `filters`, which accepts an array of filters to apply to the image.
|
||||
|
||||
### Example
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { FilterImage } from 'react-native-svg/filter-image';
|
||||
|
||||
const myImage = require('./myImage.jpg');
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<FilterImage
|
||||
style={styles.image}
|
||||
source={myImage}
|
||||
filters={[
|
||||
{ name: 'colorMatrix', type: 'saturate', values: 0.2 },
|
||||
{
|
||||
name: 'colorMatrix',
|
||||
type: 'matrix',
|
||||
values: [
|
||||
0.2, 0.2, 0.2, 0, 0, 0.2, 0.2, 0.2, 0, 0, 0.2, 0.2, 0.2, 0, 0, 0, 0,
|
||||
0, 1, 0,
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
const styles = StyleSheet.create({
|
||||
image: {
|
||||
width: 200,
|
||||
height: 200,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||

|
||||
|
||||
99
android/src/main/java/com/horcrux/svg/FeColorMatrixView.java
Normal file
99
android/src/main/java/com/horcrux/svg/FeColorMatrixView.java
Normal file
@@ -0,0 +1,99 @@
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.ColorMatrix;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import java.util.HashMap;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class FeColorMatrixView extends FilterPrimitiveView {
|
||||
String mIn1;
|
||||
FilterProperties.FeColorMatrixType mType;
|
||||
ReadableArray mValues;
|
||||
|
||||
public FeColorMatrixView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
public void setIn1(String in1) {
|
||||
this.mIn1 = in1;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.mType = FilterProperties.FeColorMatrixType.getEnum(type);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setValues(ReadableArray values) {
|
||||
this.mValues = values;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bitmap applyFilter(HashMap<String, Bitmap> resultsMap, Bitmap prevResult) {
|
||||
Bitmap source = getSource(resultsMap, prevResult, this.mIn1);
|
||||
|
||||
ColorMatrix colorMatrix = new ColorMatrix();
|
||||
switch (this.mType) {
|
||||
case MATRIX:
|
||||
if (this.mValues.size() < 20) return source;
|
||||
|
||||
float[] rawMatrix = new float[mValues.size()];
|
||||
|
||||
for (int i = 0; i < this.mValues.size(); i++) {
|
||||
rawMatrix[i] = (float) this.mValues.getDouble(i);
|
||||
}
|
||||
|
||||
colorMatrix.set(rawMatrix);
|
||||
break;
|
||||
case SATURATE:
|
||||
if (this.mValues.size() != 1) return source;
|
||||
|
||||
colorMatrix.setSaturation((float) this.mValues.getDouble(0));
|
||||
break;
|
||||
case HUE_ROTATE:
|
||||
if (this.mValues.size() != 1) return source;
|
||||
|
||||
float hue = (float) this.mValues.getDouble(0);
|
||||
float cosHue = (float) Math.cos(hue * Math.PI / 180);
|
||||
float sinHue = (float) Math.sin(hue * Math.PI / 180);
|
||||
|
||||
colorMatrix.set(
|
||||
new float[] {
|
||||
0.213f + cosHue * 0.787f - sinHue * 0.213f, // 0
|
||||
0.715f - cosHue * 0.715f - sinHue * 0.715f, // 1
|
||||
0.072f - cosHue * 0.072f + sinHue * 0.928f, // 2
|
||||
0, // 3
|
||||
0, // 4
|
||||
0.213f - cosHue * 0.213f + sinHue * 0.143f, // 5
|
||||
0.715f + cosHue * 0.285f + sinHue * 0.140f, // 6
|
||||
0.072f - cosHue * 0.072f - sinHue * 0.283f, // 7
|
||||
0, // 8
|
||||
0, // 9
|
||||
0.213f - cosHue * 0.213f - sinHue * 0.787f, // 10
|
||||
0.715f - cosHue * 0.715f + sinHue * 0.715f, // 11
|
||||
0.072f + cosHue * 0.928f + sinHue * 0.072f, // 12
|
||||
0, // 13
|
||||
0, // 14
|
||||
0, // 15
|
||||
0, // 16
|
||||
0, // 17
|
||||
1, // 18
|
||||
0, // 19
|
||||
});
|
||||
break;
|
||||
case LUMINANCE_TO_ALPHA:
|
||||
colorMatrix.set(
|
||||
new float[] {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.2125f, 0.7154f, 0.0721f, 0, 0, 0, 0, 0,
|
||||
0, 1
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
return FilterUtils.getBitmapWithColorMatrix(colorMatrix, source);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Bitmap;
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import java.util.HashMap;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class FilterPrimitiveView extends DefinitionView {
|
||||
SVGLength mX;
|
||||
SVGLength mY;
|
||||
SVGLength mW;
|
||||
SVGLength mH;
|
||||
private String mResult;
|
||||
|
||||
public FilterPrimitiveView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
public void setX(Dynamic x) {
|
||||
mX = SVGLength.from(x);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setY(Dynamic y) {
|
||||
mY = SVGLength.from(y);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setWidth(Dynamic width) {
|
||||
mW = SVGLength.from(width);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setHeight(Dynamic height) {
|
||||
mH = SVGLength.from(height);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setResult(String result) {
|
||||
mResult = result;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public String getResult() {
|
||||
return mResult;
|
||||
}
|
||||
|
||||
protected static Bitmap getSource(
|
||||
HashMap<String, Bitmap> resultsMap, Bitmap prevResult, String in1) {
|
||||
Bitmap sourceFromResults = in1 != null ? resultsMap.get(in1) : null;
|
||||
return sourceFromResults != null ? sourceFromResults : prevResult;
|
||||
}
|
||||
|
||||
public Bitmap applyFilter(HashMap<String, Bitmap> resultsMap, Bitmap prevResult) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
void saveDefinition() {}
|
||||
}
|
||||
110
android/src/main/java/com/horcrux/svg/FilterProperties.java
Normal file
110
android/src/main/java/com/horcrux/svg/FilterProperties.java
Normal file
@@ -0,0 +1,110 @@
|
||||
package com.horcrux.svg;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
class FilterProperties {
|
||||
enum Units {
|
||||
OBJECT_BOUNDING_BOX("objectBoundingBox"),
|
||||
USER_SPACE_ON_USE("userSpaceOnUse"),
|
||||
;
|
||||
|
||||
private final String units;
|
||||
|
||||
Units(String units) {
|
||||
this.units = units;
|
||||
}
|
||||
|
||||
static Units getEnum(String strVal) {
|
||||
if (!unitsToEnum.containsKey(strVal)) {
|
||||
throw new IllegalArgumentException("Unknown 'Unit' Value: " + strVal);
|
||||
}
|
||||
return unitsToEnum.get(strVal);
|
||||
}
|
||||
|
||||
private static final Map<String, Units> unitsToEnum = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (final Units en : Units.values()) {
|
||||
unitsToEnum.put(en.units, en);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return units;
|
||||
}
|
||||
}
|
||||
|
||||
enum EdgeMode {
|
||||
UNKNOWN("unknown"),
|
||||
DUPLICATE("duplicate"),
|
||||
WRAP("wrap"),
|
||||
NONE("none"),
|
||||
;
|
||||
|
||||
private final String edgeMode;
|
||||
|
||||
EdgeMode(String edgeMode) {
|
||||
this.edgeMode = edgeMode;
|
||||
}
|
||||
|
||||
static EdgeMode getEnum(String strVal) {
|
||||
if (!edgeModeToEnum.containsKey(strVal)) {
|
||||
throw new IllegalArgumentException("Unknown 'edgeMode' Value: " + strVal);
|
||||
}
|
||||
return edgeModeToEnum.get(strVal);
|
||||
}
|
||||
|
||||
private static final Map<String, EdgeMode> edgeModeToEnum = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (final EdgeMode en : EdgeMode.values()) {
|
||||
edgeModeToEnum.put(en.edgeMode, en);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return edgeMode;
|
||||
}
|
||||
}
|
||||
|
||||
enum FeColorMatrixType {
|
||||
MATRIX("matrix"),
|
||||
SATURATE("saturate"),
|
||||
HUE_ROTATE("hueRotate"),
|
||||
LUMINANCE_TO_ALPHA("luminanceToAlpha"),
|
||||
;
|
||||
|
||||
private final String type;
|
||||
|
||||
FeColorMatrixType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
static FeColorMatrixType getEnum(String strVal) {
|
||||
if (!typeToEnum.containsKey(strVal)) {
|
||||
throw new IllegalArgumentException("Unknown String Value: " + strVal);
|
||||
}
|
||||
return typeToEnum.get(strVal);
|
||||
}
|
||||
|
||||
private static final Map<String, FeColorMatrixType> typeToEnum = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (final FeColorMatrixType en : FeColorMatrixType.values()) {
|
||||
typeToEnum.put(en.type, en);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
37
android/src/main/java/com/horcrux/svg/FilterUtils.java
Normal file
37
android/src/main/java/com/horcrux/svg/FilterUtils.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorMatrix;
|
||||
import android.graphics.ColorMatrixColorFilter;
|
||||
import android.graphics.Paint;
|
||||
|
||||
public class FilterUtils {
|
||||
|
||||
public static Bitmap getBitmapWithColorMatrix(ColorMatrix colorMatrix, Bitmap sourceBitmap) {
|
||||
Bitmap results =
|
||||
Bitmap.createBitmap(
|
||||
sourceBitmap.getWidth(), sourceBitmap.getHeight(), sourceBitmap.getConfig());
|
||||
|
||||
Canvas canvas = new Canvas(results);
|
||||
|
||||
Paint paint = new Paint();
|
||||
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
|
||||
canvas.drawBitmap(sourceBitmap, 0, 0, paint);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public static Bitmap applySourceAlphaFilter(Bitmap source) {
|
||||
ColorMatrix colorMatrix = new ColorMatrix();
|
||||
colorMatrix.set(
|
||||
new float[] {
|
||||
0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0,
|
||||
0, 0, 0, 1, 0,
|
||||
0, 0, 0, 0, 1
|
||||
});
|
||||
return getBitmapWithColorMatrix(colorMatrix, source);
|
||||
}
|
||||
}
|
||||
113
android/src/main/java/com/horcrux/svg/FilterView.java
Normal file
113
android/src/main/java/com/horcrux/svg/FilterView.java
Normal file
@@ -0,0 +1,113 @@
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import java.util.HashMap;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class FilterView extends DefinitionView {
|
||||
private final HashMap<String, Bitmap> mResultsMap = new HashMap<>();
|
||||
|
||||
SVGLength mX;
|
||||
SVGLength mY;
|
||||
SVGLength mW;
|
||||
SVGLength mH;
|
||||
|
||||
private FilterProperties.Units mFilterUnits;
|
||||
|
||||
@SuppressWarnings({"FieldCanBeLocal", "unused"})
|
||||
private FilterProperties.Units mPrimitiveUnits;
|
||||
|
||||
public FilterView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
public void setX(Dynamic x) {
|
||||
mX = SVGLength.from(x);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setY(Dynamic y) {
|
||||
mY = SVGLength.from(y);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setWidth(Dynamic width) {
|
||||
mW = SVGLength.from(width);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setHeight(Dynamic height) {
|
||||
mH = SVGLength.from(height);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setFilterUnits(String filterUnits) {
|
||||
mFilterUnits = FilterProperties.Units.getEnum(filterUnits);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setPrimitiveUnits(String primitiveUnits) {
|
||||
mPrimitiveUnits = FilterProperties.Units.getEnum(primitiveUnits);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
void saveDefinition() {
|
||||
if (mName != null) {
|
||||
SvgView svg = getSvgView();
|
||||
if (svg != null) {
|
||||
svg.defineFilter(this, mName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Bitmap applyFilter(
|
||||
Bitmap source, Bitmap background, Rect renderableBounds, Rect canvasBounds) {
|
||||
mResultsMap.clear();
|
||||
mResultsMap.put("SourceGraphic", source);
|
||||
mResultsMap.put("SourceAlpha", FilterUtils.applySourceAlphaFilter(source));
|
||||
mResultsMap.put("BackgroundImage", background);
|
||||
mResultsMap.put("BackgroundAlpha", FilterUtils.applySourceAlphaFilter(background));
|
||||
|
||||
Bitmap res = source;
|
||||
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View node = getChildAt(i);
|
||||
if (node instanceof FilterPrimitiveView currentFilter) {
|
||||
res = currentFilter.applyFilter(mResultsMap, res);
|
||||
String resultName = currentFilter.getResult();
|
||||
if (resultName != null) {
|
||||
mResultsMap.put(resultName, res);
|
||||
}
|
||||
} else {
|
||||
Log.e("RNSVG", "Invalid `Filter` child: Filter children can only be `Fe...` components");
|
||||
}
|
||||
}
|
||||
|
||||
// crop Bitmap to filter coordinates
|
||||
int x, y, width, height;
|
||||
if (this.mFilterUnits == FilterProperties.Units.USER_SPACE_ON_USE) {
|
||||
x = (int) this.relativeOn(this.mX, canvasBounds.width());
|
||||
y = (int) this.relativeOn(this.mY, canvasBounds.height());
|
||||
width = (int) this.relativeOn(this.mW, canvasBounds.width());
|
||||
height = (int) this.relativeOn(this.mH, canvasBounds.height());
|
||||
} else { // FilterProperties.Units.OBJECT_BOUNDING_BOX
|
||||
x = (int) this.relativeOnFraction(this.mX, renderableBounds.width());
|
||||
y = (int) this.relativeOnFraction(this.mY, renderableBounds.height());
|
||||
width = (int) this.relativeOnFraction(this.mW, renderableBounds.width());
|
||||
height = (int) this.relativeOnFraction(this.mH, renderableBounds.height());
|
||||
}
|
||||
Rect cropRect = new Rect(x, y, x + width, y + height);
|
||||
Bitmap resultBitmap = Bitmap.createBitmap(res.getWidth(), res.getHeight(), res.getConfig());
|
||||
Canvas canvas = new Canvas(resultBitmap);
|
||||
canvas.drawBitmap(res, cropRect, cropRect, null);
|
||||
return resultBitmap;
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,8 @@ class PathParser {
|
||||
|
||||
if (!has_prev_cmd && first_char != 'M' && first_char != 'm') {
|
||||
// The first segment must be a MoveTo.
|
||||
throw new IllegalArgumentException(String.format("Unexpected character '%c' (i=%d, s=%s)", first_char, i, s));
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Unexpected character '%c' (i=%d, s=%s)", first_char, i, s));
|
||||
}
|
||||
|
||||
// TODO: simplify
|
||||
@@ -75,7 +76,8 @@ class PathParser {
|
||||
} else if (is_number_start(first_char) && has_prev_cmd) {
|
||||
if (prev_cmd == 'Z' || prev_cmd == 'z') {
|
||||
// ClosePath cannot be followed by a number.
|
||||
throw new IllegalArgumentException(String.format("Unexpected number after 'z' (s=%s)", s));
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Unexpected number after 'z' (s=%s)", s));
|
||||
}
|
||||
|
||||
if (prev_cmd == 'M' || prev_cmd == 'm') {
|
||||
@@ -93,7 +95,8 @@ class PathParser {
|
||||
cmd = prev_cmd;
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("Unexpected character '%c' (i=%d, s=%s)", first_char, i, s));
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Unexpected character '%c' (i=%d, s=%s)", first_char, i, s));
|
||||
}
|
||||
|
||||
boolean absolute = is_absolute(cmd);
|
||||
@@ -226,8 +229,8 @@ class PathParser {
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new IllegalArgumentException(String.format("Unexpected comand '%c' (s=%s)", cmd, s));
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Unexpected comand '%c' (s=%s)", cmd, s));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,7 +627,8 @@ class PathParser {
|
||||
c = s.charAt(i);
|
||||
}
|
||||
} else if (c != '.') {
|
||||
throw new IllegalArgumentException(String.format("Invalid number formating character '%c' (i=%d, s=%s)", c, i, s));
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Invalid number formating character '%c' (i=%d, s=%s)", c, i, s));
|
||||
}
|
||||
|
||||
// Consume fraction.
|
||||
@@ -649,7 +653,8 @@ class PathParser {
|
||||
} else if (c >= '0' && c <= '9') {
|
||||
skip_digits();
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("Invalid number formating character '%c' (i=%d, s=%s)", c, i, s));
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Invalid number formating character '%c' (i=%d, s=%s)", c, i, s));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -659,7 +664,8 @@ class PathParser {
|
||||
|
||||
// inf, nan, etc. are an error.
|
||||
if (Float.isInfinite(n) || Float.isNaN(n)) {
|
||||
throw new IllegalArgumentException(String.format("Invalid number '%s' (start=%d, i=%d, s=%s)", num, start, i, s));
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Invalid number '%s' (start=%d, i=%d, s=%s)", num, start, i, s));
|
||||
}
|
||||
|
||||
return n;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorMatrix;
|
||||
import android.graphics.ColorMatrixColorFilter;
|
||||
@@ -94,6 +95,8 @@ public abstract class RenderableView extends VirtualView implements ReactHitSlop
|
||||
private @Nullable ArrayList<String> mPropList;
|
||||
private @Nullable ArrayList<String> mAttributeList;
|
||||
|
||||
@Nullable String mFilter;
|
||||
|
||||
private static final Pattern regex = Pattern.compile("[0-9.-]+");
|
||||
|
||||
@Nullable
|
||||
@@ -326,29 +329,64 @@ public abstract class RenderableView extends VirtualView implements ReactHitSlop
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setFilter(String filter) {
|
||||
mFilter = filter;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void render(Canvas canvas, Paint paint, float opacity) {
|
||||
MaskView mask = null;
|
||||
FilterView filter = null;
|
||||
|
||||
if (mMask != null) {
|
||||
SvgView root = getSvgView();
|
||||
mask = (MaskView) root.getDefinedMask(mMask);
|
||||
}
|
||||
if (mFilter != null) {
|
||||
SvgView root = getSvgView();
|
||||
filter = (FilterView) root.getDefinedFilter(mFilter);
|
||||
}
|
||||
|
||||
if (mask != null) {
|
||||
// https://www.w3.org/TR/SVG11/masking.html
|
||||
// Adding a mask involves several steps
|
||||
// 1. applying luminanceToAlpha to the mask element
|
||||
// 2. merging the alpha channel of the element with the alpha channel from the previous step
|
||||
// 3. applying the result from step 2 to the target element
|
||||
if (mask != null || filter != null) {
|
||||
if (filter != null) {
|
||||
Paint bitmapPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
|
||||
canvas.saveLayer(null, bitmapPaint);
|
||||
|
||||
canvas.saveLayer(null, paint);
|
||||
draw(canvas, paint, opacity);
|
||||
Rect canvasBounds = this.getSvgView().getCanvasBounds();
|
||||
|
||||
Paint dstInPaint = new Paint();
|
||||
dstInPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
|
||||
// draw element to self bitmap
|
||||
Bitmap elementBitmap =
|
||||
Bitmap.createBitmap(
|
||||
canvasBounds.width(), canvasBounds.height(), Bitmap.Config.ARGB_8888);
|
||||
Canvas elementCanvas = new Canvas(elementBitmap);
|
||||
|
||||
// prepare step 3 - combined layer
|
||||
canvas.saveLayer(null, dstInPaint);
|
||||
draw(elementCanvas, paint, opacity);
|
||||
|
||||
// apply filters
|
||||
Bitmap backgroundBitmap = this.getSvgView().getCurrentBitmap();
|
||||
elementBitmap =
|
||||
filter.applyFilter(
|
||||
elementBitmap, backgroundBitmap, elementCanvas.getClipBounds(), canvasBounds);
|
||||
|
||||
// draw bitmap to canvas
|
||||
canvas.drawBitmap(elementBitmap, 0, 0, bitmapPaint);
|
||||
} else {
|
||||
canvas.saveLayer(null, paint);
|
||||
draw(canvas, paint, opacity);
|
||||
}
|
||||
|
||||
if (mask != null) {
|
||||
// https://www.w3.org/TR/SVG11/masking.html
|
||||
// Adding a mask involves several steps
|
||||
// 1. applying luminanceToAlpha to the mask element
|
||||
// 2. merging the alpha channel of the element with the alpha channel from the previous step
|
||||
// 3. applying the result from step 2 to the target element
|
||||
|
||||
Paint dstInPaint = new Paint();
|
||||
dstInPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
|
||||
|
||||
// prepare step 3 - combined layer
|
||||
canvas.saveLayer(null, dstInPaint);
|
||||
|
||||
if (mask.getMaskType() == MaskView.MaskType.LUMINANCE) {
|
||||
// step 1 - luminance layer
|
||||
@@ -366,32 +404,32 @@ public abstract class RenderableView extends VirtualView implements ReactHitSlop
|
||||
canvas.saveLayer(null, paint);
|
||||
}
|
||||
|
||||
// calculate mask bounds
|
||||
float maskX = (float) relativeOnWidth(mask.mX);
|
||||
float maskY = (float) relativeOnHeight(mask.mY);
|
||||
float maskWidth = (float) relativeOnWidth(mask.mW);
|
||||
float maskHeight = (float) relativeOnHeight(mask.mH);
|
||||
// clip to mask bounds
|
||||
canvas.clipRect(maskX, maskY, maskX + maskWidth, maskY + maskHeight);
|
||||
// calculate mask bounds
|
||||
float maskX = (float) relativeOnWidth(mask.mX);
|
||||
float maskY = (float) relativeOnHeight(mask.mY);
|
||||
float maskWidth = (float) relativeOnWidth(mask.mW);
|
||||
float maskHeight = (float) relativeOnHeight(mask.mH);
|
||||
// clip to mask bounds
|
||||
canvas.clipRect(maskX, maskY, maskX + maskWidth, maskY + maskHeight);
|
||||
|
||||
mask.draw(canvas, paint, 1f);
|
||||
mask.draw(canvas, paint, 1f);
|
||||
|
||||
// close luminance layer
|
||||
canvas.restore();
|
||||
// close luminance layer
|
||||
canvas.restore();
|
||||
|
||||
// step 2 - alpha layer
|
||||
canvas.saveLayer(null, dstInPaint);
|
||||
// clip to mask bounds
|
||||
canvas.clipRect(maskX, maskY, maskX + maskWidth, maskY + maskHeight);
|
||||
// step 2 - alpha layer
|
||||
canvas.saveLayer(null, dstInPaint);
|
||||
// clip to mask bounds
|
||||
canvas.clipRect(maskX, maskY, maskX + maskWidth, maskY + maskHeight);
|
||||
|
||||
mask.draw(canvas, paint, 1f);
|
||||
mask.draw(canvas, paint, 1f);
|
||||
|
||||
// close alpha layer
|
||||
canvas.restore();
|
||||
|
||||
// close combined layer
|
||||
canvas.restore();
|
||||
// close alpha layer
|
||||
canvas.restore();
|
||||
|
||||
// close combined layer
|
||||
canvas.restore();
|
||||
}
|
||||
// close element layer
|
||||
canvas.restore();
|
||||
} else {
|
||||
|
||||
@@ -103,6 +103,10 @@ import com.facebook.react.viewmanagers.RNSVGDefsManagerDelegate;
|
||||
import com.facebook.react.viewmanagers.RNSVGDefsManagerInterface;
|
||||
import com.facebook.react.viewmanagers.RNSVGEllipseManagerDelegate;
|
||||
import com.facebook.react.viewmanagers.RNSVGEllipseManagerInterface;
|
||||
import com.facebook.react.viewmanagers.RNSVGFeColorMatrixManagerDelegate;
|
||||
import com.facebook.react.viewmanagers.RNSVGFeColorMatrixManagerInterface;
|
||||
import com.facebook.react.viewmanagers.RNSVGFilterManagerDelegate;
|
||||
import com.facebook.react.viewmanagers.RNSVGFilterManagerInterface;
|
||||
import com.facebook.react.viewmanagers.RNSVGForeignObjectManagerDelegate;
|
||||
import com.facebook.react.viewmanagers.RNSVGForeignObjectManagerInterface;
|
||||
import com.facebook.react.viewmanagers.RNSVGGroupManagerDelegate;
|
||||
@@ -578,6 +582,8 @@ class VirtualViewManager<V extends VirtualView> extends ViewGroupManager<Virtual
|
||||
RNSVGRadialGradient,
|
||||
RNSVGPattern,
|
||||
RNSVGMask,
|
||||
RNSVGFilter,
|
||||
RNSVGFeColorMatrix,
|
||||
RNSVGMarker,
|
||||
RNSVGForeignObject,
|
||||
}
|
||||
@@ -622,6 +628,10 @@ class VirtualViewManager<V extends VirtualView> extends ViewGroupManager<Virtual
|
||||
return new PatternView(reactContext);
|
||||
case RNSVGMask:
|
||||
return new MaskView(reactContext);
|
||||
case RNSVGFilter:
|
||||
return new FilterView(reactContext);
|
||||
case RNSVGFeColorMatrix:
|
||||
return new FeColorMatrixView(reactContext);
|
||||
case RNSVGMarker:
|
||||
return new MarkerView(reactContext);
|
||||
case RNSVGForeignObject:
|
||||
@@ -664,6 +674,11 @@ class RenderableViewManager<T extends RenderableView> extends VirtualViewManager
|
||||
super(svgclass);
|
||||
}
|
||||
|
||||
@ReactProp(name = "filter")
|
||||
public void setFilter(T node, String filter) {
|
||||
node.setFilter(filter);
|
||||
}
|
||||
|
||||
static class GroupViewManagerAbstract<U extends GroupView> extends RenderableViewManager<U> {
|
||||
GroupViewManagerAbstract(SVGClass svgClass) {
|
||||
super(svgClass);
|
||||
@@ -1282,6 +1297,96 @@ class RenderableViewManager<T extends RenderableView> extends VirtualViewManager
|
||||
}
|
||||
}
|
||||
|
||||
static class FilterManager extends VirtualViewManager<FilterView>
|
||||
implements RNSVGFilterManagerInterface<FilterView> {
|
||||
FilterManager() {
|
||||
super(SVGClass.RNSVGFilter);
|
||||
mDelegate = new RNSVGFilterManagerDelegate(this);
|
||||
}
|
||||
|
||||
public static final String REACT_CLASS = "RNSVGFilter";
|
||||
|
||||
@ReactProp(name = "x")
|
||||
public void setX(FilterView node, Dynamic x) {
|
||||
node.setX(x);
|
||||
}
|
||||
|
||||
@ReactProp(name = "y")
|
||||
public void setY(FilterView node, Dynamic y) {
|
||||
node.setY(y);
|
||||
}
|
||||
|
||||
@ReactProp(name = "width")
|
||||
public void setWidth(FilterView node, Dynamic width) {
|
||||
node.setWidth(width);
|
||||
}
|
||||
|
||||
@ReactProp(name = "height")
|
||||
public void setHeight(FilterView node, Dynamic height) {
|
||||
node.setHeight(height);
|
||||
}
|
||||
|
||||
@ReactProp(name = "filterUnits")
|
||||
public void setFilterUnits(FilterView node, String filterUnits) {
|
||||
node.setFilterUnits(filterUnits);
|
||||
}
|
||||
|
||||
@ReactProp(name = "primitiveUnits")
|
||||
public void setPrimitiveUnits(FilterView node, String primitiveUnits) {
|
||||
node.setPrimitiveUnits(primitiveUnits);
|
||||
}
|
||||
}
|
||||
|
||||
static class FeColorMatrixManager extends VirtualViewManager<FeColorMatrixView>
|
||||
implements RNSVGFeColorMatrixManagerInterface<FeColorMatrixView> {
|
||||
FeColorMatrixManager() {
|
||||
super(SVGClass.RNSVGFeColorMatrix);
|
||||
mDelegate = new RNSVGFeColorMatrixManagerDelegate(this);
|
||||
}
|
||||
|
||||
public static final String REACT_CLASS = "RNSVGFeColorMatrix";
|
||||
|
||||
@ReactProp(name = "x")
|
||||
public void setX(FeColorMatrixView node, Dynamic x) {
|
||||
node.setX(x);
|
||||
}
|
||||
|
||||
@ReactProp(name = "y")
|
||||
public void setY(FeColorMatrixView node, Dynamic y) {
|
||||
node.setY(y);
|
||||
}
|
||||
|
||||
@ReactProp(name = "width")
|
||||
public void setWidth(FeColorMatrixView node, Dynamic width) {
|
||||
node.setWidth(width);
|
||||
}
|
||||
|
||||
@ReactProp(name = "height")
|
||||
public void setHeight(FeColorMatrixView node, Dynamic height) {
|
||||
node.setHeight(height);
|
||||
}
|
||||
|
||||
@ReactProp(name = "result")
|
||||
public void setResult(FeColorMatrixView node, String result) {
|
||||
node.setResult(result);
|
||||
}
|
||||
|
||||
@ReactProp(name = "in1")
|
||||
public void setIn1(FeColorMatrixView node, String in1) {
|
||||
node.setIn1(in1);
|
||||
}
|
||||
|
||||
@ReactProp(name = "type")
|
||||
public void setType(FeColorMatrixView node, String type) {
|
||||
node.setType(type);
|
||||
}
|
||||
|
||||
@ReactProp(name = "values")
|
||||
public void setValues(FeColorMatrixView node, @Nullable ReadableArray values) {
|
||||
node.setValues(values);
|
||||
}
|
||||
}
|
||||
|
||||
static class ForeignObjectManager extends GroupViewManagerAbstract<ForeignObjectView>
|
||||
implements RNSVGForeignObjectManagerInterface<ForeignObjectView> {
|
||||
ForeignObjectManager() {
|
||||
|
||||
@@ -205,6 +205,24 @@ public class SvgPackage extends TurboReactPackage implements ViewManagerOnDemand
|
||||
return new MaskManager();
|
||||
}
|
||||
}));
|
||||
specs.put(
|
||||
FilterManager.REACT_CLASS,
|
||||
ModuleSpec.viewManagerSpec(
|
||||
new Provider<NativeModule>() {
|
||||
@Override
|
||||
public NativeModule get() {
|
||||
return new FilterManager();
|
||||
}
|
||||
}));
|
||||
specs.put(
|
||||
FeColorMatrixManager.REACT_CLASS,
|
||||
ModuleSpec.viewManagerSpec(
|
||||
new Provider<NativeModule>() {
|
||||
@Override
|
||||
public NativeModule get() {
|
||||
return new FeColorMatrixManager();
|
||||
}
|
||||
}));
|
||||
specs.put(
|
||||
ForeignObjectManager.REACT_CLASS,
|
||||
ModuleSpec.viewManagerSpec(
|
||||
|
||||
@@ -58,6 +58,7 @@ public class SvgView extends ReactViewGroup implements ReactCompoundView, ReactC
|
||||
}
|
||||
|
||||
private @Nullable Bitmap mBitmap;
|
||||
private @Nullable Bitmap mCurrentBitmap;
|
||||
private boolean mRemovalTransitionStarted;
|
||||
|
||||
public SvgView(ReactContext reactContext) {
|
||||
@@ -161,6 +162,7 @@ public class SvgView extends ReactViewGroup implements ReactCompoundView, ReactC
|
||||
private final Map<String, VirtualView> mDefinedTemplates = new HashMap<>();
|
||||
private final Map<String, VirtualView> mDefinedMarkers = new HashMap<>();
|
||||
private final Map<String, VirtualView> mDefinedMasks = new HashMap<>();
|
||||
private final Map<String, VirtualView> mDefinedFilters = new HashMap<>();
|
||||
private final Map<String, Brush> mDefinedBrushes = new HashMap<>();
|
||||
private Canvas mCanvas;
|
||||
private final float mScale;
|
||||
@@ -264,7 +266,7 @@ public class SvgView extends ReactViewGroup implements ReactCompoundView, ReactC
|
||||
return null;
|
||||
}
|
||||
Bitmap bitmap = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888);
|
||||
|
||||
mCurrentBitmap = bitmap;
|
||||
drawChildren(new Canvas(bitmap));
|
||||
return bitmap;
|
||||
}
|
||||
@@ -423,6 +425,14 @@ public class SvgView extends ReactViewGroup implements ReactCompoundView, ReactC
|
||||
return mDefinedMasks.get(maskRef);
|
||||
}
|
||||
|
||||
void defineFilter(VirtualView filter, String filterRef) {
|
||||
mDefinedFilters.put(filterRef, filter);
|
||||
}
|
||||
|
||||
VirtualView getDefinedFilter(String filterRef) {
|
||||
return mDefinedFilters.get(filterRef);
|
||||
}
|
||||
|
||||
void defineMarker(VirtualView marker, String markerRef) {
|
||||
mDefinedMarkers.put(markerRef, marker);
|
||||
}
|
||||
@@ -430,4 +440,8 @@ public class SvgView extends ReactViewGroup implements ReactCompoundView, ReactC
|
||||
VirtualView getDefinedMarker(String markerRef) {
|
||||
return mDefinedMarkers.get(markerRef);
|
||||
}
|
||||
|
||||
public Bitmap getCurrentBitmap() {
|
||||
return mCurrentBitmap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import android.view.View;
|
||||
import android.view.ViewParent;
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import java.util.ArrayList;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
@@ -418,34 +418,36 @@ public abstract class VirtualView extends ReactViewGroup {
|
||||
return svgView;
|
||||
}
|
||||
|
||||
double relativeOnWidth(SVGLength length) {
|
||||
double relativeOnFraction(SVGLength length, float relative) {
|
||||
SVGLength.UnitType unit = length.unit;
|
||||
if (unit == SVGLength.UnitType.NUMBER) {
|
||||
return length.value * relative;
|
||||
} else if (unit == SVGLength.UnitType.PERCENTAGE) {
|
||||
return length.value / 100 * relative;
|
||||
}
|
||||
return fromRelativeFast(length);
|
||||
}
|
||||
|
||||
double relativeOn(SVGLength length, float relative) {
|
||||
SVGLength.UnitType unit = length.unit;
|
||||
if (unit == SVGLength.UnitType.NUMBER) {
|
||||
return length.value * mScale;
|
||||
} else if (unit == SVGLength.UnitType.PERCENTAGE) {
|
||||
return length.value / 100 * getCanvasWidth();
|
||||
return length.value / 100 * relative;
|
||||
}
|
||||
return fromRelativeFast(length);
|
||||
}
|
||||
|
||||
double relativeOnWidth(SVGLength length) {
|
||||
return relativeOn(length, getCanvasWidth());
|
||||
}
|
||||
|
||||
double relativeOnHeight(SVGLength length) {
|
||||
SVGLength.UnitType unit = length.unit;
|
||||
if (unit == SVGLength.UnitType.NUMBER) {
|
||||
return length.value * mScale;
|
||||
} else if (unit == SVGLength.UnitType.PERCENTAGE) {
|
||||
return length.value / 100 * getCanvasHeight();
|
||||
}
|
||||
return fromRelativeFast(length);
|
||||
return relativeOn(length, getCanvasHeight());
|
||||
}
|
||||
|
||||
double relativeOnOther(SVGLength length) {
|
||||
SVGLength.UnitType unit = length.unit;
|
||||
if (unit == SVGLength.UnitType.NUMBER) {
|
||||
return length.value * mScale;
|
||||
} else if (unit == SVGLength.UnitType.PERCENTAGE) {
|
||||
return length.value / 100 * getCanvasDiagonal();
|
||||
}
|
||||
return fromRelativeFast(length);
|
||||
return relativeOn(length, (float) getCanvasDiagonal());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -99,6 +99,9 @@ public class RNSVGCircleManagerDelegate<T extends View, U extends BaseViewManage
|
||||
case "propList":
|
||||
mViewManager.setPropList(view, (ReadableArray) value);
|
||||
break;
|
||||
case "filter":
|
||||
mViewManager.setFilter(view, value == null ? null : (String) value);
|
||||
break;
|
||||
case "cx":
|
||||
mViewManager.setCx(view, new DynamicFromObject(value));
|
||||
break;
|
||||
|
||||
@@ -41,6 +41,7 @@ public interface RNSVGCircleManagerInterface<T extends View> {
|
||||
void setStrokeMiterlimit(T view, float value);
|
||||
void setVectorEffect(T view, int value);
|
||||
void setPropList(T view, @Nullable ReadableArray value);
|
||||
void setFilter(T view, @Nullable String value);
|
||||
void setCx(T view, Dynamic value);
|
||||
void setCy(T view, Dynamic value);
|
||||
void setR(T view, Dynamic value);
|
||||
|
||||
@@ -99,6 +99,9 @@ public class RNSVGClipPathManagerDelegate<T extends View, U extends BaseViewMana
|
||||
case "propList":
|
||||
mViewManager.setPropList(view, (ReadableArray) value);
|
||||
break;
|
||||
case "filter":
|
||||
mViewManager.setFilter(view, value == null ? null : (String) value);
|
||||
break;
|
||||
case "fontSize":
|
||||
mViewManager.setFontSize(view, new DynamicFromObject(value));
|
||||
break;
|
||||
|
||||
@@ -41,6 +41,7 @@ public interface RNSVGClipPathManagerInterface<T extends View> {
|
||||
void setStrokeMiterlimit(T view, float value);
|
||||
void setVectorEffect(T view, int value);
|
||||
void setPropList(T view, @Nullable ReadableArray value);
|
||||
void setFilter(T view, @Nullable String value);
|
||||
void setFontSize(T view, Dynamic value);
|
||||
void setFontWeight(T view, Dynamic value);
|
||||
void setFont(T view, Dynamic value);
|
||||
|
||||
@@ -99,6 +99,9 @@ public class RNSVGEllipseManagerDelegate<T extends View, U extends BaseViewManag
|
||||
case "propList":
|
||||
mViewManager.setPropList(view, (ReadableArray) value);
|
||||
break;
|
||||
case "filter":
|
||||
mViewManager.setFilter(view, value == null ? null : (String) value);
|
||||
break;
|
||||
case "cx":
|
||||
mViewManager.setCx(view, new DynamicFromObject(value));
|
||||
break;
|
||||
|
||||
@@ -41,6 +41,7 @@ public interface RNSVGEllipseManagerInterface<T extends View> {
|
||||
void setStrokeMiterlimit(T view, float value);
|
||||
void setVectorEffect(T view, int value);
|
||||
void setPropList(T view, @Nullable ReadableArray value);
|
||||
void setFilter(T view, @Nullable String value);
|
||||
void setCx(T view, Dynamic value);
|
||||
void setCy(T view, Dynamic value);
|
||||
void setRx(T view, Dynamic value);
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 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.bridge.ReadableArray;
|
||||
import com.facebook.react.uimanager.BaseViewManagerDelegate;
|
||||
import com.facebook.react.uimanager.BaseViewManagerInterface;
|
||||
|
||||
public class RNSVGFeColorMatrixManagerDelegate<T extends View, U extends BaseViewManagerInterface<T> & RNSVGFeColorMatrixManagerInterface<T>> extends BaseViewManagerDelegate<T, U> {
|
||||
public RNSVGFeColorMatrixManagerDelegate(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 "in1":
|
||||
mViewManager.setIn1(view, value == null ? null : (String) value);
|
||||
break;
|
||||
case "type":
|
||||
mViewManager.setType(view, (String) value);
|
||||
break;
|
||||
case "values":
|
||||
mViewManager.setValues(view, (ReadableArray) value);
|
||||
break;
|
||||
default:
|
||||
super.setProperty(view, propName, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
|
||||
public interface RNSVGFeColorMatrixManagerInterface<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 setIn1(T view, @Nullable String value);
|
||||
void setType(T view, @Nullable String value);
|
||||
void setValues(T view, @Nullable ReadableArray value);
|
||||
}
|
||||
@@ -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 RNSVGFilterManagerDelegate<T extends View, U extends BaseViewManagerInterface<T> & RNSVGFilterManagerInterface<T>> extends BaseViewManagerDelegate<T, U> {
|
||||
public RNSVGFilterManagerDelegate(U viewManager) {
|
||||
super(viewManager);
|
||||
}
|
||||
@Override
|
||||
public void setProperty(T view, String propName, @Nullable Object value) {
|
||||
switch (propName) {
|
||||
case "name":
|
||||
mViewManager.setName(view, value == null ? null : (String) value);
|
||||
break;
|
||||
case "x":
|
||||
mViewManager.setX(view, new DynamicFromObject(value));
|
||||
break;
|
||||
case "y":
|
||||
mViewManager.setY(view, new DynamicFromObject(value));
|
||||
break;
|
||||
case "height":
|
||||
mViewManager.setHeight(view, new DynamicFromObject(value));
|
||||
break;
|
||||
case "width":
|
||||
mViewManager.setWidth(view, new DynamicFromObject(value));
|
||||
break;
|
||||
case "filterUnits":
|
||||
mViewManager.setFilterUnits(view, (String) value);
|
||||
break;
|
||||
case "primitiveUnits":
|
||||
mViewManager.setPrimitiveUnits(view, (String) value);
|
||||
break;
|
||||
default:
|
||||
super.setProperty(view, propName, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 RNSVGFilterManagerInterface<T extends View> {
|
||||
void setName(T view, @Nullable String value);
|
||||
void setX(T view, Dynamic value);
|
||||
void setY(T view, Dynamic value);
|
||||
void setHeight(T view, Dynamic value);
|
||||
void setWidth(T view, Dynamic value);
|
||||
void setFilterUnits(T view, @Nullable String value);
|
||||
void setPrimitiveUnits(T view, @Nullable String value);
|
||||
}
|
||||
@@ -99,6 +99,9 @@ public class RNSVGForeignObjectManagerDelegate<T extends View, U extends BaseVie
|
||||
case "propList":
|
||||
mViewManager.setPropList(view, (ReadableArray) value);
|
||||
break;
|
||||
case "filter":
|
||||
mViewManager.setFilter(view, value == null ? null : (String) value);
|
||||
break;
|
||||
case "fontSize":
|
||||
mViewManager.setFontSize(view, new DynamicFromObject(value));
|
||||
break;
|
||||
|
||||
@@ -41,6 +41,7 @@ public interface RNSVGForeignObjectManagerInterface<T extends View> {
|
||||
void setStrokeMiterlimit(T view, float value);
|
||||
void setVectorEffect(T view, int value);
|
||||
void setPropList(T view, @Nullable ReadableArray value);
|
||||
void setFilter(T view, @Nullable String value);
|
||||
void setFontSize(T view, Dynamic value);
|
||||
void setFontWeight(T view, Dynamic value);
|
||||
void setFont(T view, Dynamic value);
|
||||
|
||||
@@ -99,6 +99,9 @@ public class RNSVGGroupManagerDelegate<T extends View, U extends BaseViewManager
|
||||
case "propList":
|
||||
mViewManager.setPropList(view, (ReadableArray) value);
|
||||
break;
|
||||
case "filter":
|
||||
mViewManager.setFilter(view, value == null ? null : (String) value);
|
||||
break;
|
||||
case "fontSize":
|
||||
mViewManager.setFontSize(view, new DynamicFromObject(value));
|
||||
break;
|
||||
|
||||
@@ -41,6 +41,7 @@ public interface RNSVGGroupManagerInterface<T extends View> {
|
||||
void setStrokeMiterlimit(T view, float value);
|
||||
void setVectorEffect(T view, int value);
|
||||
void setPropList(T view, @Nullable ReadableArray value);
|
||||
void setFilter(T view, @Nullable String value);
|
||||
void setFontSize(T view, Dynamic value);
|
||||
void setFontWeight(T view, Dynamic value);
|
||||
void setFont(T view, Dynamic value);
|
||||
|
||||
@@ -99,6 +99,9 @@ public class RNSVGImageManagerDelegate<T extends View, U extends BaseViewManager
|
||||
case "propList":
|
||||
mViewManager.setPropList(view, (ReadableArray) value);
|
||||
break;
|
||||
case "filter":
|
||||
mViewManager.setFilter(view, value == null ? null : (String) value);
|
||||
break;
|
||||
case "x":
|
||||
mViewManager.setX(view, new DynamicFromObject(value));
|
||||
break;
|
||||
|
||||
@@ -41,6 +41,7 @@ public interface RNSVGImageManagerInterface<T extends View> {
|
||||
void setStrokeMiterlimit(T view, float value);
|
||||
void setVectorEffect(T view, int value);
|
||||
void setPropList(T view, @Nullable ReadableArray value);
|
||||
void setFilter(T view, @Nullable String value);
|
||||
void setX(T view, Dynamic value);
|
||||
void setY(T view, Dynamic value);
|
||||
void setWidth(T view, Dynamic value);
|
||||
|
||||
@@ -99,6 +99,9 @@ public class RNSVGLineManagerDelegate<T extends View, U extends BaseViewManagerI
|
||||
case "propList":
|
||||
mViewManager.setPropList(view, (ReadableArray) value);
|
||||
break;
|
||||
case "filter":
|
||||
mViewManager.setFilter(view, value == null ? null : (String) value);
|
||||
break;
|
||||
case "x1":
|
||||
mViewManager.setX1(view, new DynamicFromObject(value));
|
||||
break;
|
||||
|
||||
@@ -41,6 +41,7 @@ public interface RNSVGLineManagerInterface<T extends View> {
|
||||
void setStrokeMiterlimit(T view, float value);
|
||||
void setVectorEffect(T view, int value);
|
||||
void setPropList(T view, @Nullable ReadableArray value);
|
||||
void setFilter(T view, @Nullable String value);
|
||||
void setX1(T view, Dynamic value);
|
||||
void setY1(T view, Dynamic value);
|
||||
void setX2(T view, Dynamic value);
|
||||
|
||||
@@ -99,6 +99,9 @@ public class RNSVGMarkerManagerDelegate<T extends View, U extends BaseViewManage
|
||||
case "propList":
|
||||
mViewManager.setPropList(view, (ReadableArray) value);
|
||||
break;
|
||||
case "filter":
|
||||
mViewManager.setFilter(view, value == null ? null : (String) value);
|
||||
break;
|
||||
case "fontSize":
|
||||
mViewManager.setFontSize(view, new DynamicFromObject(value));
|
||||
break;
|
||||
|
||||
@@ -41,6 +41,7 @@ public interface RNSVGMarkerManagerInterface<T extends View> {
|
||||
void setStrokeMiterlimit(T view, float value);
|
||||
void setVectorEffect(T view, int value);
|
||||
void setPropList(T view, @Nullable ReadableArray value);
|
||||
void setFilter(T view, @Nullable String value);
|
||||
void setFontSize(T view, Dynamic value);
|
||||
void setFontWeight(T view, Dynamic value);
|
||||
void setFont(T view, Dynamic value);
|
||||
|
||||
@@ -99,6 +99,9 @@ public class RNSVGMaskManagerDelegate<T extends View, U extends BaseViewManagerI
|
||||
case "propList":
|
||||
mViewManager.setPropList(view, (ReadableArray) value);
|
||||
break;
|
||||
case "filter":
|
||||
mViewManager.setFilter(view, value == null ? null : (String) value);
|
||||
break;
|
||||
case "fontSize":
|
||||
mViewManager.setFontSize(view, new DynamicFromObject(value));
|
||||
break;
|
||||
|
||||
@@ -41,6 +41,7 @@ public interface RNSVGMaskManagerInterface<T extends View> {
|
||||
void setStrokeMiterlimit(T view, float value);
|
||||
void setVectorEffect(T view, int value);
|
||||
void setPropList(T view, @Nullable ReadableArray value);
|
||||
void setFilter(T view, @Nullable String value);
|
||||
void setFontSize(T view, Dynamic value);
|
||||
void setFontWeight(T view, Dynamic value);
|
||||
void setFont(T view, Dynamic value);
|
||||
|
||||
@@ -99,6 +99,9 @@ public class RNSVGPathManagerDelegate<T extends View, U extends BaseViewManagerI
|
||||
case "propList":
|
||||
mViewManager.setPropList(view, (ReadableArray) value);
|
||||
break;
|
||||
case "filter":
|
||||
mViewManager.setFilter(view, value == null ? null : (String) value);
|
||||
break;
|
||||
case "d":
|
||||
mViewManager.setD(view, value == null ? null : (String) value);
|
||||
break;
|
||||
|
||||
@@ -41,5 +41,6 @@ public interface RNSVGPathManagerInterface<T extends View> {
|
||||
void setStrokeMiterlimit(T view, float value);
|
||||
void setVectorEffect(T view, int value);
|
||||
void setPropList(T view, @Nullable ReadableArray value);
|
||||
void setFilter(T view, @Nullable String value);
|
||||
void setD(T view, @Nullable String value);
|
||||
}
|
||||
|
||||
@@ -99,6 +99,9 @@ public class RNSVGPatternManagerDelegate<T extends View, U extends BaseViewManag
|
||||
case "propList":
|
||||
mViewManager.setPropList(view, (ReadableArray) value);
|
||||
break;
|
||||
case "filter":
|
||||
mViewManager.setFilter(view, value == null ? null : (String) value);
|
||||
break;
|
||||
case "fontSize":
|
||||
mViewManager.setFontSize(view, new DynamicFromObject(value));
|
||||
break;
|
||||
|
||||
@@ -41,6 +41,7 @@ public interface RNSVGPatternManagerInterface<T extends View> {
|
||||
void setStrokeMiterlimit(T view, float value);
|
||||
void setVectorEffect(T view, int value);
|
||||
void setPropList(T view, @Nullable ReadableArray value);
|
||||
void setFilter(T view, @Nullable String value);
|
||||
void setFontSize(T view, Dynamic value);
|
||||
void setFontWeight(T view, Dynamic value);
|
||||
void setFont(T view, Dynamic value);
|
||||
|
||||
@@ -99,6 +99,9 @@ public class RNSVGRectManagerDelegate<T extends View, U extends BaseViewManagerI
|
||||
case "propList":
|
||||
mViewManager.setPropList(view, (ReadableArray) value);
|
||||
break;
|
||||
case "filter":
|
||||
mViewManager.setFilter(view, value == null ? null : (String) value);
|
||||
break;
|
||||
case "x":
|
||||
mViewManager.setX(view, new DynamicFromObject(value));
|
||||
break;
|
||||
|
||||
@@ -41,6 +41,7 @@ public interface RNSVGRectManagerInterface<T extends View> {
|
||||
void setStrokeMiterlimit(T view, float value);
|
||||
void setVectorEffect(T view, int value);
|
||||
void setPropList(T view, @Nullable ReadableArray value);
|
||||
void setFilter(T view, @Nullable String value);
|
||||
void setX(T view, Dynamic value);
|
||||
void setY(T view, Dynamic value);
|
||||
void setHeight(T view, Dynamic value);
|
||||
|
||||
@@ -99,6 +99,9 @@ public class RNSVGSymbolManagerDelegate<T extends View, U extends BaseViewManage
|
||||
case "propList":
|
||||
mViewManager.setPropList(view, (ReadableArray) value);
|
||||
break;
|
||||
case "filter":
|
||||
mViewManager.setFilter(view, value == null ? null : (String) value);
|
||||
break;
|
||||
case "fontSize":
|
||||
mViewManager.setFontSize(view, new DynamicFromObject(value));
|
||||
break;
|
||||
|
||||
@@ -41,6 +41,7 @@ public interface RNSVGSymbolManagerInterface<T extends View> {
|
||||
void setStrokeMiterlimit(T view, float value);
|
||||
void setVectorEffect(T view, int value);
|
||||
void setPropList(T view, @Nullable ReadableArray value);
|
||||
void setFilter(T view, @Nullable String value);
|
||||
void setFontSize(T view, Dynamic value);
|
||||
void setFontWeight(T view, Dynamic value);
|
||||
void setFont(T view, Dynamic value);
|
||||
|
||||
@@ -99,6 +99,9 @@ public class RNSVGTSpanManagerDelegate<T extends View, U extends BaseViewManager
|
||||
case "propList":
|
||||
mViewManager.setPropList(view, (ReadableArray) value);
|
||||
break;
|
||||
case "filter":
|
||||
mViewManager.setFilter(view, value == null ? null : (String) value);
|
||||
break;
|
||||
case "fontSize":
|
||||
mViewManager.setFontSize(view, new DynamicFromObject(value));
|
||||
break;
|
||||
|
||||
@@ -41,6 +41,7 @@ public interface RNSVGTSpanManagerInterface<T extends View> {
|
||||
void setStrokeMiterlimit(T view, float value);
|
||||
void setVectorEffect(T view, int value);
|
||||
void setPropList(T view, @Nullable ReadableArray value);
|
||||
void setFilter(T view, @Nullable String value);
|
||||
void setFontSize(T view, Dynamic value);
|
||||
void setFontWeight(T view, Dynamic value);
|
||||
void setFont(T view, Dynamic value);
|
||||
|
||||
@@ -99,6 +99,9 @@ public class RNSVGTextManagerDelegate<T extends View, U extends BaseViewManagerI
|
||||
case "propList":
|
||||
mViewManager.setPropList(view, (ReadableArray) value);
|
||||
break;
|
||||
case "filter":
|
||||
mViewManager.setFilter(view, value == null ? null : (String) value);
|
||||
break;
|
||||
case "fontSize":
|
||||
mViewManager.setFontSize(view, new DynamicFromObject(value));
|
||||
break;
|
||||
|
||||
@@ -41,6 +41,7 @@ public interface RNSVGTextManagerInterface<T extends View> {
|
||||
void setStrokeMiterlimit(T view, float value);
|
||||
void setVectorEffect(T view, int value);
|
||||
void setPropList(T view, @Nullable ReadableArray value);
|
||||
void setFilter(T view, @Nullable String value);
|
||||
void setFontSize(T view, Dynamic value);
|
||||
void setFontWeight(T view, Dynamic value);
|
||||
void setFont(T view, Dynamic value);
|
||||
|
||||
@@ -99,6 +99,9 @@ public class RNSVGTextPathManagerDelegate<T extends View, U extends BaseViewMana
|
||||
case "propList":
|
||||
mViewManager.setPropList(view, (ReadableArray) value);
|
||||
break;
|
||||
case "filter":
|
||||
mViewManager.setFilter(view, value == null ? null : (String) value);
|
||||
break;
|
||||
case "fontSize":
|
||||
mViewManager.setFontSize(view, new DynamicFromObject(value));
|
||||
break;
|
||||
|
||||
@@ -41,6 +41,7 @@ public interface RNSVGTextPathManagerInterface<T extends View> {
|
||||
void setStrokeMiterlimit(T view, float value);
|
||||
void setVectorEffect(T view, int value);
|
||||
void setPropList(T view, @Nullable ReadableArray value);
|
||||
void setFilter(T view, @Nullable String value);
|
||||
void setFontSize(T view, Dynamic value);
|
||||
void setFontWeight(T view, Dynamic value);
|
||||
void setFont(T view, Dynamic value);
|
||||
|
||||
@@ -99,6 +99,9 @@ public class RNSVGUseManagerDelegate<T extends View, U extends BaseViewManagerIn
|
||||
case "propList":
|
||||
mViewManager.setPropList(view, (ReadableArray) value);
|
||||
break;
|
||||
case "filter":
|
||||
mViewManager.setFilter(view, value == null ? null : (String) value);
|
||||
break;
|
||||
case "href":
|
||||
mViewManager.setHref(view, value == null ? null : (String) value);
|
||||
break;
|
||||
|
||||
@@ -41,6 +41,7 @@ public interface RNSVGUseManagerInterface<T extends View> {
|
||||
void setStrokeMiterlimit(T view, float value);
|
||||
void setVectorEffect(T view, int value);
|
||||
void setPropList(T view, @Nullable ReadableArray value);
|
||||
void setFilter(T view, @Nullable String value);
|
||||
void setHref(T view, @Nullable String value);
|
||||
void setX(T view, Dynamic value);
|
||||
void setY(T view, Dynamic value);
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
|
||||
/**
|
||||
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
||||
* 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.
|
||||
* <p>Do not edit this file as changes may cause incorrect behavior and will be lost once the code
|
||||
* is regenerated.
|
||||
*
|
||||
* @generated by codegen project: GenerateModuleJavaSpec.js
|
||||
*
|
||||
* @nolint
|
||||
*/
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
@@ -22,7 +20,8 @@ import com.facebook.react.turbomodule.core.interfaces.TurboModule;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public abstract class NativeSvgViewModuleSpec extends ReactContextBaseJavaModule implements TurboModule {
|
||||
public abstract class NativeSvgViewModuleSpec extends ReactContextBaseJavaModule
|
||||
implements TurboModule {
|
||||
public static final String NAME = "RNSVGSvgViewModule";
|
||||
|
||||
public NativeSvgViewModuleSpec(ReactApplicationContext reactContext) {
|
||||
@@ -36,5 +35,6 @@ public abstract class NativeSvgViewModuleSpec extends ReactContextBaseJavaModule
|
||||
|
||||
@ReactMethod
|
||||
@DoNotStrip
|
||||
public abstract void toDataURL(@Nullable Double tag, @Nullable ReadableMap options, @Nullable Callback callback);
|
||||
public abstract void toDataURL(
|
||||
@Nullable Double tag, @Nullable ReadableMap options, @Nullable Callback callback);
|
||||
}
|
||||
|
||||
@@ -63,6 +63,10 @@
|
||||
|
||||
- (RNSVGNode *)getDefinedMask:(NSString *)maskName;
|
||||
|
||||
- (void)defineFilter:(RNSVGNode *)filter filterName:(NSString *)filterName;
|
||||
|
||||
- (RNSVGNode *)getDefinedFilter:(NSString *)filterName;
|
||||
|
||||
- (NSString *)getDataURLWithBounds:(CGRect)bounds;
|
||||
|
||||
- (CGRect)getContextBounds;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
NSMutableDictionary<NSString *, RNSVGPainter *> *_painters;
|
||||
NSMutableDictionary<NSString *, RNSVGNode *> *_markers;
|
||||
NSMutableDictionary<NSString *, RNSVGNode *> *_masks;
|
||||
NSMutableDictionary<NSString *, RNSVGNode *> *_filters;
|
||||
CGAffineTransform _invviewBoxTransform;
|
||||
bool rendered;
|
||||
}
|
||||
@@ -113,6 +114,7 @@ using namespace facebook::react;
|
||||
_painters = nil;
|
||||
_markers = nil;
|
||||
_masks = nil;
|
||||
_filters = nil;
|
||||
_invviewBoxTransform = CGAffineTransformIdentity;
|
||||
rendered = NO;
|
||||
}
|
||||
@@ -436,6 +438,19 @@ using namespace facebook::react;
|
||||
return _masks ? [_masks objectForKey:maskName] : nil;
|
||||
}
|
||||
|
||||
- (void)defineFilter:(RNSVGNode *)filter filterName:(NSString *)filterName
|
||||
{
|
||||
if (!_filters) {
|
||||
_filters = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
[_filters setObject:filter forKey:filterName];
|
||||
}
|
||||
|
||||
- (RNSVGNode *)getDefinedFilter:(NSString *)filterName
|
||||
{
|
||||
return _filters ? [_filters objectForKey:filterName] : nil;
|
||||
}
|
||||
|
||||
- (CGRect)getContextBounds
|
||||
{
|
||||
return CGContextGetClipBoundingBox(UIGraphicsGetCurrentContext());
|
||||
|
||||
7
apple/Filters/RNSVGColorMatrixType.h
Normal file
7
apple/Filters/RNSVGColorMatrixType.h
Normal file
@@ -0,0 +1,7 @@
|
||||
typedef CF_ENUM(int32_t, RNSVGColorMatrixType) {
|
||||
SVG_FECOLORMATRIX_TYPE_UNKNOWN,
|
||||
SVG_FECOLORMATRIX_TYPE_MATRIX,
|
||||
SVG_FECOLORMATRIX_TYPE_SATURATE,
|
||||
SVG_FECOLORMATRIX_TYPE_HUEROTATE,
|
||||
SVG_FECOLORMATRIX_TYPE_LUMINANCETOALPHA
|
||||
};
|
||||
6
apple/Filters/RNSVGEdgeModeTypes.h
Normal file
6
apple/Filters/RNSVGEdgeModeTypes.h
Normal file
@@ -0,0 +1,6 @@
|
||||
typedef CF_ENUM(int32_t, RNSVGEdgeModeTypes) {
|
||||
SVG_EDGEMODE_UNKNOWN,
|
||||
SVG_EDGEMODE_DUPLICATE,
|
||||
SVG_EDGEMODE_WRAP,
|
||||
SVG_EDGEMODE_NONE
|
||||
};
|
||||
10
apple/Filters/RNSVGFeColorMatrix.h
Normal file
10
apple/Filters/RNSVGFeColorMatrix.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#import "RNSVGColorMatrixType.h"
|
||||
#import "RNSVGFilterPrimitive.h"
|
||||
|
||||
@interface RNSVGFeColorMatrix : RNSVGFilterPrimitive
|
||||
|
||||
@property (nonatomic, strong) NSString *in1;
|
||||
@property (nonatomic, assign) RNSVGColorMatrixType type;
|
||||
@property (nonatomic, strong) NSArray<NSNumber *> *values;
|
||||
|
||||
@end
|
||||
174
apple/Filters/RNSVGFeColorMatrix.mm
Normal file
174
apple/Filters/RNSVGFeColorMatrix.mm
Normal file
@@ -0,0 +1,174 @@
|
||||
#import "RNSVGFeColorMatrix.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 RNSVGFeColorMatrix
|
||||
|
||||
#ifdef RCT_NEW_ARCH_ENABLED
|
||||
using namespace facebook::react;
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
static const auto defaultProps = std::make_shared<const RNSVGFeColorMatrixProps>();
|
||||
_props = defaultProps;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - RCTComponentViewProtocol
|
||||
|
||||
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
||||
{
|
||||
return concreteComponentDescriptorProvider<RNSVGFeColorMatrixComponentDescriptor>();
|
||||
}
|
||||
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
const auto &newProps = static_cast<const RNSVGFeColorMatrixProps &>(*props);
|
||||
|
||||
self.in1 = RCTNSStringFromStringNilIfEmpty(newProps.in1);
|
||||
if (newProps.values.size() > 0) {
|
||||
NSMutableArray<NSNumber *> *valuesArray = [NSMutableArray new];
|
||||
for (auto number : newProps.values) {
|
||||
[valuesArray addObject:[NSNumber numberWithFloat:number]];
|
||||
}
|
||||
self.values = valuesArray;
|
||||
}
|
||||
self.type = [RNSVGConvert RNSVGColorMatrixTypeFromCppEquivalent:newProps.type];
|
||||
|
||||
setCommonFilterProps(newProps, self);
|
||||
_props = std::static_pointer_cast<RNSVGFeColorMatrixProps const>(props);
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
_in1 = nil;
|
||||
_values = nil;
|
||||
_type = RNSVGColorMatrixType::SVG_FECOLORMATRIX_TYPE_MATRIX;
|
||||
}
|
||||
#endif // RCT_NEW_ARCH_ENABLED
|
||||
|
||||
- (void)setIn1:(NSString *)in1
|
||||
{
|
||||
if ([in1 isEqualToString:_in1]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_in1 = in1;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setValues:(NSArray<NSNumber *> *)values
|
||||
{
|
||||
if (values == _values) {
|
||||
return;
|
||||
}
|
||||
|
||||
_values = values;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setType:(RNSVGColorMatrixType)type
|
||||
{
|
||||
if (type == _type) {
|
||||
return;
|
||||
}
|
||||
_type = type;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
#define deg2rad(degrees) ((M_PI * degrees) / 180)
|
||||
|
||||
- (CIImage *)applyFilter:(NSMutableDictionary<NSString *, CIImage *> *)results previousFilterResult:(CIImage *)previous
|
||||
{
|
||||
CIImage *inResults = self.in1 ? [results objectForKey:self.in1] : nil;
|
||||
CIImage *inputImage = inResults ? inResults : previous;
|
||||
|
||||
CIFilter *filter = nil;
|
||||
NSArray<NSNumber *> *array = self.values;
|
||||
NSUInteger count = [array count];
|
||||
|
||||
switch (self.type) {
|
||||
case SVG_FECOLORMATRIX_TYPE_UNKNOWN:
|
||||
return nil;
|
||||
case SVG_FECOLORMATRIX_TYPE_MATRIX: {
|
||||
if (count != 20) {
|
||||
return nil;
|
||||
}
|
||||
CGFloat v[20] = {0};
|
||||
for (NSUInteger i = 0; i < count; i++) {
|
||||
v[i] = (CGFloat)[array[i] doubleValue];
|
||||
}
|
||||
filter = [CIFilter filterWithName:@"CIColorMatrix"];
|
||||
[filter setDefaults];
|
||||
[filter setValue:[CIVector vectorWithX:v[0] Y:v[1] Z:v[2] W:v[3]] forKey:@"inputRVector"];
|
||||
[filter setValue:[CIVector vectorWithX:v[5] Y:v[6] Z:v[7] W:v[8]] forKey:@"inputGVector"];
|
||||
[filter setValue:[CIVector vectorWithX:v[10] Y:v[11] Z:v[12] W:v[13]] forKey:@"inputBVector"];
|
||||
[filter setValue:[CIVector vectorWithX:v[15] Y:v[16] Z:v[17] W:v[18]] forKey:@"inputAVector"];
|
||||
[filter setValue:[CIVector vectorWithX:v[4] Y:v[9] Z:v[14] W:v[19]] forKey:@"inputBiasVector"];
|
||||
break;
|
||||
}
|
||||
case SVG_FECOLORMATRIX_TYPE_SATURATE: {
|
||||
if (count != 1) {
|
||||
return nil;
|
||||
}
|
||||
float saturation = [array[0] floatValue];
|
||||
filter = [CIFilter filterWithName:@"CIColorControls"];
|
||||
[filter setDefaults];
|
||||
[filter setValue:[NSNumber numberWithFloat:saturation] forKey:@"inputSaturation"];
|
||||
break;
|
||||
}
|
||||
case SVG_FECOLORMATRIX_TYPE_HUEROTATE: {
|
||||
if (count != 1) {
|
||||
return nil;
|
||||
}
|
||||
double deg = [array[0] doubleValue];
|
||||
filter = [CIFilter filterWithName:@"CIHueAdjust"];
|
||||
[filter setDefaults];
|
||||
float radians = (float)deg2rad(deg);
|
||||
[filter setValue:[NSNumber numberWithFloat:radians] forKey:@"inputAngle"];
|
||||
break;
|
||||
}
|
||||
case SVG_FECOLORMATRIX_TYPE_LUMINANCETOALPHA: {
|
||||
if (count != 0) {
|
||||
return nil;
|
||||
}
|
||||
filter = [CIFilter filterWithName:@"CIColorMatrix"];
|
||||
[filter setDefaults];
|
||||
CGFloat zero[4] = {0, 0, 0, 0};
|
||||
CGFloat alpha[4] = {0.2125, 0.7154, 0.0721, 0};
|
||||
[filter setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputRVector"];
|
||||
[filter setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputGVector"];
|
||||
[filter setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputBVector"];
|
||||
[filter setValue:[CIVector vectorWithValues:alpha count:4] forKey:@"inputAVector"];
|
||||
[filter setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputBiasVector"];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return nil;
|
||||
}
|
||||
|
||||
[filter setValue:inputImage forKey:@"inputImage"];
|
||||
|
||||
return [filter valueForKey:@"outputImage"];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
#ifdef RCT_NEW_ARCH_ENABLED
|
||||
Class<RCTComponentViewProtocol> RNSVGFeColorMatrixCls(void)
|
||||
{
|
||||
return RNSVGFeColorMatrix.class;
|
||||
}
|
||||
#endif // RCT_NEW_ARCH_ENABLED
|
||||
|
||||
@end
|
||||
18
apple/Filters/RNSVGFilter.h
Normal file
18
apple/Filters/RNSVGFilter.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#import "RNSVGNode.h"
|
||||
|
||||
@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;
|
||||
|
||||
- (CIImage *)applyFilter:(CIImage *)img
|
||||
backgroundImg:(CIImage *)backgroundImg
|
||||
renderableBounds:(CGRect)renderableBounds
|
||||
canvasBounds:(CGRect)canvasBounds
|
||||
ctm:(CGAffineTransform)ctm;
|
||||
|
||||
@end
|
||||
232
apple/Filters/RNSVGFilter.mm
Normal file
232
apple/Filters/RNSVGFilter.mm
Normal file
@@ -0,0 +1,232 @@
|
||||
#import "RNSVGFilter.h"
|
||||
#import "RNSVGFilterPrimitive.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 RNSVGFilter {
|
||||
NSMutableDictionary<NSString *, CIImage *> *resultsMap;
|
||||
}
|
||||
|
||||
#ifdef RCT_NEW_ARCH_ENABLED
|
||||
using namespace facebook::react;
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
static const auto defaultProps = std::make_shared<const RNSVGFilterProps>();
|
||||
_props = defaultProps;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - RCTComponentViewProtocol
|
||||
|
||||
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
||||
{
|
||||
return concreteComponentDescriptorProvider<RNSVGFilterComponentDescriptor>();
|
||||
}
|
||||
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
const auto &newProps = static_cast<const RNSVGFilterProps &>(*props);
|
||||
|
||||
self.name = RCTNSStringFromStringNilIfEmpty(newProps.name);
|
||||
id x = RNSVGConvertFollyDynamicToId(newProps.x);
|
||||
if (x != nil) {
|
||||
self.x = [RCTConvert RNSVGLength:x];
|
||||
}
|
||||
id y = RNSVGConvertFollyDynamicToId(newProps.y);
|
||||
if (y != nil) {
|
||||
self.y = [RCTConvert RNSVGLength:y];
|
||||
}
|
||||
id height = RNSVGConvertFollyDynamicToId(newProps.height);
|
||||
if (height != nil) {
|
||||
self.height = [RCTConvert RNSVGLength:height];
|
||||
}
|
||||
id width = RNSVGConvertFollyDynamicToId(newProps.width);
|
||||
if (width != nil) {
|
||||
self.width = [RCTConvert RNSVGLength:width];
|
||||
}
|
||||
self.filterUnits = [RNSVGConvert RNSVGUnitsFromFilterUnitsCppEquivalent:newProps.filterUnits];
|
||||
self.primitiveUnits = [RNSVGConvert RNSVGUnitsFromPrimitiveUnitsCppEquivalent:newProps.primitiveUnits];
|
||||
|
||||
_props = std::static_pointer_cast<RNSVGFilterProps const>(props);
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
_x = nil;
|
||||
_y = nil;
|
||||
_height = nil;
|
||||
_width = nil;
|
||||
_filterUnits = kRNSVGUnitsObjectBoundingBox;
|
||||
_primitiveUnits = kRNSVGUnitsUserSpaceOnUse;
|
||||
}
|
||||
#endif // RCT_NEW_ARCH_ENABLED
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
resultsMap = [NSMutableDictionary dictionary];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CIImage *)applyFilter:(CIImage *)img
|
||||
backgroundImg:(CIImage *)backgroundImg
|
||||
renderableBounds:(CGRect)renderableBounds
|
||||
canvasBounds:(CGRect)canvasBounds
|
||||
ctm:(CGAffineTransform)ctm
|
||||
{
|
||||
[resultsMap removeAllObjects];
|
||||
[resultsMap setObject:img forKey:@"SourceGraphic"];
|
||||
[resultsMap setObject:applySourceAlphaFilter(img) forKey:@"SourceAlpha"];
|
||||
[resultsMap setObject:backgroundImg forKey:@"BackgroundImage"];
|
||||
[resultsMap setObject:applySourceAlphaFilter(backgroundImg) forKey:@"BackgroundAlpha"];
|
||||
|
||||
CIImage *result = img;
|
||||
RNSVGFilterPrimitive *currentFilter;
|
||||
for (RNSVGNode *node in self.subviews) {
|
||||
if ([node isKindOfClass:[RNSVGFilterPrimitive class]]) {
|
||||
currentFilter = (RNSVGFilterPrimitive *)node;
|
||||
result = [currentFilter applyFilter:resultsMap previousFilterResult:result];
|
||||
if (currentFilter.result) {
|
||||
[resultsMap setObject:result forKey:currentFilter.result];
|
||||
}
|
||||
} else {
|
||||
RCTLogError(@"Invalid `Filter` subview: Filter children can only be `Fe...` components");
|
||||
}
|
||||
}
|
||||
|
||||
// Crop results to filter bounds
|
||||
CIFilter *crop = [CIFilter filterWithName:@"CICrop"];
|
||||
[crop setDefaults];
|
||||
[crop setValue:result forKey:@"inputImage"];
|
||||
|
||||
CGFloat scaleX = ctm.a, scaleY = fabs(ctm.d);
|
||||
CGFloat x, y, width, height;
|
||||
if (self.filterUnits == kRNSVGUnitsUserSpaceOnUse) {
|
||||
x = [self relativeOn:self.x relative:canvasBounds.size.width / scaleX];
|
||||
y = [self relativeOn:self.y relative:canvasBounds.size.height / scaleY];
|
||||
width = [self relativeOn:self.width relative:canvasBounds.size.width / scaleX];
|
||||
height = [self relativeOn:self.height relative:canvasBounds.size.height / scaleY];
|
||||
} else { // kRNSVGUnitsObjectBoundingBox
|
||||
x = renderableBounds.origin.x + [self relativeOnFraction:self.x relative:renderableBounds.size.width];
|
||||
y = renderableBounds.origin.y + [self relativeOnFraction:self.y relative:renderableBounds.size.height];
|
||||
width = [self relativeOnFraction:self.width relative:renderableBounds.size.width];
|
||||
height = [self relativeOnFraction:self.height relative:renderableBounds.size.height];
|
||||
}
|
||||
CGRect cropCGRect = CGRectMake(x, y, width, height);
|
||||
cropCGRect = CGRectApplyAffineTransform(cropCGRect, ctm);
|
||||
CIVector *cropRect = [CIVector vectorWithCGRect:cropCGRect];
|
||||
[crop setValue:cropRect forKey:@"inputRectangle"];
|
||||
|
||||
return [crop valueForKey:@"outputImage"];
|
||||
}
|
||||
|
||||
static CIFilter *sourceAlphaFilter()
|
||||
{
|
||||
CIFilter *sourceAlpha = [CIFilter filterWithName:@"CIColorMatrix"];
|
||||
CGFloat zero[4] = {0, 0, 0, 0};
|
||||
[sourceAlpha setDefaults];
|
||||
[sourceAlpha setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputRVector"];
|
||||
[sourceAlpha setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputGVector"];
|
||||
[sourceAlpha setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputBVector"];
|
||||
[sourceAlpha setValue:[CIVector vectorWithX:0.0 Y:0.0 Z:0.0 W:1.0] forKey:@"inputAVector"];
|
||||
[sourceAlpha setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputBiasVector"];
|
||||
return sourceAlpha;
|
||||
}
|
||||
|
||||
static CIImage *applySourceAlphaFilter(CIImage *inputImage)
|
||||
{
|
||||
CIFilter *sourceAlpha = sourceAlphaFilter();
|
||||
[sourceAlpha setValue:inputImage forKey:@"inputImage"];
|
||||
return [sourceAlpha valueForKey:@"outputImage"];
|
||||
}
|
||||
|
||||
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)parseReference
|
||||
{
|
||||
[self.svgView defineFilter:self filterName:self.name];
|
||||
}
|
||||
|
||||
- (void)setX:(RNSVGLength *)x
|
||||
{
|
||||
if ([x isEqualTo:_x]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_x = x;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setY:(RNSVGLength *)y
|
||||
{
|
||||
if ([y isEqualTo:_y]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_y = y;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setWidth:(RNSVGLength *)width
|
||||
{
|
||||
if ([width isEqualTo:_width]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_width = width;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setHeight:(RNSVGLength *)height
|
||||
{
|
||||
if ([height isEqualTo:_height]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_height = height;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setFilterUnits:(RNSVGUnits)filterUnits
|
||||
{
|
||||
if (filterUnits == _filterUnits) {
|
||||
return;
|
||||
}
|
||||
|
||||
_filterUnits = filterUnits;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setPrimitiveUnits:(RNSVGUnits)primitiveUnits
|
||||
{
|
||||
if (primitiveUnits == _primitiveUnits) {
|
||||
return;
|
||||
}
|
||||
|
||||
_primitiveUnits = primitiveUnits;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#ifdef RCT_NEW_ARCH_ENABLED
|
||||
Class<RCTComponentViewProtocol> RNSVGFilterCls(void)
|
||||
{
|
||||
return RNSVGFilter.class;
|
||||
}
|
||||
#endif // RCT_NEW_ARCH_ENABLED
|
||||
14
apple/Filters/RNSVGFilterPrimitive.h
Normal file
14
apple/Filters/RNSVGFilterPrimitive.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#import "RNSVGNode.h"
|
||||
|
||||
@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;
|
||||
|
||||
- (CIImage *)applyFilter:(NSMutableDictionary<NSString *, CIImage *> *)results previousFilterResult:(CIImage *)previous;
|
||||
- (CIImage *)cropResult:(CIImage *)result;
|
||||
|
||||
@end
|
||||
110
apple/Filters/RNSVGFilterPrimitive.mm
Normal file
110
apple/Filters/RNSVGFilterPrimitive.mm
Normal file
@@ -0,0 +1,110 @@
|
||||
#import <RNSVGFilterPrimitive.h>
|
||||
#import <RNSVGNode.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 "RNSVGFabricConversions.h"
|
||||
#endif // RCT_NEW_ARCH_ENABLED
|
||||
|
||||
@implementation RNSVGFilterPrimitive
|
||||
|
||||
#ifdef RCT_NEW_ARCH_ENABLED
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
_x = nil;
|
||||
_y = nil;
|
||||
_height = nil;
|
||||
_width = nil;
|
||||
_result = nil;
|
||||
}
|
||||
#endif // RCT_NEW_ARCH_ENABLED
|
||||
|
||||
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)parseReference
|
||||
{
|
||||
}
|
||||
|
||||
- (void)setX:(RNSVGLength *)x
|
||||
{
|
||||
if ([x isEqualTo:_x]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_x = x;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
self.dirty = false;
|
||||
[super invalidate];
|
||||
}
|
||||
|
||||
- (void)setY:(RNSVGLength *)y
|
||||
{
|
||||
if ([y isEqualTo:_y]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_y = y;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setWidth:(RNSVGLength *)width
|
||||
{
|
||||
if ([width isEqualTo:_width]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_width = width;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setHeight:(RNSVGLength *)height
|
||||
{
|
||||
if ([height isEqualTo:_height]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_height = height;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setResult:(NSString *)result
|
||||
{
|
||||
if ([result isEqualToString:_result]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_result = result;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (CIImage *)applyFilter:(NSMutableDictionary<NSString *, CIImage *> *)results previousFilterResult:(CIImage *)previous
|
||||
{
|
||||
return previous;
|
||||
}
|
||||
|
||||
- (CIImage *)cropResult:(CIImage *)result
|
||||
{
|
||||
CIFilter *filter = [CIFilter filterWithName:@"CICrop"];
|
||||
[filter setDefaults];
|
||||
[filter setValue:result forKey:@"inputImage"];
|
||||
CGFloat x = [self relativeOnWidth:self.x];
|
||||
CGFloat y = [self relativeOnHeight:self.y];
|
||||
CGFloat width = [self relativeOnWidth:self.width];
|
||||
CGFloat height = [self relativeOnHeight:self.height];
|
||||
|
||||
[filter setValue:[CIVector vectorWithX:x Y:y Z:width W:height] forKey:@"inputRectangle"];
|
||||
return [filter valueForKey:@"outputImage"];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -121,6 +121,8 @@ extern CGFloat const RNSVG_DEFAULT_FONT_SIZE;
|
||||
|
||||
- (CGFloat)relativeOn:(RNSVGLength *)length relative:(CGFloat)relative;
|
||||
|
||||
- (CGFloat)relativeOnFraction:(RNSVGLength *)length relative:(CGFloat)relative;
|
||||
|
||||
- (CGFloat)relativeOnWidth:(RNSVGLength *)length;
|
||||
|
||||
- (CGFloat)relativeOnHeight:(RNSVGLength *)length;
|
||||
|
||||
@@ -446,6 +446,17 @@ CGFloat const RNSVG_DEFAULT_FONT_SIZE = 12;
|
||||
fontSize:[self getFontSizeFromContext]];
|
||||
}
|
||||
|
||||
- (CGFloat)relativeOnFraction:(RNSVGLength *)length relative:(CGFloat)relative
|
||||
{
|
||||
RNSVGLengthUnitType unit = length.unit;
|
||||
if (unit == SVG_LENGTHTYPE_NUMBER) {
|
||||
return relative * length.value;
|
||||
} else if (unit == SVG_LENGTHTYPE_PERCENTAGE) {
|
||||
return length.value / 100 * relative;
|
||||
}
|
||||
return [self fromRelative:length];
|
||||
}
|
||||
|
||||
- (CGFloat)relativeOn:(RNSVGLength *)length relative:(CGFloat)relative
|
||||
{
|
||||
RNSVGLengthUnitType unit = length.unit;
|
||||
@@ -459,35 +470,17 @@ CGFloat const RNSVG_DEFAULT_FONT_SIZE = 12;
|
||||
|
||||
- (CGFloat)relativeOnWidth:(RNSVGLength *)length
|
||||
{
|
||||
RNSVGLengthUnitType unit = length.unit;
|
||||
if (unit == SVG_LENGTHTYPE_NUMBER) {
|
||||
return length.value;
|
||||
} else if (unit == SVG_LENGTHTYPE_PERCENTAGE) {
|
||||
return length.value / 100 * [self getCanvasWidth];
|
||||
}
|
||||
return [self fromRelative:length];
|
||||
return [self relativeOn:length relative:[self getCanvasWidth]];
|
||||
}
|
||||
|
||||
- (CGFloat)relativeOnHeight:(RNSVGLength *)length
|
||||
{
|
||||
RNSVGLengthUnitType unit = length.unit;
|
||||
if (unit == SVG_LENGTHTYPE_NUMBER) {
|
||||
return length.value;
|
||||
} else if (unit == SVG_LENGTHTYPE_PERCENTAGE) {
|
||||
return length.value / 100 * [self getCanvasHeight];
|
||||
}
|
||||
return [self fromRelative:length];
|
||||
return [self relativeOn:length relative:[self getCanvasHeight]];
|
||||
}
|
||||
|
||||
- (CGFloat)relativeOnOther:(RNSVGLength *)length
|
||||
{
|
||||
RNSVGLengthUnitType unit = length.unit;
|
||||
if (unit == SVG_LENGTHTYPE_NUMBER) {
|
||||
return length.value;
|
||||
} else if (unit == SVG_LENGTHTYPE_PERCENTAGE) {
|
||||
return length.value / 100 * [self getCanvasDiagonal];
|
||||
}
|
||||
return [self fromRelative:length];
|
||||
return [self relativeOn:length relative:[self getCanvasDiagonal]];
|
||||
}
|
||||
|
||||
- (CGFloat)fromRelative:(RNSVGLength *)length
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
@property (nonatomic, assign) RNSVGVectorEffect vectorEffect;
|
||||
@property (nonatomic, copy) NSArray<NSString *> *propList;
|
||||
@property (nonatomic, assign) CGPathRef hitArea;
|
||||
@property (nonatomic, strong) NSString *filter;
|
||||
|
||||
- (void)setHitArea:(CGPathRef)path;
|
||||
|
||||
|
||||
@@ -10,9 +10,11 @@
|
||||
#import <React/RCTPointerEvents.h>
|
||||
#import "RNSVGBezierElement.h"
|
||||
#import "RNSVGClipPath.h"
|
||||
#import "RNSVGFilter.h"
|
||||
#import "RNSVGMarker.h"
|
||||
#import "RNSVGMarkerPosition.h"
|
||||
#import "RNSVGMask.h"
|
||||
#import "RNSVGRenderUtils.h"
|
||||
#import "RNSVGVectorEffect.h"
|
||||
#import "RNSVGViewBox.h"
|
||||
|
||||
@@ -175,6 +177,15 @@ static RNSVGRenderable *_contextElement;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setFilter:(NSString *)filter
|
||||
{
|
||||
if ([_filter isEqualToString:filter]) {
|
||||
return;
|
||||
}
|
||||
_filter = filter;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
CGPathRelease(_hitArea);
|
||||
@@ -219,6 +230,7 @@ static RNSVGRenderable *_contextElement;
|
||||
_strokeDashoffset = 0;
|
||||
_vectorEffect = kRNSVGVectorEffectDefault;
|
||||
_propList = nil;
|
||||
_filter = nil;
|
||||
}
|
||||
#endif // RCT_NEW_ARCH_ENABLED
|
||||
|
||||
@@ -238,9 +250,7 @@ UInt32 saturate(CGFloat value)
|
||||
|
||||
[self beginTransparencyLayer:context];
|
||||
|
||||
if (self.mask) {
|
||||
// https://www.w3.org/TR/SVG11/masking.html#MaskElement
|
||||
RNSVGMask *_maskNode = (RNSVGMask *)[self.svgView getDefinedMask:self.mask];
|
||||
if (self.mask || self.filter) {
|
||||
CGFloat height = rect.size.height;
|
||||
CGFloat width = rect.size.width;
|
||||
CGFloat scale = 0.0;
|
||||
@@ -266,123 +276,151 @@ UInt32 saturate(CGFloat value)
|
||||
// Get current context transformations for offscreenContext
|
||||
CGAffineTransform currentCTM = CGContextGetCTM(context);
|
||||
|
||||
// Allocate pixel buffer and bitmap context for mask
|
||||
NSUInteger bytesPerPixel = 4;
|
||||
NSUInteger bitsPerComponent = 8;
|
||||
NSUInteger bytesPerRow = bytesPerPixel * scaledWidth;
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
UInt32 *pixels = (UInt32 *)calloc(npixels, sizeof(UInt32));
|
||||
CGContextRef bcontext = CGBitmapContextCreate(
|
||||
pixels,
|
||||
scaledWidth,
|
||||
scaledHeight,
|
||||
bitsPerComponent,
|
||||
bytesPerRow,
|
||||
colorSpace,
|
||||
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
|
||||
#if TARGET_OS_OSX // [macOS]
|
||||
// on macOS currentCTM is not scaled properly with screen scale so we need to scale it manually
|
||||
CGContextConcatCTM(bcontext, screenScaleCTM);
|
||||
#endif // [macOS]
|
||||
CGContextConcatCTM(bcontext, currentCTM);
|
||||
CGImage *contentImage = [RNSVGRenderUtils renderToImage:self ctm:currentCTM rect:scaledRect clip:nil];
|
||||
|
||||
// Clip to mask bounds and render the mask
|
||||
CGFloat x = [self relativeOn:[_maskNode x] relative:width];
|
||||
CGFloat y = [self relativeOn:[_maskNode y] relative:height];
|
||||
CGFloat w = [self relativeOn:[_maskNode maskwidth] relative:width];
|
||||
CGFloat h = [self relativeOn:[_maskNode maskheight] relative:height];
|
||||
CGRect maskBounds = CGRectApplyAffineTransform(CGRectMake(x, y, w, h), screenScaleCTM);
|
||||
CGContextClipToRect(bcontext, maskBounds);
|
||||
[_maskNode renderLayerTo:bcontext rect:scaledRect];
|
||||
if (self.filter) {
|
||||
// https://www.w3.org/TR/SVG11/filters.html#FilterElement
|
||||
RNSVGFilter *filterNode = (RNSVGFilter *)[self.svgView getDefinedFilter:self.filter];
|
||||
|
||||
// Apply luminanceToAlpha filter primitive
|
||||
// https://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement
|
||||
UInt32 *currentPixel = pixels;
|
||||
if (_maskNode.maskType == kRNSVGMaskTypeLuminance) {
|
||||
for (NSUInteger i = 0; i < npixels; i++) {
|
||||
UInt32 color = *currentPixel;
|
||||
CIImage *content = [CIImage imageWithCGImage:contentImage];
|
||||
|
||||
UInt32 r = color & 0xFF;
|
||||
UInt32 g = (color >> 8) & 0xFF;
|
||||
UInt32 b = (color >> 16) & 0xFF;
|
||||
CGImage *backgroundImage = CGBitmapContextCreateImage(context);
|
||||
CIImage *background =
|
||||
(backgroundImage != nil) ? [CIImage imageWithCGImage:backgroundImage] : [CIImage emptyImage];
|
||||
|
||||
CGFloat luma = (CGFloat)(0.299 * r + 0.587 * g + 0.144 * b);
|
||||
*currentPixel = saturate(luma) << 24;
|
||||
currentPixel++;
|
||||
content = [filterNode applyFilter:content
|
||||
backgroundImg:background
|
||||
renderableBounds:self.pathBounds
|
||||
canvasBounds:scaledRect
|
||||
ctm:currentCTM];
|
||||
|
||||
CGImageRelease(contentImage);
|
||||
contentImage = [[RNSVGRenderUtils sharedCIContext] createCGImage:content fromRect:scaledRect];
|
||||
|
||||
if (!self.mask) {
|
||||
CGContextConcatCTM(context, CGAffineTransformInvert(currentCTM));
|
||||
CGContextDrawImage(context, scaledRect, contentImage);
|
||||
CGContextConcatCTM(context, currentCTM);
|
||||
}
|
||||
}
|
||||
|
||||
// Create mask image and release memory
|
||||
CGImageRef maskImage = CGBitmapContextCreateImage(bcontext);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
CGContextRelease(bcontext);
|
||||
free(pixels);
|
||||
CGImageRelease(backgroundImage);
|
||||
}
|
||||
if (self.mask) {
|
||||
// https://www.w3.org/TR/SVG11/masking.html#MaskElement
|
||||
RNSVGMask *_maskNode = (RNSVGMask *)[self.svgView getDefinedMask:self.mask];
|
||||
|
||||
// Allocate pixel buffer and bitmap context for mask
|
||||
NSUInteger bytesPerPixel = 4;
|
||||
NSUInteger bitsPerComponent = 8;
|
||||
NSUInteger bytesPerRow = bytesPerPixel * scaledWidth;
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
UInt32 *pixels = (UInt32 *)calloc(npixels, sizeof(UInt32));
|
||||
CGContextRef bcontext = CGBitmapContextCreate(
|
||||
pixels,
|
||||
scaledWidth,
|
||||
scaledHeight,
|
||||
bitsPerComponent,
|
||||
bytesPerRow,
|
||||
colorSpace,
|
||||
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
|
||||
#if TARGET_OS_OSX // [macOS]
|
||||
// on macOS currentCTM is not scaled properly with screen scale so we need to scale it manually
|
||||
CGContextConcatCTM(bcontext, screenScaleCTM);
|
||||
#endif // [macOS]
|
||||
CGContextConcatCTM(bcontext, currentCTM);
|
||||
|
||||
// Clip to mask bounds and render the mask
|
||||
CGFloat x = [self relativeOn:[_maskNode x] relative:width];
|
||||
CGFloat y = [self relativeOn:[_maskNode y] relative:height];
|
||||
CGFloat w = [self relativeOn:[_maskNode maskwidth] relative:width];
|
||||
CGFloat h = [self relativeOn:[_maskNode maskheight] relative:height];
|
||||
CGRect maskBounds = CGRectApplyAffineTransform(CGRectMake(x, y, w, h), screenScaleCTM);
|
||||
CGContextClipToRect(bcontext, maskBounds);
|
||||
[_maskNode renderLayerTo:bcontext rect:scaledRect];
|
||||
|
||||
// Apply luminanceToAlpha filter primitive
|
||||
// https://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement
|
||||
UInt32 *currentPixel = pixels;
|
||||
if (_maskNode.maskType == kRNSVGMaskTypeLuminance) {
|
||||
for (NSUInteger i = 0; i < npixels; i++) {
|
||||
UInt32 color = *currentPixel;
|
||||
|
||||
UInt32 r = color & 0xFF;
|
||||
UInt32 g = (color >> 8) & 0xFF;
|
||||
UInt32 b = (color >> 16) & 0xFF;
|
||||
|
||||
CGFloat luma = (CGFloat)(0.299 * r + 0.587 * g + 0.144 * b);
|
||||
*currentPixel = saturate(luma) << 24;
|
||||
currentPixel++;
|
||||
}
|
||||
}
|
||||
|
||||
// Create mask image and release memory
|
||||
CGImageRef maskImage = CGBitmapContextCreateImage(bcontext);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
CGContextRelease(bcontext);
|
||||
free(pixels);
|
||||
|
||||
#if !TARGET_OS_OSX // [macOS]
|
||||
UIGraphicsImageRendererFormat *format = [UIGraphicsImageRendererFormat defaultFormat];
|
||||
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:rect.size format:format];
|
||||
UIGraphicsImageRendererFormat *format = [UIGraphicsImageRendererFormat defaultFormat];
|
||||
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:rect.size format:format];
|
||||
|
||||
// Get the content image
|
||||
UIImage *contentImage = [renderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull rendererContext) {
|
||||
CGContextConcatCTM(
|
||||
rendererContext.CGContext, CGAffineTransformInvert(CGContextGetCTM(rendererContext.CGContext)));
|
||||
CGContextConcatCTM(rendererContext.CGContext, currentCTM);
|
||||
[self renderLayerTo:rendererContext.CGContext rect:scaledRect];
|
||||
}];
|
||||
// Blend current element and mask
|
||||
UIImage *blendedImage = [renderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull rendererContext) {
|
||||
CGContextConcatCTM(
|
||||
rendererContext.CGContext, CGAffineTransformInvert(CGContextGetCTM(rendererContext.CGContext)));
|
||||
CGContextTranslateCTM(rendererContext.CGContext, 0.0, scaledHeight);
|
||||
CGContextScaleCTM(rendererContext.CGContext, 1.0, -1.0);
|
||||
|
||||
// Blend current element and mask
|
||||
UIImage *blendedImage = [renderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull rendererContext) {
|
||||
CGContextConcatCTM(
|
||||
rendererContext.CGContext, CGAffineTransformInvert(CGContextGetCTM(rendererContext.CGContext)));
|
||||
CGContextTranslateCTM(rendererContext.CGContext, 0.0, scaledHeight);
|
||||
CGContextScaleCTM(rendererContext.CGContext, 1.0, -1.0);
|
||||
CGContextSetBlendMode(rendererContext.CGContext, kCGBlendModeCopy);
|
||||
CGContextDrawImage(rendererContext.CGContext, scaledRect, maskImage);
|
||||
CGContextSetBlendMode(rendererContext.CGContext, kCGBlendModeSourceIn);
|
||||
CGContextDrawImage(rendererContext.CGContext, scaledRect, contentImage);
|
||||
}];
|
||||
|
||||
CGContextSetBlendMode(rendererContext.CGContext, kCGBlendModeCopy);
|
||||
CGContextDrawImage(rendererContext.CGContext, scaledRect, maskImage);
|
||||
CGContextSetBlendMode(rendererContext.CGContext, kCGBlendModeSourceIn);
|
||||
CGContextDrawImage(rendererContext.CGContext, scaledRect, contentImage.CGImage);
|
||||
}];
|
||||
// Render blended result into current render context
|
||||
CGContextConcatCTM(context, CGAffineTransformInvert(currentCTM));
|
||||
[blendedImage drawInRect:scaledRect];
|
||||
CGContextConcatCTM(context, currentCTM);
|
||||
|
||||
// Render blended result into current render context
|
||||
CGContextConcatCTM(context, CGAffineTransformInvert(currentCTM));
|
||||
[blendedImage drawInRect:scaledRect];
|
||||
CGContextConcatCTM(context, currentCTM);
|
||||
|
||||
// Render blended result into current render context
|
||||
CGImageRelease(maskImage);
|
||||
// Render blended result into current render context
|
||||
CGImageRelease(maskImage);
|
||||
#else // [macOS
|
||||
// Render content of current SVG Renderable to image
|
||||
UIGraphicsBeginImageContextWithOptions(scaledRect.size, NO, 1.0);
|
||||
CGContextRef newContext = UIGraphicsGetCurrentContext();
|
||||
CGContextConcatCTM(newContext, CGAffineTransformInvert(CGContextGetCTM(newContext)));
|
||||
CGContextConcatCTM(newContext, screenScaleCTM);
|
||||
CGContextConcatCTM(newContext, currentCTM);
|
||||
[self renderLayerTo:newContext rect:scaledRect];
|
||||
CGImageRef contentImage = CGBitmapContextCreateImage(newContext);
|
||||
UIGraphicsEndImageContext();
|
||||
UIGraphicsBeginImageContextWithOptions(scaledRect.size, NO, 1.0);
|
||||
CGContextRef newContext = UIGraphicsGetCurrentContext();
|
||||
CGContextConcatCTM(newContext, CGAffineTransformInvert(CGContextGetCTM(newContext)));
|
||||
CGContextConcatCTM(newContext, screenScaleCTM);
|
||||
CGContextConcatCTM(newContext, currentCTM);
|
||||
[self renderLayerTo:newContext rect:scaledRect];
|
||||
CGImageRef contentImage = CGBitmapContextCreateImage(newContext);
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
// Blend current element and mask
|
||||
UIGraphicsBeginImageContextWithOptions(scaledRect.size, NO, 1.0);
|
||||
newContext = UIGraphicsGetCurrentContext();
|
||||
CGContextConcatCTM(newContext, CGAffineTransformInvert(CGContextGetCTM(newContext)));
|
||||
// Blend current element and mask
|
||||
UIGraphicsBeginImageContextWithOptions(scaledRect.size, NO, 0.0);
|
||||
newContext = UIGraphicsGetCurrentContext();
|
||||
CGContextTranslateCTM(newContext, 0.0, height);
|
||||
CGContextScaleCTM(newContext, 1.0, -1.0);
|
||||
|
||||
CGContextSetBlendMode(newContext, kCGBlendModeCopy);
|
||||
CGContextDrawImage(newContext, scaledRect, maskImage);
|
||||
CGImageRelease(maskImage);
|
||||
CGContextSetBlendMode(newContext, kCGBlendModeCopy);
|
||||
CGContextDrawImage(newContext, scaledRect, maskImage);
|
||||
CGImageRelease(maskImage);
|
||||
|
||||
CGContextSetBlendMode(newContext, kCGBlendModeSourceIn);
|
||||
CGContextDrawImage(newContext, scaledRect, contentImage);
|
||||
CGImageRelease(contentImage);
|
||||
CGContextSetBlendMode(newContext, kCGBlendModeSourceIn);
|
||||
CGContextDrawImage(newContext, scaledRect, contentImage);
|
||||
CGImageRelease(contentImage);
|
||||
|
||||
CGImageRef blendedImage = CGBitmapContextCreateImage(newContext);
|
||||
UIGraphicsEndImageContext();
|
||||
CGImageRef blendedImage = CGBitmapContextCreateImage(newContext);
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
// Render blended result into current render context
|
||||
CGContextConcatCTM(context, CGAffineTransformInvert(currentCTM));
|
||||
CGContextDrawImage(context, rect, blendedImage);
|
||||
CGContextConcatCTM(context, currentCTM);
|
||||
CGImageRelease(blendedImage);
|
||||
// Render blended result into current render context
|
||||
CGContextConcatCTM(context, CGAffineTransformInvert(currentCTM));
|
||||
CGContextDrawImage(context, rect, blendedImage);
|
||||
CGContextConcatCTM(context, currentCTM);
|
||||
CGImageRelease(blendedImage);
|
||||
#endif // macOS]
|
||||
}
|
||||
CGImageRelease(contentImage);
|
||||
} else {
|
||||
[self renderLayerTo:context rect:rect];
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#import <React/RCTConvert.h>
|
||||
#import "RCTConvert+RNSVG.h"
|
||||
#import "RNSVGCGFCRule.h"
|
||||
#import "RNSVGColorMatrixType.h"
|
||||
#import "RNSVGEdgeModeTypes.h"
|
||||
#import "RNSVGLength.h"
|
||||
#import "RNSVGMaskType.h"
|
||||
#import "RNSVGPathParser.h"
|
||||
|
||||
@@ -51,6 +51,27 @@ RCT_ENUM_CONVERTER(
|
||||
kRNSVGMaskTypeLuminance,
|
||||
intValue)
|
||||
|
||||
RCT_ENUM_CONVERTER(
|
||||
RNSVGEdgeModeTypes,
|
||||
(@{
|
||||
@"duplicate" : @(SVG_EDGEMODE_DUPLICATE),
|
||||
@"wrap" : @(SVG_EDGEMODE_WRAP),
|
||||
@"none" : @(SVG_EDGEMODE_NONE),
|
||||
}),
|
||||
SVG_FECOLORMATRIX_TYPE_UNKNOWN,
|
||||
intValue)
|
||||
|
||||
RCT_ENUM_CONVERTER(
|
||||
RNSVGColorMatrixType,
|
||||
(@{
|
||||
@"matrix" : @(SVG_FECOLORMATRIX_TYPE_MATRIX),
|
||||
@"saturate" : @(SVG_FECOLORMATRIX_TYPE_SATURATE),
|
||||
@"hueRotate" : @(SVG_FECOLORMATRIX_TYPE_HUEROTATE),
|
||||
@"luminanceToAlpha" : @(SVG_FECOLORMATRIX_TYPE_LUMINANCETOALPHA),
|
||||
}),
|
||||
SVG_FECOLORMATRIX_TYPE_UNKNOWN,
|
||||
intValue)
|
||||
|
||||
+ (RNSVGBrush *)RNSVGBrush:(id)json
|
||||
{
|
||||
if ([json isKindOfClass:[NSNumber class]]) {
|
||||
|
||||
17
apple/Utils/RNSVGConvert.h
Normal file
17
apple/Utils/RNSVGConvert.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifdef RCT_NEW_ARCH_ENABLED
|
||||
#import <react/renderer/components/rnsvg/Props.h>
|
||||
#import "RNSVGColorMatrixType.h"
|
||||
#import "RNSVGEdgeModeTypes.h"
|
||||
#import "RNSVGUnits.h"
|
||||
|
||||
namespace react = facebook::react;
|
||||
|
||||
@interface RNSVGConvert : NSObject
|
||||
|
||||
+ (RNSVGUnits)RNSVGUnitsFromFilterUnitsCppEquivalent:(react::RNSVGFilterFilterUnits)svgUnits;
|
||||
+ (RNSVGUnits)RNSVGUnitsFromPrimitiveUnitsCppEquivalent:(react::RNSVGFilterPrimitiveUnits)svgUnits;
|
||||
+ (RNSVGColorMatrixType)RNSVGColorMatrixTypeFromCppEquivalent:(react::RNSVGFeColorMatrixType)type;
|
||||
|
||||
@end
|
||||
|
||||
#endif // RCT_NEW_ARCH_ENABLED
|
||||
42
apple/Utils/RNSVGConvert.mm
Normal file
42
apple/Utils/RNSVGConvert.mm
Normal file
@@ -0,0 +1,42 @@
|
||||
#import "RNSVGConvert.h"
|
||||
|
||||
#ifdef RCT_NEW_ARCH_ENABLED
|
||||
@implementation RNSVGConvert
|
||||
|
||||
+ (RNSVGUnits)RNSVGUnitsFromFilterUnitsCppEquivalent:(react::RNSVGFilterFilterUnits)svgUnits
|
||||
{
|
||||
switch (svgUnits) {
|
||||
case react::RNSVGFilterFilterUnits::UserSpaceOnUse:
|
||||
return kRNSVGUnitsUserSpaceOnUse;
|
||||
case react::RNSVGFilterFilterUnits::ObjectBoundingBox:
|
||||
return kRNSVGUnitsObjectBoundingBox;
|
||||
}
|
||||
}
|
||||
|
||||
+ (RNSVGUnits)RNSVGUnitsFromPrimitiveUnitsCppEquivalent:(react::RNSVGFilterPrimitiveUnits)svgUnits
|
||||
{
|
||||
switch (svgUnits) {
|
||||
case react::RNSVGFilterPrimitiveUnits::UserSpaceOnUse:
|
||||
return kRNSVGUnitsUserSpaceOnUse;
|
||||
case react::RNSVGFilterPrimitiveUnits::ObjectBoundingBox:
|
||||
return kRNSVGUnitsObjectBoundingBox;
|
||||
}
|
||||
}
|
||||
|
||||
+ (RNSVGColorMatrixType)RNSVGColorMatrixTypeFromCppEquivalent:(react::RNSVGFeColorMatrixType)type;
|
||||
{
|
||||
switch (type) {
|
||||
case react::RNSVGFeColorMatrixType::Matrix:
|
||||
return SVG_FECOLORMATRIX_TYPE_MATRIX;
|
||||
case react::RNSVGFeColorMatrixType::Saturate:
|
||||
return SVG_FECOLORMATRIX_TYPE_SATURATE;
|
||||
case react::RNSVGFeColorMatrixType::HueRotate:
|
||||
return SVG_FECOLORMATRIX_TYPE_HUEROTATE;
|
||||
case react::RNSVGFeColorMatrixType::LuminanceToAlpha:
|
||||
return SVG_FECOLORMATRIX_TYPE_LUMINANCETOALPHA;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif // RCT_NEW_ARCH_ENABLED
|
||||
@@ -1,4 +1,5 @@
|
||||
#import "RNSVGContextBrush.h"
|
||||
#import "RNSVGFilterPrimitive.h"
|
||||
#import "RNSVGGroup.h"
|
||||
#import "RNSVGLength.h"
|
||||
#import "RNSVGPainterBrush.h"
|
||||
@@ -167,6 +168,7 @@ void setCommonRenderableProps(const T &renderableProps, RNSVGRenderable *rendera
|
||||
}
|
||||
renderableNode.propList = propArray;
|
||||
}
|
||||
renderableNode.filter = RCTNSStringFromStringNilIfEmpty(renderableProps.filter);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -193,6 +195,28 @@ void setCommonGroupProps(const T &groupProps, RNSVGGroup *groupNode)
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void setCommonFilterProps(const T &filterProps, RNSVGFilterPrimitive *filterPrimitiveNode)
|
||||
{
|
||||
id x = RNSVGConvertFollyDynamicToId(filterProps.x);
|
||||
if (x != nil) {
|
||||
filterPrimitiveNode.x = [RCTConvert RNSVGLength:x];
|
||||
}
|
||||
id y = RNSVGConvertFollyDynamicToId(filterProps.y);
|
||||
if (y != nil) {
|
||||
filterPrimitiveNode.y = [RCTConvert RNSVGLength:y];
|
||||
}
|
||||
id height = RNSVGConvertFollyDynamicToId(filterProps.height);
|
||||
if (height != nil) {
|
||||
filterPrimitiveNode.height = [RCTConvert RNSVGLength:height];
|
||||
}
|
||||
id width = RNSVGConvertFollyDynamicToId(filterProps.width);
|
||||
if (width != nil) {
|
||||
filterPrimitiveNode.width = [RCTConvert RNSVGLength:width];
|
||||
}
|
||||
filterPrimitiveNode.result = RCTNSStringFromStringNilIfEmpty(filterProps.result);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void setCommonTextProps(const T &textProps, RNSVGText *textNode)
|
||||
{
|
||||
|
||||
11
apple/Utils/RNSVGRenderUtils.h
Normal file
11
apple/Utils/RNSVGRenderUtils.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#import "RNSVGRenderable.h"
|
||||
|
||||
@interface RNSVGRenderUtils : NSObject
|
||||
|
||||
+ (CIContext *)sharedCIContext;
|
||||
+ (CGImage *)renderToImage:(RNSVGRenderable *)renderable
|
||||
ctm:(CGAffineTransform)ctm
|
||||
rect:(CGRect)rect
|
||||
clip:(CGRect *)clip;
|
||||
|
||||
@end
|
||||
35
apple/Utils/RNSVGRenderUtils.mm
Normal file
35
apple/Utils/RNSVGRenderUtils.mm
Normal file
@@ -0,0 +1,35 @@
|
||||
#import "RNSVGRenderUtils.h"
|
||||
|
||||
@implementation RNSVGRenderUtils
|
||||
|
||||
+ (CIContext *)sharedCIContext
|
||||
{
|
||||
static CIContext *sharedCIContext = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedCIContext = [[CIContext alloc] init];
|
||||
});
|
||||
|
||||
return sharedCIContext;
|
||||
}
|
||||
|
||||
+ (CGImage *)renderToImage:(RNSVGRenderable *)renderable
|
||||
ctm:(CGAffineTransform)ctm
|
||||
rect:(CGRect)rect
|
||||
clip:(CGRect *)clip
|
||||
{
|
||||
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 1.0);
|
||||
CGContextRef cgContext = UIGraphicsGetCurrentContext();
|
||||
CGContextConcatCTM(cgContext, CGAffineTransformInvert(CGContextGetCTM(cgContext)));
|
||||
CGContextConcatCTM(cgContext, ctm);
|
||||
|
||||
if (clip) {
|
||||
CGContextClipToRect(cgContext, *clip);
|
||||
}
|
||||
[renderable renderLayerTo:cgContext rect:rect];
|
||||
CGImageRef contentImage = CGBitmapContextCreateImage(cgContext);
|
||||
UIGraphicsEndImageContext();
|
||||
return contentImage;
|
||||
}
|
||||
|
||||
@end
|
||||
5
apple/ViewManagers/RNSVGFeColorMatrixManager.h
Normal file
5
apple/ViewManagers/RNSVGFeColorMatrixManager.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#import "RNSVGFilterPrimitiveManager.h"
|
||||
|
||||
@interface RNSVGFeColorMatrixManager : RNSVGFilterPrimitiveManager
|
||||
|
||||
@end
|
||||
18
apple/ViewManagers/RNSVGFeColorMatrixManager.mm
Normal file
18
apple/ViewManagers/RNSVGFeColorMatrixManager.mm
Normal file
@@ -0,0 +1,18 @@
|
||||
#import "RNSVGFeColorMatrixManager.h"
|
||||
#import "RNSVGColorMatrixType.h"
|
||||
#import "RNSVGFeColorMatrix.h"
|
||||
|
||||
@implementation RNSVGFeColorMatrixManager
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (RNSVGFeColorMatrix *)node
|
||||
{
|
||||
return [RNSVGFeColorMatrix new];
|
||||
}
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(in1, NSString)
|
||||
RCT_EXPORT_VIEW_PROPERTY(type, RNSVGColorMatrixType)
|
||||
RCT_EXPORT_VIEW_PROPERTY(values, NSArray<NSNumber *>)
|
||||
|
||||
@end
|
||||
5
apple/ViewManagers/RNSVGFilterManager.h
Normal file
5
apple/ViewManagers/RNSVGFilterManager.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#import "RNSVGNodeManager.h"
|
||||
|
||||
@interface RNSVGFilterManager : RNSVGNodeManager
|
||||
|
||||
@end
|
||||
26
apple/ViewManagers/RNSVGFilterManager.mm
Normal file
26
apple/ViewManagers/RNSVGFilterManager.mm
Normal file
@@ -0,0 +1,26 @@
|
||||
#import "RNSVGFilterManager.h"
|
||||
#import "RNSVGFilter.h"
|
||||
|
||||
@implementation RNSVGFilterManager
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (RNSVGFilter *)node
|
||||
{
|
||||
return [RNSVGFilter new];
|
||||
}
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(x, RNSVGLength *)
|
||||
RCT_EXPORT_VIEW_PROPERTY(y, RNSVGLength *)
|
||||
RCT_CUSTOM_VIEW_PROPERTY(width, id, RNSVGFilter)
|
||||
{
|
||||
view.width = [RCTConvert RNSVGLength:json];
|
||||
}
|
||||
RCT_CUSTOM_VIEW_PROPERTY(height, id, RNSVGFilter)
|
||||
{
|
||||
view.height = [RCTConvert RNSVGLength:json];
|
||||
}
|
||||
RCT_EXPORT_VIEW_PROPERTY(filterUnits, RNSVGUnits)
|
||||
RCT_EXPORT_VIEW_PROPERTY(primitiveUnits, RNSVGUnits)
|
||||
|
||||
@end
|
||||
5
apple/ViewManagers/RNSVGFilterPrimitiveManager.h
Normal file
5
apple/ViewManagers/RNSVGFilterPrimitiveManager.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#import "RNSVGNodeManager.h"
|
||||
|
||||
@interface RNSVGFilterPrimitiveManager : RNSVGNodeManager
|
||||
|
||||
@end
|
||||
25
apple/ViewManagers/RNSVGFilterPrimitiveManager.mm
Normal file
25
apple/ViewManagers/RNSVGFilterPrimitiveManager.mm
Normal file
@@ -0,0 +1,25 @@
|
||||
#import "RNSVGFilterPrimitiveManager.h"
|
||||
#import "RNSVGFilterPrimitive.h"
|
||||
|
||||
@implementation RNSVGFilterPrimitiveManager
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (RNSVGFilterPrimitive *)node
|
||||
{
|
||||
return [RNSVGFilterPrimitive new];
|
||||
}
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(x, RNSVGLength *)
|
||||
RCT_EXPORT_VIEW_PROPERTY(y, RNSVGLength *)
|
||||
RCT_CUSTOM_VIEW_PROPERTY(width, id, RNSVGFilterPrimitive)
|
||||
{
|
||||
view.width = [RCTConvert RNSVGLength:json];
|
||||
}
|
||||
RCT_CUSTOM_VIEW_PROPERTY(height, id, RNSVGFilterPrimitive)
|
||||
{
|
||||
view.height = [RCTConvert RNSVGLength:json];
|
||||
}
|
||||
RCT_EXPORT_VIEW_PROPERTY(result, NSString)
|
||||
|
||||
@end
|
||||
@@ -37,5 +37,6 @@ RCT_EXPORT_VIEW_PROPERTY(strokeDashoffset, CGFloat)
|
||||
RCT_EXPORT_VIEW_PROPERTY(strokeMiterlimit, CGFloat)
|
||||
RCT_EXPORT_VIEW_PROPERTY(vectorEffect, int)
|
||||
RCT_EXPORT_VIEW_PROPERTY(propList, NSArray<NSString *>)
|
||||
RCT_EXPORT_VIEW_PROPERTY(filter, NSString)
|
||||
|
||||
@end
|
||||
|
||||
@@ -13,86 +13,13 @@ import {
|
||||
ScrollView,
|
||||
TouchableHighlight,
|
||||
TouchableOpacity,
|
||||
SafeAreaView,
|
||||
} from 'react-native';
|
||||
import {Modal, Platform} from 'react-native';
|
||||
import {Svg, Circle, Line} from 'react-native-svg';
|
||||
|
||||
import * as examples from './src/examples';
|
||||
|
||||
const hairline = StyleSheet.hairlineWidth;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
paddingTop: 20,
|
||||
alignItems: 'center',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
contentContainer: {
|
||||
alignSelf: 'stretch',
|
||||
borderTopWidth: hairline,
|
||||
borderTopColor: '#ccc',
|
||||
borderBottomWidth: hairline,
|
||||
borderBottomColor: '#ccc',
|
||||
flexWrap: 'wrap',
|
||||
flexDirection: 'row',
|
||||
marginHorizontal: 10,
|
||||
},
|
||||
welcome: {
|
||||
padding: 10,
|
||||
color: '#f60',
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
link: {
|
||||
height: 40,
|
||||
alignSelf: 'stretch',
|
||||
width: Dimensions.get('window').width / 2 - 10,
|
||||
},
|
||||
title: {
|
||||
marginLeft: 10,
|
||||
},
|
||||
cell: {
|
||||
height: 40,
|
||||
paddingHorizontal: 10,
|
||||
alignSelf: 'stretch',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
borderTopWidth: hairline,
|
||||
borderTopColor: '#ccc',
|
||||
marginTop: -hairline,
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
close: {
|
||||
position: 'absolute',
|
||||
right: 20,
|
||||
top: 40,
|
||||
},
|
||||
scroll: {
|
||||
position: 'absolute',
|
||||
top: 30,
|
||||
right: 10,
|
||||
bottom: 20,
|
||||
left: 10,
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
scrollContent: {
|
||||
borderTopWidth: hairline,
|
||||
borderTopColor: '#ccc',
|
||||
},
|
||||
example: {
|
||||
paddingVertical: 25,
|
||||
alignSelf: 'stretch',
|
||||
alignItems: 'center',
|
||||
borderBottomWidth: hairline,
|
||||
borderBottomColor: '#ccc',
|
||||
},
|
||||
sampleTitle: {
|
||||
marginHorizontal: 15,
|
||||
fontSize: 16,
|
||||
color: '#666',
|
||||
},
|
||||
});
|
||||
import {commonStyles} from './src/commonStyles';
|
||||
|
||||
const names: (keyof typeof examples)[] = [
|
||||
'Svg',
|
||||
@@ -116,6 +43,8 @@ const names: (keyof typeof examples)[] = [
|
||||
'Transforms',
|
||||
'Markers',
|
||||
'Mask',
|
||||
'Filters',
|
||||
'FilterImage',
|
||||
];
|
||||
|
||||
const initialState = {
|
||||
@@ -142,8 +71,8 @@ export default class SvgExample extends Component {
|
||||
content: (
|
||||
<View>
|
||||
{samples.map((Sample, i) => (
|
||||
<View style={styles.example} key={`sample-${i}`}>
|
||||
<Text style={styles.sampleTitle}>{Sample.title}</Text>
|
||||
<View style={commonStyles.example} key={`sample-${i}`}>
|
||||
<Text style={commonStyles.sampleTitle}>{Sample.title}</Text>
|
||||
<Sample />
|
||||
</View>
|
||||
))}
|
||||
@@ -171,9 +100,9 @@ export default class SvgExample extends Component {
|
||||
underlayColor="#ccc"
|
||||
key={`example-${name}`}
|
||||
onPress={() => this.show(name)}>
|
||||
<View style={styles.cell}>
|
||||
<View style={commonStyles.cell}>
|
||||
{icon}
|
||||
<Text style={styles.title}>{name}</Text>
|
||||
<Text style={commonStyles.title}>{name}</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
@@ -182,13 +111,15 @@ export default class SvgExample extends Component {
|
||||
|
||||
modalContent = () => (
|
||||
<>
|
||||
<ScrollView
|
||||
style={styles.scroll}
|
||||
contentContainerStyle={styles.scrollContent}
|
||||
scrollEnabled={this.state.scroll}>
|
||||
{this.state.content}
|
||||
</ScrollView>
|
||||
<View style={styles.close}>
|
||||
<SafeAreaView style={{flex: 1}}>
|
||||
<ScrollView
|
||||
style={styles.scroll}
|
||||
contentContainerStyle={styles.scrollContent}
|
||||
scrollEnabled={this.state.scroll}>
|
||||
{this.state.content}
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
<SafeAreaView style={styles.close}>
|
||||
<TouchableOpacity activeOpacity={0.7} onPress={this.hide}>
|
||||
<Svg height="20" width="20">
|
||||
<Circle cx="10" cy="10" r="10" fill="red" />
|
||||
@@ -196,14 +127,14 @@ export default class SvgExample extends Component {
|
||||
<Line x1="4" y1="16" x2="16" y2="4" stroke="#fff" strokeWidth="2" />
|
||||
</Svg>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</>
|
||||
);
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.welcome}>SVG library for React Apps</Text>
|
||||
<SafeAreaView style={styles.container}>
|
||||
<Text style={commonStyles.welcome}>SVG library for React Apps</Text>
|
||||
<View style={styles.contentContainer}>{this.getExamples()}</View>
|
||||
{(Platform.OS === 'windows' || Platform.OS === 'macos') &&
|
||||
this.state.modal ? (
|
||||
@@ -217,7 +148,50 @@ export default class SvgExample extends Component {
|
||||
{this.modalContent()}
|
||||
</Modal>
|
||||
)}
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const hairline = StyleSheet.hairlineWidth;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
paddingTop: 20,
|
||||
alignItems: 'center',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
contentContainer: {
|
||||
alignSelf: 'stretch',
|
||||
borderTopWidth: hairline,
|
||||
borderTopColor: '#ccc',
|
||||
borderBottomWidth: hairline,
|
||||
borderBottomColor: '#ccc',
|
||||
flexWrap: 'wrap',
|
||||
flexDirection: 'row',
|
||||
marginHorizontal: 10,
|
||||
},
|
||||
link: {
|
||||
height: 40,
|
||||
alignSelf: 'stretch',
|
||||
width: Dimensions.get('window').width / 2 - 10,
|
||||
},
|
||||
close: {
|
||||
position: 'absolute',
|
||||
right: 20,
|
||||
top: 20,
|
||||
},
|
||||
scroll: {
|
||||
position: 'absolute',
|
||||
top: 30,
|
||||
right: 10,
|
||||
bottom: 20,
|
||||
left: 10,
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
scrollContent: {
|
||||
borderTopWidth: hairline,
|
||||
borderTopColor: '#ccc',
|
||||
},
|
||||
});
|
||||
|
||||
BIN
apps/examples/src/assets/office.jpg
Normal file
BIN
apps/examples/src/assets/office.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
41
apps/examples/src/commonStyles.ts
Normal file
41
apps/examples/src/commonStyles.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import {StyleSheet} from 'react-native';
|
||||
|
||||
const hairline = StyleSheet.hairlineWidth;
|
||||
|
||||
export const commonStyles = StyleSheet.create({
|
||||
welcome: {
|
||||
padding: 10,
|
||||
color: '#f60',
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'center',
|
||||
},
|
||||
link: {
|
||||
height: 40,
|
||||
},
|
||||
title: {
|
||||
marginLeft: 10,
|
||||
},
|
||||
cell: {
|
||||
height: 40,
|
||||
paddingHorizontal: 10,
|
||||
alignSelf: 'stretch',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
borderTopWidth: hairline,
|
||||
borderTopColor: '#ccc',
|
||||
marginTop: -hairline,
|
||||
},
|
||||
example: {
|
||||
paddingVertical: 25,
|
||||
alignSelf: 'stretch',
|
||||
alignItems: 'center',
|
||||
borderBottomWidth: hairline,
|
||||
borderBottomColor: '#ccc',
|
||||
},
|
||||
sampleTitle: {
|
||||
marginHorizontal: 15,
|
||||
fontSize: 16,
|
||||
color: '#666',
|
||||
},
|
||||
});
|
||||
@@ -19,6 +19,8 @@ import * as Reanimated from './examples/Reanimated';
|
||||
import * as Transforms from './examples/Transforms';
|
||||
import * as Markers from './examples/Markers';
|
||||
import * as Mask from './examples/Mask';
|
||||
import * as Filters from './examples/Filters';
|
||||
import * as FilterImage from './examples/FilterImage';
|
||||
|
||||
export {
|
||||
Svg,
|
||||
@@ -42,4 +44,6 @@ export {
|
||||
Transforms,
|
||||
Markers,
|
||||
Mask,
|
||||
Filters,
|
||||
FilterImage,
|
||||
};
|
||||
|
||||
142
apps/examples/src/examples/FilterImage/FilterPicker.tsx
Normal file
142
apps/examples/src/examples/FilterImage/FilterPicker.tsx
Normal file
@@ -0,0 +1,142 @@
|
||||
import React, {useState} from 'react';
|
||||
import {
|
||||
Dimensions,
|
||||
FlatList,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import {FilterImage, Filters} from 'react-native-svg/filter-image';
|
||||
|
||||
const img = require('../../assets/office.jpg');
|
||||
|
||||
const normal: Filters = [];
|
||||
const losAngeles: Filters = [
|
||||
{
|
||||
name: 'colorMatrix',
|
||||
type: 'matrix',
|
||||
values: [1.8, 0, 0, 0, 0, 0, 1.3, 0, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1, 0],
|
||||
},
|
||||
];
|
||||
const lagos: Filters = [
|
||||
{
|
||||
name: 'colorMatrix',
|
||||
type: 'matrix',
|
||||
values: [
|
||||
1.4, 0, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1.5, 0, 0, 0, 0, 0, 0.9, 0,
|
||||
],
|
||||
},
|
||||
];
|
||||
const tokyo: Filters = [
|
||||
{name: 'colorMatrix', type: 'saturate', values: [1.5]},
|
||||
{
|
||||
name: 'colorMatrix',
|
||||
type: 'matrix',
|
||||
|
||||
values: [
|
||||
0.2, 0.2, 0.2, 0, 0, 0.2, 0.2, 0.2, 0, 0, 0.2, 0.2, 0.2, 0, 0, 0, 0, 0, 1,
|
||||
0,
|
||||
],
|
||||
},
|
||||
];
|
||||
const saturated: Filters = [
|
||||
{name: 'colorMatrix', type: 'saturate', values: [1.5]},
|
||||
];
|
||||
const boring: Filters = [
|
||||
{
|
||||
name: 'colorMatrix',
|
||||
type: 'matrix',
|
||||
values: [
|
||||
0.6965, 0.3845, 0.0945, 0, 0, 0.1745, 0.8430000000000001, 0.084, 0, 0,
|
||||
0.136, 0.267, 0.5655, 0, 0, 0, 0, 0, 1, 0,
|
||||
],
|
||||
},
|
||||
];
|
||||
const filters = {
|
||||
normal,
|
||||
losAngeles,
|
||||
lagos,
|
||||
tokyo,
|
||||
saturated,
|
||||
boring,
|
||||
} as const;
|
||||
|
||||
type FilterKeys =
|
||||
| 'normal'
|
||||
| 'losAngeles'
|
||||
| 'lagos'
|
||||
| 'tokyo'
|
||||
| 'saturated'
|
||||
| 'boring';
|
||||
const filterKeys = Object.keys(filters) as FilterKeys[];
|
||||
const FilterImagePickerExample = () => {
|
||||
const [currentFilter, setCurrentFilter] = useState<FilterKeys>('normal');
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<FilterImage
|
||||
style={styles.image}
|
||||
source={img}
|
||||
filters={filters[currentFilter]}
|
||||
/>
|
||||
|
||||
<View>
|
||||
<FlatList
|
||||
data={filterKeys}
|
||||
horizontal
|
||||
style={styles.list}
|
||||
contentContainerStyle={styles.listElement}
|
||||
renderItem={({item}) => {
|
||||
return (
|
||||
<TouchableOpacity onPress={() => setCurrentFilter(item)}>
|
||||
<FilterImage
|
||||
style={styles.listElementImage}
|
||||
source={img}
|
||||
filters={filters[item]}
|
||||
/>
|
||||
<Text numberOfLines={1} style={styles.listElementTitle}>
|
||||
{item}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
FilterImagePickerExample.title = 'Filter picker';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
height: Dimensions.get('window').height - 150,
|
||||
width: '100%',
|
||||
},
|
||||
image: {flex: 1, width: '100%', height: '100%'},
|
||||
list: {
|
||||
marginTop: 8,
|
||||
marginHorizontal: 8,
|
||||
},
|
||||
listElement: {gap: 8},
|
||||
listElementImage: {width: 70, height: 70},
|
||||
listElementTitle: {
|
||||
width: 70,
|
||||
textAlign: 'center',
|
||||
marginTop: 2,
|
||||
marginBottom: 8,
|
||||
},
|
||||
});
|
||||
|
||||
const icon = (
|
||||
<FilterImage
|
||||
filters={[{name: 'colorMatrix', type: 'saturate', values: [0.5]}]}
|
||||
source={img}
|
||||
width={30}
|
||||
height={30}
|
||||
/>
|
||||
);
|
||||
|
||||
const samples = [FilterImagePickerExample];
|
||||
export {icon, samples};
|
||||
29
apps/examples/src/examples/FilterImage/LocalImage.tsx
Normal file
29
apps/examples/src/examples/FilterImage/LocalImage.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import {View} from 'react-native';
|
||||
import {FilterImage} from 'react-native-svg/filter-image';
|
||||
|
||||
const testImage = require('../../assets/image.jpg');
|
||||
|
||||
const FilterImageLocalExample = () => {
|
||||
return (
|
||||
<View>
|
||||
<FilterImage
|
||||
filters={[{name: 'colorMatrix', type: 'saturate', values: [0.5]}]}
|
||||
source={testImage}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
FilterImageLocalExample.title = 'Local image with filter';
|
||||
|
||||
const icon = (
|
||||
<FilterImage
|
||||
filters={[{name: 'colorMatrix', type: 'saturate', values: [0.5]}]}
|
||||
source={testImage}
|
||||
width={30}
|
||||
height={30}
|
||||
/>
|
||||
);
|
||||
|
||||
const samples = [FilterImageLocalExample];
|
||||
export {icon, samples};
|
||||
52
apps/examples/src/examples/FilterImage/RemoteImage.tsx
Normal file
52
apps/examples/src/examples/FilterImage/RemoteImage.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import React from 'react';
|
||||
import {View} from 'react-native';
|
||||
import {FilterImage} from 'react-native-svg/filter-image';
|
||||
|
||||
const testSource = {
|
||||
uri: 'https://cdn.pixabay.com/photo/2023/03/17/11/39/mountain-7858482_1280.jpg',
|
||||
};
|
||||
|
||||
const FilterImageRemoteExample = () => {
|
||||
return (
|
||||
<View>
|
||||
<FilterImage
|
||||
filters={[{name: 'colorMatrix', type: 'saturate', values: [3]}]}
|
||||
source={testSource}
|
||||
style={{width: 200, height: 200}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
FilterImageRemoteExample.title = 'Remote image with filter';
|
||||
|
||||
const FilterImageFewFiltersExample = () => {
|
||||
return (
|
||||
<View>
|
||||
<FilterImage
|
||||
filters={[
|
||||
{name: 'colorMatrix', type: 'saturate', values: [10]},
|
||||
{
|
||||
name: 'colorMatrix',
|
||||
type: 'matrix',
|
||||
values: '0.2 0.2 0.2 0 0 0.2 0.2 0.2 0 0 0.2 0.2 0.2 0 0 0 0 0 1 0',
|
||||
},
|
||||
]}
|
||||
source={testSource}
|
||||
style={{width: 200, height: 200}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
FilterImageFewFiltersExample.title = 'Remote image with filters';
|
||||
|
||||
const icon = (
|
||||
<FilterImage
|
||||
filters={[{name: 'colorMatrix', type: 'saturate', values: [0.5]}]}
|
||||
source={testSource}
|
||||
width={30}
|
||||
height={30}
|
||||
/>
|
||||
);
|
||||
|
||||
const samples = [FilterImageRemoteExample, FilterImageFewFiltersExample];
|
||||
export {icon, samples};
|
||||
4
apps/examples/src/examples/FilterImage/examples.tsx
Normal file
4
apps/examples/src/examples/FilterImage/examples.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
import * as LocalImage from './LocalImage';
|
||||
import * as RemoteImage from './RemoteImage';
|
||||
import * as FilterPicker from './FilterPicker';
|
||||
export {LocalImage, RemoteImage, FilterPicker};
|
||||
65
apps/examples/src/examples/FilterImage/index.tsx
Normal file
65
apps/examples/src/examples/FilterImage/index.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import React from 'react';
|
||||
import {StyleSheet, Text, TouchableHighlight, View} from 'react-native';
|
||||
|
||||
import {FilterImage} from 'react-native-svg/filter-image';
|
||||
import {commonStyles} from '../../commonStyles';
|
||||
import * as examples from './examples';
|
||||
|
||||
const FilterImageList = () => {
|
||||
const [example, setExample] = React.useState<keyof typeof examples | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
if (example) {
|
||||
return (
|
||||
<>
|
||||
{examples[example].samples.map((Sample, i) => (
|
||||
<View style={commonStyles.example} key={`sample-${i}`}>
|
||||
<Text style={commonStyles.sampleTitle}>{Sample.title}</Text>
|
||||
<Sample />
|
||||
</View>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={commonStyles.welcome}>Filter Image</Text>
|
||||
{Object.keys(examples).map((element, i) => {
|
||||
const name = element as keyof typeof examples;
|
||||
return (
|
||||
<TouchableHighlight
|
||||
style={styles.link}
|
||||
underlayColor="#ccc"
|
||||
key={`example-${name}`}
|
||||
onPress={() => setExample(name)}>
|
||||
<View style={commonStyles.cell}>
|
||||
{examples[name].icon}
|
||||
<Text style={commonStyles.title}>{name}</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
FilterImageList.title = '';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {width: '100%'},
|
||||
link: {height: 40},
|
||||
});
|
||||
|
||||
const icon = (
|
||||
<FilterImage
|
||||
filters={[{name: 'colorMatrix', type: 'saturate', values: [0.5]}]}
|
||||
source={require('../../assets/image.jpg')}
|
||||
width={30}
|
||||
height={30}
|
||||
/>
|
||||
);
|
||||
|
||||
const samples = [FilterImageList];
|
||||
|
||||
export {icon, samples};
|
||||
130
apps/examples/src/examples/Filters/FeColorMatrix.tsx
Normal file
130
apps/examples/src/examples/Filters/FeColorMatrix.tsx
Normal file
@@ -0,0 +1,130 @@
|
||||
import React, {Component} from 'react';
|
||||
import {Svg, Circle, FeColorMatrix, Filter, G} from 'react-native-svg';
|
||||
|
||||
class ReferenceExample extends Component {
|
||||
static title = 'Reference';
|
||||
render() {
|
||||
return (
|
||||
<Svg height="150" width="150">
|
||||
<Circle cx="75" cy="50" r="40" fill="blue" fillOpacity="0.5" />
|
||||
<Circle cx="55" cy="90" r="40" fill="green" fillOpacity="0.5" />
|
||||
<Circle cx="95" cy="90" r="40" fill="red" fillOpacity="0.5" />
|
||||
</Svg>
|
||||
);
|
||||
}
|
||||
}
|
||||
class IdentityExample extends Component {
|
||||
static title = 'Identity matrix';
|
||||
render() {
|
||||
return (
|
||||
<Svg height="150" width="150">
|
||||
<Filter id="filter">
|
||||
<FeColorMatrix values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0" />
|
||||
</Filter>
|
||||
<G filter="url(#filter)">
|
||||
<Circle cx="75" cy="50" r="40" fill="blue" fillOpacity="0.5" />
|
||||
<Circle cx="55" cy="90" r="40" fill="green" fillOpacity="0.5" />
|
||||
<Circle cx="95" cy="90" r="40" fill="red" fillOpacity="0.5" />
|
||||
</G>
|
||||
</Svg>
|
||||
);
|
||||
}
|
||||
}
|
||||
class RgbToGreenExample extends Component {
|
||||
static title = 'RGB to Green';
|
||||
render() {
|
||||
return (
|
||||
<Svg height="150" width="150">
|
||||
<Filter id="filter">
|
||||
<FeColorMatrix
|
||||
values="0 0 0 0 0
|
||||
1 1 1 1 0
|
||||
0 0 0 0 0
|
||||
0 0 0 1 0"
|
||||
/>
|
||||
</Filter>
|
||||
<G filter="url(#filter)">
|
||||
<Circle cx="75" cy="50" r="40" fill="blue" fillOpacity="0.5" />
|
||||
<Circle cx="55" cy="90" r="40" fill="green" fillOpacity="0.5" />
|
||||
<Circle cx="95" cy="90" r="40" fill="red" fillOpacity="0.5" />
|
||||
</G>
|
||||
</Svg>
|
||||
);
|
||||
}
|
||||
}
|
||||
class SaturateExample extends Component {
|
||||
static title = 'Saturate';
|
||||
render() {
|
||||
return (
|
||||
<Svg height="150" width="150">
|
||||
<Filter id="filter">
|
||||
<FeColorMatrix type="saturate" values="0.2" />
|
||||
</Filter>
|
||||
<G filter="url(#filter)">
|
||||
<Circle cx="75" cy="50" r="40" fill="blue" fillOpacity="0.5" />
|
||||
<Circle cx="55" cy="90" r="40" fill="green" fillOpacity="0.5" />
|
||||
<Circle cx="95" cy="90" r="40" fill="red" fillOpacity="0.5" />
|
||||
</G>
|
||||
</Svg>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HueRotateExample extends Component {
|
||||
static title = 'Hue Rotate';
|
||||
render() {
|
||||
return (
|
||||
<Svg height="150" width="150">
|
||||
<Filter id="filter">
|
||||
<FeColorMatrix type="hueRotate" values="180" />
|
||||
</Filter>
|
||||
<G filter="url(#filter)">
|
||||
<Circle cx="75" cy="50" r="40" fill="blue" fillOpacity="0.5" />
|
||||
<Circle cx="55" cy="90" r="40" fill="green" fillOpacity="0.5" />
|
||||
<Circle cx="95" cy="90" r="40" fill="red" fillOpacity="0.5" />
|
||||
</G>
|
||||
</Svg>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LuminanceToAlphaExample extends Component {
|
||||
static title = 'Luminance to alpha';
|
||||
render() {
|
||||
return (
|
||||
<Svg height="150" width="150">
|
||||
<Filter id="filter">
|
||||
<FeColorMatrix type="luminanceToAlpha" />
|
||||
</Filter>
|
||||
<G filter="url(#filter)">
|
||||
<Circle cx="75" cy="50" r="40" fill="blue" fillOpacity="0.5" />
|
||||
<Circle cx="55" cy="90" r="40" fill="green" fillOpacity="0.5" />
|
||||
<Circle cx="95" cy="90" r="40" fill="red" fillOpacity="0.5" />
|
||||
</G>
|
||||
</Svg>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const icon = (
|
||||
<Svg height="30" width="30" viewBox="0 0 20 20">
|
||||
<Filter id="filter">
|
||||
<FeColorMatrix values="0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 1 0" />
|
||||
</Filter>
|
||||
<G filter="url(#filter)">
|
||||
<Circle cx="10" cy="7.5" r="5" fill="blue" fillOpacity="0.5" />
|
||||
<Circle cx="7.5" cy="12.5" r="5" fill="green" fillOpacity="0.5" />
|
||||
<Circle cx="12.5" cy="12.5" r="5" fill="red" fillOpacity="0.5" />
|
||||
</G>
|
||||
</Svg>
|
||||
);
|
||||
|
||||
const samples = [
|
||||
ReferenceExample,
|
||||
IdentityExample,
|
||||
RgbToGreenExample,
|
||||
SaturateExample,
|
||||
HueRotateExample,
|
||||
LuminanceToAlphaExample,
|
||||
];
|
||||
export {icon, samples};
|
||||
@@ -0,0 +1,56 @@
|
||||
import React, {useEffect} from 'react';
|
||||
import Animated, {
|
||||
AnimatedProps,
|
||||
useAnimatedProps,
|
||||
useSharedValue,
|
||||
withRepeat,
|
||||
withTiming,
|
||||
} from 'react-native-reanimated';
|
||||
import {
|
||||
Circle,
|
||||
FeColorMatrix,
|
||||
FeColorMatrixProps,
|
||||
Filter,
|
||||
Image,
|
||||
Svg,
|
||||
} from 'react-native-svg';
|
||||
|
||||
const AnimatedFeColorMatrix = Animated.createAnimatedComponent(
|
||||
FeColorMatrix as any,
|
||||
) as React.FunctionComponent<AnimatedProps<FeColorMatrixProps>>;
|
||||
const ReanimatedHueRotateExample = () => {
|
||||
const hue = useSharedValue(0);
|
||||
|
||||
useEffect(() => {
|
||||
hue.value = withRepeat(withTiming(360, {duration: 2000}), -1, true);
|
||||
}, []);
|
||||
const animatedProps = useAnimatedProps(() => {
|
||||
return {values: [hue.value]};
|
||||
});
|
||||
|
||||
return (
|
||||
<Svg height="100" width="150">
|
||||
<Filter id="filter">
|
||||
<AnimatedFeColorMatrix type="hueRotate" animatedProps={animatedProps} />
|
||||
</Filter>
|
||||
<Image
|
||||
href="https://cdn.pixabay.com/photo/2024/05/26/00/40/lizard-8787888_1280.jpg"
|
||||
height="100"
|
||||
width="150"
|
||||
filter="url(#filter)"
|
||||
/>
|
||||
</Svg>
|
||||
);
|
||||
};
|
||||
ReanimatedHueRotateExample.title = 'Reanimated Hue Rotate';
|
||||
|
||||
const icon = (
|
||||
<Svg height="30" width="30" viewBox="0 0 20 20">
|
||||
<Circle cx="10" cy="7.5" r="5" fill="blue" fillOpacity="0.5" />
|
||||
<Circle cx="7.5" cy="12.5" r="5" fill="green" fillOpacity="0.5" />
|
||||
<Circle cx="12.5" cy="12.5" r="5" fill="red" fillOpacity="0.5" />
|
||||
</Svg>
|
||||
);
|
||||
|
||||
const samples = [ReanimatedHueRotateExample];
|
||||
export {icon, samples};
|
||||
3
apps/examples/src/examples/Filters/examples.tsx
Normal file
3
apps/examples/src/examples/Filters/examples.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
import * as FeColorMatrix from './FeColorMatrix';
|
||||
import * as ReanimatedFeColorMatrix from './ReanimatedFeColorMatrix';
|
||||
export {FeColorMatrix, ReanimatedFeColorMatrix};
|
||||
64
apps/examples/src/examples/Filters/index.tsx
Normal file
64
apps/examples/src/examples/Filters/index.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import React from 'react';
|
||||
import {StyleSheet, Text, TouchableHighlight, View} from 'react-native';
|
||||
import {Circle, Svg} from 'react-native-svg';
|
||||
|
||||
import * as examples from './examples';
|
||||
import {commonStyles} from '../../commonStyles';
|
||||
|
||||
const FiltersList = () => {
|
||||
const [example, setExample] = React.useState<keyof typeof examples | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
if (example) {
|
||||
return (
|
||||
<>
|
||||
{examples[example].samples.map((Sample, i) => (
|
||||
<View style={commonStyles.example} key={`sample-${i}`}>
|
||||
<Text style={commonStyles.sampleTitle}>{Sample.title}</Text>
|
||||
<Sample />
|
||||
</View>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={commonStyles.welcome}>SVG Filters</Text>
|
||||
{Object.keys(examples).map((element, i) => {
|
||||
const name = element as keyof typeof examples;
|
||||
return (
|
||||
<TouchableHighlight
|
||||
style={styles.link}
|
||||
underlayColor="#ccc"
|
||||
key={`example-${name}`}
|
||||
onPress={() => setExample(name)}>
|
||||
<View style={commonStyles.cell}>
|
||||
{examples[name].icon}
|
||||
<Text style={commonStyles.title}>{name}</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
FiltersList.title = '';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {width: '100%'},
|
||||
link: {height: 40},
|
||||
});
|
||||
|
||||
const icon = (
|
||||
<Svg height="30" width="30" viewBox="0 0 20 20">
|
||||
<Circle cx="10" cy="7.5" r="5" fill="blue" fillOpacity="0.5" />
|
||||
<Circle cx="7.5" cy="12.5" r="5" fill="green" fillOpacity="0.5" />
|
||||
<Circle cx="12.5" cy="12.5" r="5" fill="red" fillOpacity="0.5" />
|
||||
</Svg>
|
||||
);
|
||||
|
||||
const samples = [FiltersList];
|
||||
|
||||
export {icon, samples};
|
||||
6
filter-image/package.json
Normal file
6
filter-image/package.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"main": "../lib/commonjs/filter-image/index",
|
||||
"module": "../lib/module/filter-image/index",
|
||||
"react-native": "../src/filter-image/index",
|
||||
"types": "../lib/typescript/filter-image/index"
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
"lib",
|
||||
"src",
|
||||
"css",
|
||||
"filter-image",
|
||||
"RNSVG.podspec",
|
||||
"!android/build",
|
||||
"windows",
|
||||
@@ -64,7 +65,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"css-select": "^5.1.0",
|
||||
"css-tree": "^1.1.3"
|
||||
"css-tree": "^1.1.3",
|
||||
"warn-once": "0.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/eslint-config": "^3.0.2",
|
||||
|
||||
@@ -9,34 +9,38 @@ try {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
dependency: {
|
||||
platforms: {
|
||||
android: supportsCodegenConfig ? {
|
||||
componentDescriptors: [
|
||||
"RNSVGCircleComponentDescriptor",
|
||||
"RNSVGClipPathComponentDescriptor",
|
||||
"RNSVGDefsComponentDescriptor",
|
||||
"RNSVGEllipseComponentDescriptor",
|
||||
"RNSVGForeignObjectComponentDescriptor",
|
||||
"RNSVGGroupComponentDescriptor",
|
||||
"RNSVGImageComponentDescriptor",
|
||||
"RNSVGLinearGradientComponentDescriptor",
|
||||
"RNSVGLineComponentDescriptor",
|
||||
"RNSVGMarkerComponentDescriptor",
|
||||
"RNSVGMaskComponentDescriptor",
|
||||
"RNSVGPathComponentDescriptor",
|
||||
"RNSVGPatternComponentDescriptor",
|
||||
"RNSVGRadialGradientComponentDescriptor",
|
||||
"RNSVGRectComponentDescriptor",
|
||||
"RNSVGSvgViewAndroidComponentDescriptor",
|
||||
"RNSVGSymbolComponentDescriptor",
|
||||
"RNSVGTextComponentDescriptor",
|
||||
"RNSVGTextPathComponentDescriptor",
|
||||
"RNSVGTSpanComponentDescriptor",
|
||||
"RNSVGUseComponentDescriptor"
|
||||
],
|
||||
cmakeListsPath: "../android/src/main/jni/CMakeLists.txt"
|
||||
} : {},
|
||||
},
|
||||
dependency: {
|
||||
platforms: {
|
||||
android: supportsCodegenConfig
|
||||
? {
|
||||
componentDescriptors: [
|
||||
'RNSVGCircleComponentDescriptor',
|
||||
'RNSVGClipPathComponentDescriptor',
|
||||
'RNSVGDefsComponentDescriptor',
|
||||
'RNSVGFeColorMatrixComponentDescriptor',
|
||||
'RNSVGFilterComponentDescriptor',
|
||||
'RNSVGEllipseComponentDescriptor',
|
||||
'RNSVGForeignObjectComponentDescriptor',
|
||||
'RNSVGGroupComponentDescriptor',
|
||||
'RNSVGImageComponentDescriptor',
|
||||
'RNSVGLinearGradientComponentDescriptor',
|
||||
'RNSVGLineComponentDescriptor',
|
||||
'RNSVGMarkerComponentDescriptor',
|
||||
'RNSVGMaskComponentDescriptor',
|
||||
'RNSVGPathComponentDescriptor',
|
||||
'RNSVGPatternComponentDescriptor',
|
||||
'RNSVGRadialGradientComponentDescriptor',
|
||||
'RNSVGRectComponentDescriptor',
|
||||
'RNSVGSvgViewAndroidComponentDescriptor',
|
||||
'RNSVGSymbolComponentDescriptor',
|
||||
'RNSVGTextComponentDescriptor',
|
||||
'RNSVGTextPathComponentDescriptor',
|
||||
'RNSVGTSpanComponentDescriptor',
|
||||
'RNSVGUseComponentDescriptor',
|
||||
],
|
||||
cmakeListsPath: '../android/src/main/jni/CMakeLists.txt',
|
||||
}
|
||||
: {},
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
BIN
screenshots/feColorMatrix.png
Normal file
BIN
screenshots/feColorMatrix.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
BIN
screenshots/filterImage.png
Normal file
BIN
screenshots/filterImage.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 172 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user