chore: add CI for JS, iOS and Android formatting (#1782)

Added CI workflow and local pre-commit hook for formatting and linting the newly added JS, iOS and Android code.
This commit is contained in:
Wojciech Lewicki
2022-08-16 12:00:32 +02:00
committed by GitHub
parent 77267be5fc
commit 98c14b4f45
177 changed files with 16855 additions and 16018 deletions

91
.clang-format Normal file
View File

@@ -0,0 +1,91 @@
---
AccessModifierOffset: -1
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: true
AlignOperands: false
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: true
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ForEachMacros: [ FOR_EACH_RANGE, FOR_EACH, ]
IncludeCategories:
- Regex: '^<.*\.h(pp)?>'
Priority: 1
- Regex: '^<.*'
Priority: 2
- Regex: '.*'
Priority: 3
IndentCaseLabels: true
IndentWidth: 2
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Right
ReflowComments: true
SortIncludes: true
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 8
UseTab: Never
---
Language: ObjC
ColumnLimit: 120
BreakBeforeBraces: WebKit
...

View File

@@ -25,10 +25,8 @@ jobs:
run: yarn
- name: Build
run: yarn bob
- name: Lint
run: yarn lint
- name: Tests
run: yarn jest
- name: Test and lint
run: yarn test
- name: Build Example App
working-directory: Example/
run: yarn && yarn tsc

4
.husky/pre-commit Executable file
View File

@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
yarn lint-staged

View File

@@ -501,7 +501,7 @@ SPEC CHECKSUMS:
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
FBLazyVector: bcdeff523be9f87a135b7c6fde8736db94904716
FBReactNativeSpec: 226f8b0f1a2e736a49301883ee34bca88cdc24f6
FBReactNativeSpec: 0c3f104f594b34d7b3a923cd12e03b0d4e12eaf5
Flipper: 26fc4b7382499f1281eb8cb921e5c3ad6de91fe0
Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c
Flipper-DoubleConversion: 57ffbe81ef95306cc9e69c4aa3aeeeeb58a6a28c

View File

@@ -105,14 +105,14 @@ export default function TestComponent() {
};
return (
<>
<SvgUri
width="100"
height="100"
uri="https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/ruby.svg"
onError={onError}
onLoad={onLoad}
/>
{loading && <ActivityIndicator size="large" color="#0000ff"/>}
<SvgUri
width="100"
height="100"
uri="https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/ruby.svg"
onError={onError}
onLoad={onLoad}
/>
{loading && <ActivityIndicator size="large" color="#0000ff" />}
</>
);
}
@@ -129,16 +129,20 @@ import * as React from 'react';
import { SvgUri } from 'react-native-svg';
export default () => {
const [uri, setUri] = React.useState('https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/not_existing.svg')
const [uri, setUri] = React.useState(
'https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/not_existing.svg',
);
return (
<SvgUri
onError={() => setUri('https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/ruby.svg')}
onError={() =>
setUri('https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/ruby.svg')
}
width="100%"
height="100%"
uri={uri}
/>
);
}
};
```
# Use with svg files

View File

@@ -10,6 +10,7 @@ buildscript {
dependencies {
classpath("com.android.tools.build:gradle:3.6.1")
classpath "com.diffplug.spotless:spotless-plugin-gradle:5.15.0"
}
}
}
@@ -26,6 +27,10 @@ if (isNewArchitectureEnabled()) {
apply plugin: "com.facebook.react"
}
if (project == rootProject) {
apply from: 'spotless.gradle'
}
apply plugin: 'com.android.library'
def safeExtGet(prop, fallback) {

Binary file not shown.

View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

240
android/gradlew vendored Executable file
View File

@@ -0,0 +1,240 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

91
android/gradlew.bat vendored Normal file
View File

@@ -0,0 +1,91 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

9
android/spotless.gradle Normal file
View File

@@ -0,0 +1,9 @@
// formatter & linter configuration for java
apply plugin: 'com.diffplug.spotless'
spotless {
java {
target 'src/fabric/**/*.java', 'src/main/java/**/*.java', 'src/paper/java/com/horcrux/svg/**/*.java'
googleJavaFormat()
}
}

View File

@@ -1,59 +1,62 @@
package com.horcrux.svg;
import android.content.Context;
import android.view.ViewGroup;
import androidx.annotation.UiThread;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.uimanager.FabricViewStateManager;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.FabricViewStateManager.HasFabricViewStateManager;
import com.facebook.react.uimanager.FabricViewStateManager.StateUpdateCallback;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.views.view.ReactViewGroup;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class FabricEnabledViewGroup extends ReactViewGroup implements HasFabricViewStateManager {
private final FabricViewStateManager mFabricViewStateManager = new FabricViewStateManager();
public abstract class FabricEnabledViewGroup extends ReactViewGroup
implements HasFabricViewStateManager {
private final FabricViewStateManager mFabricViewStateManager = new FabricViewStateManager();
@NotNull
public FabricViewStateManager getFabricViewStateManager() {
return this.mFabricViewStateManager;
}
@NotNull
public FabricViewStateManager getFabricViewStateManager() {
return this.mFabricViewStateManager;
}
protected final void updateScreenSizeFabric(int width, int height) {
this.updateState(width, height);
}
protected final void updateScreenSizeFabric(int width, int height) {
this.updateState(width, height);
}
@UiThread
public final void updateState(int width, int height) {
final float realWidth = PixelUtil.toDIPFromPixel((float)width);
final float realHeight = PixelUtil.toDIPFromPixel((float)height);
ReadableMap currentState = this.mFabricViewStateManager.getStateData();
if (currentState != null) {
float delta = 0.9F;
float stateFrameHeight = currentState.hasKey("frameHeight") ? (float)currentState.getDouble("frameHeight") : 0.0F;
float stateFrameWidth = currentState.hasKey("frameWidth") ? (float)currentState.getDouble("frameWidth") : 0.0F;
if (Math.abs(stateFrameWidth - realWidth) < delta &&
Math.abs(stateFrameHeight - realHeight) < delta) {
return;
}
@UiThread
public final void updateState(int width, int height) {
final float realWidth = PixelUtil.toDIPFromPixel((float) width);
final float realHeight = PixelUtil.toDIPFromPixel((float) height);
ReadableMap currentState = this.mFabricViewStateManager.getStateData();
if (currentState != null) {
float delta = 0.9F;
float stateFrameHeight =
currentState.hasKey("frameHeight") ? (float) currentState.getDouble("frameHeight") : 0.0F;
float stateFrameWidth =
currentState.hasKey("frameWidth") ? (float) currentState.getDouble("frameWidth") : 0.0F;
if (Math.abs(stateFrameWidth - realWidth) < delta
&& Math.abs(stateFrameHeight - realHeight) < delta) {
return;
}
}
this.mFabricViewStateManager.setState((StateUpdateCallback)(new StateUpdateCallback() {
public final WritableMap getStateUpdate() {
WritableMap map = (WritableMap)(new WritableNativeMap());
map.putDouble("frameWidth", (double)realWidth);
map.putDouble("frameHeight", (double)realHeight);
return map;
}
}));
}
this.mFabricViewStateManager.setState(
(StateUpdateCallback)
(new StateUpdateCallback() {
public final WritableMap getStateUpdate() {
WritableMap map = (WritableMap) (new WritableNativeMap());
map.putDouble("frameWidth", (double) realWidth);
map.putDouble("frameHeight", (double) realHeight);
return map;
}
}));
}
public FabricEnabledViewGroup(@Nullable ReactContext context) {
super((Context)context);
}
public FabricEnabledViewGroup(@Nullable ReactContext context) {
super((Context) context);
}
}

View File

@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.graphics.Bitmap;
@@ -19,215 +18,214 @@ import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.common.ReactConstants;
class Brush {
private final BrushType mType;
private final SVGLength[] mPoints;
private ReadableArray mColors;
private final boolean mUseObjectBoundingBox;
private final BrushType mType;
private final SVGLength[] mPoints;
private ReadableArray mColors;
private final boolean mUseObjectBoundingBox;
// TODO implement pattern units
@SuppressWarnings({"unused"})
private boolean mUseContentObjectBoundingBoxUnits;
// TODO implement pattern units
@SuppressWarnings({"unused"})
private boolean mUseContentObjectBoundingBoxUnits;
private Matrix mMatrix;
private Rect mUserSpaceBoundingBox;
private PatternView mPattern;
private Matrix mMatrix;
private Rect mUserSpaceBoundingBox;
private PatternView mPattern;
Brush(BrushType type, SVGLength[] points, BrushUnits units) {
mType = type;
mPoints = points;
mUseObjectBoundingBox = units == BrushUnits.OBJECT_BOUNDING_BOX;
Brush(BrushType type, SVGLength[] points, BrushUnits units) {
mType = type;
mPoints = points;
mUseObjectBoundingBox = units == BrushUnits.OBJECT_BOUNDING_BOX;
}
void setContentUnits(BrushUnits units) {
mUseContentObjectBoundingBoxUnits = units == BrushUnits.OBJECT_BOUNDING_BOX;
}
void setPattern(PatternView pattern) {
mPattern = pattern;
}
enum BrushType {
LINEAR_GRADIENT,
RADIAL_GRADIENT,
PATTERN
}
enum BrushUnits {
OBJECT_BOUNDING_BOX,
USER_SPACE_ON_USE
}
private static void parseGradientStops(
ReadableArray value, int stopsCount, float[] stops, int[] stopsColors, float opacity) {
for (int i = 0; i < stopsCount; i++) {
int stopIndex = i * 2;
stops[i] = (float) value.getDouble(stopIndex);
int color = value.getInt(stopIndex + 1);
int alpha = color >>> 24;
int combined = Math.round((float) alpha * opacity);
stopsColors[i] = combined << 24 | (color & 0x00ffffff);
}
}
void setUserSpaceBoundingBox(Rect userSpaceBoundingBox) {
mUserSpaceBoundingBox = userSpaceBoundingBox;
}
void setGradientColors(ReadableArray colors) {
mColors = colors;
}
void setGradientTransform(Matrix matrix) {
mMatrix = matrix;
}
private RectF getPaintRect(RectF pathBoundingBox) {
RectF rect = mUseObjectBoundingBox ? pathBoundingBox : new RectF(mUserSpaceBoundingBox);
float width = rect.width();
float height = rect.height();
float x = 0f;
float y = 0f;
if (mUseObjectBoundingBox) {
x = rect.left;
y = rect.top;
}
void setContentUnits(BrushUnits units) {
mUseContentObjectBoundingBoxUnits = units == BrushUnits.OBJECT_BOUNDING_BOX;
return new RectF(x, y, x + width, y + height);
}
private double getVal(SVGLength length, double relative, float scale, float textSize) {
return PropHelper.fromRelative(
length,
relative,
0,
mUseObjectBoundingBox && length.unit == SVGLength.UnitType.NUMBER ? relative : scale,
textSize);
}
void setupPaint(Paint paint, RectF pathBoundingBox, float scale, float opacity) {
RectF rect = getPaintRect(pathBoundingBox);
float width = rect.width();
float height = rect.height();
float offsetX = rect.left;
float offsetY = rect.top;
float textSize = paint.getTextSize();
if (mType == BrushType.PATTERN) {
double x = getVal(mPoints[0], width, scale, textSize);
double y = getVal(mPoints[1], height, scale, textSize);
double w = getVal(mPoints[2], width, scale, textSize);
double h = getVal(mPoints[3], height, scale, textSize);
if (!(w > 1 && h > 1)) {
return;
}
Bitmap bitmap = Bitmap.createBitmap((int) w, (int) h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
RectF vbRect = mPattern.getViewBox();
if (vbRect != null && vbRect.width() > 0 && vbRect.height() > 0) {
RectF eRect = new RectF((float) x, (float) y, (float) w, (float) h);
Matrix mViewBoxMatrix =
ViewBox.getTransform(vbRect, eRect, mPattern.mAlign, mPattern.mMeetOrSlice);
canvas.concat(mViewBoxMatrix);
}
if (mUseContentObjectBoundingBoxUnits) {
canvas.scale(width / scale, height / scale);
}
mPattern.draw(canvas, new Paint(), opacity);
Matrix patternMatrix = new Matrix();
if (mMatrix != null) {
patternMatrix.preConcat(mMatrix);
}
BitmapShader bitmapShader =
new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
bitmapShader.setLocalMatrix(patternMatrix);
paint.setShader(bitmapShader);
return;
}
void setPattern(PatternView pattern) {
mPattern = pattern;
int size = mColors.size();
if (size == 0) {
FLog.w(ReactConstants.TAG, "Gradient contains no stops");
return;
}
int stopsCount = size / 2;
int[] stopsColors = new int[stopsCount];
float[] stops = new float[stopsCount];
parseGradientStops(mColors, stopsCount, stops, stopsColors, opacity);
if (stops.length == 1) {
// Gradient with only one stop will make LinearGradient/RadialGradient
// throw. It may happen when source SVG contains only one stop or
// two stops at the same spot (see lib/extract/extractGradient.js).
// Although it's mistake SVGs like this can be produced by vector
// editors or other tools, so let's handle that gracefully.
stopsColors = new int[] {stopsColors[0], stopsColors[0]};
stops = new float[] {stops[0], stops[0]};
FLog.w(ReactConstants.TAG, "Gradient contains only one stop");
}
enum BrushType {
LINEAR_GRADIENT,
RADIAL_GRADIENT,
PATTERN
}
enum BrushUnits {
OBJECT_BOUNDING_BOX,
USER_SPACE_ON_USE
}
private static void parseGradientStops(ReadableArray value, int stopsCount, float[] stops, int[] stopsColors, float opacity) {
for (int i = 0; i < stopsCount; i++) {
int stopIndex = i * 2;
stops[i] = (float) value.getDouble(stopIndex);
int color = value.getInt(stopIndex + 1);
int alpha = color >>> 24;
int combined = Math.round((float)alpha * opacity);
stopsColors[i] = combined << 24 | (color & 0x00ffffff);
}
}
void setUserSpaceBoundingBox(Rect userSpaceBoundingBox) {
mUserSpaceBoundingBox = userSpaceBoundingBox;
}
void setGradientColors(ReadableArray colors) {
mColors = colors;
}
void setGradientTransform(Matrix matrix) {
mMatrix = matrix;
}
private RectF getPaintRect(RectF pathBoundingBox) {
RectF rect = mUseObjectBoundingBox ? pathBoundingBox : new RectF(mUserSpaceBoundingBox);
float width = rect.width();
float height = rect.height();
float x = 0f;
float y = 0f;
if (mUseObjectBoundingBox) {
x = rect.left;
y = rect.top;
}
return new RectF(x, y, x + width, y + height);
}
private double getVal(SVGLength length, double relative, float scale, float textSize) {
return PropHelper.fromRelative(length, relative, 0, mUseObjectBoundingBox &&
length.unit == SVGLength.UnitType.NUMBER ? relative : scale, textSize);
}
void setupPaint(Paint paint, RectF pathBoundingBox, float scale, float opacity) {
RectF rect = getPaintRect(pathBoundingBox);
float width = rect.width();
float height = rect.height();
float offsetX = rect.left;
float offsetY = rect.top;
float textSize = paint.getTextSize();
if (mType == BrushType.PATTERN) {
double x = getVal(mPoints[0], width, scale, textSize);
double y = getVal(mPoints[1], height, scale, textSize);
double w = getVal(mPoints[2], width, scale, textSize);
double h = getVal(mPoints[3], height, scale, textSize);
if (!(w > 1 && h > 1)) {
return;
}
Bitmap bitmap = Bitmap.createBitmap(
(int) w,
(int) h,
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
RectF vbRect = mPattern.getViewBox();
if (vbRect != null && vbRect.width() > 0 && vbRect.height() > 0) {
RectF eRect = new RectF((float) x, (float) y, (float) w, (float) h);
Matrix mViewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mPattern.mAlign, mPattern.mMeetOrSlice);
canvas.concat(mViewBoxMatrix);
}
if (mUseContentObjectBoundingBoxUnits) {
canvas.scale(width / scale, height / scale);
}
mPattern.draw(canvas, new Paint(), opacity);
Matrix patternMatrix = new Matrix();
if (mMatrix != null) {
patternMatrix.preConcat(mMatrix);
}
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
bitmapShader.setLocalMatrix(patternMatrix);
paint.setShader(bitmapShader);
return;
}
int size = mColors.size();
if (size == 0) {
FLog.w(ReactConstants.TAG, "Gradient contains no stops");
return;
}
int stopsCount = size / 2;
int[] stopsColors = new int[stopsCount];
float[] stops = new float[stopsCount];
parseGradientStops(mColors, stopsCount, stops, stopsColors, opacity);
if (stops.length == 1) {
// Gradient with only one stop will make LinearGradient/RadialGradient
// throw. It may happen when source SVG contains only one stop or
// two stops at the same spot (see lib/extract/extractGradient.js).
// Although it's mistake SVGs like this can be produced by vector
// editors or other tools, so let's handle that gracefully.
stopsColors = new int[] { stopsColors[0], stopsColors[0] };
stops = new float[] { stops[0], stops[0] };
FLog.w(ReactConstants.TAG, "Gradient contains only one stop");
}
if (mType == BrushType.LINEAR_GRADIENT) {
double x1 = getVal(mPoints[0], width, scale, textSize) + offsetX;
double y1 = getVal(mPoints[1], height, scale, textSize) + offsetY;
double x2 = getVal(mPoints[2], width, scale, textSize) + offsetX;
double y2 = getVal(mPoints[3], height, scale, textSize) + offsetY;
Shader linearGradient = new LinearGradient(
(float) x1,
(float) y1,
(float) x2,
(float) y2,
stopsColors,
stops,
Shader.TileMode.CLAMP);
if (mMatrix != null) {
Matrix m = new Matrix();
m.preConcat(mMatrix);
linearGradient.setLocalMatrix(m);
}
paint.setShader(linearGradient);
} else if (mType == BrushType.RADIAL_GRADIENT) {
double rx = getVal(mPoints[2], width, scale, textSize);
double ry = getVal(mPoints[3], height, scale, textSize);
double ratio = ry / rx;
double cx = getVal(mPoints[4], width, scale, textSize) + offsetX;
double cy = getVal(mPoints[5], height / ratio, scale, textSize) + offsetY / ratio;
// TODO: support focus point.
//double fx = PropHelper.fromRelative(mPoints[0], width, offsetX, scale);
//double fy = PropHelper.fromRelative(mPoints[1], height, offsetY, scale) / (ry / rx);
Shader radialGradient = new RadialGradient(
(float) cx,
(float) cy,
(float) rx,
stopsColors,
stops,
Shader.TileMode.CLAMP
);
Matrix radialMatrix = new Matrix();
radialMatrix.preScale(1f, (float) ratio);
if (mMatrix != null) {
radialMatrix.preConcat(mMatrix);
}
radialGradient.setLocalMatrix(radialMatrix);
paint.setShader(radialGradient);
}
if (mType == BrushType.LINEAR_GRADIENT) {
double x1 = getVal(mPoints[0], width, scale, textSize) + offsetX;
double y1 = getVal(mPoints[1], height, scale, textSize) + offsetY;
double x2 = getVal(mPoints[2], width, scale, textSize) + offsetX;
double y2 = getVal(mPoints[3], height, scale, textSize) + offsetY;
Shader linearGradient =
new LinearGradient(
(float) x1,
(float) y1,
(float) x2,
(float) y2,
stopsColors,
stops,
Shader.TileMode.CLAMP);
if (mMatrix != null) {
Matrix m = new Matrix();
m.preConcat(mMatrix);
linearGradient.setLocalMatrix(m);
}
paint.setShader(linearGradient);
} else if (mType == BrushType.RADIAL_GRADIENT) {
double rx = getVal(mPoints[2], width, scale, textSize);
double ry = getVal(mPoints[3], height, scale, textSize);
double ratio = ry / rx;
double cx = getVal(mPoints[4], width, scale, textSize) + offsetX;
double cy = getVal(mPoints[5], height / ratio, scale, textSize) + offsetY / ratio;
// TODO: support focus point.
// double fx = PropHelper.fromRelative(mPoints[0], width, offsetX, scale);
// double fy = PropHelper.fromRelative(mPoints[1], height, offsetY, scale) / (ry / rx);
Shader radialGradient =
new RadialGradient(
(float) cx, (float) cy, (float) rx, stopsColors, stops, Shader.TileMode.CLAMP);
Matrix radialMatrix = new Matrix();
radialMatrix.preScale(1f, (float) ratio);
if (mMatrix != null) {
radialMatrix.preConcat(mMatrix);
}
radialGradient.setLocalMatrix(radialMatrix);
paint.setShader(radialGradient);
}
}
}

View File

@@ -6,71 +6,68 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.annotation.SuppressLint;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
@SuppressLint("ViewConstructor")
class CircleView extends RenderableView {
private SVGLength mCx;
private SVGLength mCy;
private SVGLength mR;
private SVGLength mCx;
private SVGLength mCy;
private SVGLength mR;
public CircleView(ReactContext reactContext) {
super(reactContext);
}
public CircleView(ReactContext reactContext) {
super(reactContext);
}
@ReactProp(name = "cx")
public void setCx(Dynamic cx) {
mCx = SVGLength.from(cx);
invalidate();
}
@ReactProp(name = "cx")
public void setCx(Dynamic cx) {
mCx = SVGLength.from(cx);
invalidate();
}
public void setCx(String cx) {
mCx = SVGLength.from(cx);
invalidate();
}
@ReactProp(name = "cy")
public void setCy(Dynamic cy) {
mCy = SVGLength.from(cy);
invalidate();
}
@ReactProp(name = "cy")
public void setCy(Dynamic cy) {
mCy = SVGLength.from(cy);
invalidate();
}
public void setCy(String cy) {
mCy = SVGLength.from(cy);
invalidate();
}
@ReactProp(name = "r")
public void setR(Dynamic r) {
mR = SVGLength.from(r);
invalidate();
}
@ReactProp(name = "r")
public void setR(Dynamic r) {
mR = SVGLength.from(r);
invalidate();
}
public void setR(String r) {
mR = SVGLength.from(r);
invalidate();
}
@Override
Path getPath(Canvas canvas, Paint paint) {
Path path = new Path();
@Override
Path getPath(Canvas canvas, Paint paint) {
Path path = new Path();
double cx = relativeOnWidth(mCx);
double cy = relativeOnHeight(mCy);
double r = relativeOnOther(mR);
double cx = relativeOnWidth(mCx);
double cy = relativeOnHeight(mCy);
double r = relativeOnOther(mR);
path.addCircle((float) cx, (float) cy, (float) r, Path.Direction.CW);
return path;
}
path.addCircle((float) cx, (float) cy, (float) r, Path.Direction.CW);
return path;
}
}

View File

@@ -6,13 +6,11 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.annotation.SuppressLint;
import android.graphics.Canvas;
import android.graphics.Paint;
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.common.ReactConstants;
@@ -20,33 +18,35 @@ import com.facebook.react.common.ReactConstants;
@SuppressLint("ViewConstructor")
class ClipPathView extends GroupView {
public ClipPathView(ReactContext reactContext) {
super(reactContext);
}
public ClipPathView(ReactContext reactContext) {
super(reactContext);
}
@Override
void draw(Canvas canvas, Paint paint, float opacity) {
FLog.w(ReactConstants.TAG, "RNSVG: ClipPath can't be drawn, it should be defined as a child component for `Defs` ");
}
@Override
void draw(Canvas canvas, Paint paint, float opacity) {
FLog.w(
ReactConstants.TAG,
"RNSVG: ClipPath can't be drawn, it should be defined as a child component for `Defs` ");
}
@Override
void saveDefinition() {
getSvgView().defineClipPath(this, mName);
}
@Override
void saveDefinition() {
getSvgView().defineClipPath(this, mName);
}
@Override
boolean isResponsible() {
return false;
}
@Override
boolean isResponsible() {
return false;
}
@Override
int hitTest(float[] src) {
return -1;
}
@Override
int hitTest(float[] src) {
return -1;
}
@Override
void mergeProperties(RenderableView target) {}
@Override
void mergeProperties(RenderableView target) {}
@Override
void resetProperties() {}
@Override
void resetProperties() {}
}

View File

@@ -6,38 +6,36 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.annotation.SuppressLint;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import com.facebook.react.bridge.ReactContext;
@SuppressLint("ViewConstructor")
class DefinitionView extends VirtualView {
DefinitionView(ReactContext reactContext) {
super(reactContext);
}
DefinitionView(ReactContext reactContext) {
super(reactContext);
}
@SuppressWarnings("EmptyMethod")
void draw(Canvas canvas, Paint paint, float opacity) {}
@SuppressWarnings("EmptyMethod")
void draw(Canvas canvas, Paint paint, float opacity) {}
@Override
boolean isResponsible() {
return false;
}
@Override
boolean isResponsible() {
return false;
}
@Override
Path getPath(Canvas canvas, Paint paint) {
return null;
}
@Override
Path getPath(Canvas canvas, Paint paint) {
return null;
}
@Override
int hitTest(float[] src) {
return -1;
}
@Override
int hitTest(float[] src) {
return -1;
}
}

View File

@@ -6,32 +6,30 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.annotation.SuppressLint;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.View;
import com.facebook.react.bridge.ReactContext;
@SuppressLint("ViewConstructor")
class DefsView extends DefinitionView {
public DefsView(ReactContext reactContext) {
super(reactContext);
}
public DefsView(ReactContext reactContext) {
super(reactContext);
}
@Override
void draw(Canvas canvas, Paint paint, float opacity) {}
@Override
void draw(Canvas canvas, Paint paint, float opacity) {}
void saveDefinition() {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child instanceof VirtualView) {
((VirtualView)child).saveDefinition();
}
}
void saveDefinition() {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child instanceof VirtualView) {
((VirtualView) child).saveDefinition();
}
}
}
}

View File

@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.annotation.SuppressLint;
@@ -14,76 +13,76 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
@SuppressLint("ViewConstructor")
class EllipseView extends RenderableView {
private SVGLength mCx;
private SVGLength mCy;
private SVGLength mRx;
private SVGLength mRy;
private SVGLength mCx;
private SVGLength mCy;
private SVGLength mRx;
private SVGLength mRy;
public EllipseView(ReactContext reactContext) {
super(reactContext);
}
public EllipseView(ReactContext reactContext) {
super(reactContext);
}
@ReactProp(name = "cx")
public void setCx(Dynamic cx) {
mCx = SVGLength.from(cx);
invalidate();
}
@ReactProp(name = "cx")
public void setCx(Dynamic cx) {
mCx = SVGLength.from(cx);
invalidate();
}
public void setCx(String cx) {
mCx = SVGLength.from(cx);
invalidate();
}
@ReactProp(name = "cy")
public void setCy(Dynamic cy) {
mCy = SVGLength.from(cy);
invalidate();
}
@ReactProp(name = "cy")
public void setCy(Dynamic cy) {
mCy = SVGLength.from(cy);
invalidate();
}
public void setCy(String cy) {
mCy = SVGLength.from(cy);
invalidate();
}
@ReactProp(name = "rx")
public void setRx(Dynamic rx) {
mRx = SVGLength.from(rx);
invalidate();
}
@ReactProp(name = "rx")
public void setRx(Dynamic rx) {
mRx = SVGLength.from(rx);
invalidate();
}
public void setRx(String rx) {
mRx = SVGLength.from(rx);
invalidate();
}
@ReactProp(name = "ry")
public void setRy(Dynamic ry) {
mRy = SVGLength.from(ry);
invalidate();
}
@ReactProp(name = "ry")
public void setRy(Dynamic ry) {
mRy = SVGLength.from(ry);
invalidate();
}
public void setRy(String ry) {
mRy = SVGLength.from(ry);
invalidate();
}
@Override
Path getPath(Canvas canvas, Paint paint) {
Path path = new Path();
double cx = relativeOnWidth(mCx);
double cy = relativeOnHeight(mCy);
double rx = relativeOnWidth(mRx);
double ry = relativeOnHeight(mRy);
RectF oval = new RectF((float) (cx - rx), (float) (cy - ry), (float) (cx + rx), (float) (cy + ry));
path.addOval(oval, Path.Direction.CW);
@Override
Path getPath(Canvas canvas, Paint paint) {
Path path = new Path();
double cx = relativeOnWidth(mCx);
double cy = relativeOnHeight(mCy);
double rx = relativeOnWidth(mRx);
double ry = relativeOnHeight(mRy);
RectF oval =
new RectF((float) (cx - rx), (float) (cy - ry), (float) (cx + rx), (float) (cy + ry));
path.addOval(oval, Path.Direction.CW);
return path;
}
return path;
}
}

View File

@@ -1,214 +1,232 @@
package com.horcrux.svg;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableType;
import static com.facebook.react.uimanager.ViewProps.FONT_FAMILY;
import static com.facebook.react.uimanager.ViewProps.FONT_SIZE;
import static com.facebook.react.uimanager.ViewProps.FONT_STYLE;
import static com.facebook.react.uimanager.ViewProps.FONT_WEIGHT;
import static com.horcrux.svg.TextProperties.*;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableType;
class FontData {
static class AbsoluteFontWeight {
static class AbsoluteFontWeight {
static final int normal = 400;
static final int normal = 400;
private static final FontWeight[] WEIGHTS = new FontWeight[]{
FontWeight.w100,
FontWeight.w100,
FontWeight.w200,
FontWeight.w300,
FontWeight.Normal,
FontWeight.w500,
FontWeight.w600,
FontWeight.Bold,
FontWeight.w800,
FontWeight.w900,
FontWeight.w900,
private static final FontWeight[] WEIGHTS =
new FontWeight[] {
FontWeight.w100,
FontWeight.w100,
FontWeight.w200,
FontWeight.w300,
FontWeight.Normal,
FontWeight.w500,
FontWeight.w600,
FontWeight.Bold,
FontWeight.w800,
FontWeight.w900,
FontWeight.w900,
};
static FontWeight nearestFontWeight(int absoluteFontWeight) {
return WEIGHTS[Math.round(absoluteFontWeight / 100f)];
}
private static final int[] absoluteFontWeights = new int[]{
400, 700, 100, 200, 300, 400, 500, 600, 700, 800, 900
};
// https://drafts.csswg.org/css-fonts-4/#relative-weights
static int from(FontWeight fontWeight, FontData parent) {
if (fontWeight == FontWeight.Bolder) {
return bolder(parent.absoluteFontWeight);
} else if (fontWeight == FontWeight.Lighter) {
return lighter(parent.absoluteFontWeight);
} else {
return absoluteFontWeights[fontWeight.ordinal()];
}
}
private static int bolder(int inherited) {
if (inherited < 350) {
return 400;
} else if (inherited < 550) {
return 700;
} else if (inherited < 900) {
return 900;
} else {
return inherited;
}
}
private static int lighter(int inherited) {
if (inherited < 100) {
return inherited;
} else if (inherited < 550) {
return 100;
} else if (inherited < 750) {
return 400;
} else {
return 700;
}
}
static FontWeight nearestFontWeight(int absoluteFontWeight) {
return WEIGHTS[Math.round(absoluteFontWeight / 100f)];
}
static final double DEFAULT_FONT_SIZE = 12d;
private static final int[] absoluteFontWeights =
new int[] {400, 700, 100, 200, 300, 400, 500, 600, 700, 800, 900};
private static final double DEFAULT_KERNING = 0d;
private static final double DEFAULT_WORD_SPACING = 0d;
private static final double DEFAULT_LETTER_SPACING = 0d;
private static final String KERNING = "kerning";
private static final String FONT_DATA = "fontData";
private static final String TEXT_ANCHOR = "textAnchor";
private static final String WORD_SPACING = "wordSpacing";
private static final String LETTER_SPACING = "letterSpacing";
private static final String TEXT_DECORATION = "textDecoration";
private static final String FONT_FEATURE_SETTINGS = "fontFeatureSettings";
private static final String FONT_VARIATION_SETTINGS = "fontVariationSettings";
private static final String FONT_VARIANT_LIGATURES = "fontVariantLigatures";
final double fontSize;
final String fontFamily;
final FontStyle fontStyle;
final ReadableMap fontData;
FontWeight fontWeight;
int absoluteFontWeight;
final String fontFeatureSettings;
final String fontVariationSettings;
final FontVariantLigatures fontVariantLigatures;
final TextAnchor textAnchor;
private final TextDecoration textDecoration;
final double kerning;
final double wordSpacing;
final double letterSpacing;
final boolean manualKerning;
static final FontData Defaults = new FontData();
private FontData() {
fontData = null;
fontFamily = "";
fontStyle = FontStyle.normal;
fontWeight = FontWeight.Normal;
absoluteFontWeight = AbsoluteFontWeight.normal;
fontFeatureSettings = "";
fontVariationSettings = "";
fontVariantLigatures = FontVariantLigatures.normal;
textAnchor = TextAnchor.start;
textDecoration = TextDecoration.None;
manualKerning = false;
kerning = DEFAULT_KERNING;
fontSize = DEFAULT_FONT_SIZE;
wordSpacing = DEFAULT_WORD_SPACING;
letterSpacing = DEFAULT_LETTER_SPACING;
// https://drafts.csswg.org/css-fonts-4/#relative-weights
static int from(FontWeight fontWeight, FontData parent) {
if (fontWeight == FontWeight.Bolder) {
return bolder(parent.absoluteFontWeight);
} else if (fontWeight == FontWeight.Lighter) {
return lighter(parent.absoluteFontWeight);
} else {
return absoluteFontWeights[fontWeight.ordinal()];
}
}
private double toAbsolute(ReadableMap font, String prop, double scale, double fontSize, double relative) {
ReadableType propType = font.getType(prop);
if (propType == ReadableType.Number) {
return font.getDouble(prop);
private static int bolder(int inherited) {
if (inherited < 350) {
return 400;
} else if (inherited < 550) {
return 700;
} else if (inherited < 900) {
return 900;
} else {
return inherited;
}
}
private static int lighter(int inherited) {
if (inherited < 100) {
return inherited;
} else if (inherited < 550) {
return 100;
} else if (inherited < 750) {
return 400;
} else {
return 700;
}
}
}
static final double DEFAULT_FONT_SIZE = 12d;
private static final double DEFAULT_KERNING = 0d;
private static final double DEFAULT_WORD_SPACING = 0d;
private static final double DEFAULT_LETTER_SPACING = 0d;
private static final String KERNING = "kerning";
private static final String FONT_DATA = "fontData";
private static final String TEXT_ANCHOR = "textAnchor";
private static final String WORD_SPACING = "wordSpacing";
private static final String LETTER_SPACING = "letterSpacing";
private static final String TEXT_DECORATION = "textDecoration";
private static final String FONT_FEATURE_SETTINGS = "fontFeatureSettings";
private static final String FONT_VARIATION_SETTINGS = "fontVariationSettings";
private static final String FONT_VARIANT_LIGATURES = "fontVariantLigatures";
final double fontSize;
final String fontFamily;
final FontStyle fontStyle;
final ReadableMap fontData;
FontWeight fontWeight;
int absoluteFontWeight;
final String fontFeatureSettings;
final String fontVariationSettings;
final FontVariantLigatures fontVariantLigatures;
final TextAnchor textAnchor;
private final TextDecoration textDecoration;
final double kerning;
final double wordSpacing;
final double letterSpacing;
final boolean manualKerning;
static final FontData Defaults = new FontData();
private FontData() {
fontData = null;
fontFamily = "";
fontStyle = FontStyle.normal;
fontWeight = FontWeight.Normal;
absoluteFontWeight = AbsoluteFontWeight.normal;
fontFeatureSettings = "";
fontVariationSettings = "";
fontVariantLigatures = FontVariantLigatures.normal;
textAnchor = TextAnchor.start;
textDecoration = TextDecoration.None;
manualKerning = false;
kerning = DEFAULT_KERNING;
fontSize = DEFAULT_FONT_SIZE;
wordSpacing = DEFAULT_WORD_SPACING;
letterSpacing = DEFAULT_LETTER_SPACING;
}
private double toAbsolute(
ReadableMap font, String prop, double scale, double fontSize, double relative) {
ReadableType propType = font.getType(prop);
if (propType == ReadableType.Number) {
return font.getDouble(prop);
} else {
String string = font.getString(prop);
return PropHelper.fromRelative(string, relative, scale, fontSize);
}
}
private void setInheritedWeight(FontData parent) {
absoluteFontWeight = parent.absoluteFontWeight;
fontWeight = parent.fontWeight;
}
private void handleNumericWeight(FontData parent, double number) {
long weight = Math.round(number);
if (weight >= 1 && weight <= 1000) {
absoluteFontWeight = (int) weight;
fontWeight = AbsoluteFontWeight.nearestFontWeight(absoluteFontWeight);
} else {
setInheritedWeight(parent);
}
}
FontData(ReadableMap font, FontData parent, double scale) {
double parentFontSize = parent.fontSize;
if (font.hasKey(FONT_SIZE)) {
fontSize = toAbsolute(font, FONT_SIZE, 1, parentFontSize, parentFontSize);
} else {
fontSize = parentFontSize;
}
if (font.hasKey(FONT_WEIGHT)) {
ReadableType fontWeightType = font.getType(FONT_WEIGHT);
if (fontWeightType == ReadableType.Number) {
handleNumericWeight(parent, font.getDouble(FONT_WEIGHT));
} else {
String string = font.getString(FONT_WEIGHT);
if (FontWeight.hasEnum(string)) {
absoluteFontWeight = AbsoluteFontWeight.from(FontWeight.get(string), parent);
fontWeight = AbsoluteFontWeight.nearestFontWeight(absoluteFontWeight);
} else if (string != null) {
handleNumericWeight(parent, Double.parseDouble(string));
} else {
String string = font.getString(prop);
return PropHelper.fromRelative(
string,
relative,
scale,
fontSize
);
setInheritedWeight(parent);
}
}
} else {
setInheritedWeight(parent);
}
private void setInheritedWeight(FontData parent) {
absoluteFontWeight = parent.absoluteFontWeight;
fontWeight = parent.fontWeight;
}
fontData = font.hasKey(FONT_DATA) ? font.getMap(FONT_DATA) : parent.fontData;
private void handleNumericWeight(FontData parent, double number) {
long weight = Math.round(number);
if (weight >= 1 && weight <= 1000) {
absoluteFontWeight = (int)weight;
fontWeight = AbsoluteFontWeight.nearestFontWeight(absoluteFontWeight);
} else {
setInheritedWeight(parent);
}
}
fontFamily = font.hasKey(FONT_FAMILY) ? font.getString(FONT_FAMILY) : parent.fontFamily;
fontStyle =
font.hasKey(FONT_STYLE) ? FontStyle.valueOf(font.getString(FONT_STYLE)) : parent.fontStyle;
fontFeatureSettings =
font.hasKey(FONT_FEATURE_SETTINGS)
? font.getString(FONT_FEATURE_SETTINGS)
: parent.fontFeatureSettings;
fontVariationSettings =
font.hasKey(FONT_VARIATION_SETTINGS)
? font.getString(FONT_VARIATION_SETTINGS)
: parent.fontVariationSettings;
fontVariantLigatures =
font.hasKey(FONT_VARIANT_LIGATURES)
? FontVariantLigatures.valueOf(font.getString(FONT_VARIANT_LIGATURES))
: parent.fontVariantLigatures;
FontData(ReadableMap font, FontData parent, double scale) {
double parentFontSize = parent.fontSize;
textAnchor =
font.hasKey(TEXT_ANCHOR)
? TextAnchor.valueOf(font.getString(TEXT_ANCHOR))
: parent.textAnchor;
textDecoration =
font.hasKey(TEXT_DECORATION)
? TextDecoration.getEnum(font.getString(TEXT_DECORATION))
: parent.textDecoration;
if (font.hasKey(FONT_SIZE)) {
fontSize = toAbsolute(font, FONT_SIZE, 1, parentFontSize, parentFontSize);
} else {
fontSize = parentFontSize;
}
final boolean hasKerning = font.hasKey(KERNING);
manualKerning = hasKerning || parent.manualKerning;
if (font.hasKey(FONT_WEIGHT)) {
ReadableType fontWeightType = font.getType(FONT_WEIGHT);
if (fontWeightType == ReadableType.Number) {
handleNumericWeight(parent, font.getDouble(FONT_WEIGHT));
} else {
String string = font.getString(FONT_WEIGHT);
if (FontWeight.hasEnum(string)) {
absoluteFontWeight = AbsoluteFontWeight.from(FontWeight.get(string), parent);
fontWeight = AbsoluteFontWeight.nearestFontWeight(absoluteFontWeight);
} else if (string != null) {
handleNumericWeight(parent, Double.parseDouble(string));
} else {
setInheritedWeight(parent);
}
}
} else {
setInheritedWeight(parent);
}
fontData = font.hasKey(FONT_DATA) ? font.getMap(FONT_DATA) : parent.fontData;
fontFamily = font.hasKey(FONT_FAMILY) ? font.getString(FONT_FAMILY) : parent.fontFamily;
fontStyle = font.hasKey(FONT_STYLE) ? FontStyle.valueOf(font.getString(FONT_STYLE)) : parent.fontStyle;
fontFeatureSettings = font.hasKey(FONT_FEATURE_SETTINGS) ? font.getString(FONT_FEATURE_SETTINGS) : parent.fontFeatureSettings;
fontVariationSettings = font.hasKey(FONT_VARIATION_SETTINGS) ? font.getString(FONT_VARIATION_SETTINGS) : parent.fontVariationSettings;
fontVariantLigatures = font.hasKey(FONT_VARIANT_LIGATURES) ? FontVariantLigatures.valueOf(font.getString(FONT_VARIANT_LIGATURES)) : parent.fontVariantLigatures;
textAnchor = font.hasKey(TEXT_ANCHOR) ? TextAnchor.valueOf(font.getString(TEXT_ANCHOR)) : parent.textAnchor;
textDecoration = font.hasKey(TEXT_DECORATION) ? TextDecoration.getEnum(font.getString(TEXT_DECORATION)) : parent.textDecoration;
final boolean hasKerning = font.hasKey(KERNING);
manualKerning = hasKerning || parent.manualKerning;
// https://www.w3.org/TR/SVG11/text.html#SpacingProperties
// https://drafts.csswg.org/css-text-3/#spacing
// calculated values for units in: kerning, word-spacing, and, letter-spacing.
kerning = hasKerning ? toAbsolute(font, KERNING, scale, fontSize, 0) : parent.kerning;
wordSpacing = font.hasKey(WORD_SPACING) ? toAbsolute(font, WORD_SPACING, scale, fontSize, 0) : parent.wordSpacing;
letterSpacing = font.hasKey(LETTER_SPACING) ? toAbsolute(font, LETTER_SPACING, scale, fontSize, 0) : parent.letterSpacing;
}
// https://www.w3.org/TR/SVG11/text.html#SpacingProperties
// https://drafts.csswg.org/css-text-3/#spacing
// calculated values for units in: kerning, word-spacing, and, letter-spacing.
kerning = hasKerning ? toAbsolute(font, KERNING, scale, fontSize, 0) : parent.kerning;
wordSpacing =
font.hasKey(WORD_SPACING)
? toAbsolute(font, WORD_SPACING, scale, fontSize, 0)
: parent.wordSpacing;
letterSpacing =
font.hasKey(LETTER_SPACING)
? toAbsolute(font, LETTER_SPACING, scale, fontSize, 0)
: parent.letterSpacing;
}
}

View File

@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.annotation.SuppressLint;
@@ -15,9 +14,7 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.view.View;
import androidx.annotation.NonNull;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
@@ -25,136 +22,136 @@ import com.facebook.react.uimanager.annotations.ReactProp;
@SuppressLint("ViewConstructor")
class ForeignObjectView extends GroupView {
SVGLength mX;
SVGLength mY;
SVGLength mW;
SVGLength mH;
SVGLength mX;
SVGLength mY;
SVGLength mW;
SVGLength mH;
public ForeignObjectView(ReactContext reactContext) {
super(reactContext);
}
public ForeignObjectView(ReactContext reactContext) {
super(reactContext);
}
@Override
void draw(Canvas canvas, Paint paint, float opacity) {
float x = (float)relativeOnWidth(mX);
float y = (float)relativeOnHeight(mY);
float w = (float)relativeOnWidth(mW);
float h = (float)relativeOnHeight(mH);
canvas.translate(x, y);
canvas.clipRect(0, 0, w, h);
super.draw(canvas, paint, opacity);
}
@Override
void draw(Canvas canvas, Paint paint, float opacity) {
float x = (float) relativeOnWidth(mX);
float y = (float) relativeOnHeight(mY);
float w = (float) relativeOnWidth(mW);
float h = (float) relativeOnHeight(mH);
canvas.translate(x, y);
canvas.clipRect(0, 0, w, h);
super.draw(canvas, paint, opacity);
}
@Override
public void onDescendantInvalidated(@NonNull View child, @NonNull View target) {
super.onDescendantInvalidated(child, target);
invalidate();
}
@Override
public void onDescendantInvalidated(@NonNull View child, @NonNull View target) {
super.onDescendantInvalidated(child, target);
invalidate();
}
@ReactProp(name = "x")
public void setX(Dynamic x) {
mX = SVGLength.from(x);
invalidate();
}
@ReactProp(name = "x")
public void setX(Dynamic x) {
mX = SVGLength.from(x);
invalidate();
}
public void setX(String x) {
mX = SVGLength.from(x);
invalidate();
}
@ReactProp(name = "y")
public void setY(Dynamic y) {
mY = SVGLength.from(y);
invalidate();
}
@ReactProp(name = "y")
public void setY(Dynamic y) {
mY = SVGLength.from(y);
invalidate();
}
public void setY(String y) {
mY = SVGLength.from(y);
invalidate();
}
@ReactProp(name = "width")
public void setWidth(Dynamic width) {
mW = SVGLength.from(width);
invalidate();
}
@ReactProp(name = "width")
public void setWidth(Dynamic width) {
mW = SVGLength.from(width);
invalidate();
}
public void setWidth(String width) {
mW = SVGLength.from(width);
invalidate();
}
@ReactProp(name = "height")
public void setHeight(Dynamic height) {
mH = SVGLength.from(height);
invalidate();
}
@ReactProp(name = "height")
public void setHeight(Dynamic height) {
mH = SVGLength.from(height);
invalidate();
}
public void setHeight(String height) {
mH = SVGLength.from(height);
invalidate();
}
void drawGroup(final Canvas canvas, final Paint paint, final float opacity) {
pushGlyphContext();
final SvgView svg = getSvgView();
final GroupView self = this;
final RectF groupRect = new RectF();
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child instanceof MaskView) {
continue;
}
if (child instanceof VirtualView) {
VirtualView node = ((VirtualView)child);
if ("none".equals(node.mDisplay)) {
continue;
}
if (node instanceof RenderableView) {
((RenderableView)node).mergeProperties(self);
}
int count = node.saveAndSetupCanvas(canvas, mCTM);
node.render(canvas, paint, opacity * mOpacity);
RectF r = node.getClientRect();
if (r != null) {
groupRect.union(r);
}
node.restoreCanvas(canvas, count);
if (node instanceof RenderableView) {
((RenderableView)node).resetProperties();
}
if (node.isResponsible()) {
svg.enableTouchEvents();
}
} else if (child instanceof SvgView) {
SvgView svgView = (SvgView)child;
svgView.drawChildren(canvas);
if (svgView.isResponsible()) {
svg.enableTouchEvents();
}
} else {
// Enable rendering other native ancestor views in e.g. masks
child.draw(canvas);
}
void drawGroup(final Canvas canvas, final Paint paint, final float opacity) {
pushGlyphContext();
final SvgView svg = getSvgView();
final GroupView self = this;
final RectF groupRect = new RectF();
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child instanceof MaskView) {
continue;
}
if (child instanceof VirtualView) {
VirtualView node = ((VirtualView) child);
if ("none".equals(node.mDisplay)) {
continue;
}
if (node instanceof RenderableView) {
((RenderableView) node).mergeProperties(self);
}
this.setClientRect(groupRect);
popGlyphContext();
}
// Enable rendering other native ancestor views in e.g. masks, but don't render them another time
Bitmap fakeBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
Canvas fake = new Canvas(fakeBitmap);
int count = node.saveAndSetupCanvas(canvas, mCTM);
node.render(canvas, paint, opacity * mOpacity);
RectF r = node.getClientRect();
if (r != null) {
groupRect.union(r);
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(fake);
}
node.restoreCanvas(canvas, count);
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return super.drawChild(fake, child, drawingTime);
if (node instanceof RenderableView) {
((RenderableView) node).resetProperties();
}
if (node.isResponsible()) {
svg.enableTouchEvents();
}
} else if (child instanceof SvgView) {
SvgView svgView = (SvgView) child;
svgView.drawChildren(canvas);
if (svgView.isResponsible()) {
svg.enableTouchEvents();
}
} else {
// Enable rendering other native ancestor views in e.g. masks
child.draw(canvas);
}
}
this.setClientRect(groupRect);
popGlyphContext();
}
// Enable rendering other native ancestor views in e.g. masks, but don't render them another time
Bitmap fakeBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
Canvas fake = new Canvas(fakeBitmap);
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(fake);
}
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return super.drawChild(fake, child, drawingTime);
}
}

View File

@@ -6,423 +6,410 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import com.facebook.react.bridge.ReadableMap;
import java.util.ArrayList;
import javax.annotation.Nullable;
// https://www.w3.org/TR/SVG/text.html#TSpanElement
class GlyphContext {
// Current stack (one per node push/pop)
final ArrayList<FontData> mFontContext = new ArrayList<>();
// Current stack (one per node push/pop)
final ArrayList<FontData> mFontContext = new ArrayList<>();
// Unique input attribute lists (only added if node sets a value)
private final ArrayList<SVGLength[]> mXsContext = new ArrayList<>();
private final ArrayList<SVGLength[]> mYsContext = new ArrayList<>();
private final ArrayList<SVGLength[]> mDXsContext = new ArrayList<>();
private final ArrayList<SVGLength[]> mDYsContext = new ArrayList<>();
private final ArrayList<double[]> mRsContext = new ArrayList<>();
// Unique input attribute lists (only added if node sets a value)
private final ArrayList<SVGLength[]> mXsContext = new ArrayList<>();
private final ArrayList<SVGLength[]> mYsContext = new ArrayList<>();
private final ArrayList<SVGLength[]> mDXsContext = new ArrayList<>();
private final ArrayList<SVGLength[]> mDYsContext = new ArrayList<>();
private final ArrayList<double[]> mRsContext = new ArrayList<>();
// Unique index into attribute list (one per unique list)
private final ArrayList<Integer> mXIndices = new ArrayList<>();
private final ArrayList<Integer> mYIndices = new ArrayList<>();
private final ArrayList<Integer> mDXIndices = new ArrayList<>();
private final ArrayList<Integer> mDYIndices = new ArrayList<>();
private final ArrayList<Integer> mRIndices = new ArrayList<>();
// Unique index into attribute list (one per unique list)
private final ArrayList<Integer> mXIndices = new ArrayList<>();
private final ArrayList<Integer> mYIndices = new ArrayList<>();
private final ArrayList<Integer> mDXIndices = new ArrayList<>();
private final ArrayList<Integer> mDYIndices = new ArrayList<>();
private final ArrayList<Integer> mRIndices = new ArrayList<>();
// Index of unique context used (one per node push/pop)
private final ArrayList<Integer> mXsIndices = new ArrayList<>();
private final ArrayList<Integer> mYsIndices = new ArrayList<>();
private final ArrayList<Integer> mDXsIndices = new ArrayList<>();
private final ArrayList<Integer> mDYsIndices = new ArrayList<>();
private final ArrayList<Integer> mRsIndices = new ArrayList<>();
// Index of unique context used (one per node push/pop)
private final ArrayList<Integer> mXsIndices = new ArrayList<>();
private final ArrayList<Integer> mYsIndices = new ArrayList<>();
private final ArrayList<Integer> mDXsIndices = new ArrayList<>();
private final ArrayList<Integer> mDYsIndices = new ArrayList<>();
private final ArrayList<Integer> mRsIndices = new ArrayList<>();
// Calculated on push context, percentage and em length depends on parent font size
private double mFontSize = FontData.DEFAULT_FONT_SIZE;
private FontData topFont = FontData.Defaults;
// Calculated on push context, percentage and em length depends on parent font size
private double mFontSize = FontData.DEFAULT_FONT_SIZE;
private FontData topFont = FontData.Defaults;
// Current accumulated values
// https://www.w3.org/TR/SVG/types.html#DataTypeCoordinate
// <coordinate> syntax is the same as that for <length>
private double mX;
private double mY;
// Current accumulated values
// https://www.w3.org/TR/SVG/types.html#DataTypeCoordinate
// <coordinate> syntax is the same as that for <length>
private double mX;
private double mY;
// https://www.w3.org/TR/SVG/types.html#Length
private double mDX;
private double mDY;
// https://www.w3.org/TR/SVG/types.html#Length
private double mDX;
private double mDY;
// Current <list-of-coordinates> SVGLengthList
// https://www.w3.org/TR/SVG/types.html#InterfaceSVGLengthList
// https://www.w3.org/TR/SVG/types.html#DataTypeCoordinates
// Current <list-of-coordinates> SVGLengthList
// https://www.w3.org/TR/SVG/types.html#InterfaceSVGLengthList
// https://www.w3.org/TR/SVG/types.html#DataTypeCoordinates
// https://www.w3.org/TR/SVG/text.html#TSpanElementXAttribute
private SVGLength[] mXs = new SVGLength[]{};
// https://www.w3.org/TR/SVG/text.html#TSpanElementXAttribute
private SVGLength[] mXs = new SVGLength[] {};
// https://www.w3.org/TR/SVG/text.html#TSpanElementYAttribute
private SVGLength[] mYs = new SVGLength[]{};
// https://www.w3.org/TR/SVG/text.html#TSpanElementYAttribute
private SVGLength[] mYs = new SVGLength[] {};
// Current <list-of-lengths> SVGLengthList
// https://www.w3.org/TR/SVG/types.html#DataTypeLengths
// Current <list-of-lengths> SVGLengthList
// https://www.w3.org/TR/SVG/types.html#DataTypeLengths
// https://www.w3.org/TR/SVG/text.html#TSpanElementDXAttribute
private SVGLength[] mDXs = new SVGLength[]{};
// https://www.w3.org/TR/SVG/text.html#TSpanElementDXAttribute
private SVGLength[] mDXs = new SVGLength[] {};
// https://www.w3.org/TR/SVG/text.html#TSpanElementDYAttribute
private SVGLength[] mDYs = new SVGLength[]{};
// https://www.w3.org/TR/SVG/text.html#TSpanElementDYAttribute
private SVGLength[] mDYs = new SVGLength[] {};
// Current <list-of-numbers> SVGLengthList
// https://www.w3.org/TR/SVG/types.html#DataTypeNumbers
// Current <list-of-numbers> SVGLengthList
// https://www.w3.org/TR/SVG/types.html#DataTypeNumbers
// https://www.w3.org/TR/SVG/text.html#TSpanElementRotateAttribute
private double[] mRs = new double[]{0};
// https://www.w3.org/TR/SVG/text.html#TSpanElementRotateAttribute
private double[] mRs = new double[] {0};
// Current attribute list index
private int mXsIndex;
private int mYsIndex;
private int mDXsIndex;
private int mDYsIndex;
private int mRsIndex;
// Current attribute list index
private int mXsIndex;
private int mYsIndex;
private int mDXsIndex;
private int mDYsIndex;
private int mRsIndex;
// Current value index in current attribute list
private int mXIndex = -1;
private int mYIndex = -1;
private int mDXIndex = -1;
private int mDYIndex = -1;
private int mRIndex = -1;
// Current value index in current attribute list
private int mXIndex = -1;
private int mYIndex = -1;
private int mDXIndex = -1;
private int mDYIndex = -1;
private int mRIndex = -1;
// Top index of stack
private int mTop;
// Top index of stack
private int mTop;
// Constructor parameters
private final float mScale;
private final float mWidth;
private final float mHeight;
// Constructor parameters
private final float mScale;
private final float mWidth;
private final float mHeight;
private void pushIndices() {
mXsIndices.add(mXsIndex);
mYsIndices.add(mYsIndex);
mDXsIndices.add(mDXsIndex);
mDYsIndices.add(mDYsIndex);
mRsIndices.add(mRsIndex);
private void pushIndices() {
mXsIndices.add(mXsIndex);
mYsIndices.add(mYsIndex);
mDXsIndices.add(mDXsIndex);
mDYsIndices.add(mDYsIndex);
mRsIndices.add(mRsIndex);
}
GlyphContext(float scale, float width, float height) {
mScale = scale;
mWidth = width;
mHeight = height;
mXsContext.add(mXs);
mYsContext.add(mYs);
mDXsContext.add(mDXs);
mDYsContext.add(mDYs);
mRsContext.add(mRs);
mXIndices.add(mXIndex);
mYIndices.add(mYIndex);
mDXIndices.add(mDXIndex);
mDYIndices.add(mDYIndex);
mRIndices.add(mRIndex);
mFontContext.add(topFont);
pushIndices();
}
private void reset() {
mXsIndex = mYsIndex = mDXsIndex = mDYsIndex = mRsIndex = 0;
mXIndex = mYIndex = mDXIndex = mDYIndex = mRIndex = -1;
mX = mY = mDX = mDY = 0;
}
FontData getFont() {
return topFont;
}
private FontData getTopOrParentFont(GroupView child) {
if (mTop > 0) {
return topFont;
} else {
GroupView parentRoot = child.getParentTextRoot();
while (parentRoot != null) {
FontData map = parentRoot.getGlyphContext().getFont();
if (map != FontData.Defaults) {
return map;
}
parentRoot = parentRoot.getParentTextRoot();
}
return FontData.Defaults;
}
}
private void pushNodeAndFont(GroupView node, @Nullable ReadableMap font) {
FontData parent = getTopOrParentFont(node);
mTop++;
if (font == null) {
mFontContext.add(parent);
return;
}
GlyphContext(float scale, float width, float height) {
mScale = scale;
mWidth = width;
mHeight = height;
FontData data = new FontData(font, parent, mScale);
mFontSize = data.fontSize;
mFontContext.add(data);
topFont = data;
}
mXsContext.add(mXs);
mYsContext.add(mYs);
mDXsContext.add(mDXs);
mDYsContext.add(mDYs);
mRsContext.add(mRs);
void pushContext(GroupView node, @Nullable ReadableMap font) {
pushNodeAndFont(node, font);
pushIndices();
}
mXIndices.add(mXIndex);
mYIndices.add(mYIndex);
mDXIndices.add(mDXIndex);
mDYIndices.add(mDYIndex);
mRIndices.add(mRIndex);
private SVGLength[] getStringArrayFromReadableArray(ArrayList<SVGLength> readableArray) {
int size = readableArray.size();
SVGLength[] strings = new SVGLength[size];
for (int i = 0; i < size; i++) {
strings[i] = readableArray.get(i);
}
return strings;
}
mFontContext.add(topFont);
private double[] getDoubleArrayFromReadableArray(ArrayList<SVGLength> readableArray) {
int size = readableArray.size();
double[] doubles = new double[size];
for (int i = 0; i < size; i++) {
SVGLength length = readableArray.get(i);
doubles[i] = length.value;
}
return doubles;
}
pushIndices();
void pushContext(
boolean reset,
TextView node,
@Nullable ReadableMap font,
@Nullable ArrayList<SVGLength> x,
@Nullable ArrayList<SVGLength> y,
@Nullable ArrayList<SVGLength> deltaX,
@Nullable ArrayList<SVGLength> deltaY,
@Nullable ArrayList<SVGLength> rotate) {
if (reset) {
this.reset();
}
private void reset() {
mXsIndex = mYsIndex = mDXsIndex = mDYsIndex = mRsIndex = 0;
mXIndex = mYIndex = mDXIndex = mDYIndex = mRIndex = -1;
mX = mY = mDX = mDY = 0;
pushNodeAndFont(node, font);
if (x != null && x.size() != 0) {
mXsIndex++;
mXIndex = -1;
mXIndices.add(mXIndex);
mXs = getStringArrayFromReadableArray(x);
mXsContext.add(mXs);
}
FontData getFont() {
return topFont;
if (y != null && y.size() != 0) {
mYsIndex++;
mYIndex = -1;
mYIndices.add(mYIndex);
mYs = getStringArrayFromReadableArray(y);
mYsContext.add(mYs);
}
private FontData getTopOrParentFont(GroupView child) {
if (mTop > 0) {
return topFont;
} else {
GroupView parentRoot = child.getParentTextRoot();
while (parentRoot != null) {
FontData map = parentRoot.getGlyphContext().getFont();
if (map != FontData.Defaults) {
return map;
}
parentRoot = parentRoot.getParentTextRoot();
}
return FontData.Defaults;
}
if (deltaX != null && deltaX.size() != 0) {
mDXsIndex++;
mDXIndex = -1;
mDXIndices.add(mDXIndex);
mDXs = getStringArrayFromReadableArray(deltaX);
mDXsContext.add(mDXs);
}
private void pushNodeAndFont(GroupView node, @Nullable ReadableMap font) {
FontData parent = getTopOrParentFont(node);
mTop++;
if (font == null) {
mFontContext.add(parent);
return;
}
FontData data = new FontData(font, parent, mScale);
mFontSize = data.fontSize;
mFontContext.add(data);
topFont = data;
if (deltaY != null && deltaY.size() != 0) {
mDYsIndex++;
mDYIndex = -1;
mDYIndices.add(mDYIndex);
mDYs = getStringArrayFromReadableArray(deltaY);
mDYsContext.add(mDYs);
}
void pushContext(GroupView node, @Nullable ReadableMap font) {
pushNodeAndFont(node, font);
pushIndices();
if (rotate != null && rotate.size() != 0) {
mRsIndex++;
mRIndex = -1;
mRIndices.add(mRIndex);
mRs = getDoubleArrayFromReadableArray(rotate);
mRsContext.add(mRs);
}
private SVGLength[] getStringArrayFromReadableArray(ArrayList<SVGLength> readableArray) {
int size = readableArray.size();
SVGLength[] strings = new SVGLength[size];
for (int i = 0; i < size; i++) {
strings[i] = readableArray.get(i);
}
return strings;
pushIndices();
}
void popContext() {
mFontContext.remove(mTop);
mXsIndices.remove(mTop);
mYsIndices.remove(mTop);
mDXsIndices.remove(mTop);
mDYsIndices.remove(mTop);
mRsIndices.remove(mTop);
mTop--;
int x = mXsIndex;
int y = mYsIndex;
int dx = mDXsIndex;
int dy = mDYsIndex;
int r = mRsIndex;
topFont = mFontContext.get(mTop);
mXsIndex = mXsIndices.get(mTop);
mYsIndex = mYsIndices.get(mTop);
mDXsIndex = mDXsIndices.get(mTop);
mDYsIndex = mDYsIndices.get(mTop);
mRsIndex = mRsIndices.get(mTop);
if (x != mXsIndex) {
mXsContext.remove(x);
mXs = mXsContext.get(mXsIndex);
mXIndex = mXIndices.get(mXsIndex);
}
if (y != mYsIndex) {
mYsContext.remove(y);
mYs = mYsContext.get(mYsIndex);
mYIndex = mYIndices.get(mYsIndex);
}
if (dx != mDXsIndex) {
mDXsContext.remove(dx);
mDXs = mDXsContext.get(mDXsIndex);
mDXIndex = mDXIndices.get(mDXsIndex);
}
if (dy != mDYsIndex) {
mDYsContext.remove(dy);
mDYs = mDYsContext.get(mDYsIndex);
mDYIndex = mDYIndices.get(mDYsIndex);
}
if (r != mRsIndex) {
mRsContext.remove(r);
mRs = mRsContext.get(mRsIndex);
mRIndex = mRIndices.get(mRsIndex);
}
}
private static void incrementIndices(ArrayList<Integer> indices, int topIndex) {
for (int index = topIndex; index >= 0; index--) {
int xIndex = indices.get(index);
indices.set(index, xIndex + 1);
}
}
// https://www.w3.org/TR/SVG11/text.html#FontSizeProperty
/**
* Get font size from context.
*
* <p>font-size Value: < absolute-size > | < relative-size > | < length > | < percentage > |
* inherit Initial: medium Applies to: text content elements Inherited: yes, the computed value is
* inherited Percentages: refer to parent element's font size Media: visual Animatable: yes
*
* <p>This property refers to the size of the font from baseline to baseline when multiple lines
* of text are set solid in a multiline layout environment.
*
* <p>For SVG, if a < length > is provided without a unit identifier (e.g., an unqualified number
* such as 128), the SVG user agent processes the < length > as a height value in the current user
* coordinate system.
*
* <p>If a < length > is provided with one of the unit identifiers (e.g., 12pt or 10%), then the
* SVG user agent converts the < length > into a corresponding value in the current user
* coordinate system by applying the rules described in Units.
*
* <p>Except for any additional information provided in this specification, the normative
* definition of the property is in CSS2 ([CSS2], section 15.2.4).
*/
double getFontSize() {
return mFontSize;
}
double nextX(double advance) {
incrementIndices(mXIndices, mXsIndex);
int nextIndex = mXIndex + 1;
if (nextIndex < mXs.length) {
mDX = 0;
mXIndex = nextIndex;
SVGLength string = mXs[nextIndex];
mX = PropHelper.fromRelative(string, mWidth, 0, mScale, mFontSize);
}
private double[] getDoubleArrayFromReadableArray(ArrayList<SVGLength> readableArray) {
int size = readableArray.size();
double[] doubles = new double[size];
for (int i = 0; i < size; i++) {
SVGLength length = readableArray.get(i);
doubles[i] = length.value;
}
return doubles;
mX += advance;
return mX;
}
double nextY() {
incrementIndices(mYIndices, mYsIndex);
int nextIndex = mYIndex + 1;
if (nextIndex < mYs.length) {
mDY = 0;
mYIndex = nextIndex;
SVGLength string = mYs[nextIndex];
mY = PropHelper.fromRelative(string, mHeight, 0, mScale, mFontSize);
}
void pushContext(
boolean reset,
TextView node,
@Nullable ReadableMap font,
@Nullable ArrayList<SVGLength> x,
@Nullable ArrayList<SVGLength> y,
@Nullable ArrayList<SVGLength> deltaX,
@Nullable ArrayList<SVGLength> deltaY,
@Nullable ArrayList<SVGLength> rotate
) {
if (reset) {
this.reset();
}
return mY;
}
pushNodeAndFont(node, font);
double nextDeltaX() {
incrementIndices(mDXIndices, mDXsIndex);
if (x != null && x.size() != 0) {
mXsIndex++;
mXIndex = -1;
mXIndices.add(mXIndex);
mXs = getStringArrayFromReadableArray(x);
mXsContext.add(mXs);
}
if (y != null && y.size() != 0) {
mYsIndex++;
mYIndex = -1;
mYIndices.add(mYIndex);
mYs = getStringArrayFromReadableArray(y);
mYsContext.add(mYs);
}
if (deltaX != null && deltaX.size() != 0) {
mDXsIndex++;
mDXIndex = -1;
mDXIndices.add(mDXIndex);
mDXs = getStringArrayFromReadableArray(deltaX);
mDXsContext.add(mDXs);
}
if (deltaY != null && deltaY.size() != 0) {
mDYsIndex++;
mDYIndex = -1;
mDYIndices.add(mDYIndex);
mDYs = getStringArrayFromReadableArray(deltaY);
mDYsContext.add(mDYs);
}
if (rotate != null && rotate.size() != 0) {
mRsIndex++;
mRIndex = -1;
mRIndices.add(mRIndex);
mRs = getDoubleArrayFromReadableArray(rotate);
mRsContext.add(mRs);
}
pushIndices();
int nextIndex = mDXIndex + 1;
if (nextIndex < mDXs.length) {
mDXIndex = nextIndex;
SVGLength string = mDXs[nextIndex];
double val = PropHelper.fromRelative(string, mWidth, 0, mScale, mFontSize);
mDX += val;
}
void popContext() {
mFontContext.remove(mTop);
mXsIndices.remove(mTop);
mYsIndices.remove(mTop);
mDXsIndices.remove(mTop);
mDYsIndices.remove(mTop);
mRsIndices.remove(mTop);
return mDX;
}
mTop--;
double nextDeltaY() {
incrementIndices(mDYIndices, mDYsIndex);
int x = mXsIndex;
int y = mYsIndex;
int dx = mDXsIndex;
int dy = mDYsIndex;
int r = mRsIndex;
topFont = mFontContext.get(mTop);
mXsIndex = mXsIndices.get(mTop);
mYsIndex = mYsIndices.get(mTop);
mDXsIndex = mDXsIndices.get(mTop);
mDYsIndex = mDYsIndices.get(mTop);
mRsIndex = mRsIndices.get(mTop);
if (x != mXsIndex) {
mXsContext.remove(x);
mXs = mXsContext.get(mXsIndex);
mXIndex = mXIndices.get(mXsIndex);
}
if (y != mYsIndex) {
mYsContext.remove(y);
mYs = mYsContext.get(mYsIndex);
mYIndex = mYIndices.get(mYsIndex);
}
if (dx != mDXsIndex) {
mDXsContext.remove(dx);
mDXs = mDXsContext.get(mDXsIndex);
mDXIndex = mDXIndices.get(mDXsIndex);
}
if (dy != mDYsIndex) {
mDYsContext.remove(dy);
mDYs = mDYsContext.get(mDYsIndex);
mDYIndex = mDYIndices.get(mDYsIndex);
}
if (r != mRsIndex) {
mRsContext.remove(r);
mRs = mRsContext.get(mRsIndex);
mRIndex = mRIndices.get(mRsIndex);
}
int nextIndex = mDYIndex + 1;
if (nextIndex < mDYs.length) {
mDYIndex = nextIndex;
SVGLength string = mDYs[nextIndex];
double val = PropHelper.fromRelative(string, mHeight, 0, mScale, mFontSize);
mDY += val;
}
private static void incrementIndices(ArrayList<Integer> indices, int topIndex) {
for (int index = topIndex; index >= 0; index--) {
int xIndex = indices.get(index);
indices.set(index, xIndex + 1);
}
}
return mDY;
}
// https://www.w3.org/TR/SVG11/text.html#FontSizeProperty
double nextRotation() {
incrementIndices(mRIndices, mRsIndex);
/**
* Get font size from context.
* <p>
* font-size
* Value: < absolute-size > | < relative-size > | < length > | < percentage > | inherit
* Initial: medium
* Applies to: text content elements
* Inherited: yes, the computed value is inherited
* Percentages: refer to parent element's font size
* Media: visual
* Animatable: yes
* <p>
* This property refers to the size of the font from baseline to
* baseline when multiple lines of text are set solid in a multiline
* layout environment.
* <p>
* For SVG, if a < length > is provided without a unit identifier
* (e.g., an unqualified number such as 128), the SVG user agent
* processes the < length > as a height value in the current user
* coordinate system.
* <p>
* If a < length > is provided with one of the unit identifiers
* (e.g., 12pt or 10%), then the SVG user agent converts the
* < length > into a corresponding value in the current user
* coordinate system by applying the rules described in Units.
* <p>
* Except for any additional information provided in this specification,
* the normative definition of the property is in CSS2 ([CSS2], section 15.2.4).
*/
double getFontSize() {
return mFontSize;
}
mRIndex = Math.min(mRIndex + 1, mRs.length - 1);
double nextX(double advance) {
incrementIndices(mXIndices, mXsIndex);
return mRs[mRIndex];
}
int nextIndex = mXIndex + 1;
if (nextIndex < mXs.length) {
mDX = 0;
mXIndex = nextIndex;
SVGLength string = mXs[nextIndex];
mX = PropHelper.fromRelative(string, mWidth, 0, mScale, mFontSize);
}
float getWidth() {
return mWidth;
}
mX += advance;
return mX;
}
double nextY() {
incrementIndices(mYIndices, mYsIndex);
int nextIndex = mYIndex + 1;
if (nextIndex < mYs.length) {
mDY = 0;
mYIndex = nextIndex;
SVGLength string = mYs[nextIndex];
mY = PropHelper.fromRelative(string, mHeight, 0, mScale, mFontSize);
}
return mY;
}
double nextDeltaX() {
incrementIndices(mDXIndices, mDXsIndex);
int nextIndex = mDXIndex + 1;
if (nextIndex < mDXs.length) {
mDXIndex = nextIndex;
SVGLength string = mDXs[nextIndex];
double val = PropHelper.fromRelative(string, mWidth, 0, mScale, mFontSize);
mDX += val;
}
return mDX;
}
double nextDeltaY() {
incrementIndices(mDYIndices, mDYsIndex);
int nextIndex = mDYIndex + 1;
if (nextIndex < mDYs.length) {
mDYIndex = nextIndex;
SVGLength string = mDYs[nextIndex];
double val = PropHelper.fromRelative(string, mHeight, 0, mScale, mFontSize);
mDY += val;
}
return mDY;
}
double nextRotation() {
incrementIndices(mRIndices, mRsIndex);
mRIndex = Math.min(mRIndex + 1, mRs.length - 1);
return mRs[mRIndex];
}
float getWidth() {
return mWidth;
}
float getHeight() {
return mHeight;
}
float getHeight() {
return mHeight;
}
}

View File

@@ -2,47 +2,46 @@ package com.horcrux.svg;
import android.graphics.Paint;
import android.graphics.Path;
import java.util.ArrayList;
class GlyphPathBag {
private final ArrayList<Path> paths = new ArrayList<>();
private final int[][] data = new int[256][];
private final Paint paint;
private final ArrayList<Path> paths = new ArrayList<>();
private final int[][] data = new int[256][];
private final Paint paint;
GlyphPathBag(Paint paint) {
this.paint = paint;
// Make indexed-by-one, to allow zero to represent non-cached
paths.add(new Path());
GlyphPathBag(Paint paint) {
this.paint = paint;
// Make indexed-by-one, to allow zero to represent non-cached
paths.add(new Path());
}
Path getOrCreateAndCache(char ch, String current) {
int index = getIndex(ch);
Path cached;
if (index != 0) {
cached = paths.get(index);
} else {
cached = new Path();
paint.getTextPath(current, 0, 1, 0, 0, cached);
int[] bin = data[ch >> 8];
if (bin == null) {
bin = data[ch >> 8] = new int[256];
}
bin[ch & 0xFF] = paths.size();
paths.add(cached);
}
Path getOrCreateAndCache(char ch, String current) {
int index = getIndex(ch);
Path cached;
Path glyph = new Path();
glyph.addPath(cached);
return glyph;
}
if (index != 0) {
cached = paths.get(index);
} else {
cached = new Path();
paint.getTextPath(current, 0, 1, 0, 0, cached);
int[] bin = data[ch >> 8];
if (bin == null) {
bin = data[ch >> 8] = new int[256];
}
bin[ch & 0xFF] = paths.size();
paths.add(cached);
}
Path glyph = new Path();
glyph.addPath(cached);
return glyph;
}
private int getIndex(char ch) {
int[] bin = data[ch >> 8];
if (bin == null) return 0;
return bin[ch & 0xFF];
}
private int getIndex(char ch) {
int[] bin = data[ch >> 8];
if (bin == null) return 0;
return bin[ch & 0xFF];
}
}

View File

@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.annotation.SuppressLint;
@@ -19,268 +18,264 @@ import android.graphics.RectF;
import android.graphics.Region;
import android.os.Build;
import android.view.View;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.views.view.ReactViewGroup;
import javax.annotation.Nullable;
@SuppressLint("ViewConstructor")
class GroupView extends RenderableView {
@Nullable ReadableMap mFont;
private GlyphContext mGlyphContext;
@Nullable ReadableMap mFont;
private GlyphContext mGlyphContext;
public GroupView(ReactContext reactContext) {
super(reactContext);
public GroupView(ReactContext reactContext) {
super(reactContext);
}
@ReactProp(name = "font")
public void setFont(@Nullable ReadableMap font) {
mFont = font;
invalidate();
}
void setupGlyphContext(Canvas canvas) {
RectF clipBounds = new RectF(canvas.getClipBounds());
if (mMatrix != null) {
mMatrix.mapRect(clipBounds);
}
@ReactProp(name = "font")
public void setFont(@Nullable ReadableMap font) {
mFont = font;
invalidate();
if (mTransform != null) {
mTransform.mapRect(clipBounds);
}
mGlyphContext = new GlyphContext(mScale, clipBounds.width(), clipBounds.height());
}
void setupGlyphContext(Canvas canvas) {
RectF clipBounds = new RectF(canvas.getClipBounds());
if (mMatrix != null) {
mMatrix.mapRect(clipBounds);
GlyphContext getGlyphContext() {
return mGlyphContext;
}
private static <T> T requireNonNull(T obj) {
if (obj == null) throw new NullPointerException();
return obj;
}
GlyphContext getTextRootGlyphContext() {
return requireNonNull(getTextRoot()).getGlyphContext();
}
void pushGlyphContext() {
getTextRootGlyphContext().pushContext(this, mFont);
}
void popGlyphContext() {
getTextRootGlyphContext().popContext();
}
void draw(final Canvas canvas, final Paint paint, final float opacity) {
setupGlyphContext(canvas);
clip(canvas, paint);
drawGroup(canvas, paint, opacity);
}
void drawGroup(final Canvas canvas, final Paint paint, final float opacity) {
pushGlyphContext();
final SvgView svg = getSvgView();
final GroupView self = this;
final RectF groupRect = new RectF();
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child instanceof MaskView) {
continue;
}
if (child instanceof VirtualView) {
VirtualView node = ((VirtualView) child);
if ("none".equals(node.mDisplay)) {
continue;
}
if (mTransform != null) {
mTransform.mapRect(clipBounds);
}
mGlyphContext = new GlyphContext(mScale, clipBounds.width(), clipBounds.height());
}
GlyphContext getGlyphContext() {
return mGlyphContext;
}
private static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
GlyphContext getTextRootGlyphContext() {
return requireNonNull(getTextRoot()).getGlyphContext();
}
void pushGlyphContext() {
getTextRootGlyphContext().pushContext(this, mFont);
}
void popGlyphContext() {
getTextRootGlyphContext().popContext();
}
void draw(final Canvas canvas, final Paint paint, final float opacity) {
setupGlyphContext(canvas);
clip(canvas, paint);
drawGroup(canvas, paint, opacity);
}
void drawGroup(final Canvas canvas, final Paint paint, final float opacity) {
pushGlyphContext();
final SvgView svg = getSvgView();
final GroupView self = this;
final RectF groupRect = new RectF();
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child instanceof MaskView) {
continue;
}
if (child instanceof VirtualView) {
VirtualView node = ((VirtualView)child);
if ("none".equals(node.mDisplay)) {
continue;
}
if (node instanceof RenderableView) {
((RenderableView)node).mergeProperties(self);
}
int count = node.saveAndSetupCanvas(canvas, mCTM);
node.render(canvas, paint, opacity * mOpacity);
RectF r = node.getClientRect();
if (r != null) {
groupRect.union(r);
}
node.restoreCanvas(canvas, count);
if (node instanceof RenderableView) {
((RenderableView)node).resetProperties();
}
if (node.isResponsible()) {
svg.enableTouchEvents();
}
} else if (child instanceof SvgView) {
SvgView svgView = (SvgView)child;
svgView.drawChildren(canvas);
if (svgView.isResponsible()) {
svg.enableTouchEvents();
}
}
}
this.setClientRect(groupRect);
popGlyphContext();
}
void drawPath(Canvas canvas, Paint paint, float opacity) {
super.draw(canvas, paint, opacity);
}
@Override
Path getPath(final Canvas canvas, final Paint paint) {
if (mPath != null) {
return mPath;
}
mPath = new Path();
for (int i = 0; i < getChildCount(); i++) {
View node = getChildAt(i);
if (node instanceof MaskView) {
continue;
}
if (node instanceof VirtualView) {
VirtualView n = (VirtualView)node;
Matrix transform = n.mMatrix;
mPath.addPath(n.getPath(canvas, paint), transform);
}
if (node instanceof RenderableView) {
((RenderableView) node).mergeProperties(self);
}
return mPath;
int count = node.saveAndSetupCanvas(canvas, mCTM);
node.render(canvas, paint, opacity * mOpacity);
RectF r = node.getClientRect();
if (r != null) {
groupRect.union(r);
}
node.restoreCanvas(canvas, count);
if (node instanceof RenderableView) {
((RenderableView) node).resetProperties();
}
if (node.isResponsible()) {
svg.enableTouchEvents();
}
} else if (child instanceof SvgView) {
SvgView svgView = (SvgView) child;
svgView.drawChildren(canvas);
if (svgView.isResponsible()) {
svg.enableTouchEvents();
}
}
}
this.setClientRect(groupRect);
popGlyphContext();
}
void drawPath(Canvas canvas, Paint paint, float opacity) {
super.draw(canvas, paint, opacity);
}
@Override
Path getPath(final Canvas canvas, final Paint paint) {
if (mPath != null) {
return mPath;
}
mPath = new Path();
for (int i = 0; i < getChildCount(); i++) {
View node = getChildAt(i);
if (node instanceof MaskView) {
continue;
}
if (node instanceof VirtualView) {
VirtualView n = (VirtualView) node;
Matrix transform = n.mMatrix;
mPath.addPath(n.getPath(canvas, paint), transform);
}
}
Path getPath(final Canvas canvas, final Paint paint, final Region.Op op) {
final Path path = new Path();
return mPath;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
final Path.Op pop = Path.Op.valueOf(op.name());
for (int i = 0; i < getChildCount(); i++) {
View node = getChildAt(i);
if (node instanceof MaskView) {
continue;
}
if (node instanceof VirtualView) {
VirtualView n = (VirtualView)node;
Matrix transform = n.mMatrix;
Path p2;
if (n instanceof GroupView) {
p2 = ((GroupView)n).getPath(canvas, paint, op);
} else {
p2 = n.getPath(canvas, paint);
}
p2.transform(transform);
path.op(p2, pop);
}
}
} else {
Rect clipBounds = canvas.getClipBounds();
final Region bounds = new Region(clipBounds);
final Region r = new Region();
for (int i = 0; i < getChildCount(); i++) {
View node = getChildAt(i);
if (node instanceof MaskView) {
continue;
}
if (node instanceof VirtualView) {
VirtualView n = (VirtualView)node;
Matrix transform = n.mMatrix;
Path p2;
if (n instanceof GroupView) {
p2 = ((GroupView)n).getPath(canvas, paint, op);
} else {
p2 = n.getPath(canvas, paint);
}
if (transform != null) {
p2.transform(transform);
}
Region r2 = new Region();
r2.setPath(p2, bounds);
r.op(r2, op);
}
}
path.addPath(r.getBoundaryPath());
Path getPath(final Canvas canvas, final Paint paint, final Region.Op op) {
final Path path = new Path();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
final Path.Op pop = Path.Op.valueOf(op.name());
for (int i = 0; i < getChildCount(); i++) {
View node = getChildAt(i);
if (node instanceof MaskView) {
continue;
}
return path;
if (node instanceof VirtualView) {
VirtualView n = (VirtualView) node;
Matrix transform = n.mMatrix;
Path p2;
if (n instanceof GroupView) {
p2 = ((GroupView) n).getPath(canvas, paint, op);
} else {
p2 = n.getPath(canvas, paint);
}
p2.transform(transform);
path.op(p2, pop);
}
}
} else {
Rect clipBounds = canvas.getClipBounds();
final Region bounds = new Region(clipBounds);
final Region r = new Region();
for (int i = 0; i < getChildCount(); i++) {
View node = getChildAt(i);
if (node instanceof MaskView) {
continue;
}
if (node instanceof VirtualView) {
VirtualView n = (VirtualView) node;
Matrix transform = n.mMatrix;
Path p2;
if (n instanceof GroupView) {
p2 = ((GroupView) n).getPath(canvas, paint, op);
} else {
p2 = n.getPath(canvas, paint);
}
if (transform != null) {
p2.transform(transform);
}
Region r2 = new Region();
r2.setPath(p2, bounds);
r.op(r2, op);
}
}
path.addPath(r.getBoundaryPath());
}
@Override
int hitTest(final float[] src) {
if (!mInvertible || !mTransformInvertible) {
return -1;
}
return path;
}
float[] dst = new float[2];
mInvMatrix.mapPoints(dst, src);
mInvTransform.mapPoints(dst);
@Override
int hitTest(final float[] src) {
if (!mInvertible || !mTransformInvertible) {
return -1;
}
int x = Math.round(dst[0]);
int y = Math.round(dst[1]);
float[] dst = new float[2];
mInvMatrix.mapPoints(dst, src);
mInvTransform.mapPoints(dst);
Path clipPath = getClipPath();
if (clipPath != null) {
if (mClipRegionPath != clipPath) {
mClipRegionPath = clipPath;
mClipBounds = new RectF();
clipPath.computeBounds(mClipBounds, true);
mClipRegion = getRegion(clipPath, mClipBounds);
}
if (!mClipRegion.contains(x, y)) {
return -1;
}
}
for (int i = getChildCount() - 1; i >= 0; i--) {
View child = getChildAt(i);
if (child instanceof VirtualView) {
if (child instanceof MaskView) {
continue;
}
VirtualView node = (VirtualView) child;
int hitChild = node.hitTest(dst);
if (hitChild != -1) {
return (node.isResponsible() || hitChild != child.getId()) ? hitChild : getId();
}
} else if (child instanceof SvgView) {
SvgView node = (SvgView) child;
int hitChild = node.reactTagForTouch(dst[0], dst[1]);
if (hitChild != child.getId()) {
return hitChild;
}
}
}
int x = Math.round(dst[0]);
int y = Math.round(dst[1]);
Path clipPath = getClipPath();
if (clipPath != null) {
if (mClipRegionPath != clipPath) {
mClipRegionPath = clipPath;
mClipBounds = new RectF();
clipPath.computeBounds(mClipBounds, true);
mClipRegion = getRegion(clipPath, mClipBounds);
}
if (!mClipRegion.contains(x, y)) {
return -1;
}
}
void saveDefinition() {
if (mName != null) {
getSvgView().defineTemplate(this, mName);
for (int i = getChildCount() - 1; i >= 0; i--) {
View child = getChildAt(i);
if (child instanceof VirtualView) {
if (child instanceof MaskView) {
continue;
}
for (int i = 0; i < getChildCount(); i++) {
View node = getChildAt(i);
if (node instanceof VirtualView) {
((VirtualView)node).saveDefinition();
}
VirtualView node = (VirtualView) child;
int hitChild = node.hitTest(dst);
if (hitChild != -1) {
return (node.isResponsible() || hitChild != child.getId()) ? hitChild : getId();
}
} else if (child instanceof SvgView) {
SvgView node = (SvgView) child;
int hitChild = node.reactTagForTouch(dst[0], dst[1]);
if (hitChild != child.getId()) {
return hitChild;
}
}
}
@Override
void resetProperties() {
for (int i = 0; i < getChildCount(); i++) {
View node = getChildAt(i);
if (node instanceof RenderableView) {
((RenderableView)node).resetProperties();
}
}
return -1;
}
void saveDefinition() {
if (mName != null) {
getSvgView().defineTemplate(this, mName);
}
for (int i = 0; i < getChildCount(); i++) {
View node = getChildAt(i);
if (node instanceof VirtualView) {
((VirtualView) node).saveDefinition();
}
}
}
@Override
void resetProperties() {
for (int i = 0; i < getChildCount(); i++) {
View node = getChildAt(i);
if (node instanceof RenderableView) {
((RenderableView) node).resetProperties();
}
}
}
}

View File

@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.annotation.SuppressLint;
@@ -17,7 +16,6 @@ import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.net.Uri;
import com.facebook.common.executors.UiThreadImmediateExecutorService;
import com.facebook.common.logging.FLog;
import com.facebook.common.references.CloseableReference;
@@ -35,235 +33,240 @@ import com.facebook.react.common.ReactConstants;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.views.imagehelper.ImageSource;
import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@SuppressLint("ViewConstructor")
class ImageView extends RenderableView {
private SVGLength mX;
private SVGLength mY;
private SVGLength mW;
private SVGLength mH;
private String uriString;
private int mImageWidth;
private int mImageHeight;
private String mAlign;
private int mMeetOrSlice;
private final AtomicBoolean mLoading = new AtomicBoolean(false);
private SVGLength mX;
private SVGLength mY;
private SVGLength mW;
private SVGLength mH;
private String uriString;
private int mImageWidth;
private int mImageHeight;
private String mAlign;
private int mMeetOrSlice;
private final AtomicBoolean mLoading = new AtomicBoolean(false);
public ImageView(ReactContext reactContext) {
super(reactContext);
}
public ImageView(ReactContext reactContext) {
super(reactContext);
}
@ReactProp(name = "x")
public void setX(Dynamic x) {
mX = SVGLength.from(x);
invalidate();
}
@ReactProp(name = "x")
public void setX(Dynamic x) {
mX = SVGLength.from(x);
invalidate();
}
public void setX(String x) {
mX = SVGLength.from(x);
invalidate();
}
@ReactProp(name = "y")
public void setY(Dynamic y) {
mY = SVGLength.from(y);
invalidate();
}
@ReactProp(name = "y")
public void setY(Dynamic y) {
mY = SVGLength.from(y);
invalidate();
}
public void setY(String y) {
mY = SVGLength.from(y);
invalidate();
}
@ReactProp(name = "width")
public void setWidth(Dynamic width) {
mW = SVGLength.from(width);
invalidate();
}
@ReactProp(name = "width")
public void setWidth(Dynamic width) {
mW = SVGLength.from(width);
invalidate();
}
public void setWidth(String width) {
mW = SVGLength.from(width);
invalidate();
}
@ReactProp(name = "height")
public void setHeight(Dynamic height) {
mH = SVGLength.from(height);
invalidate();
}
@ReactProp(name = "height")
public void setHeight(Dynamic height) {
mH = SVGLength.from(height);
invalidate();
}
public void setHeight(String height) {
mH = SVGLength.from(height);
invalidate();
}
@ReactProp(name = "src")
public void setSrc(@Nullable ReadableMap src) {
if (src != null) {
uriString = src.getString("uri");
@ReactProp(name = "src")
public void setSrc(@Nullable ReadableMap src) {
if (src != null) {
uriString = src.getString("uri");
if (uriString == null || uriString.isEmpty()) {
// TODO: give warning about this
return;
}
if (uriString == null || uriString.isEmpty()) {
//TODO: give warning about this
return;
}
if (src.hasKey("width") && src.hasKey("height")) {
mImageWidth = src.getInt("width");
mImageHeight = src.getInt("height");
} else {
mImageWidth = 0;
mImageHeight = 0;
}
Uri mUri = Uri.parse(uriString);
if (mUri.getScheme() == null) {
ResourceDrawableIdHelper.getInstance().getResourceDrawableUri(mContext, uriString);
}
}
if (src.hasKey("width") && src.hasKey("height")) {
mImageWidth = src.getInt("width");
mImageHeight = src.getInt("height");
} else {
mImageWidth = 0;
mImageHeight = 0;
}
Uri mUri = Uri.parse(uriString);
if (mUri.getScheme() == null) {
ResourceDrawableIdHelper.getInstance().getResourceDrawableUri(mContext, uriString);
}
}
}
@ReactProp(name = "align")
public void setAlign(String align) {
mAlign = align;
invalidate();
@ReactProp(name = "align")
public void setAlign(String align) {
mAlign = align;
invalidate();
}
@ReactProp(name = "meetOrSlice")
public void setMeetOrSlice(int meetOrSlice) {
mMeetOrSlice = meetOrSlice;
invalidate();
}
@Override
void draw(final Canvas canvas, final Paint paint, final float opacity) {
if (!mLoading.get()) {
ImagePipeline imagePipeline = Fresco.getImagePipeline();
ImageSource imageSource = new ImageSource(mContext, uriString);
ImageRequest request = ImageRequest.fromUri(imageSource.getUri());
boolean inMemoryCache = imagePipeline.isInBitmapMemoryCache(request);
if (inMemoryCache) {
tryRenderFromBitmapCache(imagePipeline, request, canvas, paint, opacity * mOpacity);
} else {
loadBitmap(imagePipeline, request);
}
}
}
@ReactProp(name = "meetOrSlice")
public void setMeetOrSlice(int meetOrSlice) {
mMeetOrSlice = meetOrSlice;
invalidate();
}
@Override
Path getPath(Canvas canvas, Paint paint) {
mPath = new Path();
mPath.addRect(getRect(), Path.Direction.CW);
return mPath;
}
@Override
void draw(final Canvas canvas, final Paint paint, final float opacity) {
if (!mLoading.get()) {
ImagePipeline imagePipeline = Fresco.getImagePipeline();
ImageSource imageSource = new ImageSource(mContext, uriString);
ImageRequest request = ImageRequest.fromUri(imageSource.getUri());
boolean inMemoryCache = imagePipeline.isInBitmapMemoryCache(request);
if (inMemoryCache) {
tryRenderFromBitmapCache(imagePipeline, request, canvas, paint, opacity * mOpacity);
} else {
loadBitmap(imagePipeline, request);
private void loadBitmap(final ImagePipeline imagePipeline, final ImageRequest request) {
mLoading.set(true);
final DataSource<CloseableReference<CloseableImage>> dataSource =
imagePipeline.fetchDecodedImage(request, mContext);
BaseBitmapDataSubscriber subscriber =
new BaseBitmapDataSubscriber() {
@Override
public void onNewResultImpl(Bitmap bitmap) {
mLoading.set(false);
SvgView view = getSvgView();
if (view != null) {
view.invalidate();
}
}
}
}
@Override
Path getPath(Canvas canvas, Paint paint) {
mPath = new Path();
mPath.addRect(getRect(), Path.Direction.CW);
return mPath;
}
private void loadBitmap(final ImagePipeline imagePipeline, final ImageRequest request) {
mLoading.set(true);
final DataSource<CloseableReference<CloseableImage>> dataSource
= imagePipeline.fetchDecodedImage(request, mContext);
BaseBitmapDataSubscriber subscriber = new BaseBitmapDataSubscriber() {
@Override
public void onNewResultImpl(Bitmap bitmap) {
mLoading.set(false);
SvgView view = getSvgView();
if (view != null) {
view.invalidate();
}
}
@Override
public void onFailureImpl(DataSource dataSource) {
// No cleanup required here.
// TODO: more details about this failure
mLoading.set(false);
FLog.w(ReactConstants.TAG, dataSource.getFailureCause(), "RNSVG: fetchDecodedImage failed!");
}
@Override
public void onFailureImpl(DataSource dataSource) {
// No cleanup required here.
// TODO: more details about this failure
mLoading.set(false);
FLog.w(
ReactConstants.TAG,
dataSource.getFailureCause(),
"RNSVG: fetchDecodedImage failed!");
}
};
dataSource.subscribe(subscriber, UiThreadImmediateExecutorService.getInstance());
dataSource.subscribe(subscriber, UiThreadImmediateExecutorService.getInstance());
}
@Nonnull
private RectF getRect() {
double x = relativeOnWidth(mX);
double y = relativeOnHeight(mY);
double w = relativeOnWidth(mW);
double h = relativeOnHeight(mH);
if (w == 0) {
w = mImageWidth * mScale;
}
if (h == 0) {
h = mImageHeight * mScale;
}
@Nonnull
private RectF getRect() {
double x = relativeOnWidth(mX);
double y = relativeOnHeight(mY);
double w = relativeOnWidth(mW);
double h = relativeOnHeight(mH);
if (w == 0) {
w = mImageWidth * mScale;
}
if (h == 0) {
h = mImageHeight * mScale;
}
return new RectF((float) x, (float) y, (float) (x + w), (float) (y + h));
}
return new RectF((float) x, (float) y, (float) (x + w), (float) (y + h));
private void doRender(Canvas canvas, Paint paint, Bitmap bitmap, float opacity) {
if (mImageWidth == 0 || mImageHeight == 0) {
mImageWidth = bitmap.getWidth();
mImageHeight = bitmap.getHeight();
}
private void doRender(Canvas canvas, Paint paint, Bitmap bitmap, float opacity) {
if (mImageWidth == 0 || mImageHeight == 0) {
mImageWidth = bitmap.getWidth();
mImageHeight = bitmap.getHeight();
}
RectF renderRect = getRect();
RectF vbRect = new RectF(0, 0, mImageWidth, mImageHeight);
Matrix transform = ViewBox.getTransform(vbRect, renderRect, mAlign, mMeetOrSlice);
transform.mapRect(vbRect);
RectF renderRect = getRect();
RectF vbRect = new RectF(0, 0, mImageWidth, mImageHeight);
Matrix transform = ViewBox.getTransform(vbRect, renderRect, mAlign, mMeetOrSlice);
transform.mapRect(vbRect);
canvas.clipPath(getPath(canvas, paint));
canvas.clipPath(getPath(canvas, paint));
Path clipPath = getClipPath(canvas, paint);
if (clipPath != null) {
canvas.clipPath(clipPath);
}
Paint alphaPaint = new Paint();
alphaPaint.setAlpha((int) (opacity * 255));
canvas.drawBitmap(bitmap, null, vbRect, alphaPaint);
mCTM.mapRect(vbRect);
this.setClientRect(vbRect);
Path clipPath = getClipPath(canvas, paint);
if (clipPath != null) {
canvas.clipPath(clipPath);
}
private void tryRenderFromBitmapCache(ImagePipeline imagePipeline, ImageRequest request, Canvas canvas, Paint paint, float opacity) {
final DataSource<CloseableReference<CloseableImage>> dataSource
= imagePipeline.fetchImageFromBitmapCache(request, mContext);
Paint alphaPaint = new Paint();
alphaPaint.setAlpha((int) (opacity * 255));
canvas.drawBitmap(bitmap, null, vbRect, alphaPaint);
mCTM.mapRect(vbRect);
this.setClientRect(vbRect);
}
try {
final CloseableReference<CloseableImage> imageReference = dataSource.getResult();
if (imageReference == null) {
return;
}
private void tryRenderFromBitmapCache(
ImagePipeline imagePipeline,
ImageRequest request,
Canvas canvas,
Paint paint,
float opacity) {
final DataSource<CloseableReference<CloseableImage>> dataSource =
imagePipeline.fetchImageFromBitmapCache(request, mContext);
try {
CloseableImage closeableImage = imageReference.get();
if (!(closeableImage instanceof CloseableBitmap)) {
return;
}
try {
final CloseableReference<CloseableImage> imageReference = dataSource.getResult();
if (imageReference == null) {
return;
}
CloseableBitmap closeableBitmap = (CloseableBitmap) closeableImage;
final Bitmap bitmap = closeableBitmap.getUnderlyingBitmap();
if (bitmap == null) {
return;
}
doRender(canvas, paint, bitmap, opacity);
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
CloseableReference.closeSafely(imageReference);
}
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
dataSource.close();
try {
CloseableImage closeableImage = imageReference.get();
if (!(closeableImage instanceof CloseableBitmap)) {
return;
}
}
CloseableBitmap closeableBitmap = (CloseableBitmap) closeableImage;
final Bitmap bitmap = closeableBitmap.getUnderlyingBitmap();
if (bitmap == null) {
return;
}
doRender(canvas, paint, bitmap, opacity);
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
CloseableReference.closeSafely(imageReference);
}
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
dataSource.close();
}
}
}

View File

@@ -6,83 +6,81 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.annotation.SuppressLint;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
@SuppressLint("ViewConstructor")
class LineView extends RenderableView {
private SVGLength mX1;
private SVGLength mY1;
private SVGLength mX2;
private SVGLength mY2;
private SVGLength mX1;
private SVGLength mY1;
private SVGLength mX2;
private SVGLength mY2;
public LineView(ReactContext reactContext) {
super(reactContext);
}
public LineView(ReactContext reactContext) {
super(reactContext);
}
@ReactProp(name = "x1")
public void setX1(Dynamic x1) {
mX1 = SVGLength.from(x1);
invalidate();
}
@ReactProp(name = "x1")
public void setX1(Dynamic x1) {
mX1 = SVGLength.from(x1);
invalidate();
}
public void setX1(String x1) {
mX1 = SVGLength.from(x1);
invalidate();
}
@ReactProp(name = "y1")
public void setY1(Dynamic y1) {
mY1 = SVGLength.from(y1);
invalidate();
}
@ReactProp(name = "y1")
public void setY1(Dynamic y1) {
mY1 = SVGLength.from(y1);
invalidate();
}
public void setY1(String y1) {
mY1 = SVGLength.from(y1);
invalidate();
}
@ReactProp(name = "x2")
public void setX2(Dynamic x2) {
mX2 = SVGLength.from(x2);
invalidate();
}
@ReactProp(name = "x2")
public void setX2(Dynamic x2) {
mX2 = SVGLength.from(x2);
invalidate();
}
public void setX2(String x2) {
mX2 = SVGLength.from(x2);
invalidate();
}
@ReactProp(name = "y2")
public void setY2(Dynamic y2) {
mY2 = SVGLength.from(y2);
invalidate();
}
@ReactProp(name = "y2")
public void setY2(Dynamic y2) {
mY2 = SVGLength.from(y2);
invalidate();
}
public void setY2(String y2) {
mY2 = SVGLength.from(y2);
invalidate();
}
@Override
Path getPath(Canvas canvas, Paint paint) {
Path path = new Path();
double x1 = relativeOnWidth(mX1);
double y1 = relativeOnHeight(mY1);
double x2 = relativeOnWidth(mX2);
double y2 = relativeOnHeight(mY2);
@Override
Path getPath(Canvas canvas, Paint paint) {
Path path = new Path();
double x1 = relativeOnWidth(mX1);
double y1 = relativeOnHeight(mY1);
double x2 = relativeOnWidth(mX2);
double y2 = relativeOnHeight(mY2);
path.moveTo((float) x1, (float) y1);
path.lineTo((float) x2, (float) y2);
return path;
}
path.moveTo((float) x1, (float) y1);
path.lineTo((float) x2, (float) y2);
return path;
}
}

View File

@@ -6,140 +6,138 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.annotation.SuppressLint;
import android.graphics.Matrix;
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.uimanager.annotations.ReactProp;
import javax.annotation.Nullable;
@SuppressLint("ViewConstructor")
class LinearGradientView extends DefinitionView {
private SVGLength mX1;
private SVGLength mY1;
private SVGLength mX2;
private SVGLength mY2;
private ReadableArray mGradient;
private Brush.BrushUnits mGradientUnits;
private SVGLength mX1;
private SVGLength mY1;
private SVGLength mX2;
private SVGLength mY2;
private ReadableArray mGradient;
private Brush.BrushUnits mGradientUnits;
private static final float[] sRawMatrix = new float[]{
private static final float[] sRawMatrix =
new float[] {
1, 0, 0,
0, 1, 0,
0, 0, 1
};
private Matrix mMatrix = null;
};
private Matrix mMatrix = null;
public LinearGradientView(ReactContext reactContext) {
super(reactContext);
}
public LinearGradientView(ReactContext reactContext) {
super(reactContext);
}
@ReactProp(name = "x1")
public void setX1(Dynamic x1) {
mX1 = SVGLength.from(x1);
invalidate();
}
@ReactProp(name = "x1")
public void setX1(Dynamic x1) {
mX1 = SVGLength.from(x1);
invalidate();
}
public void setX1(String x1) {
mX1 = SVGLength.from(x1);
invalidate();
}
@ReactProp(name = "y1")
public void setY1(Dynamic y1) {
mY1 = SVGLength.from(y1);
invalidate();
}
@ReactProp(name = "y1")
public void setY1(Dynamic y1) {
mY1 = SVGLength.from(y1);
invalidate();
}
public void setY1(String y1) {
mY1 = SVGLength.from(y1);
invalidate();
}
@ReactProp(name = "x2")
public void setX2(Dynamic x2) {
mX2 = SVGLength.from(x2);
invalidate();
}
@ReactProp(name = "x2")
public void setX2(Dynamic x2) {
mX2 = SVGLength.from(x2);
invalidate();
}
public void setX2(String x2) {
mX2 = SVGLength.from(x2);
invalidate();
}
@ReactProp(name = "y2")
public void setY2(Dynamic y2) {
mY2 = SVGLength.from(y2);
invalidate();
}
@ReactProp(name = "y2")
public void setY2(Dynamic y2) {
mY2 = SVGLength.from(y2);
invalidate();
}
public void setY2(String y2) {
mY2 = SVGLength.from(y2);
invalidate();
}
@ReactProp(name = "gradient")
public void setGradient(ReadableArray gradient) {
mGradient = gradient;
invalidate();
}
@ReactProp(name = "gradient")
public void setGradient(ReadableArray gradient) {
mGradient = gradient;
invalidate();
}
@ReactProp(name = "gradientUnits")
public void setGradientUnits(int gradientUnits) {
switch (gradientUnits) {
case 0:
mGradientUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
break;
case 1:
mGradientUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
break;
@ReactProp(name = "gradientUnits")
public void setGradientUnits(int gradientUnits) {
switch (gradientUnits) {
case 0:
mGradientUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
break;
case 1:
mGradientUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
break;
}
invalidate();
}
@ReactProp(name = "gradientTransform")
public void setGradientTransform(@Nullable ReadableArray matrixArray) {
if (matrixArray != null) {
int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
if (matrixSize == 6) {
if (mMatrix == null) {
mMatrix = new Matrix();
}
invalidate();
mMatrix.setValues(sRawMatrix);
} else if (matrixSize != -1) {
FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
}
} else {
mMatrix = null;
}
@ReactProp(name = "gradientTransform")
public void setGradientTransform(@Nullable ReadableArray matrixArray) {
if (matrixArray != null) {
int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
if (matrixSize == 6) {
if (mMatrix == null) {
mMatrix = new Matrix();
}
mMatrix.setValues(sRawMatrix);
} else if (matrixSize != -1) {
FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
}
} else {
mMatrix = null;
}
invalidate();
}
invalidate();
}
@Override
void saveDefinition() {
if (mName != null) {
SVGLength[] points = new SVGLength[]{mX1, mY1, mX2, mY2};
Brush brush = new Brush(Brush.BrushType.LINEAR_GRADIENT, points, mGradientUnits);
brush.setGradientColors(mGradient);
if (mMatrix != null) {
brush.setGradientTransform(mMatrix);
}
SvgView svg = getSvgView();
if (mGradientUnits == Brush.BrushUnits.USER_SPACE_ON_USE) {
brush.setUserSpaceBoundingBox(svg.getCanvasBounds());
}
svg.defineBrush(brush, mName);
}
@Override
void saveDefinition() {
if (mName != null) {
SVGLength[] points = new SVGLength[] {mX1, mY1, mX2, mY2};
Brush brush = new Brush(Brush.BrushType.LINEAR_GRADIENT, points, mGradientUnits);
brush.setGradientColors(mGradient);
if (mMatrix != null) {
brush.setGradientTransform(mMatrix);
}
SvgView svg = getSvgView();
if (mGradientUnits == Brush.BrushUnits.USER_SPACE_ON_USE) {
brush.setUserSpaceBoundingBox(svg.getCanvasBounds());
}
svg.defineBrush(brush, mName);
}
}
}

View File

@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.annotation.SuppressLint;
@@ -15,7 +14,6 @@ import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.view.View;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
@@ -23,167 +21,173 @@ import com.facebook.react.uimanager.annotations.ReactProp;
@SuppressLint("ViewConstructor")
class MarkerView extends GroupView {
private SVGLength mRefX;
private SVGLength mRefY;
private SVGLength mMarkerWidth;
private SVGLength mMarkerHeight;
private String mMarkerUnits;
private String mOrient;
private SVGLength mRefX;
private SVGLength mRefY;
private SVGLength mMarkerWidth;
private SVGLength mMarkerHeight;
private String mMarkerUnits;
private String mOrient;
private float mMinX;
private float mMinY;
private float mVbWidth;
private float mVbHeight;
String mAlign;
int mMeetOrSlice;
private float mMinX;
private float mMinY;
private float mVbWidth;
private float mVbHeight;
String mAlign;
int mMeetOrSlice;
Matrix markerTransform = new Matrix();
Matrix markerTransform = new Matrix();
public MarkerView(ReactContext reactContext) {
super(reactContext);
}
public MarkerView(ReactContext reactContext) {
super(reactContext);
}
@ReactProp(name = "refX")
public void setRefX(Dynamic refX) {
mRefX = SVGLength.from(refX);
invalidate();
}
@ReactProp(name = "refX")
public void setRefX(Dynamic refX) {
mRefX = SVGLength.from(refX);
invalidate();
}
public void setRefX(String refX) {
mRefX = SVGLength.from(refX);
invalidate();
}
@ReactProp(name = "refY")
public void setRefY(Dynamic refY) {
mRefY = SVGLength.from(refY);
invalidate();
}
@ReactProp(name = "refY")
public void setRefY(Dynamic refY) {
mRefY = SVGLength.from(refY);
invalidate();
}
public void setRefY(String refY) {
mRefY = SVGLength.from(refY);
invalidate();
}
@ReactProp(name = "markerWidth")
public void setMarkerWidth(Dynamic markerWidth) {
mMarkerWidth = SVGLength.from(markerWidth);
invalidate();
}
@ReactProp(name = "markerWidth")
public void setMarkerWidth(Dynamic markerWidth) {
mMarkerWidth = SVGLength.from(markerWidth);
invalidate();
}
public void setMarkerWidth(String markerWidth) {
mMarkerWidth = SVGLength.from(markerWidth);
invalidate();
}
@ReactProp(name = "markerHeight")
public void setMarkerHeight(Dynamic markerHeight) {
mMarkerHeight = SVGLength.from(markerHeight);
invalidate();
}
@ReactProp(name = "markerHeight")
public void setMarkerHeight(Dynamic markerHeight) {
mMarkerHeight = SVGLength.from(markerHeight);
invalidate();
}
public void setMarkerHeight(String markerHeight) {
mMarkerHeight = SVGLength.from(markerHeight);
invalidate();
}
@ReactProp(name = "markerUnits")
public void setMarkerUnits(String markerUnits) {
mMarkerUnits = markerUnits;
invalidate();
}
@ReactProp(name = "markerUnits")
public void setMarkerUnits(String markerUnits) {
mMarkerUnits = markerUnits;
invalidate();
}
@ReactProp(name = "orient")
public void setOrient(String orient) {
mOrient = orient;
invalidate();
}
@ReactProp(name = "orient")
public void setOrient(String orient) {
mOrient = orient;
invalidate();
}
@ReactProp(name = "minX")
public void setMinX(float minX) {
mMinX = minX;
invalidate();
}
@ReactProp(name = "minX")
public void setMinX(float minX) {
mMinX = minX;
invalidate();
}
@ReactProp(name = "minY")
public void setMinY(float minY) {
mMinY = minY;
invalidate();
}
@ReactProp(name = "minY")
public void setMinY(float minY) {
mMinY = minY;
invalidate();
}
@ReactProp(name = "vbWidth")
public void setVbWidth(float vbWidth) {
mVbWidth = vbWidth;
invalidate();
}
@ReactProp(name = "vbWidth")
public void setVbWidth(float vbWidth) {
mVbWidth = vbWidth;
invalidate();
}
@ReactProp(name = "vbHeight")
public void setVbHeight(float vbHeight) {
mVbHeight = vbHeight;
invalidate();
}
@ReactProp(name = "vbHeight")
public void setVbHeight(float vbHeight) {
mVbHeight = vbHeight;
invalidate();
}
@ReactProp(name = "align")
public void setAlign(String align) {
mAlign = align;
invalidate();
}
@ReactProp(name = "align")
public void setAlign(String align) {
mAlign = align;
invalidate();
}
@ReactProp(name = "meetOrSlice")
public void setMeetOrSlice(int meetOrSlice) {
mMeetOrSlice = meetOrSlice;
invalidate();
}
@ReactProp(name = "meetOrSlice")
public void setMeetOrSlice(int meetOrSlice) {
mMeetOrSlice = meetOrSlice;
invalidate();
}
@Override
void saveDefinition() {
if (mName != null) {
SvgView svg = getSvgView();
svg.defineMarker(this, mName);
for (int i = 0; i < getChildCount(); i++) {
View node = getChildAt(i);
if (node instanceof VirtualView) {
((VirtualView)node).saveDefinition();
}
}
@Override
void saveDefinition() {
if (mName != null) {
SvgView svg = getSvgView();
svg.defineMarker(this, mName);
for (int i = 0; i < getChildCount(); i++) {
View node = getChildAt(i);
if (node instanceof VirtualView) {
((VirtualView) node).saveDefinition();
}
}
}
}
void renderMarker(
Canvas canvas, Paint paint, float opacity, RNSVGMarkerPosition position, float strokeWidth) {
int count = saveAndSetupCanvas(canvas, mCTM);
markerTransform.reset();
Point origin = position.origin;
markerTransform.setTranslate((float) origin.x * mScale, (float) origin.y * mScale);
double markerAngle = "auto".equals(mOrient) ? -1 : Double.parseDouble(mOrient);
float degrees = 180 + (float) (markerAngle == -1 ? position.angle : markerAngle);
markerTransform.preRotate(degrees);
boolean useStrokeWidth = "strokeWidth".equals(mMarkerUnits);
if (useStrokeWidth) {
markerTransform.preScale(strokeWidth, strokeWidth);
}
void renderMarker(Canvas canvas, Paint paint, float opacity, RNSVGMarkerPosition position, float strokeWidth) {
int count = saveAndSetupCanvas(canvas, mCTM);
markerTransform.reset();
Point origin = position.origin;
markerTransform.setTranslate((float)origin.x * mScale, (float)origin.y * mScale);
double markerAngle = "auto".equals(mOrient) ? -1 : Double.parseDouble(mOrient);
float degrees = 180 + (float) (markerAngle == -1 ? position.angle : markerAngle);
markerTransform.preRotate(degrees);
boolean useStrokeWidth = "strokeWidth".equals(mMarkerUnits);
if (useStrokeWidth) {
markerTransform.preScale(strokeWidth, strokeWidth);
}
double width = relativeOnWidth(mMarkerWidth) / mScale;
double height = relativeOnHeight(mMarkerHeight) / mScale;
RectF eRect = new RectF(0, 0, (float)width, (float)height);
if (mAlign != null) {
RectF vbRect = new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale);
Matrix viewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice);
float[] values = new float[9];
viewBoxMatrix.getValues(values);
markerTransform.preScale(values[Matrix.MSCALE_X], values[Matrix.MSCALE_Y]);
}
double x = relativeOnWidth(mRefX);
double y = relativeOnHeight(mRefY);
markerTransform.preTranslate((float)-x, (float)-y);
canvas.concat(markerTransform);
drawGroup(canvas, paint, opacity);
restoreCanvas(canvas, count);
double width = relativeOnWidth(mMarkerWidth) / mScale;
double height = relativeOnHeight(mMarkerHeight) / mScale;
RectF eRect = new RectF(0, 0, (float) width, (float) height);
if (mAlign != null) {
RectF vbRect =
new RectF(
mMinX * mScale,
mMinY * mScale,
(mMinX + mVbWidth) * mScale,
(mMinY + mVbHeight) * mScale);
Matrix viewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice);
float[] values = new float[9];
viewBoxMatrix.getValues(values);
markerTransform.preScale(values[Matrix.MSCALE_X], values[Matrix.MSCALE_Y]);
}
double x = relativeOnWidth(mRefX);
double y = relativeOnHeight(mRefY);
markerTransform.preTranslate((float) -x, (float) -y);
canvas.concat(markerTransform);
drawGroup(canvas, paint, opacity);
restoreCanvas(canvas, count);
}
}

View File

@@ -6,140 +6,139 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.annotation.SuppressLint;
import android.graphics.Matrix;
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.uimanager.annotations.ReactProp;
import javax.annotation.Nullable;
@SuppressLint("ViewConstructor")
class MaskView extends GroupView {
SVGLength mX;
SVGLength mY;
SVGLength mW;
SVGLength mH;
SVGLength mX;
SVGLength mY;
SVGLength mW;
SVGLength mH;
// TODO implement proper support for units
@SuppressWarnings({"FieldCanBeLocal", "unused"})
private Brush.BrushUnits mMaskUnits;
@SuppressWarnings({"FieldCanBeLocal", "unused"})
private Brush.BrushUnits mMaskContentUnits;
// TODO implement proper support for units
@SuppressWarnings({"FieldCanBeLocal", "unused"})
private Brush.BrushUnits mMaskUnits;
private static final float[] sRawMatrix = new float[]{
@SuppressWarnings({"FieldCanBeLocal", "unused"})
private Brush.BrushUnits mMaskContentUnits;
private static final float[] sRawMatrix =
new float[] {
1, 0, 0,
0, 1, 0,
0, 0, 1
};
private Matrix mMatrix = null;
};
private Matrix mMatrix = null;
public MaskView(ReactContext reactContext) {
super(reactContext);
}
public MaskView(ReactContext reactContext) {
super(reactContext);
}
@ReactProp(name = "x")
public void setX(Dynamic x) {
mX = SVGLength.from(x);
invalidate();
}
@ReactProp(name = "x")
public void setX(Dynamic x) {
mX = SVGLength.from(x);
invalidate();
}
public void setX(String x) {
mX = SVGLength.from(x);
invalidate();
}
@ReactProp(name = "y")
public void setY(Dynamic y) {
mY = SVGLength.from(y);
invalidate();
}
@ReactProp(name = "y")
public void setY(Dynamic y) {
mY = SVGLength.from(y);
invalidate();
}
public void setY(String y) {
mY = SVGLength.from(y);
invalidate();
}
@ReactProp(name = "width")
public void setWidth(Dynamic width) {
mW = SVGLength.from(width);
invalidate();
}
@ReactProp(name = "width")
public void setWidth(Dynamic width) {
mW = SVGLength.from(width);
invalidate();
}
public void setWidth(String width) {
mW = SVGLength.from(width);
invalidate();
}
@ReactProp(name = "height")
public void setHeight(Dynamic height) {
mH = SVGLength.from(height);
invalidate();
}
@ReactProp(name = "height")
public void setHeight(Dynamic height) {
mH = SVGLength.from(height);
invalidate();
}
public void setHeight(String height) {
mH = SVGLength.from(height);
invalidate();
}
@ReactProp(name = "maskUnits")
public void setMaskUnits(int maskUnits) {
switch (maskUnits) {
case 0:
mMaskUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
break;
case 1:
mMaskUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
break;
@ReactProp(name = "maskUnits")
public void setMaskUnits(int maskUnits) {
switch (maskUnits) {
case 0:
mMaskUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
break;
case 1:
mMaskUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
break;
}
invalidate();
}
@ReactProp(name = "maskContentUnits")
public void setMaskContentUnits(int maskContentUnits) {
switch (maskContentUnits) {
case 0:
mMaskContentUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
break;
case 1:
mMaskContentUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
break;
}
invalidate();
}
@ReactProp(name = "maskTransform")
public void setMaskTransform(@Nullable ReadableArray matrixArray) {
if (matrixArray != null) {
int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
if (matrixSize == 6) {
if (mMatrix == null) {
mMatrix = new Matrix();
}
invalidate();
mMatrix.setValues(sRawMatrix);
} else if (matrixSize != -1) {
FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
}
} else {
mMatrix = null;
}
@ReactProp(name = "maskContentUnits")
public void setMaskContentUnits(int maskContentUnits) {
switch (maskContentUnits) {
case 0:
mMaskContentUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
break;
case 1:
mMaskContentUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
break;
}
invalidate();
}
invalidate();
}
@ReactProp(name = "maskTransform")
public void setMaskTransform(@Nullable ReadableArray matrixArray) {
if (matrixArray != null) {
int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
if (matrixSize == 6) {
if (mMatrix == null) {
mMatrix = new Matrix();
}
mMatrix.setValues(sRawMatrix);
} else if (matrixSize != -1) {
FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
}
} else {
mMatrix = null;
}
invalidate();
}
@Override
void saveDefinition() {
if (mName != null) {
SvgView svg = getSvgView();
svg.defineMask(this, mName);
}
@Override
void saveDefinition() {
if (mName != null) {
SvgView svg = getSvgView();
svg.defineMask(this, mName);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -6,37 +6,34 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.annotation.SuppressLint;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
@SuppressLint("ViewConstructor")
class PathView extends RenderableView {
private Path mPath;
private Path mPath;
public PathView(ReactContext reactContext) {
super(reactContext);
PathParser.mScale = mScale;
mPath = new Path();
}
public PathView(ReactContext reactContext) {
super(reactContext);
PathParser.mScale = mScale;
mPath = new Path();
}
@ReactProp(name = "d")
public void setD(String d) {
mPath = PathParser.parse(d);
elements = PathParser.elements;
invalidate();
}
@Override
Path getPath(Canvas canvas, Paint paint) {
return mPath;
}
@ReactProp(name = "d")
public void setD(String d) {
mPath = PathParser.parse(d);
elements = PathParser.elements;
invalidate();
}
@Override
Path getPath(Canvas canvas, Paint paint) {
return mPath;
}
}

View File

@@ -6,197 +6,197 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.annotation.SuppressLint;
import android.graphics.Matrix;
import android.graphics.RectF;
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.uimanager.annotations.ReactProp;
import javax.annotation.Nullable;
@SuppressLint("ViewConstructor")
class PatternView extends GroupView {
private SVGLength mX;
private SVGLength mY;
private SVGLength mW;
private SVGLength mH;
private Brush.BrushUnits mPatternUnits;
private Brush.BrushUnits mPatternContentUnits;
private SVGLength mX;
private SVGLength mY;
private SVGLength mW;
private SVGLength mH;
private Brush.BrushUnits mPatternUnits;
private Brush.BrushUnits mPatternContentUnits;
private float mMinX;
private float mMinY;
private float mVbWidth;
private float mVbHeight;
String mAlign;
int mMeetOrSlice;
private float mMinX;
private float mMinY;
private float mVbWidth;
private float mVbHeight;
String mAlign;
int mMeetOrSlice;
private static final float[] sRawMatrix = new float[]{
private static final float[] sRawMatrix =
new float[] {
1, 0, 0,
0, 1, 0,
0, 0, 1
};
private Matrix mMatrix = null;
};
private Matrix mMatrix = null;
public PatternView(ReactContext reactContext) {
super(reactContext);
}
public PatternView(ReactContext reactContext) {
super(reactContext);
}
@ReactProp(name = "x")
public void setX(Dynamic x) {
mX = SVGLength.from(x);
invalidate();
}
@ReactProp(name = "x")
public void setX(Dynamic x) {
mX = SVGLength.from(x);
invalidate();
}
public void setX(String x) {
mX = SVGLength.from(x);
invalidate();
}
@ReactProp(name = "y")
public void setY(Dynamic y) {
mY = SVGLength.from(y);
invalidate();
}
@ReactProp(name = "y")
public void setY(Dynamic y) {
mY = SVGLength.from(y);
invalidate();
}
public void setY(String y) {
mY = SVGLength.from(y);
invalidate();
}
@ReactProp(name = "width")
public void setWidth(Dynamic width) {
mW = SVGLength.from(width);
invalidate();
}
@ReactProp(name = "width")
public void setWidth(Dynamic width) {
mW = SVGLength.from(width);
invalidate();
}
public void setWidth(String width) {
mW = SVGLength.from(width);
invalidate();
}
@ReactProp(name = "height")
public void setHeight(Dynamic height) {
mH = SVGLength.from(height);
invalidate();
}
@ReactProp(name = "height")
public void setHeight(Dynamic height) {
mH = SVGLength.from(height);
invalidate();
}
public void setHeight(String height) {
mH = SVGLength.from(height);
invalidate();
}
@ReactProp(name = "patternUnits")
public void setPatternUnits(int patternUnits) {
switch (patternUnits) {
case 0:
mPatternUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
break;
case 1:
mPatternUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
break;
@ReactProp(name = "patternUnits")
public void setPatternUnits(int patternUnits) {
switch (patternUnits) {
case 0:
mPatternUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
break;
case 1:
mPatternUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
break;
}
invalidate();
}
@ReactProp(name = "patternContentUnits")
public void setPatternContentUnits(int patternContentUnits) {
switch (patternContentUnits) {
case 0:
mPatternContentUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
break;
case 1:
mPatternContentUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
break;
}
invalidate();
}
@ReactProp(name = "patternTransform")
public void setPatternTransform(@Nullable ReadableArray matrixArray) {
if (matrixArray != null) {
int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
if (matrixSize == 6) {
if (mMatrix == null) {
mMatrix = new Matrix();
}
invalidate();
mMatrix.setValues(sRawMatrix);
} else if (matrixSize != -1) {
FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
}
} else {
mMatrix = null;
}
@ReactProp(name = "patternContentUnits")
public void setPatternContentUnits(int patternContentUnits) {
switch (patternContentUnits) {
case 0:
mPatternContentUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
break;
case 1:
mPatternContentUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
break;
}
invalidate();
}
@ReactProp(name = "patternTransform")
public void setPatternTransform(@Nullable ReadableArray matrixArray) {
if (matrixArray != null) {
int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
if (matrixSize == 6) {
if (mMatrix == null) {
mMatrix = new Matrix();
}
mMatrix.setValues(sRawMatrix);
} else if (matrixSize != -1) {
FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
}
} else {
mMatrix = null;
}
invalidate();
}
@ReactProp(name = "minX")
public void setMinX(float minX) {
mMinX = minX;
invalidate();
}
@ReactProp(name = "minY")
public void setMinY(float minY) {
mMinY = minY;
invalidate();
}
@ReactProp(name = "vbWidth")
public void setVbWidth(float vbWidth) {
mVbWidth = vbWidth;
invalidate();
}
@ReactProp(name = "vbHeight")
public void setVbHeight(float vbHeight) {
mVbHeight = vbHeight;
invalidate();
}
@ReactProp(name = "align")
public void setAlign(String align) {
mAlign = align;
invalidate();
}
@ReactProp(name = "meetOrSlice")
public void setMeetOrSlice(int meetOrSlice) {
mMeetOrSlice = meetOrSlice;
invalidate();
}
RectF getViewBox() {
return new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale);
}
@Override
void saveDefinition() {
if (mName != null) {
SVGLength[] points = new SVGLength[]{mX,mY,mW,mH};
Brush brush = new Brush(Brush.BrushType.PATTERN, points, mPatternUnits);
brush.setContentUnits(mPatternContentUnits);
brush.setPattern(this);
if (mMatrix != null) {
brush.setGradientTransform(mMatrix);
}
SvgView svg = getSvgView();
if (mPatternUnits == Brush.BrushUnits.USER_SPACE_ON_USE || mPatternContentUnits == Brush.BrushUnits.USER_SPACE_ON_USE) {
brush.setUserSpaceBoundingBox(svg.getCanvasBounds());
}
svg.defineBrush(brush, mName);
}
invalidate();
}
@ReactProp(name = "minX")
public void setMinX(float minX) {
mMinX = minX;
invalidate();
}
@ReactProp(name = "minY")
public void setMinY(float minY) {
mMinY = minY;
invalidate();
}
@ReactProp(name = "vbWidth")
public void setVbWidth(float vbWidth) {
mVbWidth = vbWidth;
invalidate();
}
@ReactProp(name = "vbHeight")
public void setVbHeight(float vbHeight) {
mVbHeight = vbHeight;
invalidate();
}
@ReactProp(name = "align")
public void setAlign(String align) {
mAlign = align;
invalidate();
}
@ReactProp(name = "meetOrSlice")
public void setMeetOrSlice(int meetOrSlice) {
mMeetOrSlice = meetOrSlice;
invalidate();
}
RectF getViewBox() {
return new RectF(
mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale);
}
@Override
void saveDefinition() {
if (mName != null) {
SVGLength[] points = new SVGLength[] {mX, mY, mW, mH};
Brush brush = new Brush(Brush.BrushType.PATTERN, points, mPatternUnits);
brush.setContentUnits(mPatternContentUnits);
brush.setPattern(this);
if (mMatrix != null) {
brush.setGradientTransform(mMatrix);
}
SvgView svg = getSvgView();
if (mPatternUnits == Brush.BrushUnits.USER_SPACE_ON_USE
|| mPatternContentUnits == Brush.BrushUnits.USER_SPACE_ON_USE) {
brush.setUserSpaceBoundingBox(svg.getCanvasBounds());
}
svg.defineBrush(brush, mName);
}
}
}

View File

@@ -6,215 +6,211 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import com.facebook.react.bridge.ReadableArray;
/**
* Contains static helper methods for accessing props.
*/
/** Contains static helper methods for accessing props. */
class PropHelper {
private static final int inputMatrixDataSize = 6;
private static final int inputMatrixDataSize = 6;
/**
* Converts given {@link ReadableArray} to a matrix data array, {@code float[6]}.
* Writes result to the array passed in {@param into}.
* This method will write exactly six items to the output array from the input array.
*
* If the input array has a different size, then only the size is returned;
* Does not check output array size. Ensure space for at least six elements.
*
* @param value input array
* @param sRawMatrix output matrix
* @param mScale current resolution scaling
* @return size of input array
*/
static int toMatrixData(ReadableArray value, float[] sRawMatrix, float mScale) {
int fromSize = value.size();
if (fromSize != inputMatrixDataSize) {
return fromSize;
}
sRawMatrix[0] = (float) value.getDouble(0);
sRawMatrix[1] = (float) value.getDouble(2);
sRawMatrix[2] = (float) value.getDouble(4) * mScale;
sRawMatrix[3] = (float) value.getDouble(1);
sRawMatrix[4] = (float) value.getDouble(3);
sRawMatrix[5] = (float) value.getDouble(5) * mScale;
return inputMatrixDataSize;
/**
* Converts given {@link ReadableArray} to a matrix data array, {@code float[6]}. Writes result to
* the array passed in {@param into}. This method will write exactly six items to the output array
* from the input array.
*
* <p>If the input array has a different size, then only the size is returned; Does not check
* output array size. Ensure space for at least six elements.
*
* @param value input array
* @param sRawMatrix output matrix
* @param mScale current resolution scaling
* @return size of input array
*/
static int toMatrixData(ReadableArray value, float[] sRawMatrix, float mScale) {
int fromSize = value.size();
if (fromSize != inputMatrixDataSize) {
return fromSize;
}
/**
* Converts length string into px / user units
* in the current user coordinate system
*
* @param length length string
* @param relative relative size for percentages
* @param scale scaling parameter
* @param fontSize current font size
* @return value in the current user coordinate system
*/
static double fromRelative(String length, double relative, double scale, double fontSize) {
/*
TODO list
sRawMatrix[0] = (float) value.getDouble(0);
sRawMatrix[1] = (float) value.getDouble(2);
sRawMatrix[2] = (float) value.getDouble(4) * mScale;
sRawMatrix[3] = (float) value.getDouble(1);
sRawMatrix[4] = (float) value.getDouble(3);
sRawMatrix[5] = (float) value.getDouble(5) * mScale;
unit relative to
em font size of the element
ex x-height of the elements font
ch width of the "0" (ZERO, U+0030) glyph in the elements font
rem font size of the root element
vw 1% of viewports width
vh 1% of viewports height
vmin 1% of viewports smaller dimension
vmax 1% of viewports larger dimension
return inputMatrixDataSize;
}
relative-size [ larger | smaller ]
absolute-size: [ xx-small | x-small | small | medium | large | x-large | xx-large ]
/**
* Converts length string into px / user units in the current user coordinate system
*
* @param length length string
* @param relative relative size for percentages
* @param scale scaling parameter
* @param fontSize current font size
* @return value in the current user coordinate system
*/
static double fromRelative(String length, double relative, double scale, double fontSize) {
/*
TODO list
https://www.w3.org/TR/css3-values/#relative-lengths
https://www.w3.org/TR/css3-values/#absolute-lengths
https://drafts.csswg.org/css-cascade-4/#computed-value
https://drafts.csswg.org/css-fonts-3/#propdef-font-size
https://drafts.csswg.org/css2/fonts.html#propdef-font-size
*/
length = length.trim();
int stringLength = length.length();
int percentIndex = stringLength - 1;
if (stringLength == 0 || length.equals("normal")) {
return 0d;
} else if (length.codePointAt(percentIndex) == '%') {
return Double.valueOf(length.substring(0, percentIndex)) / 100 * relative;
} else {
int twoLetterUnitIndex = stringLength - 2;
if (twoLetterUnitIndex > 0) {
String lastTwo = length.substring(twoLetterUnitIndex);
int end = twoLetterUnitIndex;
double unit = 1;
unit relative to
em font size of the element
ex x-height of the elements font
ch width of the "0" (ZERO, U+0030) glyph in the elements font
rem font size of the root element
vw 1% of viewports width
vh 1% of viewports height
vmin 1% of viewports smaller dimension
vmax 1% of viewports larger dimension
switch (lastTwo) {
case "px":
break;
relative-size [ larger | smaller ]
absolute-size: [ xx-small | x-small | small | medium | large | x-large | xx-large ]
case "em":
unit = fontSize;
break;
/*
"1pt" equals "1.25px" (and therefore 1.25 user units)
"1pc" equals "15px" (and therefore 15 user units)
"1mm" would be "3.543307px" (3.543307 user units)
"1cm" equals "35.43307px" (and therefore 35.43307 user units)
"1in" equals "90px" (and therefore 90 user units)
*/
case "pt":
unit = 1.25d;
break;
case "pc":
unit = 15;
break;
case "mm":
unit = 3.543307d;
break;
case "cm":
unit = 35.43307d;
break;
case "in":
unit = 90;
break;
default:
end = stringLength;
}
return Double.valueOf(length.substring(0, end)) * unit * scale;
} else {
return Double.valueOf(length) * scale;
}
}
}
/**
* Converts SVGLength into px / user units
* in the current user coordinate system
*
* @param length length string
* @param relative relative size for percentages
* @param offset offset for all units
* @param scale scaling parameter
* @param fontSize current font size
* @return value in the current user coordinate system
*/
static double fromRelative(SVGLength length, double relative, double offset, double scale, double fontSize) {
/*
TODO list
unit relative to
em font size of the element
ex x-height of the elements font
ch width of the "0" (ZERO, U+0030) glyph in the elements font
rem font size of the root element
vw 1% of viewports width
vh 1% of viewports height
vmin 1% of viewports smaller dimension
vmax 1% of viewports larger dimension
relative-size [ larger | smaller ]
absolute-size: [ xx-small | x-small | small | medium | large | x-large | xx-large ]
https://www.w3.org/TR/css3-values/#relative-lengths
https://www.w3.org/TR/css3-values/#absolute-lengths
https://drafts.csswg.org/css-cascade-4/#computed-value
https://drafts.csswg.org/css-fonts-3/#propdef-font-size
https://drafts.csswg.org/css2/fonts.html#propdef-font-size
*/
if (length == null) {
return offset;
}
SVGLength.UnitType unitType = length.unit;
double value = length.value;
https://www.w3.org/TR/css3-values/#relative-lengths
https://www.w3.org/TR/css3-values/#absolute-lengths
https://drafts.csswg.org/css-cascade-4/#computed-value
https://drafts.csswg.org/css-fonts-3/#propdef-font-size
https://drafts.csswg.org/css2/fonts.html#propdef-font-size
*/
length = length.trim();
int stringLength = length.length();
int percentIndex = stringLength - 1;
if (stringLength == 0 || length.equals("normal")) {
return 0d;
} else if (length.codePointAt(percentIndex) == '%') {
return Double.valueOf(length.substring(0, percentIndex)) / 100 * relative;
} else {
int twoLetterUnitIndex = stringLength - 2;
if (twoLetterUnitIndex > 0) {
String lastTwo = length.substring(twoLetterUnitIndex);
int end = twoLetterUnitIndex;
double unit = 1;
switch (unitType) {
case NUMBER:
case PX:
break;
case PERCENTAGE:
return value / 100 * relative + offset;
switch (lastTwo) {
case "px":
break;
case EMS:
unit = fontSize;
break;
case EXS:
unit = fontSize / 2;
break;
case "em":
unit = fontSize;
break;
case CM:
unit = 35.43307;
break;
case MM:
unit = 3.543307;
break;
case IN:
unit = 90;
break;
case PT:
unit = 1.25;
break;
case PC:
unit = 15;
break;
/*
"1pt" equals "1.25px" (and therefore 1.25 user units)
"1pc" equals "15px" (and therefore 15 user units)
"1mm" would be "3.543307px" (3.543307 user units)
"1cm" equals "35.43307px" (and therefore 35.43307 user units)
"1in" equals "90px" (and therefore 90 user units)
*/
default:
case UNKNOWN:
return value * scale + offset;
case "pt":
unit = 1.25d;
break;
case "pc":
unit = 15;
break;
case "mm":
unit = 3.543307d;
break;
case "cm":
unit = 35.43307d;
break;
case "in":
unit = 90;
break;
default:
end = stringLength;
}
return value * unit * scale + offset;
return Double.valueOf(length.substring(0, end)) * unit * scale;
} else {
return Double.valueOf(length) * scale;
}
}
}
/**
* Converts SVGLength into px / user units in the current user coordinate system
*
* @param length length string
* @param relative relative size for percentages
* @param offset offset for all units
* @param scale scaling parameter
* @param fontSize current font size
* @return value in the current user coordinate system
*/
static double fromRelative(
SVGLength length, double relative, double offset, double scale, double fontSize) {
/*
TODO list
unit relative to
em font size of the element
ex x-height of the elements font
ch width of the "0" (ZERO, U+0030) glyph in the elements font
rem font size of the root element
vw 1% of viewports width
vh 1% of viewports height
vmin 1% of viewports smaller dimension
vmax 1% of viewports larger dimension
relative-size [ larger | smaller ]
absolute-size: [ xx-small | x-small | small | medium | large | x-large | xx-large ]
https://www.w3.org/TR/css3-values/#relative-lengths
https://www.w3.org/TR/css3-values/#absolute-lengths
https://drafts.csswg.org/css-cascade-4/#computed-value
https://drafts.csswg.org/css-fonts-3/#propdef-font-size
https://drafts.csswg.org/css2/fonts.html#propdef-font-size
*/
if (length == null) {
return offset;
}
SVGLength.UnitType unitType = length.unit;
double value = length.value;
double unit = 1;
switch (unitType) {
case NUMBER:
case PX:
break;
case PERCENTAGE:
return value / 100 * relative + offset;
case EMS:
unit = fontSize;
break;
case EXS:
unit = fontSize / 2;
break;
case CM:
unit = 35.43307;
break;
case MM:
unit = 3.543307;
break;
case IN:
unit = 90;
break;
case PT:
unit = 1.25;
break;
case PC:
unit = 15;
break;
default:
case UNKNOWN:
return value * scale + offset;
}
return value * unit * scale + offset;
}
}

View File

@@ -3,178 +3,170 @@ package com.horcrux.svg;
import java.util.ArrayList;
enum RNSVGMarkerType {
kStartMarker,
kMidMarker,
kEndMarker
kStartMarker,
kMidMarker,
kEndMarker
}
enum ElementType {
kCGPathElementAddCurveToPoint,
kCGPathElementAddQuadCurveToPoint,
kCGPathElementMoveToPoint,
kCGPathElementAddLineToPoint,
kCGPathElementCloseSubpath
kCGPathElementAddCurveToPoint,
kCGPathElementAddQuadCurveToPoint,
kCGPathElementMoveToPoint,
kCGPathElementAddLineToPoint,
kCGPathElementCloseSubpath
}
class Point {
double x;
double y;
Point(double x, double y){
this.x = x;
this.y = y;
}
double x;
double y;
Point(double x, double y) {
this.x = x;
this.y = y;
}
}
class SegmentData {
Point start_tangent; // Tangent in the start point of the segment.
Point end_tangent; // Tangent in the end point of the segment.
Point position; // The end point of the segment.
Point start_tangent; // Tangent in the start point of the segment.
Point end_tangent; // Tangent in the end point of the segment.
Point position; // The end point of the segment.
}
class RNSVGMarkerPosition {
static private ArrayList<RNSVGMarkerPosition> positions_;
static private int element_index_;
static private Point origin_;
static private Point subpath_start_;
static private Point in_slope_;
static private Point out_slope_;
@SuppressWarnings("unused")
static private boolean auto_start_reverse_; // TODO
private static ArrayList<RNSVGMarkerPosition> positions_;
private static int element_index_;
private static Point origin_;
private static Point subpath_start_;
private static Point in_slope_;
private static Point out_slope_;
RNSVGMarkerType type;
Point origin;
double angle;
@SuppressWarnings("unused")
private static boolean auto_start_reverse_; // TODO
private RNSVGMarkerPosition(RNSVGMarkerType type, Point origin, double angle) {
this.type = type;
this.origin = origin;
this.angle = angle;
RNSVGMarkerType type;
Point origin;
double angle;
private RNSVGMarkerPosition(RNSVGMarkerType type, Point origin, double angle) {
this.type = type;
this.origin = origin;
this.angle = angle;
}
static ArrayList<RNSVGMarkerPosition> fromPath(ArrayList<PathElement> elements) {
positions_ = new ArrayList<>();
element_index_ = 0;
origin_ = new Point(0, 0);
subpath_start_ = new Point(0, 0);
for (PathElement e : elements) {
UpdateFromPathElement(e);
}
PathIsDone();
return positions_;
}
static ArrayList<RNSVGMarkerPosition> fromPath(ArrayList<PathElement> elements) {
positions_ = new ArrayList<>();
element_index_ = 0;
origin_ = new Point(0, 0);
subpath_start_ = new Point(0, 0);
for (PathElement e : elements) {
UpdateFromPathElement(e);
}
PathIsDone();
return positions_;
}
private static void PathIsDone() {
double angle = CurrentAngle(RNSVGMarkerType.kEndMarker);
positions_.add(new RNSVGMarkerPosition(RNSVGMarkerType.kEndMarker, origin_, angle));
}
private static void PathIsDone() {
double angle = CurrentAngle(RNSVGMarkerType.kEndMarker);
positions_.add(new RNSVGMarkerPosition(RNSVGMarkerType.kEndMarker, origin_, angle));
}
private static double BisectingAngle(double in_angle, double out_angle) {
// WK193015: Prevent bugs due to angles being non-continuous.
if (Math.abs(in_angle - out_angle) > 180) in_angle += 360;
return (in_angle + out_angle) / 2;
}
private static double BisectingAngle(double in_angle, double out_angle) {
// WK193015: Prevent bugs due to angles being non-continuous.
if (Math.abs(in_angle - out_angle) > 180)
in_angle += 360;
return (in_angle + out_angle) / 2;
}
private static double rad2deg(double rad) {
double RNSVG_radToDeg = 180 / Math.PI;
return rad * RNSVG_radToDeg;
}
private static double rad2deg(double rad) {
double RNSVG_radToDeg = 180 / Math.PI;
return rad * RNSVG_radToDeg;
}
private static double SlopeAngleRadians(Point p) {
return Math.atan2(p.y, p.x);
}
private static double SlopeAngleRadians(Point p) {
return Math.atan2(p.y, p.x);
private static double CurrentAngle(RNSVGMarkerType type) {
// For details of this calculation, see:
// http://www.w3.org/TR/SVG/single-page.html#painting-MarkerElement
double in_angle = rad2deg(SlopeAngleRadians(in_slope_));
double out_angle = rad2deg(SlopeAngleRadians(out_slope_));
switch (type) {
case kStartMarker:
if (auto_start_reverse_) out_angle += 180;
return out_angle;
case kMidMarker:
return BisectingAngle(in_angle, out_angle);
case kEndMarker:
return in_angle;
}
return 0;
}
private static double CurrentAngle(RNSVGMarkerType type) {
// For details of this calculation, see:
// http://www.w3.org/TR/SVG/single-page.html#painting-MarkerElement
double in_angle = rad2deg(SlopeAngleRadians(in_slope_));
double out_angle = rad2deg(SlopeAngleRadians(out_slope_));
switch (type) {
case kStartMarker:
if (auto_start_reverse_)
out_angle += 180;
return out_angle;
case kMidMarker:
return BisectingAngle(in_angle, out_angle);
case kEndMarker:
return in_angle;
}
return 0;
}
private static Point subtract(Point p1, Point p2) {
return new Point(p2.x - p1.x, p2.y - p1.y);
}
private static Point subtract(Point p1, Point p2) {
return new Point(p2.x - p1.x, p2.y - p1.y);
}
private static boolean isZero(Point p) {
return p.x == 0 && p.y == 0;
}
private static boolean isZero(Point p) {
return p.x == 0 && p.y == 0;
}
private static void ComputeQuadTangents(SegmentData data, Point start, Point control, Point end) {
data.start_tangent = subtract(control, start);
data.end_tangent = subtract(end, control);
if (isZero(data.start_tangent)) data.start_tangent = data.end_tangent;
else if (isZero(data.end_tangent)) data.end_tangent = data.start_tangent;
}
private static void ComputeQuadTangents(SegmentData data,
Point start,
Point control,
Point end) {
data.start_tangent = subtract(control, start);
data.end_tangent = subtract(end, control);
if (isZero(data.start_tangent))
data.start_tangent = data.end_tangent;
else if (isZero(data.end_tangent))
data.end_tangent = data.start_tangent;
private static SegmentData ExtractPathElementFeatures(PathElement element) {
SegmentData data = new SegmentData();
Point[] points = element.points;
switch (element.type) {
case kCGPathElementAddCurveToPoint:
data.position = points[2];
data.start_tangent = subtract(points[0], origin_);
data.end_tangent = subtract(points[2], points[1]);
if (isZero(data.start_tangent)) ComputeQuadTangents(data, points[0], points[1], points[2]);
else if (isZero(data.end_tangent)) ComputeQuadTangents(data, origin_, points[0], points[1]);
break;
case kCGPathElementAddQuadCurveToPoint:
data.position = points[1];
ComputeQuadTangents(data, origin_, points[0], points[1]);
break;
case kCGPathElementMoveToPoint:
case kCGPathElementAddLineToPoint:
data.position = points[0];
data.start_tangent = subtract(data.position, origin_);
data.end_tangent = subtract(data.position, origin_);
break;
case kCGPathElementCloseSubpath:
data.position = subpath_start_;
data.start_tangent = subtract(data.position, origin_);
data.end_tangent = subtract(data.position, origin_);
break;
}
return data;
}
private static SegmentData ExtractPathElementFeatures(PathElement element) {
SegmentData data = new SegmentData();
Point[] points = element.points;
switch (element.type) {
case kCGPathElementAddCurveToPoint:
data.position = points[2];
data.start_tangent = subtract(points[0], origin_);
data.end_tangent = subtract(points[2], points[1]);
if (isZero(data.start_tangent))
ComputeQuadTangents(data, points[0], points[1], points[2]);
else if (isZero(data.end_tangent))
ComputeQuadTangents(data, origin_, points[0], points[1]);
break;
case kCGPathElementAddQuadCurveToPoint:
data.position = points[1];
ComputeQuadTangents(data, origin_, points[0], points[1]);
break;
case kCGPathElementMoveToPoint:
case kCGPathElementAddLineToPoint:
data.position = points[0];
data.start_tangent = subtract(data.position, origin_);
data.end_tangent = subtract(data.position, origin_);
break;
case kCGPathElementCloseSubpath:
data.position = subpath_start_;
data.start_tangent = subtract(data.position, origin_);
data.end_tangent = subtract(data.position, origin_);
break;
}
return data;
}
private static void UpdateFromPathElement(PathElement element) {
SegmentData segment_data = ExtractPathElementFeatures(element);
// First update the outgoing slope for the previous element.
out_slope_ = segment_data.start_tangent;
// Record the marker for the previous element.
if (element_index_ > 0) {
RNSVGMarkerType marker_type =
element_index_ == 1 ? RNSVGMarkerType.kStartMarker : RNSVGMarkerType.kMidMarker;
double angle = CurrentAngle(marker_type);
positions_.add(new RNSVGMarkerPosition(marker_type, origin_, angle));
}
// Update the incoming slope for this marker position.
in_slope_ = segment_data.end_tangent;
// Update marker position.
origin_ = segment_data.position;
// If this is a 'move to' segment, save the point for use with 'close'.
if (element.type == ElementType.kCGPathElementMoveToPoint)
subpath_start_ = element.points[0];
else if (element.type == ElementType.kCGPathElementCloseSubpath)
subpath_start_ = new Point(0, 0);
++element_index_;
private static void UpdateFromPathElement(PathElement element) {
SegmentData segment_data = ExtractPathElementFeatures(element);
// First update the outgoing slope for the previous element.
out_slope_ = segment_data.start_tangent;
// Record the marker for the previous element.
if (element_index_ > 0) {
RNSVGMarkerType marker_type =
element_index_ == 1 ? RNSVGMarkerType.kStartMarker : RNSVGMarkerType.kMidMarker;
double angle = CurrentAngle(marker_type);
positions_.add(new RNSVGMarkerPosition(marker_type, origin_, angle));
}
// Update the incoming slope for this marker position.
in_slope_ = segment_data.end_tangent;
// Update marker position.
origin_ = segment_data.position;
// If this is a 'move to' segment, save the point for use with 'close'.
if (element.type == ElementType.kCGPathElementMoveToPoint) subpath_start_ = element.points[0];
else if (element.type == ElementType.kCGPathElementCloseSubpath)
subpath_start_ = new Point(0, 0);
++element_index_;
}
}

View File

@@ -6,16 +6,16 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import static com.facebook.react.common.StandardCharsets.UTF_8;
import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.RectF;
import android.graphics.Region;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
@@ -23,251 +23,247 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.annotation.Nonnull;
import static com.facebook.react.common.StandardCharsets.UTF_8;
class RNSVGRenderableManager extends ReactContextBaseJavaModule {
RNSVGRenderableManager(ReactApplicationContext reactContext) {
super(reactContext);
RNSVGRenderableManager(ReactApplicationContext reactContext) {
super(reactContext);
}
@Nonnull
@Override
public String getName() {
return "RNSVGRenderableManager";
}
@SuppressWarnings("unused")
@ReactMethod(isBlockingSynchronousMethod = true)
public boolean isPointInFill(int tag, ReadableMap options) {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
if (svg == null) {
return false;
}
@Nonnull
@Override
public String getName() {
return "RNSVGRenderableManager";
float scale = svg.mScale;
float x = (float) options.getDouble("x") * scale;
float y = (float) options.getDouble("y") * scale;
int i = svg.hitTest(new float[] {x, y});
return i != -1;
}
@SuppressWarnings("unused")
@ReactMethod(isBlockingSynchronousMethod = true)
public boolean isPointInStroke(int tag, ReadableMap options) {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
if (svg == null) {
return false;
}
@SuppressWarnings("unused")
@ReactMethod(isBlockingSynchronousMethod = true)
public boolean isPointInFill(int tag, ReadableMap options) {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
if (svg == null) {
return false;
try {
svg.getPath(null, null);
} catch (NullPointerException e) {
svg.invalidate();
return false;
}
svg.initBounds();
float scale = svg.mScale;
int x = (int) (options.getDouble("x") * scale);
int y = (int) (options.getDouble("y") * scale);
Region strokeRegion = svg.mStrokeRegion;
return strokeRegion != null && strokeRegion.contains(x, y);
}
@SuppressWarnings("unused")
@ReactMethod(isBlockingSynchronousMethod = true)
public float getTotalLength(int tag) {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
if (svg == null) {
return 0;
}
Path path;
try {
path = svg.getPath(null, null);
} catch (NullPointerException e) {
svg.invalidate();
return -1;
}
PathMeasure pm = new PathMeasure(path, false);
return pm.getLength() / svg.mScale;
}
@SuppressWarnings("unused")
@ReactMethod(isBlockingSynchronousMethod = true)
public WritableMap getPointAtLength(int tag, ReadableMap options) {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
if (svg == null) {
return Arguments.createMap();
}
Path path;
try {
path = svg.getPath(null, null);
} catch (NullPointerException e) {
svg.invalidate();
return Arguments.createMap();
}
PathMeasure pm = new PathMeasure(path, false);
float length = (float) options.getDouble("length");
float scale = svg.mScale;
float[] pos = new float[2];
float[] tan = new float[2];
float distance = Math.max(0, Math.min(length, pm.getLength()));
pm.getPosTan(distance, pos, tan);
double angle = Math.atan2(tan[1], tan[0]);
WritableMap result = Arguments.createMap();
result.putDouble("x", pos[0] / scale);
result.putDouble("y", pos[1] / scale);
result.putDouble("angle", angle);
return result;
}
@SuppressWarnings("unused")
@ReactMethod(isBlockingSynchronousMethod = true)
public WritableMap getBBox(int tag, ReadableMap options) {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
if (svg == null) {
return Arguments.createMap();
}
boolean fill = options.getBoolean("fill");
boolean stroke = options.getBoolean("stroke");
boolean markers = options.getBoolean("markers");
boolean clipped = options.getBoolean("clipped");
try {
svg.getPath(null, null);
} catch (NullPointerException e) {
svg.invalidate();
return Arguments.createMap();
}
float scale = svg.mScale;
svg.initBounds();
RectF bounds = new RectF();
RectF fillBounds = svg.mFillBounds;
RectF strokeBounds = svg.mStrokeBounds;
RectF markerBounds = svg.mMarkerBounds;
RectF clipBounds = svg.mClipBounds;
if (fill && fillBounds != null) {
bounds.union(fillBounds);
}
if (stroke && strokeBounds != null) {
bounds.union(strokeBounds);
}
if (markers && markerBounds != null) {
bounds.union(markerBounds);
}
if (clipped && clipBounds != null) {
bounds.intersect(clipBounds);
}
WritableMap result = Arguments.createMap();
result.putDouble("x", bounds.left / scale);
result.putDouble("y", bounds.top / scale);
result.putDouble("width", bounds.width() / scale);
result.putDouble("height", bounds.height() / scale);
return result;
}
@SuppressWarnings("unused")
@ReactMethod(isBlockingSynchronousMethod = true)
public WritableMap getCTM(int tag) {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
if (svg == null) {
return Arguments.createMap();
}
float scale = svg.mScale;
Matrix ctm = new Matrix(svg.mCTM);
Matrix invViewBoxMatrix = svg.getSvgView().mInvViewBoxMatrix;
ctm.preConcat(invViewBoxMatrix);
float[] values = new float[9];
ctm.getValues(values);
WritableMap result = Arguments.createMap();
result.putDouble("a", values[Matrix.MSCALE_X]);
result.putDouble("b", values[Matrix.MSKEW_Y]);
result.putDouble("c", values[Matrix.MSKEW_X]);
result.putDouble("d", values[Matrix.MSCALE_Y]);
result.putDouble("e", values[Matrix.MTRANS_X] / scale);
result.putDouble("f", values[Matrix.MTRANS_Y] / scale);
return result;
}
@SuppressWarnings("unused")
@ReactMethod(isBlockingSynchronousMethod = true)
public WritableMap getScreenCTM(int tag) {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
if (svg == null) {
return Arguments.createMap();
}
float[] values = new float[9];
svg.mCTM.getValues(values);
float scale = svg.mScale;
WritableMap result = Arguments.createMap();
result.putDouble("a", values[Matrix.MSCALE_X]);
result.putDouble("b", values[Matrix.MSKEW_Y]);
result.putDouble("c", values[Matrix.MSKEW_X]);
result.putDouble("d", values[Matrix.MSCALE_Y]);
result.putDouble("e", values[Matrix.MTRANS_X] / scale);
result.putDouble("f", values[Matrix.MTRANS_Y] / scale);
return result;
}
@ReactMethod
public void getRawResource(String name, Promise promise) {
try {
ReactApplicationContext context = getReactApplicationContext();
Resources resources = context.getResources();
String packageName = context.getPackageName();
int id = resources.getIdentifier(name, "raw", packageName);
InputStream stream = resources.openRawResource(id);
try {
InputStreamReader reader = new InputStreamReader(stream, UTF_8);
char[] buffer = new char[DEFAULT_BUFFER_SIZE];
StringBuilder builder = new StringBuilder();
int n;
while ((n = reader.read(buffer, 0, DEFAULT_BUFFER_SIZE)) != EOF) {
builder.append(buffer, 0, n);
}
float scale = svg.mScale;
float x = (float) options.getDouble("x") * scale;
float y = (float) options.getDouble("y") * scale;
int i = svg.hitTest(new float[]{x, y});
return i != -1;
}
@SuppressWarnings("unused")
@ReactMethod(isBlockingSynchronousMethod = true)
public boolean isPointInStroke(int tag, ReadableMap options) {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
if (svg == null) {
return false;
}
String result = builder.toString();
promise.resolve(result);
} finally {
try {
svg.getPath(null, null);
} catch (NullPointerException e) {
svg.invalidate();
return false;
stream.close();
} catch (IOException ioe) {
// ignore
}
svg.initBounds();
float scale = svg.mScale;
int x = (int) (options.getDouble("x") * scale);
int y = (int) (options.getDouble("y") * scale);
Region strokeRegion = svg.mStrokeRegion;
return strokeRegion != null && strokeRegion.contains(x, y);
}
} catch (Exception e) {
e.printStackTrace();
promise.reject(e);
}
}
@SuppressWarnings("unused")
@ReactMethod(isBlockingSynchronousMethod = true)
public float getTotalLength(int tag) {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
if (svg == null) {
return 0;
}
Path path;
try {
path = svg.getPath(null, null);
} catch (NullPointerException e) {
svg.invalidate();
return -1;
}
PathMeasure pm = new PathMeasure(path, false);
return pm.getLength() / svg.mScale;
}
@SuppressWarnings("unused")
@ReactMethod(isBlockingSynchronousMethod = true)
public WritableMap getPointAtLength(int tag, ReadableMap options) {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
if (svg == null) {
return Arguments.createMap();
}
Path path;
try {
path = svg.getPath(null, null);
} catch (NullPointerException e) {
svg.invalidate();
return Arguments.createMap();
}
PathMeasure pm = new PathMeasure(path, false);
float length = (float) options.getDouble("length");
float scale = svg.mScale;
float[] pos = new float[2];
float[] tan = new float[2];
float distance = Math.max(0, Math.min(length, pm.getLength()));
pm.getPosTan(distance, pos, tan);
double angle = Math.atan2(tan[1], tan[0]);
WritableMap result = Arguments.createMap();
result.putDouble("x", pos[0] / scale);
result.putDouble("y", pos[1] / scale);
result.putDouble("angle", angle);
return result;
}
@SuppressWarnings("unused")
@ReactMethod(isBlockingSynchronousMethod = true)
public WritableMap getBBox(int tag, ReadableMap options) {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
if (svg == null) {
return Arguments.createMap();
}
boolean fill = options.getBoolean("fill");
boolean stroke = options.getBoolean("stroke");
boolean markers = options.getBoolean("markers");
boolean clipped = options.getBoolean("clipped");
try {
svg.getPath(null, null);
} catch (NullPointerException e) {
svg.invalidate();
return Arguments.createMap();
}
float scale = svg.mScale;
svg.initBounds();
RectF bounds = new RectF();
RectF fillBounds = svg.mFillBounds;
RectF strokeBounds = svg.mStrokeBounds;
RectF markerBounds = svg.mMarkerBounds;
RectF clipBounds = svg.mClipBounds;
if (fill && fillBounds != null) {
bounds.union(fillBounds);
}
if (stroke && strokeBounds != null) {
bounds.union(strokeBounds);
}
if (markers && markerBounds != null) {
bounds.union(markerBounds);
}
if (clipped && clipBounds != null) {
bounds.intersect(clipBounds);
}
WritableMap result = Arguments.createMap();
result.putDouble("x", bounds.left / scale);
result.putDouble("y", bounds.top / scale);
result.putDouble("width", bounds.width() / scale);
result.putDouble("height", bounds.height() / scale);
return result;
}
@SuppressWarnings("unused")
@ReactMethod(isBlockingSynchronousMethod = true)
public WritableMap getCTM(int tag) {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
if (svg == null) {
return Arguments.createMap();
}
float scale = svg.mScale;
Matrix ctm = new Matrix(svg.mCTM);
Matrix invViewBoxMatrix = svg.getSvgView().mInvViewBoxMatrix;
ctm.preConcat(invViewBoxMatrix);
float[] values = new float[9];
ctm.getValues(values);
WritableMap result = Arguments.createMap();
result.putDouble("a", values[Matrix.MSCALE_X]);
result.putDouble("b", values[Matrix.MSKEW_Y]);
result.putDouble("c", values[Matrix.MSKEW_X]);
result.putDouble("d", values[Matrix.MSCALE_Y]);
result.putDouble("e", values[Matrix.MTRANS_X] / scale);
result.putDouble("f", values[Matrix.MTRANS_Y] / scale);
return result;
}
@SuppressWarnings("unused")
@ReactMethod(isBlockingSynchronousMethod = true)
public WritableMap getScreenCTM(int tag) {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
if (svg == null) {
return Arguments.createMap();
}
float[] values = new float[9];
svg.mCTM.getValues(values);
float scale = svg.mScale;
WritableMap result = Arguments.createMap();
result.putDouble("a", values[Matrix.MSCALE_X]);
result.putDouble("b", values[Matrix.MSKEW_Y]);
result.putDouble("c", values[Matrix.MSKEW_X]);
result.putDouble("d", values[Matrix.MSCALE_Y]);
result.putDouble("e", values[Matrix.MTRANS_X] / scale);
result.putDouble("f", values[Matrix.MTRANS_Y] / scale);
return result;
}
@ReactMethod
public void getRawResource(String name, Promise promise) {
try {
ReactApplicationContext context = getReactApplicationContext();
Resources resources = context.getResources();
String packageName = context.getPackageName();
int id = resources.getIdentifier(name, "raw", packageName);
InputStream stream = resources.openRawResource(id);
try {
InputStreamReader reader = new InputStreamReader(stream, UTF_8);
char[] buffer = new char[DEFAULT_BUFFER_SIZE];
StringBuilder builder = new StringBuilder();
int n;
while ((n = reader.read(buffer, 0, DEFAULT_BUFFER_SIZE)) != EOF) {
builder.append(buffer, 0, n);
}
String result = builder.toString();
promise.resolve(result);
} finally {
try {
stream.close();
} catch (IOException ioe) {
// ignore
}
}
} catch (Exception e) {
e.printStackTrace();
promise.reject(e);
}
}
private static final int EOF = -1;
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
private static final int EOF = -1;
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
}

View File

@@ -6,163 +6,161 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.annotation.SuppressLint;
import android.graphics.Matrix;
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.uimanager.annotations.ReactProp;
import javax.annotation.Nullable;
@SuppressLint("ViewConstructor")
class RadialGradientView extends DefinitionView {
private SVGLength mFx;
private SVGLength mFy;
private SVGLength mRx;
private SVGLength mRy;
private SVGLength mCx;
private SVGLength mCy;
private ReadableArray mGradient;
private Brush.BrushUnits mGradientUnits;
private SVGLength mFx;
private SVGLength mFy;
private SVGLength mRx;
private SVGLength mRy;
private SVGLength mCx;
private SVGLength mCy;
private ReadableArray mGradient;
private Brush.BrushUnits mGradientUnits;
private static final float[] sRawMatrix = new float[]{
private static final float[] sRawMatrix =
new float[] {
1, 0, 0,
0, 1, 0,
0, 0, 1
};
private Matrix mMatrix = null;
};
private Matrix mMatrix = null;
public RadialGradientView(ReactContext reactContext) {
super(reactContext);
}
public RadialGradientView(ReactContext reactContext) {
super(reactContext);
}
@ReactProp(name = "fx")
public void setFx(Dynamic fx) {
mFx = SVGLength.from(fx);
invalidate();
}
@ReactProp(name = "fx")
public void setFx(Dynamic fx) {
mFx = SVGLength.from(fx);
invalidate();
}
public void setFx(String fx) {
mFx = SVGLength.from(fx);
invalidate();
}
@ReactProp(name = "fy")
public void setFy(Dynamic fy) {
mFy = SVGLength.from(fy);
invalidate();
}
@ReactProp(name = "fy")
public void setFy(Dynamic fy) {
mFy = SVGLength.from(fy);
invalidate();
}
public void setFy(String fy) {
mFy = SVGLength.from(fy);
invalidate();
}
@ReactProp(name = "rx")
public void setRx(Dynamic rx) {
mRx = SVGLength.from(rx);
invalidate();
}
@ReactProp(name = "rx")
public void setRx(Dynamic rx) {
mRx = SVGLength.from(rx);
invalidate();
}
public void setRx(String rx) {
mRx = SVGLength.from(rx);
invalidate();
}
@ReactProp(name = "ry")
public void setRy(Dynamic ry) {
mRy = SVGLength.from(ry);
invalidate();
}
@ReactProp(name = "ry")
public void setRy(Dynamic ry) {
mRy = SVGLength.from(ry);
invalidate();
}
public void setRy(String ry) {
mRy = SVGLength.from(ry);
invalidate();
}
@ReactProp(name = "cx")
public void setCx(Dynamic cx) {
mCx = SVGLength.from(cx);
invalidate();
}
@ReactProp(name = "cx")
public void setCx(Dynamic cx) {
mCx = SVGLength.from(cx);
invalidate();
}
public void setCx(String cx) {
mCx = SVGLength.from(cx);
invalidate();
}
@ReactProp(name = "cy")
public void setCy(Dynamic cy) {
mCy = SVGLength.from(cy);
invalidate();
}
@ReactProp(name = "cy")
public void setCy(Dynamic cy) {
mCy = SVGLength.from(cy);
invalidate();
}
public void setCy(String cy) {
mCy = SVGLength.from(cy);
invalidate();
}
@ReactProp(name = "gradient")
public void setGradient(ReadableArray gradient) {
mGradient = gradient;
invalidate();
}
@ReactProp(name = "gradient")
public void setGradient(ReadableArray gradient) {
mGradient = gradient;
invalidate();
}
@ReactProp(name = "gradientUnits")
public void setGradientUnits(int gradientUnits) {
switch (gradientUnits) {
case 0:
mGradientUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
break;
case 1:
mGradientUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
break;
@ReactProp(name = "gradientUnits")
public void setGradientUnits(int gradientUnits) {
switch (gradientUnits) {
case 0:
mGradientUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
break;
case 1:
mGradientUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
break;
}
invalidate();
}
@ReactProp(name = "gradientTransform")
public void setGradientTransform(@Nullable ReadableArray matrixArray) {
if (matrixArray != null) {
int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
if (matrixSize == 6) {
if (mMatrix == null) {
mMatrix = new Matrix();
}
invalidate();
mMatrix.setValues(sRawMatrix);
} else if (matrixSize != -1) {
FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
}
} else {
mMatrix = null;
}
@ReactProp(name = "gradientTransform")
public void setGradientTransform(@Nullable ReadableArray matrixArray) {
if (matrixArray != null) {
int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
if (matrixSize == 6) {
if (mMatrix == null) {
mMatrix = new Matrix();
}
mMatrix.setValues(sRawMatrix);
} else if (matrixSize != -1) {
FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
}
} else {
mMatrix = null;
}
invalidate();
}
invalidate();
}
@Override
void saveDefinition() {
if (mName != null) {
SVGLength[] points = new SVGLength[]{mFx,mFy,mRx,mRy,mCx,mCy};
Brush brush = new Brush(Brush.BrushType.RADIAL_GRADIENT, points, mGradientUnits);
brush.setGradientColors(mGradient);
if (mMatrix != null) {
brush.setGradientTransform(mMatrix);
}
SvgView svg = getSvgView();
if (mGradientUnits == Brush.BrushUnits.USER_SPACE_ON_USE) {
brush.setUserSpaceBoundingBox(svg.getCanvasBounds());
}
svg.defineBrush(brush, mName);
}
@Override
void saveDefinition() {
if (mName != null) {
SVGLength[] points = new SVGLength[] {mFx, mFy, mRx, mRy, mCx, mCy};
Brush brush = new Brush(Brush.BrushType.RADIAL_GRADIENT, points, mGradientUnits);
brush.setGradientColors(mGradient);
if (mMatrix != null) {
brush.setGradientTransform(mMatrix);
}
SvgView svg = getSvgView();
if (mGradientUnits == Brush.BrushUnits.USER_SPACE_ON_USE) {
brush.setUserSpaceBoundingBox(svg.getCanvasBounds());
}
svg.defineBrush(brush, mName);
}
}
}

View File

@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.annotation.SuppressLint;
@@ -15,128 +14,139 @@ import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.os.Build;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
@SuppressLint("ViewConstructor")
class RectView extends RenderableView {
private SVGLength mX;
private SVGLength mY;
private SVGLength mW;
private SVGLength mH;
private SVGLength mRx;
private SVGLength mRy;
private SVGLength mX;
private SVGLength mY;
private SVGLength mW;
private SVGLength mH;
private SVGLength mRx;
private SVGLength mRy;
public RectView(ReactContext reactContext) {
super(reactContext);
}
public RectView(ReactContext reactContext) {
super(reactContext);
}
@ReactProp(name = "x")
public void setX(Dynamic x) {
mX = SVGLength.from(x);
invalidate();
}
@ReactProp(name = "x")
public void setX(Dynamic x) {
mX = SVGLength.from(x);
invalidate();
}
public void setX(String x) {
mX = SVGLength.from(x);
invalidate();
}
@ReactProp(name = "y")
public void setY(Dynamic y) {
mY = SVGLength.from(y);
invalidate();
}
@ReactProp(name = "y")
public void setY(Dynamic y) {
mY = SVGLength.from(y);
invalidate();
}
public void setY(String y) {
mY = SVGLength.from(y);
invalidate();
}
@ReactProp(name = "width")
public void setWidth(Dynamic width) {
mW = SVGLength.from(width);
invalidate();
}
@ReactProp(name = "width")
public void setWidth(Dynamic width) {
mW = SVGLength.from(width);
invalidate();
}
public void setWidth(String width) {
mW = SVGLength.from(width);
invalidate();
}
@ReactProp(name = "height")
public void setHeight(Dynamic height) {
mH = SVGLength.from(height);
invalidate();
}
@ReactProp(name = "height")
public void setHeight(Dynamic height) {
mH = SVGLength.from(height);
invalidate();
}
public void setHeight(String height) {
mH = SVGLength.from(height);
invalidate();
}
@ReactProp(name = "rx")
public void setRx(Dynamic rx) {
mRx = SVGLength.from(rx);
invalidate();
}
@ReactProp(name = "rx")
public void setRx(Dynamic rx) {
mRx = SVGLength.from(rx);
invalidate();
}
public void setRx(String rx) {
mRx = SVGLength.from(rx);
invalidate();
}
@ReactProp(name = "ry")
public void setRy(Dynamic ry) {
mRy = SVGLength.from(ry);
invalidate();
}
@ReactProp(name = "ry")
public void setRy(Dynamic ry) {
mRy = SVGLength.from(ry);
invalidate();
}
public void setRy(String ry) {
mRy = SVGLength.from(ry);
invalidate();
}
@Override
Path getPath(Canvas canvas, Paint paint) {
Path path = new Path();
double x = relativeOnWidth(mX);
double y = relativeOnHeight(mY);
double w = relativeOnWidth(mW);
double h = relativeOnHeight(mH);
@Override
Path getPath(Canvas canvas, Paint paint) {
Path path = new Path();
double x = relativeOnWidth(mX);
double y = relativeOnHeight(mY);
double w = relativeOnWidth(mW);
double h = relativeOnHeight(mH);
if (mRx != null || mRy != null) {
double rx = 0d;
double ry = 0d;
if (mRx == null) {
ry = relativeOnHeight(mRy);
rx = ry;
} else if (mRy == null) {
rx = relativeOnWidth(mRx);
ry = rx;
} else {
rx = relativeOnWidth(mRx);
ry = relativeOnHeight(mRy);
}
if (mRx != null || mRy != null) {
double rx = 0d;
double ry = 0d;
if (mRx == null) {
ry = relativeOnHeight(mRy);
rx = ry;
} else if (mRy == null) {
rx = relativeOnWidth(mRx);
ry = rx;
} else {
rx = relativeOnWidth(mRx);
ry = relativeOnHeight(mRy);
}
if (rx > w / 2) {
rx = w / 2;
}
if (rx > w / 2) {
rx = w / 2;
}
if (ry > h / 2) {
ry = h / 2;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
path.addRoundRect((float) x, (float) y, (float) (x + w), (float) (y + h), (float) rx, (float) ry, Path.Direction.CW);
} else {
path.addRoundRect(new RectF((float) x, (float) y, (float) (x + w), (float) (y + h)), (float) rx, (float) ry, Path.Direction.CW);
}
} else {
path.addRect((float) x, (float) y, (float) (x + w), (float) (y + h), Path.Direction.CW);
path.close(); // Ensure isSimplePath = false such that rect doesn't become represented using integers
}
return path;
if (ry > h / 2) {
ry = h / 2;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
path.addRoundRect(
(float) x,
(float) y,
(float) (x + w),
(float) (y + h),
(float) rx,
(float) ry,
Path.Direction.CW);
} else {
path.addRoundRect(
new RectF((float) x, (float) y, (float) (x + w), (float) (y + h)),
(float) rx,
(float) ry,
Path.Direction.CW);
}
} else {
path.addRect((float) x, (float) y, (float) (x + w), (float) (y + h), Path.Direction.CW);
path.close(); // Ensure isSimplePath = false such that rect doesn't become represented using
// integers
}
return path;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,6 @@ package com.horcrux.svg;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReadableArray;
import java.util.ArrayList;
class SVGLength {
@@ -121,26 +120,29 @@ class SVGLength {
static ArrayList<SVGLength> arrayFrom(Dynamic dynamic) {
switch (dynamic.getType()) {
case Number: {
ArrayList<SVGLength> list = new ArrayList<>(1);
list.add(new SVGLength(dynamic.asDouble()));
return list;
}
case Array: {
ReadableArray arr = dynamic.asArray();
int size = arr.size();
ArrayList<SVGLength> list = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
Dynamic val = arr.getDynamic(i);
list.add(from(val));
case Number:
{
ArrayList<SVGLength> list = new ArrayList<>(1);
list.add(new SVGLength(dynamic.asDouble()));
return list;
}
case Array:
{
ReadableArray arr = dynamic.asArray();
int size = arr.size();
ArrayList<SVGLength> list = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
Dynamic val = arr.getDynamic(i);
list.add(from(val));
}
return list;
}
case String:
{
ArrayList<SVGLength> list = new ArrayList<>(1);
list.add(new SVGLength(dynamic.asString()));
return list;
}
return list;
}
case String: {
ArrayList<SVGLength> list = new ArrayList<>(1);
list.add(new SVGLength(dynamic.asString()));
return list;
}
default:
return null;
}

View File

@@ -6,71 +6,66 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import static com.horcrux.svg.RenderableViewManager.*;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.soloader.SoLoader;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nonnull;
import static com.horcrux.svg.RenderableViewManager.*;
public class SvgPackage implements ReactPackage {
@Nonnull
@Override
public List<ViewManager> createViewManagers(@Nonnull ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(
new GroupViewManager(),
new PathViewManager(),
new CircleViewManager(),
new EllipseViewManager(),
new LineViewManager(),
new RectViewManager(),
new TextViewManager(),
new TSpanViewManager(),
new TextPathViewManager(),
new ImageViewManager(),
new ClipPathViewManager(),
new DefsViewManager(),
new UseViewManager(),
new SymbolManager(),
new LinearGradientManager(),
new RadialGradientManager(),
new PatternManager(),
new MaskManager(),
new ForeignObjectManager(),
new MarkerManager(),
new SvgViewManager());
}
@Nonnull
@Override
public List<ViewManager> createViewManagers(@Nonnull ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(
new GroupViewManager(),
new PathViewManager(),
new CircleViewManager(),
new EllipseViewManager(),
new LineViewManager(),
new RectViewManager(),
new TextViewManager(),
new TSpanViewManager(),
new TextPathViewManager(),
new ImageViewManager(),
new ClipPathViewManager(),
new DefsViewManager(),
new UseViewManager(),
new SymbolManager(),
new LinearGradientManager(),
new RadialGradientManager(),
new PatternManager(),
new MaskManager(),
new ForeignObjectManager(),
new MarkerManager(),
new SvgViewManager());
}
@Nonnull
@Override
public List<NativeModule> createNativeModules(@Nonnull ReactApplicationContext reactContext) {
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// For Fabric, we load c++ native library here, this triggers svg's Fabric
// component registration which is necessary in order to avoid asking users
// to manually add init calls in their application code.
// This should no longer be needed if RN's autolink mechanism has Fabric support
SoLoader.loadLibrary("rnsvg_modules");
}
return Arrays.<NativeModule>asList(
new SvgViewModule(reactContext),
new RNSVGRenderableManager(reactContext)
);
@Nonnull
@Override
public List<NativeModule> createNativeModules(@Nonnull ReactApplicationContext reactContext) {
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// For Fabric, we load c++ native library here, this triggers svg's Fabric
// component registration which is necessary in order to avoid asking users
// to manually add init calls in their application code.
// This should no longer be needed if RN's autolink mechanism has Fabric support
SoLoader.loadLibrary("rnsvg_modules");
}
return Arrays.<NativeModule>asList(
new SvgViewModule(reactContext), new RNSVGRenderableManager(reactContext));
}
@SuppressWarnings("unused")
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@SuppressWarnings("unused")
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
}

View File

@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.annotation.SuppressLint;
@@ -20,9 +19,6 @@ import android.graphics.Typeface;
import android.util.Base64;
import android.view.View;
import android.view.ViewParent;
import androidx.annotation.NonNull;
import com.facebook.react.bridge.ColorPropConverter;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext;
@@ -30,207 +26,203 @@ import com.facebook.react.uimanager.DisplayMetricsHolder;
import com.facebook.react.uimanager.ReactCompoundView;
import com.facebook.react.uimanager.ReactCompoundViewGroup;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.views.view.ReactViewGroup;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Custom {@link View} implementation that draws an RNSVGSvg React view and its children.
*/
/** Custom {@link View} implementation that draws an RNSVGSvg React view and its children. */
@SuppressLint("ViewConstructor")
public class SvgView extends FabricEnabledViewGroup implements ReactCompoundView, ReactCompoundViewGroup {
public class SvgView extends FabricEnabledViewGroup
implements ReactCompoundView, ReactCompoundViewGroup {
@Override
public boolean interceptsTouchEvent(float touchX, float touchY) {
return true;
@Override
public boolean interceptsTouchEvent(float touchX, float touchY) {
return true;
}
@SuppressWarnings("unused")
public enum Events {
EVENT_DATA_URL("onDataURL");
private final String mName;
Events(final String name) {
mName = name;
}
@SuppressWarnings("unused")
public enum Events {
EVENT_DATA_URL("onDataURL");
private final String mName;
Events(final String name) {
mName = name;
}
@Nonnull
public String toString() {
return mName;
}
@Nonnull
public String toString() {
return mName;
}
}
private @Nullable Bitmap mBitmap;
private @Nullable Bitmap mBitmap;
public SvgView(ReactContext reactContext) {
super(reactContext);
mScale = DisplayMetricsHolder.getScreenDisplayMetrics().density;
// for some reason on Fabric the `onDraw` won't be called without it
setWillNotDraw(false);
}
public SvgView(ReactContext reactContext) {
super(reactContext);
mScale = DisplayMetricsHolder.getScreenDisplayMetrics().density;
// for some reason on Fabric the `onDraw` won't be called without it
setWillNotDraw(false);
}
@Override
public void setId(int id) {
super.setId(id);
SvgViewManager.setSvgView(id, this);
}
@Override
public void setId(int id) {
super.setId(id);
SvgViewManager.setSvgView(id, this);
}
@Override
public void invalidate() {
super.invalidate();
ViewParent parent = getParent();
if (parent instanceof VirtualView) {
if (!mRendered) {
return;
}
mRendered = false;
((VirtualView) parent).getSvgView().invalidate();
return;
}
if (mBitmap != null) {
mBitmap.recycle();
}
mBitmap = null;
}
@Override
protected void onDraw(Canvas canvas) {
if (getParent() instanceof VirtualView) {
return;
}
super.onDraw(canvas);
if (mBitmap == null) {
mBitmap = drawOutput();
}
if (mBitmap != null) {
canvas.drawBitmap(mBitmap, 0, 0, null);
if (toDataUrlTask != null) {
toDataUrlTask.run();
toDataUrlTask = null;
}
}
}
private Runnable toDataUrlTask = null;
void setToDataUrlTask(Runnable task) {
toDataUrlTask = task;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
this.invalidate();
}
@Override
public int reactTagForTouch(float touchX, float touchY) {
return hitTest(touchX, touchY);
}
private boolean mResponsible = false;
private final Map<String, VirtualView> mDefinedClipPaths = new HashMap<>();
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, Brush> mDefinedBrushes = new HashMap<>();
private Canvas mCanvas;
private final float mScale;
private float mMinX;
private float mMinY;
private float mVbWidth;
private float mVbHeight;
private SVGLength mbbWidth;
private SVGLength mbbHeight;
private String mAlign;
private int mMeetOrSlice;
final Matrix mInvViewBoxMatrix = new Matrix();
private boolean mInvertible = true;
private boolean mRendered = false;
int mTintColor = 0;
boolean notRendered() {
return !mRendered;
}
private void clearChildCache() {
if (!mRendered) {
return;
}
mRendered = false;
for (int i = 0; i < getChildCount(); i++) {
View node = getChildAt(i);
if (node instanceof VirtualView) {
VirtualView n = ((VirtualView)node);
n.clearChildCache();
}
}
}
@ReactProp(name = "tintColor")
public void setTintColor(@Nullable Dynamic tintColor) {
switch (tintColor.getType()) {
case Map:
mTintColor = ColorPropConverter.getColor(tintColor.asMap(), getContext());
break;
case Number:
mTintColor = tintColor.asInt();
break;
default:
mTintColor = 0;
@Override
public void invalidate() {
super.invalidate();
ViewParent parent = getParent();
if (parent instanceof VirtualView) {
if (!mRendered) {
return;
}
invalidate();
clearChildCache();
mRendered = false;
((VirtualView) parent).getSvgView().invalidate();
return;
}
if (mBitmap != null) {
mBitmap.recycle();
}
mBitmap = null;
}
public void setTintColor(@Nullable Integer tintColor) {
mTintColor = tintColor;
@Override
protected void onDraw(Canvas canvas) {
if (getParent() instanceof VirtualView) {
return;
}
super.onDraw(canvas);
if (mBitmap == null) {
mBitmap = drawOutput();
}
if (mBitmap != null) {
canvas.drawBitmap(mBitmap, 0, 0, null);
if (toDataUrlTask != null) {
toDataUrlTask.run();
toDataUrlTask = null;
}
}
}
private Runnable toDataUrlTask = null;
void setToDataUrlTask(Runnable task) {
toDataUrlTask = task;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
this.invalidate();
}
@Override
public int reactTagForTouch(float touchX, float touchY) {
return hitTest(touchX, touchY);
}
private boolean mResponsible = false;
private final Map<String, VirtualView> mDefinedClipPaths = new HashMap<>();
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, Brush> mDefinedBrushes = new HashMap<>();
private Canvas mCanvas;
private final float mScale;
private float mMinX;
private float mMinY;
private float mVbWidth;
private float mVbHeight;
private SVGLength mbbWidth;
private SVGLength mbbHeight;
private String mAlign;
private int mMeetOrSlice;
final Matrix mInvViewBoxMatrix = new Matrix();
private boolean mInvertible = true;
private boolean mRendered = false;
int mTintColor = 0;
boolean notRendered() {
return !mRendered;
}
private void clearChildCache() {
if (!mRendered) {
return;
}
mRendered = false;
for (int i = 0; i < getChildCount(); i++) {
View node = getChildAt(i);
if (node instanceof VirtualView) {
VirtualView n = ((VirtualView) node);
n.clearChildCache();
}
}
}
@ReactProp(name = "tintColor")
public void setTintColor(@Nullable Dynamic tintColor) {
switch (tintColor.getType()) {
case Map:
mTintColor = ColorPropConverter.getColor(tintColor.asMap(), getContext());
break;
case Number:
mTintColor = tintColor.asInt();
break;
default:
mTintColor = 0;
}
invalidate();
clearChildCache();
}
@ReactProp(name = "minX")
public void setMinX(float minX) {
mMinX = minX;
invalidate();
clearChildCache();
}
public void setTintColor(@Nullable Integer tintColor) {
mTintColor = tintColor;
invalidate();
clearChildCache();
}
@ReactProp(name = "minY")
public void setMinY(float minY) {
mMinY = minY;
invalidate();
clearChildCache();
}
@ReactProp(name = "minX")
public void setMinX(float minX) {
mMinX = minX;
invalidate();
clearChildCache();
}
@ReactProp(name = "vbWidth")
public void setVbWidth(float vbWidth) {
mVbWidth = vbWidth;
invalidate();
clearChildCache();
}
@ReactProp(name = "minY")
public void setMinY(float minY) {
mMinY = minY;
invalidate();
clearChildCache();
}
@ReactProp(name = "vbHeight")
public void setVbHeight(float vbHeight) {
mVbHeight = vbHeight;
invalidate();
clearChildCache();
}
@ReactProp(name = "vbWidth")
public void setVbWidth(float vbWidth) {
mVbWidth = vbWidth;
invalidate();
clearChildCache();
}
@ReactProp(name = "bbWidth")
public void setBbWidth(Dynamic bbWidth) {
mbbWidth = SVGLength.from(bbWidth);
invalidate();
clearChildCache();
}
@ReactProp(name = "vbHeight")
public void setVbHeight(float vbHeight) {
mVbHeight = vbHeight;
invalidate();
clearChildCache();
}
@ReactProp(name = "bbWidth")
public void setBbWidth(Dynamic bbWidth) {
mbbWidth = SVGLength.from(bbWidth);
invalidate();
clearChildCache();
}
public void setBbWidth(String bbWidth) {
mbbWidth = SVGLength.from(bbWidth);
@@ -238,12 +230,12 @@ public class SvgView extends FabricEnabledViewGroup implements ReactCompoundView
clearChildCache();
}
@ReactProp(name = "bbHeight")
public void setBbHeight(Dynamic bbHeight) {
mbbHeight = SVGLength.from(bbHeight);
invalidate();
clearChildCache();
}
@ReactProp(name = "bbHeight")
public void setBbHeight(Dynamic bbHeight) {
mbbHeight = SVGLength.from(bbHeight);
invalidate();
clearChildCache();
}
public void setBbHeight(String bbHeight) {
mbbHeight = SVGLength.from(bbHeight);
@@ -251,202 +243,198 @@ public class SvgView extends FabricEnabledViewGroup implements ReactCompoundView
clearChildCache();
}
@ReactProp(name = "align")
public void setAlign(String align) {
mAlign = align;
invalidate();
clearChildCache();
@ReactProp(name = "align")
public void setAlign(String align) {
mAlign = align;
invalidate();
clearChildCache();
}
@ReactProp(name = "meetOrSlice")
public void setMeetOrSlice(int meetOrSlice) {
mMeetOrSlice = meetOrSlice;
invalidate();
clearChildCache();
}
private Bitmap drawOutput() {
mRendered = true;
float width = getWidth();
float height = getHeight();
boolean invalid =
Float.isNaN(width)
|| Float.isNaN(height)
|| width < 1
|| height < 1
|| (Math.log10(width) + Math.log10(height) > 42);
if (invalid) {
return null;
}
Bitmap bitmap = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888);
drawChildren(new Canvas(bitmap));
return bitmap;
}
Rect getCanvasBounds() {
return mCanvas.getClipBounds();
}
synchronized void drawChildren(final Canvas canvas) {
mRendered = true;
mCanvas = canvas;
Matrix mViewBoxMatrix = new Matrix();
if (mAlign != null) {
RectF vbRect = getViewBox();
float width = canvas.getWidth();
float height = canvas.getHeight();
boolean nested = getParent() instanceof VirtualView;
if (nested) {
width = (float) PropHelper.fromRelative(mbbWidth, width, 0f, mScale, 12);
height = (float) PropHelper.fromRelative(mbbHeight, height, 0f, mScale, 12);
}
RectF eRect = new RectF(0, 0, width, height);
if (nested) {
canvas.clipRect(eRect);
}
mViewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice);
mInvertible = mViewBoxMatrix.invert(mInvViewBoxMatrix);
canvas.concat(mViewBoxMatrix);
}
@ReactProp(name = "meetOrSlice")
public void setMeetOrSlice(int meetOrSlice) {
mMeetOrSlice = meetOrSlice;
invalidate();
clearChildCache();
final Paint paint = new Paint();
paint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG | Paint.SUBPIXEL_TEXT_FLAG);
paint.setTypeface(Typeface.DEFAULT);
for (int i = 0; i < getChildCount(); i++) {
View node = getChildAt(i);
if (node instanceof VirtualView) {
((VirtualView) node).saveDefinition();
}
}
private Bitmap drawOutput() {
mRendered = true;
float width = getWidth();
float height = getHeight();
boolean invalid = Float.isNaN(width) || Float.isNaN(height) || width < 1 || height < 1 || (Math.log10(width) + Math.log10(height) > 42);
if (invalid) {
return null;
for (int i = 0; i < getChildCount(); i++) {
View lNode = getChildAt(i);
if (lNode instanceof VirtualView) {
VirtualView node = (VirtualView) lNode;
int count = node.saveAndSetupCanvas(canvas, mViewBoxMatrix);
node.render(canvas, paint, 1f);
node.restoreCanvas(canvas, count);
if (node.isResponsible() && !mResponsible) {
mResponsible = true;
}
Bitmap bitmap = Bitmap.createBitmap(
(int) width,
(int) height,
Bitmap.Config.ARGB_8888);
}
}
}
drawChildren(new Canvas(bitmap));
return bitmap;
private RectF getViewBox() {
return new RectF(
mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale);
}
String toDataURL() {
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
clearChildCache();
drawChildren(new Canvas(bitmap));
clearChildCache();
this.invalidate();
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
bitmap.recycle();
byte[] bitmapBytes = stream.toByteArray();
return Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
}
String toDataURL(int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
clearChildCache();
drawChildren(new Canvas(bitmap));
clearChildCache();
this.invalidate();
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
bitmap.recycle();
byte[] bitmapBytes = stream.toByteArray();
return Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
}
void enableTouchEvents() {
if (!mResponsible) {
mResponsible = true;
}
}
boolean isResponsible() {
return mResponsible;
}
private int hitTest(float touchX, float touchY) {
if (!mResponsible || !mInvertible) {
return getId();
}
Rect getCanvasBounds() {
return mCanvas.getClipBounds();
float[] transformed = {touchX, touchY};
mInvViewBoxMatrix.mapPoints(transformed);
int count = getChildCount();
int viewTag = -1;
for (int i = count - 1; i >= 0; i--) {
View child = getChildAt(i);
if (child instanceof VirtualView) {
viewTag = ((VirtualView) child).hitTest(transformed);
} else if (child instanceof SvgView) {
viewTag = ((SvgView) child).hitTest(touchX, touchY);
}
if (viewTag != -1) {
break;
}
}
synchronized void drawChildren(final Canvas canvas) {
mRendered = true;
mCanvas = canvas;
Matrix mViewBoxMatrix = new Matrix();
if (mAlign != null) {
RectF vbRect = getViewBox();
float width = canvas.getWidth();
float height = canvas.getHeight();
boolean nested = getParent() instanceof VirtualView;
if (nested) {
width = (float) PropHelper.fromRelative(mbbWidth, width, 0f, mScale, 12);
height = (float) PropHelper.fromRelative(mbbHeight, height, 0f, mScale, 12);
}
RectF eRect = new RectF(0,0, width, height);
if (nested) {
canvas.clipRect(eRect);
}
mViewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice);
mInvertible = mViewBoxMatrix.invert(mInvViewBoxMatrix);
canvas.concat(mViewBoxMatrix);
}
return viewTag == -1 ? getId() : viewTag;
}
final Paint paint = new Paint();
void defineClipPath(VirtualView clipPath, String clipPathRef) {
mDefinedClipPaths.put(clipPathRef, clipPath);
}
paint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG | Paint.SUBPIXEL_TEXT_FLAG);
VirtualView getDefinedClipPath(String clipPathRef) {
return mDefinedClipPaths.get(clipPathRef);
}
paint.setTypeface(Typeface.DEFAULT);
void defineTemplate(VirtualView template, String templateRef) {
mDefinedTemplates.put(templateRef, template);
}
VirtualView getDefinedTemplate(String templateRef) {
return mDefinedTemplates.get(templateRef);
}
for (int i = 0; i < getChildCount(); i++) {
View node = getChildAt(i);
if (node instanceof VirtualView) {
((VirtualView)node).saveDefinition();
}
}
void defineBrush(Brush brush, String brushRef) {
mDefinedBrushes.put(brushRef, brush);
}
for (int i = 0; i < getChildCount(); i++) {
View lNode = getChildAt(i);
if (lNode instanceof VirtualView) {
VirtualView node = (VirtualView)lNode;
int count = node.saveAndSetupCanvas(canvas, mViewBoxMatrix);
node.render(canvas, paint, 1f);
node.restoreCanvas(canvas, count);
Brush getDefinedBrush(String brushRef) {
return mDefinedBrushes.get(brushRef);
}
if (node.isResponsible() && !mResponsible) {
mResponsible = true;
}
}
}
}
void defineMask(VirtualView mask, String maskRef) {
mDefinedMasks.put(maskRef, mask);
}
private RectF getViewBox() {
return new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale);
}
VirtualView getDefinedMask(String maskRef) {
return mDefinedMasks.get(maskRef);
}
String toDataURL() {
Bitmap bitmap = Bitmap.createBitmap(
getWidth(),
getHeight(),
Bitmap.Config.ARGB_8888);
void defineMarker(VirtualView marker, String markerRef) {
mDefinedMarkers.put(markerRef, marker);
}
clearChildCache();
drawChildren(new Canvas(bitmap));
clearChildCache();
this.invalidate();
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
bitmap.recycle();
byte[] bitmapBytes = stream.toByteArray();
return Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
}
String toDataURL(int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(
width,
height,
Bitmap.Config.ARGB_8888);
clearChildCache();
drawChildren(new Canvas(bitmap));
clearChildCache();
this.invalidate();
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
bitmap.recycle();
byte[] bitmapBytes = stream.toByteArray();
return Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
}
void enableTouchEvents() {
if (!mResponsible) {
mResponsible = true;
}
}
boolean isResponsible() {
return mResponsible;
}
private int hitTest(float touchX, float touchY) {
if (!mResponsible || !mInvertible) {
return getId();
}
float[] transformed = { touchX, touchY };
mInvViewBoxMatrix.mapPoints(transformed);
int count = getChildCount();
int viewTag = -1;
for (int i = count - 1; i >= 0; i--) {
View child = getChildAt(i);
if (child instanceof VirtualView) {
viewTag = ((VirtualView) child).hitTest(transformed);
} else if (child instanceof SvgView) {
viewTag = ((SvgView) child).hitTest(touchX, touchY);
}
if (viewTag != -1) {
break;
}
}
return viewTag == -1 ? getId() : viewTag;
}
void defineClipPath(VirtualView clipPath, String clipPathRef) {
mDefinedClipPaths.put(clipPathRef, clipPath);
}
VirtualView getDefinedClipPath(String clipPathRef) {
return mDefinedClipPaths.get(clipPathRef);
}
void defineTemplate(VirtualView template, String templateRef) {
mDefinedTemplates.put(templateRef, template);
}
VirtualView getDefinedTemplate(String templateRef) {
return mDefinedTemplates.get(templateRef);
}
void defineBrush(Brush brush, String brushRef) {
mDefinedBrushes.put(brushRef, brush);
}
Brush getDefinedBrush(String brushRef) {
return mDefinedBrushes.get(brushRef);
}
void defineMask(VirtualView mask, String maskRef) {
mDefinedMasks.put(maskRef, mask);
}
VirtualView getDefinedMask(String maskRef) {
return mDefinedMasks.get(maskRef);
}
void defineMarker(VirtualView marker, String markerRef) {
mDefinedMarkers.put(markerRef, marker);
}
VirtualView getDefinedMarker(String markerRef) {
return mDefinedMarkers.get(markerRef);
}
VirtualView getDefinedMarker(String markerRef) {
return mDefinedMarkers.get(markerRef);
}
}

View File

@@ -6,145 +6,142 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.util.SparseArray;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewManagerDelegate;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.views.view.ReactViewGroup;
import com.facebook.react.views.view.ReactViewManager;
import com.facebook.react.viewmanagers.RNSVGSvgViewManagerDelegate;
import com.facebook.react.viewmanagers.RNSVGSvgViewManagerInterface;
import com.facebook.react.views.view.ReactViewGroup;
import com.facebook.react.views.view.ReactViewManager;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* ViewManager for RNSVGSvgView React views. Renders as a {@link SvgView} and handles
* invalidating the native view on view updates happening in the underlying tree.
* ViewManager for RNSVGSvgView React views. Renders as a {@link SvgView} and handles invalidating
* the native view on view updates happening in the underlying tree.
*/
class SvgViewManager extends ReactViewManager implements RNSVGSvgViewManagerInterface<SvgView> {
private static final String REACT_CLASS = "RNSVGSvgView";
private static final String REACT_CLASS = "RNSVGSvgView";
private static final SparseArray<SvgView> mTagToSvgView = new SparseArray<>();
private static final SparseArray<Runnable> mTagToRunnable = new SparseArray<>();
private static final SparseArray<SvgView> mTagToSvgView = new SparseArray<>();
private static final SparseArray<Runnable> mTagToRunnable = new SparseArray<>();
private final ViewManagerDelegate<SvgView> mDelegate;
private final ViewManagerDelegate<SvgView> mDelegate;
protected ViewManagerDelegate getDelegate(){
return mDelegate;
protected ViewManagerDelegate getDelegate() {
return mDelegate;
}
public SvgViewManager() {
mDelegate = new RNSVGSvgViewManagerDelegate(this);
}
static void setSvgView(int tag, SvgView svg) {
mTagToSvgView.put(tag, svg);
Runnable task = mTagToRunnable.get(tag);
if (task != null) {
task.run();
mTagToRunnable.delete(tag);
}
}
public SvgViewManager() {
mDelegate = new RNSVGSvgViewManagerDelegate(this);
}
static void runWhenViewIsAvailable(int tag, Runnable task) {
mTagToRunnable.put(tag, task);
}
static void setSvgView(int tag, SvgView svg) {
mTagToSvgView.put(tag, svg);
Runnable task = mTagToRunnable.get(tag);
if (task != null) {
task.run();
mTagToRunnable.delete(tag);
}
}
static @Nullable SvgView getSvgViewByTag(int tag) {
return mTagToSvgView.get(tag);
}
static void runWhenViewIsAvailable(int tag, Runnable task) {
mTagToRunnable.put(tag, task);
}
@Nonnull
@Override
public String getName() {
return REACT_CLASS;
}
static @Nullable SvgView getSvgViewByTag(int tag) {
return mTagToSvgView.get(tag);
}
@Nonnull
@Override
public ReactViewGroup createViewInstance(ThemedReactContext reactContext) {
return new SvgView(reactContext);
}
@Nonnull
@Override
public String getName() {
return REACT_CLASS;
}
@Override
public void updateExtraData(ReactViewGroup root, Object extraData) {
super.updateExtraData(root, extraData);
root.invalidate();
}
@Nonnull
@Override
public ReactViewGroup createViewInstance(ThemedReactContext reactContext) {
return new SvgView(reactContext);
}
@Override
public void onDropViewInstance(@Nonnull ReactViewGroup view) {
super.onDropViewInstance(view);
mTagToSvgView.remove(view.getId());
}
@Override
public void updateExtraData(ReactViewGroup root, Object extraData) {
super.updateExtraData(root, extraData);
root.invalidate();
}
@Override
public boolean needsCustomLayoutForChildren() {
return true;
}
@Override
public void onDropViewInstance(@Nonnull ReactViewGroup view) {
super.onDropViewInstance(view);
mTagToSvgView.remove(view.getId());
}
@ReactProp(name = "tintColor")
public void setTintColor(SvgView node, @Nullable Dynamic tintColor) {
node.setTintColor(tintColor);
}
@Override
public boolean needsCustomLayoutForChildren() {
return true;
}
@ReactProp(name = "color")
public void setColor(SvgView node, @Nullable Dynamic color) {
node.setTintColor(color);
}
@ReactProp(name = "tintColor")
public void setTintColor(SvgView node, @Nullable Dynamic tintColor) {
node.setTintColor(tintColor);
}
@ReactProp(name = "minX")
@Override
public void setMinX(SvgView node, float minX) {
node.setMinX(minX);
}
@ReactProp(name = "color")
public void setColor(SvgView node, @Nullable Dynamic color) {
node.setTintColor(color);
}
@ReactProp(name = "minY")
@Override
public void setMinY(SvgView node, float minY) {
node.setMinY(minY);
}
@ReactProp(name = "minX")
@Override
public void setMinX(SvgView node, float minX) {
node.setMinX(minX);
}
@ReactProp(name = "vbWidth")
@Override
public void setVbWidth(SvgView node, float vbWidth) {
node.setVbWidth(vbWidth);
}
@ReactProp(name = "minY")
@Override
public void setMinY(SvgView node, float minY) {
node.setMinY(minY);
}
@ReactProp(name = "vbHeight")
@Override
public void setVbHeight(SvgView node, float vbHeight) {
node.setVbHeight(vbHeight);
}
@ReactProp(name = "vbWidth")
@Override
public void setVbWidth(SvgView node, float vbWidth) {
node.setVbWidth(vbWidth);
}
@ReactProp(name = "bbWidth")
public void setBbWidth(SvgView node, Dynamic bbWidth) {
node.setBbWidth(bbWidth);
}
@ReactProp(name = "vbHeight")
@Override
public void setVbHeight(SvgView node, float vbHeight) {
node.setVbHeight(vbHeight);
}
@ReactProp(name = "bbHeight")
public void setBbHeight(SvgView node, Dynamic bbHeight) {
node.setBbHeight(bbHeight);
}
@ReactProp(name = "bbWidth")
public void setBbWidth(SvgView node, Dynamic bbWidth) {
node.setBbWidth(bbWidth);
}
@ReactProp(name = "align")
@Override
public void setAlign(SvgView node, String align) {
node.setAlign(align);
}
@ReactProp(name = "bbHeight")
public void setBbHeight(SvgView node, Dynamic bbHeight) {
node.setBbHeight(bbHeight);
}
@ReactProp(name = "align")
@Override
public void setAlign(SvgView node, String align) {
node.setAlign(align);
}
@ReactProp(name = "meetOrSlice")
@Override
public void setMeetOrSlice(SvgView node, int meetOrSlice) {
node.setMeetOrSlice(meetOrSlice);
}
@ReactProp(name = "meetOrSlice")
@Override
public void setMeetOrSlice(SvgView node, int meetOrSlice) {
node.setMeetOrSlice(meetOrSlice);
}
@Override
public void setTintColor(SvgView view, @Nullable Integer value) {
@@ -165,5 +162,4 @@ class SvgViewManager extends ReactViewManager implements RNSVGSvgViewManagerInte
public void setBbHeight(SvgView view, @Nullable String value) {
view.setBbHeight(value);
}
}

View File

@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import com.facebook.react.bridge.Callback;
@@ -15,70 +14,69 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.UiThreadUtil;
import javax.annotation.Nonnull;
class SvgViewModule extends ReactContextBaseJavaModule {
SvgViewModule(ReactApplicationContext reactContext) {
super(reactContext);
}
SvgViewModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Nonnull
@Override
public String getName() {
return "RNSVGSvgViewManager";
}
@Nonnull
@Override
public String getName() {
return "RNSVGSvgViewManager";
}
private static void toDataURL(final int tag, final ReadableMap options, final Callback successCallback, final int attempt) {
UiThreadUtil.runOnUiThread(
new Runnable() {
private static void toDataURL(
final int tag, final ReadableMap options, final Callback successCallback, final int attempt) {
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
SvgView svg = SvgViewManager.getSvgViewByTag(tag);
if (svg == null) {
SvgViewManager.runWhenViewIsAvailable(
tag,
new Runnable() {
@Override
public void run() {
SvgView svg = SvgViewManager.getSvgViewByTag(tag);
if (svg == null) {
SvgViewManager.runWhenViewIsAvailable(tag, new Runnable() {
@Override
public void run() {
SvgView svg = SvgViewManager.getSvgViewByTag(tag);
if (svg == null) { // Should never happen
return;
}
svg.setToDataUrlTask(new Runnable() {
@Override
public void run() {
toDataURL(tag, options, successCallback, attempt + 1);
}
});
}
});
} else if (svg.notRendered()) {
svg.setToDataUrlTask(new Runnable() {
@Override
public void run() {
toDataURL(tag, options, successCallback, attempt + 1);
}
});
} else {
if (options != null) {
successCallback.invoke(
svg.toDataURL(
options.getInt("width"),
options.getInt("height")
)
);
} else {
successCallback.invoke(svg.toDataURL());
SvgView svg = SvgViewManager.getSvgViewByTag(tag);
if (svg == null) { // Should never happen
return;
}
svg.setToDataUrlTask(
new Runnable() {
@Override
public void run() {
toDataURL(tag, options, successCallback, attempt + 1);
}
}
});
}
}
);
}
});
} else if (svg.notRendered()) {
svg.setToDataUrlTask(
new Runnable() {
@Override
public void run() {
toDataURL(tag, options, successCallback, attempt + 1);
}
});
} else {
if (options != null) {
successCallback.invoke(
svg.toDataURL(options.getInt("width"), options.getInt("height")));
} else {
successCallback.invoke(svg.toDataURL());
}
}
}
});
}
@SuppressWarnings("unused")
@ReactMethod
public void toDataURL(int tag, ReadableMap options, Callback successCallback) {
toDataURL(tag, options, successCallback, 0);
}
@SuppressWarnings("unused")
@ReactMethod
public void toDataURL(int tag, ReadableMap options, Callback successCallback) {
toDataURL(tag, options, successCallback, 0);
}
}

View File

@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.annotation.SuppressLint;
@@ -14,72 +13,76 @@ import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
@SuppressLint("ViewConstructor")
class SymbolView extends GroupView {
private float mMinX;
private float mMinY;
private float mVbWidth;
private float mVbHeight;
private String mAlign;
private int mMeetOrSlice;
private float mMinX;
private float mMinY;
private float mVbWidth;
private float mVbHeight;
private String mAlign;
private int mMeetOrSlice;
public SymbolView(ReactContext reactContext) {
super(reactContext);
}
public SymbolView(ReactContext reactContext) {
super(reactContext);
}
@ReactProp(name = "minX")
public void setMinX(float minX) {
mMinX = minX;
invalidate();
}
@ReactProp(name = "minX")
public void setMinX(float minX) {
mMinX = minX;
invalidate();
}
@ReactProp(name = "minY")
public void setMinY(float minY) {
mMinY = minY;
invalidate();
}
@ReactProp(name = "minY")
public void setMinY(float minY) {
mMinY = minY;
invalidate();
}
@ReactProp(name = "vbWidth")
public void setVbWidth(float vbWidth) {
mVbWidth = vbWidth;
invalidate();
}
@ReactProp(name = "vbWidth")
public void setVbWidth(float vbWidth) {
mVbWidth = vbWidth;
invalidate();
}
@ReactProp(name = "vbHeight")
public void setVbHeight(float vbHeight) {
mVbHeight = vbHeight;
invalidate();
}
@ReactProp(name = "vbHeight")
public void setVbHeight(float vbHeight) {
mVbHeight = vbHeight;
invalidate();
}
@ReactProp(name = "align")
public void setAlign(String align) {
mAlign = align;
invalidate();
}
@ReactProp(name = "align")
public void setAlign(String align) {
mAlign = align;
invalidate();
}
@ReactProp(name = "meetOrSlice")
public void setMeetOrSlice(int meetOrSlice) {
mMeetOrSlice = meetOrSlice;
invalidate();
}
@ReactProp(name = "meetOrSlice")
public void setMeetOrSlice(int meetOrSlice) {
mMeetOrSlice = meetOrSlice;
invalidate();
}
@Override
void draw(Canvas canvas, Paint paint, float opacity) {
saveDefinition();
}
@Override
void draw(Canvas canvas, Paint paint, float opacity) {
saveDefinition();
}
void drawSymbol(Canvas canvas, Paint paint, float opacity, float width, float height) {
if (mAlign != null) {
RectF vbRect = new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale);
RectF eRect = new RectF(0, 0, width, height);
Matrix viewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice);
canvas.concat(viewBoxMatrix);
super.draw(canvas, paint, opacity);
}
void drawSymbol(Canvas canvas, Paint paint, float opacity, float width, float height) {
if (mAlign != null) {
RectF vbRect =
new RectF(
mMinX * mScale,
mMinY * mScale,
(mMinX + mVbWidth) * mScale,
(mMinY + mVbHeight) * mScale);
RectF eRect = new RectF(0, 0, width, height);
Matrix viewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice);
canvas.concat(viewBoxMatrix);
super.draw(canvas, paint, opacity);
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -6,129 +6,126 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import static com.horcrux.svg.TextProperties.*;
import android.annotation.SuppressLint;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
import javax.annotation.Nullable;
import static com.horcrux.svg.TextProperties.*;
@SuppressLint("ViewConstructor")
class TextPathView extends TextView {
private String mHref;
private TextPathSide mSide;
private TextPathMidLine mMidLine;
private @Nullable SVGLength mStartOffset;
private TextPathMethod mMethod = TextPathMethod.align;
private TextPathSpacing mSpacing = TextPathSpacing.exact;
private String mHref;
private TextPathSide mSide;
private TextPathMidLine mMidLine;
private @Nullable SVGLength mStartOffset;
private TextPathMethod mMethod = TextPathMethod.align;
private TextPathSpacing mSpacing = TextPathSpacing.exact;
public TextPathView(ReactContext reactContext) {
super(reactContext);
}
public TextPathView(ReactContext reactContext) {
super(reactContext);
}
@ReactProp(name = "href")
public void setHref(String href) {
mHref = href;
invalidate();
}
@ReactProp(name = "href")
public void setHref(String href) {
mHref = href;
invalidate();
}
@ReactProp(name = "startOffset")
public void setStartOffset(Dynamic startOffset) {
mStartOffset = SVGLength.from(startOffset);
invalidate();
}
@ReactProp(name = "startOffset")
public void setStartOffset(Dynamic startOffset) {
mStartOffset = SVGLength.from(startOffset);
invalidate();
}
public void setStartOffset(String startOffset) {
mStartOffset = SVGLength.from(startOffset);
invalidate();
}
@ReactProp(name = "method")
public void setMethod(@Nullable String method) {
mMethod = TextPathMethod.valueOf(method);
invalidate();
@ReactProp(name = "method")
public void setMethod(@Nullable String method) {
mMethod = TextPathMethod.valueOf(method);
invalidate();
}
@ReactProp(name = "spacing")
public void setSpacing(@Nullable String spacing) {
mSpacing = TextPathSpacing.valueOf(spacing);
invalidate();
}
@ReactProp(name = "side")
public void setSide(@Nullable String side) {
mSide = TextPathSide.valueOf(side);
invalidate();
}
@ReactProp(name = "midLine")
public void setSharp(@Nullable String midLine) {
mMidLine = TextPathMidLine.valueOf(midLine);
invalidate();
}
@SuppressWarnings("unused")
TextPathMethod getMethod() {
return mMethod;
}
@SuppressWarnings("unused")
TextPathSpacing getSpacing() {
return mSpacing;
}
TextPathSide getSide() {
return mSide;
}
TextPathMidLine getMidLine() {
return mMidLine;
}
SVGLength getStartOffset() {
return mStartOffset;
}
@Override
void draw(Canvas canvas, Paint paint, float opacity) {
drawGroup(canvas, paint, opacity);
}
Path getTextPath(Canvas canvas, Paint paint) {
SvgView svg = getSvgView();
VirtualView template = svg.getDefinedTemplate(mHref);
if (!(template instanceof RenderableView)) {
// warning about this.
return null;
}
@ReactProp(name = "spacing")
public void setSpacing(@Nullable String spacing) {
mSpacing = TextPathSpacing.valueOf(spacing);
invalidate();
}
RenderableView view = (RenderableView) template;
return view.getPath(canvas, paint);
}
@ReactProp(name = "side")
public void setSide(@Nullable String side) {
mSide = TextPathSide.valueOf(side);
invalidate();
}
@Override
Path getPath(Canvas canvas, Paint paint) {
return getGroupPath(canvas, paint);
}
@ReactProp(name = "midLine")
public void setSharp(@Nullable String midLine) {
mMidLine = TextPathMidLine.valueOf(midLine);
invalidate();
}
@Override
void pushGlyphContext() {
// do nothing
}
@SuppressWarnings("unused")
TextPathMethod getMethod() {
return mMethod;
}
@SuppressWarnings("unused")
TextPathSpacing getSpacing() {
return mSpacing;
}
TextPathSide getSide() {
return mSide;
}
TextPathMidLine getMidLine() {
return mMidLine;
}
SVGLength getStartOffset() {
return mStartOffset;
}
@Override
void draw(Canvas canvas, Paint paint, float opacity) {
drawGroup(canvas, paint, opacity);
}
Path getTextPath(Canvas canvas, Paint paint) {
SvgView svg = getSvgView();
VirtualView template = svg.getDefinedTemplate(mHref);
if (!(template instanceof RenderableView)) {
// warning about this.
return null;
}
RenderableView view = (RenderableView)template;
return view.getPath(canvas, paint);
}
@Override
Path getPath(Canvas canvas, Paint paint) {
return getGroupPath(canvas, paint);
}
@Override
void pushGlyphContext() {
// do nothing
}
@Override
void popGlyphContext() {
// do nothing
}
@Override
void popGlyphContext() {
// do nothing
}
}

View File

@@ -2,212 +2,218 @@ package com.horcrux.svg;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nonnull;
class TextProperties {
/*
https://drafts.csswg.org/css-inline/#propdef-alignment-baseline
2.2.1. Alignment Point: alignment-baseline longhand
Name: alignment-baseline
Value: baseline | text-bottom | alphabetic | ideographic | middle | central | mathematical | text-top | bottom | center | top
Initial: baseline
Applies to: inline-level boxes, flex items, grid items, table cells
Inherited: no
Percentages: N/A
Media: visual
Computed value: as specified
Canonical order: per grammar
Animation type: discrete
*/
enum AlignmentBaseline {
baseline("baseline"),
textBottom("text-bottom"),
alphabetic("alphabetic"),
ideographic("ideographic"),
middle("middle"),
central("central"),
mathematical("mathematical"),
textTop("text-top"),
bottom("bottom"),
center("center"),
top("top"),
/*
https://drafts.csswg.org/css-inline/#propdef-alignment-baseline
2.2.1. Alignment Point: alignment-baseline longhand
SVG implementations may support the following aliases in order to support legacy content:
Name: alignment-baseline
Value: baseline | text-bottom | alphabetic | ideographic | middle | central | mathematical | text-top | bottom | center | top
Initial: baseline
Applies to: inline-level boxes, flex items, grid items, table cells
Inherited: no
Percentages: N/A
Media: visual
Computed value: as specified
Canonical order: per grammar
Animation type: discrete
*/
enum AlignmentBaseline {
baseline("baseline"),
textBottom("text-bottom"),
alphabetic("alphabetic"),
ideographic("ideographic"),
middle("middle"),
central("central"),
mathematical("mathematical"),
textTop("text-top"),
bottom("bottom"),
center("center"),
top("top"),
/*
SVG implementations may support the following aliases in order to support legacy content:
text-before-edge = text-top
text-after-edge = text-bottom
*/
textBeforeEdge("text-before-edge"),
textAfterEdge("text-after-edge"),
// SVG 1.1
beforeEdge("before-edge"),
afterEdge("after-edge"),
hanging("hanging"),
;
text-before-edge = text-top
text-after-edge = text-bottom
*/
textBeforeEdge("text-before-edge"),
textAfterEdge("text-after-edge"),
// SVG 1.1
beforeEdge("before-edge"),
afterEdge("after-edge"),
hanging("hanging"),
;
private final String alignment;
private final String alignment;
AlignmentBaseline(String alignment) {
this.alignment = alignment;
}
static AlignmentBaseline getEnum(String strVal) {
if (!alignmentToEnum.containsKey(strVal)) {
throw new IllegalArgumentException("Unknown String Value: " + strVal);
}
return alignmentToEnum.get(strVal);
}
private static final Map<String, AlignmentBaseline> alignmentToEnum = new HashMap<>();
static {
for (final AlignmentBaseline en : AlignmentBaseline.values()) {
alignmentToEnum.put(en.alignment, en);
}
}
@Nonnull
@Override
public String toString() {
return alignment;
}
AlignmentBaseline(String alignment) {
this.alignment = alignment;
}
// TODO implement rtl
static AlignmentBaseline getEnum(String strVal) {
if (!alignmentToEnum.containsKey(strVal)) {
throw new IllegalArgumentException("Unknown String Value: " + strVal);
}
return alignmentToEnum.get(strVal);
}
private static final Map<String, AlignmentBaseline> alignmentToEnum = new HashMap<>();
static {
for (final AlignmentBaseline en : AlignmentBaseline.values()) {
alignmentToEnum.put(en.alignment, en);
}
}
@Nonnull
@Override
public String toString() {
return alignment;
}
}
// TODO implement rtl
@SuppressWarnings("unused")
enum Direction {
ltr,
rtl
}
enum FontVariantLigatures {
normal,
@SuppressWarnings("unused")
enum Direction {
ltr,
rtl
none
}
enum FontStyle {
normal,
italic,
@SuppressWarnings("unused")
oblique
}
enum FontWeight {
// Absolute
Normal("normal"),
Bold("bold"),
w100("100"),
w200("200"),
w300("300"),
w400("400"),
w500("500"),
w600("600"),
w700("700"),
w800("800"),
w900("900"),
// Relative
Bolder("bolder"),
Lighter("lighter");
private final String weight;
FontWeight(String weight) {
this.weight = weight;
}
enum FontVariantLigatures {
normal,
@SuppressWarnings("unused")none
static boolean hasEnum(String strVal) {
return weightToEnum.containsKey(strVal);
}
enum FontStyle {
normal,
italic,
@SuppressWarnings("unused")oblique
static FontWeight get(String strVal) {
return weightToEnum.get(strVal);
}
enum FontWeight {
// Absolute
Normal ("normal"),
Bold ("bold"),
w100 ("100"),
w200 ("200"),
w300 ("300"),
w400 ("400"),
w500 ("500"),
w600 ("600"),
w700 ("700"),
w800 ("800"),
w900 ("900"),
// Relative
Bolder ("bolder"),
Lighter ("lighter");
private static final Map<String, FontWeight> weightToEnum = new HashMap<>();
private final String weight;
FontWeight(String weight) {
this.weight = weight;
}
static boolean hasEnum(String strVal) {
return weightToEnum.containsKey(strVal);
}
static FontWeight get(String strVal) {
return weightToEnum.get(strVal);
}
private static final Map<String, FontWeight> weightToEnum = new HashMap<>();
static {
for (final FontWeight en : FontWeight.values()) {
weightToEnum.put(en.weight, en);
}
}
@Nonnull
@Override
public String toString() {
return weight;
}
static {
for (final FontWeight en : FontWeight.values()) {
weightToEnum.put(en.weight, en);
}
}
enum TextAnchor
{
start,
middle,
end
@Nonnull
@Override
public String toString() {
return weight;
}
}
enum TextAnchor {
start,
middle,
end
}
enum TextDecoration {
None("none"),
Underline("underline"),
Overline("overline"),
LineThrough("line-through"),
Blink("blink");
private final String decoration;
TextDecoration(String decoration) {
this.decoration = decoration;
}
enum TextDecoration
{
None("none"),
Underline("underline"),
Overline("overline"),
LineThrough("line-through"),
Blink("blink");
private final String decoration;
TextDecoration(String decoration) {
this.decoration = decoration;
}
static TextDecoration getEnum(String strVal) {
if(!decorationToEnum.containsKey(strVal)) {
throw new IllegalArgumentException("Unknown String Value: " + strVal);
}
return decorationToEnum.get(strVal);
}
private static final Map<String, TextDecoration> decorationToEnum = new HashMap<>();
static {
for (final TextDecoration en : TextDecoration.values()) {
decorationToEnum.put(en.decoration, en);
}
}
@Nonnull
@Override
public String toString() {
return decoration;
}
static TextDecoration getEnum(String strVal) {
if (!decorationToEnum.containsKey(strVal)) {
throw new IllegalArgumentException("Unknown String Value: " + strVal);
}
return decorationToEnum.get(strVal);
}
enum TextLengthAdjust
{
spacing,
spacingAndGlyphs
private static final Map<String, TextDecoration> decorationToEnum = new HashMap<>();
static {
for (final TextDecoration en : TextDecoration.values()) {
decorationToEnum.put(en.decoration, en);
}
}
enum TextPathMethod {
align,
@SuppressWarnings("unused")stretch
@Nonnull
@Override
public String toString() {
return decoration;
}
}
/*
TODO suggest adding a compatibility mid-line rendering attribute to textPath,
for a chrome/firefox/opera/safari compatible sharp text path rendering,
which doesn't bend text smoothly along a right angle curve, (like Edge does)
but keeps the mid-line orthogonal to the mid-point tangent at all times instead.
*/
enum TextPathMidLine {
sharp,
@SuppressWarnings("unused")smooth
}
enum TextLengthAdjust {
spacing,
spacingAndGlyphs
}
enum TextPathSide {
@SuppressWarnings("unused")left,
right
}
enum TextPathMethod {
align,
@SuppressWarnings("unused")
stretch
}
enum TextPathSpacing {
@SuppressWarnings("unused")auto,
exact
}
/*
TODO suggest adding a compatibility mid-line rendering attribute to textPath,
for a chrome/firefox/opera/safari compatible sharp text path rendering,
which doesn't bend text smoothly along a right angle curve, (like Edge does)
but keeps the mid-line orthogonal to the mid-point tangent at all times instead.
*/
enum TextPathMidLine {
sharp,
@SuppressWarnings("unused")
smooth
}
enum TextPathSide {
@SuppressWarnings("unused")
left,
right
}
enum TextPathSpacing {
@SuppressWarnings("unused")
auto,
exact
}
}

View File

@@ -6,9 +6,11 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import static com.horcrux.svg.TextProperties.AlignmentBaseline;
import static com.horcrux.svg.TextProperties.TextLengthAdjust;
import android.annotation.SuppressLint;
import android.graphics.Canvas;
import android.graphics.Paint;
@@ -16,292 +18,288 @@ import android.graphics.Path;
import android.graphics.Region;
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 com.facebook.react.uimanager.annotations.ReactProp;
import java.util.ArrayList;
import javax.annotation.Nullable;
import static com.horcrux.svg.TextProperties.AlignmentBaseline;
import static com.horcrux.svg.TextProperties.TextLengthAdjust;
@SuppressLint("ViewConstructor")
class TextView extends GroupView {
SVGLength mInlineSize = null;
SVGLength mTextLength = null;
private String mBaselineShift = null;
TextLengthAdjust mLengthAdjust = TextLengthAdjust.spacing;
private AlignmentBaseline mAlignmentBaseline;
@Nullable private ArrayList<SVGLength> mPositionX;
@Nullable private ArrayList<SVGLength> mPositionY;
@Nullable private ArrayList<SVGLength> mRotate;
@Nullable private ArrayList<SVGLength> mDeltaX;
@Nullable private ArrayList<SVGLength> mDeltaY;
double cachedAdvance = Double.NaN;
SVGLength mInlineSize = null;
SVGLength mTextLength = null;
private String mBaselineShift = null;
TextLengthAdjust mLengthAdjust = TextLengthAdjust.spacing;
private AlignmentBaseline mAlignmentBaseline;
@Nullable private ArrayList<SVGLength> mPositionX;
@Nullable private ArrayList<SVGLength> mPositionY;
@Nullable private ArrayList<SVGLength> mRotate;
@Nullable private ArrayList<SVGLength> mDeltaX;
@Nullable private ArrayList<SVGLength> mDeltaY;
double cachedAdvance = Double.NaN;
public TextView(ReactContext reactContext) {
super(reactContext);
}
public TextView(ReactContext reactContext) {
super(reactContext);
}
@Override
public void invalidate() {
if (mPath == null) {
return;
}
super.invalidate();
getTextContainer().clearChildCache();
@Override
public void invalidate() {
if (mPath == null) {
return;
}
super.invalidate();
getTextContainer().clearChildCache();
}
void clearCache() {
cachedAdvance = Double.NaN;
super.clearCache();
}
void clearCache() {
cachedAdvance = Double.NaN;
super.clearCache();
}
@ReactProp(name = "inlineSize")
public void setInlineSize(Dynamic inlineSize) {
mInlineSize = SVGLength.from(inlineSize);
invalidate();
}
@ReactProp(name = "inlineSize")
public void setInlineSize(Dynamic inlineSize) {
mInlineSize = SVGLength.from(inlineSize);
invalidate();
}
public void setInlineSize(String inlineSize) {
mInlineSize = SVGLength.from(inlineSize);
invalidate();
}
@ReactProp(name = "textLength")
public void setTextLength(Dynamic length) {
mTextLength = SVGLength.from(length);
invalidate();
}
@ReactProp(name = "textLength")
public void setTextLength(Dynamic length) {
mTextLength = SVGLength.from(length);
invalidate();
}
public void setTextLength(String length) {
mTextLength = SVGLength.from(length);
invalidate();
}
@ReactProp(name = "lengthAdjust")
public void setLengthAdjust(@Nullable String adjustment) {
mLengthAdjust = TextLengthAdjust.valueOf(adjustment);
invalidate();
}
@ReactProp(name = "lengthAdjust")
public void setLengthAdjust(@Nullable String adjustment) {
mLengthAdjust = TextLengthAdjust.valueOf(adjustment);
invalidate();
}
@ReactProp(name = "alignmentBaseline")
public void setMethod(@Nullable String alignment) {
mAlignmentBaseline = AlignmentBaseline.getEnum(alignment);
invalidate();
}
@ReactProp(name = "alignmentBaseline")
public void setMethod(@Nullable String alignment) {
mAlignmentBaseline = AlignmentBaseline.getEnum(alignment);
invalidate();
}
@ReactProp(name = "baselineShift")
public void setBaselineShift(Dynamic baselineShift) {
mBaselineShift = SVGLength.toString(baselineShift);
invalidate();
}
@ReactProp(name = "baselineShift")
public void setBaselineShift(Dynamic baselineShift) {
mBaselineShift = SVGLength.toString(baselineShift);
invalidate();
}
public void setBaselineShift(String baselineShift) {
mBaselineShift = baselineShift;
invalidate();
}
@ReactProp(name = "verticalAlign")
public void setVerticalAlign(@Nullable String verticalAlign) {
if (verticalAlign != null) {
verticalAlign = verticalAlign.trim();
int i = verticalAlign.lastIndexOf(' ');
try {
mAlignmentBaseline = AlignmentBaseline.getEnum(verticalAlign.substring(i));
} catch (IllegalArgumentException e) {
mAlignmentBaseline = AlignmentBaseline.baseline;
}
try {
mBaselineShift = verticalAlign.substring(0, i);
} catch (IndexOutOfBoundsException e) {
mBaselineShift = null;
}
} else {
mAlignmentBaseline = AlignmentBaseline.baseline;
mBaselineShift = null;
}
invalidate();
@ReactProp(name = "verticalAlign")
public void setVerticalAlign(@Nullable String verticalAlign) {
if (verticalAlign != null) {
verticalAlign = verticalAlign.trim();
int i = verticalAlign.lastIndexOf(' ');
try {
mAlignmentBaseline = AlignmentBaseline.getEnum(verticalAlign.substring(i));
} catch (IllegalArgumentException e) {
mAlignmentBaseline = AlignmentBaseline.baseline;
}
try {
mBaselineShift = verticalAlign.substring(0, i);
} catch (IndexOutOfBoundsException e) {
mBaselineShift = null;
}
} else {
mAlignmentBaseline = AlignmentBaseline.baseline;
mBaselineShift = null;
}
invalidate();
}
@ReactProp(name = "rotate")
public void setRotate(Dynamic rotate) {
mRotate = SVGLength.arrayFrom(rotate);
invalidate();
}
@ReactProp(name = "rotate")
public void setRotate(Dynamic rotate) {
mRotate = SVGLength.arrayFrom(rotate);
invalidate();
}
public void setRotate(ReadableArray rotate) {
mRotate = SVGLength.arrayFrom(rotate);
invalidate();
}
@ReactProp(name = "dx")
public void setDeltaX(Dynamic deltaX) {
mDeltaX = SVGLength.arrayFrom(deltaX);
invalidate();
}
@ReactProp(name = "dx")
public void setDeltaX(Dynamic deltaX) {
mDeltaX = SVGLength.arrayFrom(deltaX);
invalidate();
}
public void setDeltaX(ReadableArray deltaX) {
mDeltaX = SVGLength.arrayFrom(deltaX);
invalidate();
}
@ReactProp(name = "dy")
public void setDeltaY(Dynamic deltaY) {
mDeltaY = SVGLength.arrayFrom(deltaY);
invalidate();
}
@ReactProp(name = "dy")
public void setDeltaY(Dynamic deltaY) {
mDeltaY = SVGLength.arrayFrom(deltaY);
invalidate();
}
public void setDeltaY(ReadableArray deltaY) {
mDeltaY = SVGLength.arrayFrom(deltaY);
invalidate();
}
@ReactProp(name = "x")
public void setPositionX(Dynamic positionX) {
mPositionX = SVGLength.arrayFrom(positionX);
invalidate();
}
@ReactProp(name = "x")
public void setPositionX(Dynamic positionX) {
mPositionX = SVGLength.arrayFrom(positionX);
invalidate();
}
public void setPositionX(ReadableArray positionX) {
mPositionX = SVGLength.arrayFrom(positionX);
invalidate();
}
@ReactProp(name = "y")
public void setPositionY(Dynamic positionY) {
mPositionY = SVGLength.arrayFrom(positionY);
invalidate();
}
public void setPositionY(Dynamic positionY) {
mPositionY = SVGLength.arrayFrom(positionY);
invalidate();
}
public void setPositionY(ReadableArray positionY) {
mPositionY = SVGLength.arrayFrom(positionY);
invalidate();
}
@Override
void draw(Canvas canvas, Paint paint, float opacity) {
setupGlyphContext(canvas);
clip(canvas, paint);
getGroupPath(canvas, paint);
pushGlyphContext();
drawGroup(canvas, paint, opacity);
popGlyphContext();
}
@Override
void draw(Canvas canvas, Paint paint, float opacity) {
setupGlyphContext(canvas);
clip(canvas, paint);
getGroupPath(canvas, paint);
pushGlyphContext();
drawGroup(canvas, paint, opacity);
popGlyphContext();
}
@Override
Path getPath(Canvas canvas, Paint paint) {
if (mPath != null) {
return mPath;
@Override
Path getPath(Canvas canvas, Paint paint) {
if (mPath != null) {
return mPath;
}
setupGlyphContext(canvas);
return getGroupPath(canvas, paint);
}
@Override
Path getPath(Canvas canvas, Paint paint, Region.Op op) {
return getPath(canvas, paint);
}
AlignmentBaseline getAlignmentBaseline() {
if (mAlignmentBaseline == null) {
ViewParent parent = this.getParent();
while (parent != null) {
if (parent instanceof TextView) {
TextView node = (TextView) parent;
final AlignmentBaseline baseline = node.mAlignmentBaseline;
if (baseline != null) {
mAlignmentBaseline = baseline;
return baseline;
}
}
setupGlyphContext(canvas);
return getGroupPath(canvas, paint);
parent = parent.getParent();
}
}
@Override
Path getPath(Canvas canvas, Paint paint, Region.Op op) {
return getPath(canvas, paint);
if (mAlignmentBaseline == null) {
mAlignmentBaseline = AlignmentBaseline.baseline;
}
return mAlignmentBaseline;
}
AlignmentBaseline getAlignmentBaseline() {
if (mAlignmentBaseline == null) {
ViewParent parent = this.getParent();
while (parent != null) {
if (parent instanceof TextView) {
TextView node = (TextView)parent;
final AlignmentBaseline baseline = node.mAlignmentBaseline;
if (baseline != null) {
mAlignmentBaseline = baseline;
return baseline;
}
}
parent = parent.getParent();
}
String getBaselineShift() {
if (mBaselineShift == null) {
ViewParent parent = this.getParent();
while (parent != null) {
if (parent instanceof TextView) {
TextView node = (TextView) parent;
final String baselineShift = node.mBaselineShift;
if (baselineShift != null) {
mBaselineShift = baselineShift;
return baselineShift;
}
}
if (mAlignmentBaseline == null) {
mAlignmentBaseline = AlignmentBaseline.baseline;
}
return mAlignmentBaseline;
parent = parent.getParent();
}
}
return mBaselineShift;
}
String getBaselineShift() {
if (mBaselineShift == null) {
ViewParent parent = this.getParent();
while (parent != null) {
if (parent instanceof TextView) {
TextView node = (TextView)parent;
final String baselineShift = node.mBaselineShift;
if (baselineShift != null) {
mBaselineShift = baselineShift;
return baselineShift;
}
}
parent = parent.getParent();
}
}
return mBaselineShift;
Path getGroupPath(Canvas canvas, Paint paint) {
if (mPath != null) {
return mPath;
}
pushGlyphContext();
mPath = super.getPath(canvas, paint);
popGlyphContext();
Path getGroupPath(Canvas canvas, Paint paint) {
if (mPath != null) {
return mPath;
}
pushGlyphContext();
mPath = super.getPath(canvas, paint);
popGlyphContext();
return mPath;
}
return mPath;
}
@Override
void pushGlyphContext() {
boolean isTextNode = !(this instanceof TextPathView) && !(this instanceof TSpanView);
getTextRootGlyphContext()
.pushContext(isTextNode, this, mFont, mPositionX, mPositionY, mDeltaX, mDeltaY, mRotate);
}
@Override
void pushGlyphContext() {
boolean isTextNode = !(this instanceof TextPathView) && !(this instanceof TSpanView);
getTextRootGlyphContext().pushContext(isTextNode, this, mFont, mPositionX, mPositionY, mDeltaX, mDeltaY, mRotate);
}
TextView getTextAnchorRoot() {
GlyphContext gc = getTextRootGlyphContext();
ArrayList<FontData> font = gc.mFontContext;
TextView node = this;
ViewParent parent = this.getParent();
for (int i = font.size() - 1; i >= 0; i--) {
if (!(parent instanceof TextView) || font.get(i).textAnchor == TextProperties.TextAnchor.start || node.mPositionX != null) {
return node;
}
node = (TextView) parent;
parent = node.getParent();
}
TextView getTextAnchorRoot() {
GlyphContext gc = getTextRootGlyphContext();
ArrayList<FontData> font = gc.mFontContext;
TextView node = this;
ViewParent parent = this.getParent();
for (int i = font.size() - 1; i >= 0; i--) {
if (!(parent instanceof TextView)
|| font.get(i).textAnchor == TextProperties.TextAnchor.start
|| node.mPositionX != null) {
return node;
}
node = (TextView) parent;
parent = node.getParent();
}
return node;
}
double getSubtreeTextChunksTotalAdvance(Paint paint) {
if (!Double.isNaN(cachedAdvance)) {
return cachedAdvance;
}
double advance = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child instanceof TextView) {
TextView text = (TextView) child;
advance += text.getSubtreeTextChunksTotalAdvance(paint);
}
}
cachedAdvance = advance;
return advance;
double getSubtreeTextChunksTotalAdvance(Paint paint) {
if (!Double.isNaN(cachedAdvance)) {
return cachedAdvance;
}
double advance = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child instanceof TextView) {
TextView text = (TextView) child;
advance += text.getSubtreeTextChunksTotalAdvance(paint);
}
}
cachedAdvance = advance;
return advance;
}
TextView getTextContainer() {
TextView node = this;
ViewParent parent = this.getParent();
while (parent instanceof TextView) {
node = (TextView) parent;
parent = node.getParent();
}
return node;
TextView getTextContainer() {
TextView node = this;
ViewParent parent = this.getParent();
while (parent instanceof TextView) {
node = (TextView) parent;
parent = node.getParent();
}
return node;
}
}

View File

@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.annotation.SuppressLint;
@@ -14,7 +13,6 @@ import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext;
@@ -23,138 +21,151 @@ import com.facebook.react.uimanager.annotations.ReactProp;
@SuppressLint("ViewConstructor")
class UseView extends RenderableView {
private String mHref;
private SVGLength mX;
private SVGLength mY;
private SVGLength mW;
private SVGLength mH;
private String mHref;
private SVGLength mX;
private SVGLength mY;
private SVGLength mW;
private SVGLength mH;
public UseView(ReactContext reactContext) {
super(reactContext);
}
public UseView(ReactContext reactContext) {
super(reactContext);
}
@ReactProp(name = "href")
public void setHref(String href) {
mHref = href;
invalidate();
}
@ReactProp(name = "href")
public void setHref(String href) {
mHref = href;
invalidate();
}
@ReactProp(name = "x")
public void setX(Dynamic x) {
mX = SVGLength.from(x);
invalidate();
}
@ReactProp(name = "x")
public void setX(Dynamic x) {
mX = SVGLength.from(x);
invalidate();
}
public void setX(String x) {
mX = SVGLength.from(x);
invalidate();
}
@ReactProp(name = "y")
public void setY(Dynamic y) {
mY = SVGLength.from(y);
invalidate();
}
@ReactProp(name = "y")
public void setY(Dynamic y) {
mY = SVGLength.from(y);
invalidate();
}
public void setY(String y) {
mY = SVGLength.from(y);
invalidate();
}
@ReactProp(name = "width")
public void setWidth(Dynamic width) {
mW = SVGLength.from(width);
invalidate();
}
@ReactProp(name = "width")
public void setWidth(Dynamic width) {
mW = SVGLength.from(width);
invalidate();
}
public void setWidth(String width) {
mW = SVGLength.from(width);
invalidate();
}
@ReactProp(name = "height")
public void setHeight(Dynamic height) {
mH = SVGLength.from(height);
invalidate();
}
@ReactProp(name = "height")
public void setHeight(Dynamic height) {
mH = SVGLength.from(height);
invalidate();
}
public void setHeight(String height) {
mH = SVGLength.from(height);
invalidate();
}
@Override
void draw(Canvas canvas, Paint paint, float opacity) {
VirtualView template = getSvgView().getDefinedTemplate(mHref);
@Override
void draw(Canvas canvas, Paint paint, float opacity) {
VirtualView template = getSvgView().getDefinedTemplate(mHref);
if (template == null) {
FLog.w(ReactConstants.TAG, "`Use` element expected a pre-defined svg template as `href` prop, " +
"template named: " + mHref + " is not defined.");
return;
}
template.clearCache();
canvas.translate((float) relativeOnWidth(mX), (float) relativeOnHeight(mY));
if (template instanceof RenderableView) {
((RenderableView)template).mergeProperties(this);
}
int count = template.saveAndSetupCanvas(canvas, mCTM);
clip(canvas, paint);
if (template instanceof SymbolView) {
SymbolView symbol = (SymbolView)template;
symbol.drawSymbol(canvas, paint, opacity, (float) relativeOnWidth(mW), (float) relativeOnHeight(mH));
} else {
template.draw(canvas, paint, opacity * mOpacity);
}
this.setClientRect(template.getClientRect());
template.restoreCanvas(canvas, count);
if (template instanceof RenderableView) {
((RenderableView)template).resetProperties();
}
if (template == null) {
FLog.w(
ReactConstants.TAG,
"`Use` element expected a pre-defined svg template as `href` prop, "
+ "template named: "
+ mHref
+ " is not defined.");
return;
}
@Override
int hitTest(float[] src) {
if (!mInvertible || !mTransformInvertible) {
return -1;
}
float[] dst = new float[2];
mInvMatrix.mapPoints(dst, src);
mInvTransform.mapPoints(dst);
VirtualView template = getSvgView().getDefinedTemplate(mHref);
if (template == null) {
FLog.w(ReactConstants.TAG, "`Use` element expected a pre-defined svg template as `href` prop, " +
"template named: " + mHref + " is not defined.");
return -1;
}
int hitChild = template.hitTest(dst);
if (hitChild != -1) {
return (template.isResponsible() || hitChild != template.getId()) ? hitChild : getId();
}
return -1;
template.clearCache();
canvas.translate((float) relativeOnWidth(mX), (float) relativeOnHeight(mY));
if (template instanceof RenderableView) {
((RenderableView) template).mergeProperties(this);
}
@Override
Path getPath(Canvas canvas, Paint paint) {
VirtualView template = getSvgView().getDefinedTemplate(mHref);
if (template == null) {
FLog.w(ReactConstants.TAG, "`Use` element expected a pre-defined svg template as `href` prop, " +
"template named: " + mHref + " is not defined.");
return null;
}
Path path = template.getPath(canvas, paint);
Path use = new Path();
Matrix m = new Matrix();
m.setTranslate((float) relativeOnWidth(mX), (float) relativeOnHeight(mY));
path.transform(m, use);
return use;
int count = template.saveAndSetupCanvas(canvas, mCTM);
clip(canvas, paint);
if (template instanceof SymbolView) {
SymbolView symbol = (SymbolView) template;
symbol.drawSymbol(
canvas, paint, opacity, (float) relativeOnWidth(mW), (float) relativeOnHeight(mH));
} else {
template.draw(canvas, paint, opacity * mOpacity);
}
this.setClientRect(template.getClientRect());
template.restoreCanvas(canvas, count);
if (template instanceof RenderableView) {
((RenderableView) template).resetProperties();
}
}
@Override
int hitTest(float[] src) {
if (!mInvertible || !mTransformInvertible) {
return -1;
}
float[] dst = new float[2];
mInvMatrix.mapPoints(dst, src);
mInvTransform.mapPoints(dst);
VirtualView template = getSvgView().getDefinedTemplate(mHref);
if (template == null) {
FLog.w(
ReactConstants.TAG,
"`Use` element expected a pre-defined svg template as `href` prop, "
+ "template named: "
+ mHref
+ " is not defined.");
return -1;
}
int hitChild = template.hitTest(dst);
if (hitChild != -1) {
return (template.isResponsible() || hitChild != template.getId()) ? hitChild : getId();
}
return -1;
}
@Override
Path getPath(Canvas canvas, Paint paint) {
VirtualView template = getSvgView().getDefinedTemplate(mHref);
if (template == null) {
FLog.w(
ReactConstants.TAG,
"`Use` element expected a pre-defined svg template as `href` prop, "
+ "template named: "
+ mHref
+ " is not defined.");
return null;
}
Path path = template.getPath(canvas, paint);
Path use = new Path();
Matrix m = new Matrix();
m.setTranslate((float) relativeOnWidth(mX), (float) relativeOnHeight(mY));
path.transform(m, use);
return use;
}
}

View File

@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree.
*/
package com.horcrux.svg;
import android.graphics.Matrix;
@@ -14,90 +13,91 @@ import android.graphics.RectF;
class ViewBox {
private static final int MOS_MEET = 0;
private static final int MOS_SLICE = 1;
private static final int MOS_NONE = 2;
private static final int MOS_MEET = 0;
private static final int MOS_SLICE = 1;
private static final int MOS_NONE = 2;
static Matrix getTransform(RectF vbRect, RectF eRect, String align, int meetOrSlice) {
// based on https://svgwg.org/svg2-draft/coords.html#ComputingAViewportsTransform
static Matrix getTransform(RectF vbRect, RectF eRect, String align, int meetOrSlice) {
// based on https://svgwg.org/svg2-draft/coords.html#ComputingAViewportsTransform
// Let vb-x, vb-y, vb-width, vb-height be the min-x, min-y, width and height values of the viewBox attribute respectively.
double vbX = vbRect.left;
double vbY = vbRect.top;
double vbWidth = vbRect.width();
double vbHeight = vbRect.height();
// Let vb-x, vb-y, vb-width, vb-height be the min-x, min-y, width and height values of the
// viewBox attribute respectively.
double vbX = vbRect.left;
double vbY = vbRect.top;
double vbWidth = vbRect.width();
double vbHeight = vbRect.height();
// Let e-x, e-y, e-width, e-height be the position and size of the element respectively.
double eX = eRect.left;
double eY = eRect.top;
double eWidth = eRect.width();
double eHeight = eRect.height();
// Let e-x, e-y, e-width, e-height be the position and size of the element respectively.
double eX = eRect.left;
double eY = eRect.top;
double eWidth = eRect.width();
double eHeight = eRect.height();
// Initialize scale-x to e-width/vb-width.
double scaleX = eWidth / vbWidth;
// Initialize scale-x to e-width/vb-width.
double scaleX = eWidth / vbWidth;
// Initialize scale-y to e-height/vb-height.
double scaleY = eHeight / vbHeight;
// Initialize scale-y to e-height/vb-height.
double scaleY = eHeight / vbHeight;
// Initialize translate-x to e-x - (vb-x * scale-x).
// Initialize translate-y to e-y - (vb-y * scale-y).
double translateX = eX - (vbX * scaleX);
double translateY = eY - (vbY * scaleY);
// Initialize translate-x to e-x - (vb-x * scale-x).
// Initialize translate-y to e-y - (vb-y * scale-y).
double translateX = eX - (vbX * scaleX);
double translateY = eY - (vbY * scaleY);
// If align is 'none'
if (meetOrSlice == MOS_NONE) {
// Let scale be set the smaller value of scale-x and scale-y.
// Assign scale-x and scale-y to scale.
double scale = scaleX = scaleY = Math.min(scaleX, scaleY);
// If align is 'none'
if (meetOrSlice == MOS_NONE) {
// Let scale be set the smaller value of scale-x and scale-y.
// Assign scale-x and scale-y to scale.
double scale = scaleX = scaleY = Math.min(scaleX, scaleY);
// If scale is greater than 1
if (scale > 1) {
// Minus translateX by (eWidth / scale - vbWidth) / 2
// Minus translateY by (eHeight / scale - vbHeight) / 2
translateX -= (eWidth / scale - vbWidth) / 2;
translateY -= (eHeight / scale - vbHeight) / 2;
} else {
translateX -= (eWidth - vbWidth * scale) / 2;
translateY -= (eHeight - vbHeight * scale) / 2;
}
} else {
// If align is not 'none' and meetOrSlice is 'meet', set the larger of scale-x and scale-y to
// the smaller.
// Otherwise, if align is not 'none' and meetOrSlice is 'slice', set the smaller of scale-x
// and scale-y to the larger.
// If scale is greater than 1
if (scale > 1) {
// Minus translateX by (eWidth / scale - vbWidth) / 2
// Minus translateY by (eHeight / scale - vbHeight) / 2
translateX -= (eWidth / scale - vbWidth) / 2;
translateY -= (eHeight / scale - vbHeight) / 2;
} else {
translateX -= (eWidth - vbWidth * scale) / 2;
translateY -= (eHeight - vbHeight * scale) / 2;
}
} else {
// If align is not 'none' and meetOrSlice is 'meet', set the larger of scale-x and scale-y to the smaller.
// Otherwise, if align is not 'none' and meetOrSlice is 'slice', set the smaller of scale-x and scale-y to the larger.
if (!align.equals("none") && meetOrSlice == MOS_MEET) {
scaleX = scaleY = Math.min(scaleX, scaleY);
} else if (!align.equals("none") && meetOrSlice == MOS_SLICE) {
scaleX = scaleY = Math.max(scaleX, scaleY);
}
if (!align.equals("none") && meetOrSlice == MOS_MEET) {
scaleX = scaleY = Math.min(scaleX, scaleY);
} else if (!align.equals("none") && meetOrSlice == MOS_SLICE) {
scaleX = scaleY = Math.max(scaleX, scaleY);
}
// If align contains 'xMid', add (e-width - vb-width * scale-x) / 2 to translate-x.
if (align.contains("xMid")) {
translateX += (eWidth - vbWidth * scaleX) / 2.0d;
}
// If align contains 'xMid', add (e-width - vb-width * scale-x) / 2 to translate-x.
if (align.contains("xMid")) {
translateX += (eWidth - vbWidth * scaleX) / 2.0d;
}
// If align contains 'xMax', add (e-width - vb-width * scale-x) to translate-x.
if (align.contains("xMax")) {
translateX += (eWidth - vbWidth * scaleX);
}
// If align contains 'xMax', add (e-width - vb-width * scale-x) to translate-x.
if (align.contains("xMax")) {
translateX += (eWidth - vbWidth * scaleX);
}
// If align contains 'yMid', add (e-height - vb-height * scale-y) / 2 to translate-y.
if (align.contains("YMid")) {
translateY += (eHeight - vbHeight * scaleY) / 2.0d;
}
// If align contains 'yMid', add (e-height - vb-height * scale-y) / 2 to translate-y.
if (align.contains("YMid")) {
translateY += (eHeight - vbHeight * scaleY) / 2.0d;
}
// If align contains 'yMax', add (e-height - vb-height * scale-y) to translate-y.
if (align.contains("YMax")) {
translateY += (eHeight - vbHeight * scaleY);
}
}
// The transform applied to content contained by the element is given by
// translate(translate-x, translate-y) scale(scale-x, scale-y).
Matrix transform = new Matrix();
transform.postTranslate((float) translateX, (float) translateY);
transform.preScale((float) scaleX, (float) scaleY);
return transform;
// If align contains 'yMax', add (e-height - vb-height * scale-y) to translate-y.
if (align.contains("YMax")) {
translateY += (eHeight - vbHeight * scaleY);
}
}
// The transform applied to content contained by the element is given by
// translate(translate-x, translate-y) scale(scale-x, scale-y).
Matrix transform = new Matrix();
transform.postTranslate((float) translateX, (float) translateY);
transform.preScale((float) scaleX, (float) scaleY);
return transform;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,7 @@
package com.horcrux.svg;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.views.view.ReactViewGroup;
import org.jetbrains.annotations.Nullable;
public abstract class FabricEnabledViewGroup extends ReactViewGroup {

View File

@@ -12,7 +12,7 @@
@interface RNSVGBrush : NSObject
@property (nonatomic, strong) NSString* brushRef;
@property (nonatomic, strong) NSString *brushRef;
/* @abstract */
- (instancetype)initWithArray:(NSArray *)data;

View File

@@ -13,31 +13,30 @@
- (instancetype)initWithArray:(NSArray *)data
{
return [super init];
return [super init];
}
- (void)paint:(CGContextRef)context opacity:(CGFloat)opacity painter:(RNSVGPainter *)painter bounds:(CGRect)bounds
{
}
- (BOOL)applyFillColor:(CGContextRef)context opacity:(CGFloat)opacity
{
return NO;
return NO;
}
- (BOOL)applyStrokeColor:(CGContextRef)context opacity:(CGFloat)opacity
{
return NO;
return NO;
}
- (CGColorRef)getColorWithOpacity:(CGFloat)opacity
{
return nil;
return nil;
}
- (void)paint:(CGContextRef)context opacity:(CGFloat)opacity painter:(RNSVGPainter *)painter
{
// abstract
// abstract
}
@end

View File

@@ -7,8 +7,8 @@
*/
typedef enum {
kRNSVGUndefinedType,
kRNSVGLinearGradient,
kRNSVGRadialGradient,
kRNSVGPattern
kRNSVGUndefinedType,
kRNSVGLinearGradient,
kRNSVGRadialGradient,
kRNSVGPattern
} RNSVGBrushType;

View File

@@ -7,31 +7,30 @@
*/
#import "RNSVGContextBrush.h"
#import "RNSVGRenderable.h"
#import "RNSVGNode.h"
#import "RNSVGRenderable.h"
#import "RCTConvert+RNSVG.h"
#import <React/RCTLog.h>
#import "RCTConvert+RNSVG.h"
@implementation RNSVGContextBrush
{
BOOL _isStroke;
@implementation RNSVGContextBrush {
BOOL _isStroke;
}
- (instancetype)initFill
{
if ((self = [super initWithArray:nil])) {
_isStroke = NO;
}
return self;
if ((self = [super initWithArray:nil])) {
_isStroke = NO;
}
return self;
}
- (instancetype)initStroke
{
if ((self = [super initWithArray:nil])) {
_isStroke = YES;
}
return self;
if ((self = [super initWithArray:nil])) {
_isStroke = YES;
}
return self;
}
- (void)dealloc
@@ -40,46 +39,44 @@
- (BOOL)applyFillColor:(CGContextRef)context opacity:(CGFloat)opacity
{
RNSVGRenderable *element = RNSVGRenderable.contextElement;
if (!element) {
return NO;
}
RNSVGRenderable *element = RNSVGRenderable.contextElement;
if (!element) {
return NO;
}
RNSVGBrush *brush = _isStroke ? element.stroke : element.fill;
RNSVGBrush *brush = _isStroke ? element.stroke : element.fill;
BOOL fillColor;
BOOL fillColor;
if (brush.class == RNSVGBrush.class) {
CGContextSetFillColorWithColor(context, [element.tintColor CGColor]);
fillColor = YES;
} else {
fillColor = [brush applyFillColor:context opacity:opacity];
}
if (brush.class == RNSVGBrush.class) {
CGContextSetFillColorWithColor(context, [element.tintColor CGColor]);
fillColor = YES;
} else {
fillColor = [brush applyFillColor:context opacity:opacity];
}
return fillColor;
return fillColor;
}
- (BOOL)applyStrokeColor:(CGContextRef)context opacity:(CGFloat)opacity
{
RNSVGRenderable *element = RNSVGRenderable.contextElement;
if (!element) {
return NO;
}
RNSVGRenderable *element = RNSVGRenderable.contextElement;
if (!element) {
return NO;
}
RNSVGBrush *brush = _isStroke ? element.stroke : element.fill;
RNSVGBrush *brush = _isStroke ? element.stroke : element.fill;
BOOL strokeColor;
BOOL strokeColor;
if (brush.class == RNSVGBrush.class) {
CGContextSetStrokeColorWithColor(context, [element.tintColor CGColor]);
strokeColor = YES;
} else {
strokeColor = [brush applyStrokeColor:context opacity:opacity];
}
if (brush.class == RNSVGBrush.class) {
CGContextSetStrokeColorWithColor(context, [element.tintColor CGColor]);
strokeColor = YES;
} else {
strokeColor = [brush applyStrokeColor:context opacity:opacity];
}
return YES;
return YES;
}
@end

View File

@@ -8,14 +8,14 @@
#import "RCTConvert+RNSVG.h"
#import "RNSVGBrushType.h"
#import "RNSVGUnits.h"
#import "RNSVGLength.h"
#import "RNSVGUnits.h"
@class RNSVGPattern;
@interface RNSVGPainter : NSObject
@property (nonatomic, assign) RNSVGPattern* pattern;
@property (nonatomic, assign) RNSVGPattern *pattern;
@property (nonatomic, assign) CGRect paintBounds;
@property (nonatomic, assign) bool useObjectBoundingBoxForContentUnits;
@property (nonatomic, assign) CGRect bounds;

View File

@@ -10,246 +10,242 @@
#import "RNSVGPattern.h"
#import "RNSVGViewBox.h"
@implementation RNSVGPainter
{
NSArray<RNSVGLength *> *_points;
NSArray<NSNumber *> *_colors;
RNSVGBrushType _type;
BOOL _useObjectBoundingBox;
BOOL _useContentObjectBoundingBox;
CGAffineTransform _transform;
CGRect _userSpaceBoundingBox;
@implementation RNSVGPainter {
NSArray<RNSVGLength *> *_points;
NSArray<NSNumber *> *_colors;
RNSVGBrushType _type;
BOOL _useObjectBoundingBox;
BOOL _useContentObjectBoundingBox;
CGAffineTransform _transform;
CGRect _userSpaceBoundingBox;
}
- (instancetype)initWithPointsArray:(NSArray<RNSVGLength *> *)pointsArray
{
if ((self = [super init])) {
_points = pointsArray;
}
return self;
if ((self = [super init])) {
_points = pointsArray;
}
return self;
}
RCT_NOT_IMPLEMENTED(- (instancetype)init)
RCT_NOT_IMPLEMENTED(-(instancetype)init)
- (void)setUnits:(RNSVGUnits)unit
{
_useObjectBoundingBox = unit == kRNSVGUnitsObjectBoundingBox;
_useObjectBoundingBox = unit == kRNSVGUnitsObjectBoundingBox;
}
- (void)setContentUnits:(RNSVGUnits)unit
{
_useContentObjectBoundingBox = unit == kRNSVGUnitsObjectBoundingBox;
_useContentObjectBoundingBox = unit == kRNSVGUnitsObjectBoundingBox;
}
- (void)setUserSpaceBoundingBox:(CGRect)userSpaceBoundingBox
{
_userSpaceBoundingBox = userSpaceBoundingBox;
_userSpaceBoundingBox = userSpaceBoundingBox;
}
- (void)setTransform:(CGAffineTransform)transform
{
_transform = transform;
_transform = transform;
}
- (void)setPattern:(RNSVGPattern *)pattern
{
if (_type != kRNSVGUndefinedType) {
// todo: throw error
return;
}
if (_type != kRNSVGUndefinedType) {
// todo: throw error
return;
}
_type = kRNSVGPattern;
_pattern = pattern;
_type = kRNSVGPattern;
_pattern = pattern;
}
- (void)setLinearGradientColors:(NSArray<NSNumber *> *)colors
{
if (_type != kRNSVGUndefinedType) {
// todo: throw error
return;
}
if (_type != kRNSVGUndefinedType) {
// todo: throw error
return;
}
_type = kRNSVGLinearGradient;
_colors = colors;
_type = kRNSVGLinearGradient;
_colors = colors;
}
- (void)setRadialGradientColors:(NSArray<NSNumber *> *)colors
{
if (_type != kRNSVGUndefinedType) {
// todo: throw error
return;
}
if (_type != kRNSVGUndefinedType) {
// todo: throw error
return;
}
_type = kRNSVGRadialGradient;
_colors = colors;
_type = kRNSVGRadialGradient;
_colors = colors;
}
- (void)paint:(CGContextRef)context bounds:(CGRect)bounds
{
if (_type == kRNSVGLinearGradient) {
[self paintLinearGradient:context bounds:bounds];
} else if (_type == kRNSVGRadialGradient) {
[self paintRadialGradient:context bounds:bounds];
} else if (_type == kRNSVGPattern) {
[self paintPattern:context bounds:bounds];
}
if (_type == kRNSVGLinearGradient) {
[self paintLinearGradient:context bounds:bounds];
} else if (_type == kRNSVGRadialGradient) {
[self paintRadialGradient:context bounds:bounds];
} else if (_type == kRNSVGPattern) {
[self paintPattern:context bounds:bounds];
}
}
- (CGRect)getPaintRect:(CGContextRef)context bounds:(CGRect)bounds
{
CGRect rect = _useObjectBoundingBox ? bounds : _userSpaceBoundingBox;
CGFloat height = CGRectGetHeight(rect);
CGFloat width = CGRectGetWidth(rect);
CGFloat x = 0.0;
CGFloat y = 0.0;
CGRect rect = _useObjectBoundingBox ? bounds : _userSpaceBoundingBox;
CGFloat height = CGRectGetHeight(rect);
CGFloat width = CGRectGetWidth(rect);
CGFloat x = 0.0;
CGFloat y = 0.0;
if (_useObjectBoundingBox) {
x = CGRectGetMinX(rect);
y = CGRectGetMinY(rect);
}
if (_useObjectBoundingBox) {
x = CGRectGetMinX(rect);
y = CGRectGetMinY(rect);
}
return CGRectMake(x, y, width, height);
return CGRectMake(x, y, width, height);
}
void PatternFunction(void* info, CGContextRef context)
void PatternFunction(void *info, CGContextRef context)
{
RNSVGPainter *_painter = (__bridge RNSVGPainter *)info;
RNSVGPattern *_pattern = [_painter pattern];
CGRect rect = _painter.paintBounds;
CGFloat minX = _pattern.minX;
CGFloat minY = _pattern.minY;
CGFloat vbWidth = _pattern.vbWidth;
CGFloat vbHeight = _pattern.vbHeight;
if (vbWidth > 0 && vbHeight > 0) {
CGRect vbRect = CGRectMake(minX, minY, vbWidth, vbHeight);
CGAffineTransform _viewBoxTransform = [RNSVGViewBox
getTransform:vbRect
eRect:rect
align:_pattern.align
meetOrSlice:_pattern.meetOrSlice];
CGContextConcatCTM(context, _viewBoxTransform);
}
RNSVGPainter *_painter = (__bridge RNSVGPainter *)info;
RNSVGPattern *_pattern = [_painter pattern];
CGRect rect = _painter.paintBounds;
CGFloat minX = _pattern.minX;
CGFloat minY = _pattern.minY;
CGFloat vbWidth = _pattern.vbWidth;
CGFloat vbHeight = _pattern.vbHeight;
if (vbWidth > 0 && vbHeight > 0) {
CGRect vbRect = CGRectMake(minX, minY, vbWidth, vbHeight);
CGAffineTransform _viewBoxTransform = [RNSVGViewBox getTransform:vbRect
eRect:rect
align:_pattern.align
meetOrSlice:_pattern.meetOrSlice];
CGContextConcatCTM(context, _viewBoxTransform);
}
if (_painter.useObjectBoundingBoxForContentUnits) {
CGRect bounds = _painter.bounds;
CGContextConcatCTM(context, CGAffineTransformMakeScale(bounds.size.width, bounds.size.height));
}
if (_painter.useObjectBoundingBoxForContentUnits) {
CGRect bounds = _painter.bounds;
CGContextConcatCTM(context, CGAffineTransformMakeScale(bounds.size.width, bounds.size.height));
}
[_pattern renderTo:context rect:rect];
[_pattern renderTo:context rect:rect];
}
- (CGFloat)getVal:(RNSVGLength*)length relative:(CGFloat)relative
- (CGFloat)getVal:(RNSVGLength *)length relative:(CGFloat)relative
{
RNSVGLengthUnitType unit = [length unit];
CGFloat val = [RNSVGPropHelper fromRelative:length
relative:relative];
return _useObjectBoundingBox &&
unit == SVG_LENGTHTYPE_NUMBER ? val * relative : val;
RNSVGLengthUnitType unit = [length unit];
CGFloat val = [RNSVGPropHelper fromRelative:length relative:relative];
return _useObjectBoundingBox && unit == SVG_LENGTHTYPE_NUMBER ? val * relative : val;
}
- (void)paintPattern:(CGContextRef)context bounds:(CGRect)bounds
{
CGRect rect = [self getPaintRect:context bounds:bounds];
CGFloat height = CGRectGetHeight(rect);
CGFloat width = CGRectGetWidth(rect);
CGRect rect = [self getPaintRect:context bounds:bounds];
CGFloat height = CGRectGetHeight(rect);
CGFloat width = CGRectGetWidth(rect);
CGFloat x = [self getVal:[_points objectAtIndex:0] relative:width];
CGFloat y = [self getVal:[_points objectAtIndex:1] relative:height];
CGFloat w = [self getVal:[_points objectAtIndex:2] relative:width];
CGFloat h = [self getVal:[_points objectAtIndex:3] relative:height];
CGFloat x = [self getVal:[_points objectAtIndex:0] relative:width];
CGFloat y = [self getVal:[_points objectAtIndex:1] relative:height];
CGFloat w = [self getVal:[_points objectAtIndex:2] relative:width];
CGFloat h = [self getVal:[_points objectAtIndex:3] relative:height];
CGAffineTransform viewbox = [self.pattern.svgView getViewBoxTransform];
CGAffineTransform viewbox = [self.pattern.svgView getViewBoxTransform];
#if TARGET_OS_OSX
// This is needed because macOS and iOS have different conventions for where the origin is.
// For macOS, it's in the bottom-left corner. For iOS, it's in the top-left corner.
viewbox = CGAffineTransformScale(viewbox, 1, -1);
// This is needed because macOS and iOS have different conventions for where the origin is.
// For macOS, it's in the bottom-left corner. For iOS, it's in the top-left corner.
viewbox = CGAffineTransformScale(viewbox, 1, -1);
#endif // TARGET_OS_OSX
CGRect newBounds = CGRectMake(x, y, w, h);
CGSize size = newBounds.size;
self.useObjectBoundingBoxForContentUnits = _useContentObjectBoundingBox;
self.paintBounds = newBounds;
self.bounds = rect;
CGRect newBounds = CGRectMake(x, y, w, h);
CGSize size = newBounds.size;
self.useObjectBoundingBoxForContentUnits = _useContentObjectBoundingBox;
self.paintBounds = newBounds;
self.bounds = rect;
const CGPatternCallbacks callbacks = { 0, &PatternFunction, NULL };
CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL);
CGContextSetFillColorSpace(context, patternSpace);
CGColorSpaceRelease(patternSpace);
const CGPatternCallbacks callbacks = {0, &PatternFunction, NULL};
CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL);
CGContextSetFillColorSpace(context, patternSpace);
CGColorSpaceRelease(patternSpace);
CGPatternRef pattern = CGPatternCreate((__bridge void * _Nullable)(self),
newBounds,
viewbox,
size.width,
size.height,
kCGPatternTilingConstantSpacing,
true,
&callbacks);
CGFloat alpha = 1.0;
CGContextSetFillPattern(context, pattern, &alpha);
CGPatternRelease(pattern);
CGPatternRef pattern = CGPatternCreate(
(__bridge void *_Nullable)(self),
newBounds,
viewbox,
size.width,
size.height,
kCGPatternTilingConstantSpacing,
true,
&callbacks);
CGFloat alpha = 1.0;
CGContextSetFillPattern(context, pattern, &alpha);
CGPatternRelease(pattern);
CGContextFillRect(context, bounds);
CGContextFillRect(context, bounds);
}
- (void)paintLinearGradient:(CGContextRef)context bounds:(CGRect)bounds
{
if ([_colors count] == 0) {
RCTLogWarn(@"No stops in gradient");
return;
}
CGGradientRef gradient = CGGradientRetain([RCTConvert RNSVGCGGradient:_colors]);
CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
if ([_colors count] == 0) {
RCTLogWarn(@"No stops in gradient");
return;
}
CGGradientRef gradient = CGGradientRetain([RCTConvert RNSVGCGGradient:_colors]);
CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
CGRect rect = [self getPaintRect:context bounds:bounds];
CGFloat height = CGRectGetHeight(rect);
CGFloat width = CGRectGetWidth(rect);
CGFloat offsetX = CGRectGetMinX(rect);
CGFloat offsetY = CGRectGetMinY(rect);
CGRect rect = [self getPaintRect:context bounds:bounds];
CGFloat height = CGRectGetHeight(rect);
CGFloat width = CGRectGetWidth(rect);
CGFloat offsetX = CGRectGetMinX(rect);
CGFloat offsetY = CGRectGetMinY(rect);
CGFloat x1 = [self getVal:[_points objectAtIndex:0] relative:width] + offsetX;
CGFloat y1 = [self getVal:[_points objectAtIndex:1] relative:height] + offsetY;
CGFloat x2 = [self getVal:[_points objectAtIndex:2] relative:width] + offsetX;
CGFloat y2 = [self getVal:[_points objectAtIndex:3] relative:height] + offsetY;
CGFloat x1 = [self getVal:[_points objectAtIndex:0] relative:width] + offsetX;
CGFloat y1 = [self getVal:[_points objectAtIndex:1] relative:height] + offsetY;
CGFloat x2 = [self getVal:[_points objectAtIndex:2] relative:width] + offsetX;
CGFloat y2 = [self getVal:[_points objectAtIndex:3] relative:height] + offsetY;
CGContextConcatCTM(context, _transform);
CGContextDrawLinearGradient(context, gradient, CGPointMake(x1, y1), CGPointMake(x2, y2), extendOptions);
CGGradientRelease(gradient);
CGContextConcatCTM(context, _transform);
CGContextDrawLinearGradient(context, gradient, CGPointMake(x1, y1), CGPointMake(x2, y2), extendOptions);
CGGradientRelease(gradient);
}
- (void)paintRadialGradient:(CGContextRef)context bounds:(CGRect)bounds
{
if ([_colors count] == 0) {
RCTLogWarn(@"No stops in gradient");
return;
}
CGGradientRef gradient = CGGradientRetain([RCTConvert RNSVGCGGradient:_colors]);
CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
if ([_colors count] == 0) {
RCTLogWarn(@"No stops in gradient");
return;
}
CGGradientRef gradient = CGGradientRetain([RCTConvert RNSVGCGGradient:_colors]);
CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
CGRect rect = [self getPaintRect:context bounds:bounds];
CGRect rect = [self getPaintRect:context bounds:bounds];
CGFloat width = CGRectGetWidth(rect);
CGFloat height = CGRectGetHeight(rect);
CGFloat width = CGRectGetWidth(rect);
CGFloat height = CGRectGetHeight(rect);
CGFloat offsetX = CGRectGetMinX(rect);
CGFloat offsetY = CGRectGetMinY(rect);
CGFloat offsetX = CGRectGetMinX(rect);
CGFloat offsetY = CGRectGetMinY(rect);
CGFloat rx = [self getVal:[_points objectAtIndex:2] relative:width];
CGFloat ry = [self getVal:[_points objectAtIndex:3] relative:height];
CGFloat rx = [self getVal:[_points objectAtIndex:2] relative:width];
CGFloat ry = [self getVal:[_points objectAtIndex:3] relative:height];
double ratio = ry / rx;
double ratio = ry / rx;
CGFloat fx = [self getVal:[_points objectAtIndex:0] relative:width] + offsetX;
CGFloat fy = ([self getVal:[_points objectAtIndex:1] relative:height] + offsetY) / ratio;
CGFloat fx = [self getVal:[_points objectAtIndex:0] relative:width] + offsetX;
CGFloat fy = ([self getVal:[_points objectAtIndex:1] relative:height] + offsetY) / ratio;
CGFloat cx = [self getVal:[_points objectAtIndex:4] relative:width] + offsetX;
CGFloat cy = ([self getVal:[_points objectAtIndex:5] relative:height] + offsetY) / ratio;
CGFloat cx = [self getVal:[_points objectAtIndex:4] relative:width] + offsetX;
CGFloat cy = ([self getVal:[_points objectAtIndex:5] relative:height] + offsetY) / ratio;
CGAffineTransform transform = CGAffineTransformMakeScale(1, ratio);
CGContextConcatCTM(context, transform);
CGAffineTransform transform = CGAffineTransformMakeScale(1, ratio);
CGContextConcatCTM(context, transform);
CGContextConcatCTM(context, _transform);
CGContextDrawRadialGradient(context, gradient, CGPointMake(fx, fy), 0, CGPointMake(cx, cy), rx, extendOptions);
CGGradientRelease(gradient);
CGContextConcatCTM(context, _transform);
CGContextDrawRadialGradient(context, gradient, CGPointMake(fx, fy), 0, CGPointMake(cx, cy), rx, extendOptions);
CGGradientRelease(gradient);
}
@end

View File

@@ -7,39 +7,38 @@
*/
#import "RNSVGPainterBrush.h"
#import "RNSVGPainter.h"
#import "RCTConvert+RNSVG.h"
#import <React/RCTLog.h>
#import "RCTConvert+RNSVG.h"
#import "RNSVGPainter.h"
@implementation RNSVGPainterBrush
- (instancetype)initWithArray:(NSArray *)array
{
if ((self = [super initWithArray:array])) {
if (array.count != 2) {
RCTLogError(@"-[%@ %@] expects 2 elements, received %@",
self.class, NSStringFromSelector(_cmd), array);
return nil;
}
self.brushRef = [array objectAtIndex:1];
if ((self = [super initWithArray:array])) {
if (array.count != 2) {
RCTLogError(@"-[%@ %@] expects 2 elements, received %@", self.class, NSStringFromSelector(_cmd), array);
return nil;
}
return self;
self.brushRef = [array objectAtIndex:1];
}
return self;
}
- (void)paint:(CGContextRef)context opacity:(CGFloat)opacity painter:(RNSVGPainter *)painter bounds:(CGRect)bounds
{
BOOL transparency = opacity < 1;
if (transparency) {
CGContextSetAlpha(context, opacity);
CGContextBeginTransparencyLayer(context, NULL);
}
BOOL transparency = opacity < 1;
if (transparency) {
CGContextSetAlpha(context, opacity);
CGContextBeginTransparencyLayer(context, NULL);
}
[painter paint:context bounds:bounds];
[painter paint:context bounds:bounds];
if (transparency) {
CGContextEndTransparencyLayer(context);
}
if (transparency) {
CGContextEndTransparencyLayer(context);
}
}
@end

View File

@@ -9,65 +9,63 @@
#import "RNSVGSolidColorBrush.h"
#import "RNSVGUIKit.h"
#import "RCTConvert+RNSVG.h"
#import <React/RCTLog.h>
#import "RCTConvert+RNSVG.h"
@implementation RNSVGSolidColorBrush
{
RNSVGColor *_color;
@implementation RNSVGSolidColorBrush {
RNSVGColor *_color;
}
- (instancetype)initWithArray:(NSArray<RNSVGLength *> *)array
{
if ((self = [super initWithArray:array])) {
_color = [RCTConvert RNSVGColor:array offset:1];
}
return self;
if ((self = [super initWithArray:array])) {
_color = [RCTConvert RNSVGColor:array offset:1];
}
return self;
}
- (instancetype)initWithNumber:(NSNumber *)number
{
if ((self = [super init])) {
_color = [RCTConvert RNSVGColor:number];
}
return self;
if ((self = [super init])) {
_color = [RCTConvert RNSVGColor:number];
}
return self;
}
- (instancetype)initWithColor:(RNSVGColor *)color
{
if ((self = [super init])) {
_color = color;
}
return self;
if ((self = [super init])) {
_color = color;
}
return self;
}
- (void)dealloc
{
_color = nil;
_color = nil;
}
- (CGColorRef)getColorWithOpacity:(CGFloat)opacity
{
CGColorRef baseColor = _color.CGColor;
CGColorRef color = CGColorCreateCopyWithAlpha(baseColor, opacity * CGColorGetAlpha(baseColor));
return color;
CGColorRef baseColor = _color.CGColor;
CGColorRef color = CGColorCreateCopyWithAlpha(baseColor, opacity * CGColorGetAlpha(baseColor));
return color;
}
- (BOOL)applyFillColor:(CGContextRef)context opacity:(CGFloat)opacity
{
CGColorRef color = [self getColorWithOpacity:opacity];
CGContextSetFillColorWithColor(context, color);
CGColorRelease(color);
return YES;
CGColorRef color = [self getColorWithOpacity:opacity];
CGContextSetFillColorWithColor(context, color);
CGColorRelease(color);
return YES;
}
- (BOOL)applyStrokeColor:(CGContextRef)context opacity:(CGFloat)opacity
{
CGColorRef color = [self getColorWithOpacity:opacity];
CGContextSetStrokeColorWithColor(context, color);
CGColorRelease(color);
return YES;
CGColorRef color = [self getColorWithOpacity:opacity];
CGContextSetStrokeColorWithColor(context, color);
CGColorRelease(color);
return YES;
}
@end

View File

@@ -10,9 +10,9 @@
#ifdef RN_FABRIC_ENABLED
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import "RCTFabricComponentsPlugins.h"
#import "RCTConversions.h"
#import <react/renderer/components/view/conversions.h>
#import "RCTConversions.h"
#import "RCTFabricComponentsPlugins.h"
#import "RNSVGFabricConversions.h"
#endif // RN_FABRIC_ENABLED
@@ -39,33 +39,32 @@ using namespace facebook::react;
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &newProps = *std::static_pointer_cast<const RNSVGClipPathProps>(props);
setCommonNodeProps(newProps, self);
const auto &newProps = *std::static_pointer_cast<const RNSVGClipPathProps>(props);
setCommonNodeProps(newProps, self);
}
- (void)prepareForRecycle
{
[super prepareForRecycle];
[super prepareForRecycle];
}
#endif // RN_FABRIC_ENABLED
- (void)parseReference
{
self.dirty = false;
[self.svgView defineClipPath:self clipPathName:self.name];
self.dirty = false;
[self.svgView defineClipPath:self clipPathName:self.name];
}
- (BOOL)isSimpleClipPath
{
NSArray<RNSVGView*> *children = self.subviews;
if (children.count == 1) {
RNSVGView* child = children[0];
if ([child class] != [RNSVGGroup class]) {
return true;
}
NSArray<RNSVGView *> *children = self.subviews;
if (children.count == 1) {
RNSVGView *child = children[0];
if ([child class] != [RNSVGGroup class]) {
return true;
}
return false;
}
return false;
}
@end

View File

@@ -9,9 +9,9 @@
#ifdef RN_FABRIC_ENABLED
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import "RCTFabricComponentsPlugins.h"
#import "RCTConversions.h"
#import <react/renderer/components/view/conversions.h>
#import "RCTConversions.h"
#import "RCTFabricComponentsPlugins.h"
#import "RNSVGFabricConversions.h"
#endif // RN_FABRIC_ENABLED
@@ -39,23 +39,23 @@ using namespace facebook::react;
- (void)renderTo:(CGContextRef)context
{
// Defs do not render
// Defs do not render
}
- (void)parseReference
{
self.dirty = false;
[self traverseSubviews:^(RNSVGNode *node) {
if ([node isKindOfClass:[RNSVGNode class]]) {
[node parseReference];
}
return YES;
}];
self.dirty = false;
[self traverseSubviews:^(RNSVGNode *node) {
if ([node isKindOfClass:[RNSVGNode class]]) {
[node parseReference];
}
return YES;
}];
}
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
return nil;
return nil;
}
@end

View File

@@ -12,9 +12,9 @@
#ifdef RN_FABRIC_ENABLED
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import "RCTFabricComponentsPlugins.h"
#import "RCTConversions.h"
#import <react/renderer/components/view/conversions.h>
#import "RCTConversions.h"
#import "RCTFabricComponentsPlugins.h"
#import "RNSVGFabricConversions.h"
#endif // RN_FABRIC_ENABLED
@@ -41,177 +41,177 @@ using namespace facebook::react;
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &newProps = *std::static_pointer_cast<const RNSVGForeignObjectProps>(props);
const auto &newProps = *std::static_pointer_cast<const RNSVGForeignObjectProps>(props);
self.x = RCTNSStringFromStringNilIfEmpty(newProps.x) ? [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)] : nil;
self.y = RCTNSStringFromStringNilIfEmpty(newProps.y) ? [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)] : nil;
if (RCTNSStringFromStringNilIfEmpty(newProps.foreignObjectheight)) {
self.foreignObjectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.foreignObjectheight)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.foreignObjectwidth)) {
self.foreignObjectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.foreignObjectwidth)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
self.foreignObjectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
self.foreignObjectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
}
self.x = RCTNSStringFromStringNilIfEmpty(newProps.x)
? [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)]
: nil;
self.y = RCTNSStringFromStringNilIfEmpty(newProps.y)
? [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)]
: nil;
if (RCTNSStringFromStringNilIfEmpty(newProps.foreignObjectheight)) {
self.foreignObjectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.foreignObjectheight)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.foreignObjectwidth)) {
self.foreignObjectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.foreignObjectwidth)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
self.foreignObjectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
self.foreignObjectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
}
setCommonGroupProps(newProps, self);
setCommonGroupProps(newProps, self);
}
- (void)prepareForRecycle
{
[super prepareForRecycle];
_x = nil;
_y = nil;
_foreignObjectheight = nil;
_foreignObjectwidth = nil;
[super prepareForRecycle];
_x = nil;
_y = nil;
_foreignObjectheight = nil;
_foreignObjectwidth = nil;
}
#endif // RN_FABRIC_ENABLED
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
return nil;
return nil;
}
- (void)parseReference
{
self.dirty = false;
self.dirty = false;
}
- (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect
{
[self clip:context];
CGContextTranslateCTM(context, [self relativeOnWidth:self.x], [self relativeOnHeight:self.y]);
CGRect clip = CGRectMake(
0,
0,
[self relativeOnWidth:self.foreignObjectwidth],
[self relativeOnHeight:self.foreignObjectheight]
);
CGContextClipToRect(context, clip);
[super renderLayerTo:context rect:rect];
[self clip:context];
CGContextTranslateCTM(context, [self relativeOnWidth:self.x], [self relativeOnHeight:self.y]);
CGRect clip = CGRectMake(
0, 0, [self relativeOnWidth:self.foreignObjectwidth], [self relativeOnHeight:self.foreignObjectheight]);
CGContextClipToRect(context, clip);
[super renderLayerTo:context rect:rect];
}
- (void)renderGroupTo:(CGContextRef)context rect:(CGRect)rect
{
[self pushGlyphContext];
[self pushGlyphContext];
__block CGRect bounds = CGRectNull;
[self traverseSubviews:^(RNSVGView *node) {
if ([node isKindOfClass:[RNSVGMask class]] || [node isKindOfClass:[RNSVGClipPath class]]) {
// no-op
} else if ([node isKindOfClass:[RNSVGNode class]]) {
RNSVGNode* svgNode = (RNSVGNode*)node;
if (svgNode.display && [@"none" isEqualToString:svgNode.display]) {
return YES;
}
if (svgNode.responsible && !self.svgView.responsible) {
self.svgView.responsible = YES;
}
if ([node isKindOfClass:[RNSVGRenderable class]]) {
[(RNSVGRenderable*)node mergeProperties:self];
}
[svgNode renderTo:context rect:rect];
CGRect nodeRect = svgNode.clientRect;
if (!CGRectIsEmpty(nodeRect)) {
bounds = CGRectUnion(bounds, nodeRect);
}
if ([node isKindOfClass:[RNSVGRenderable class]]) {
[(RNSVGRenderable*)node resetProperties];
}
} else if ([node isKindOfClass:[RNSVGSvgView class]]) {
RNSVGSvgView* svgView = (RNSVGSvgView*)node;
CGFloat width = [self relativeOnWidth:svgView.bbWidth];
CGFloat height = [self relativeOnHeight:svgView.bbHeight];
CGRect rect = CGRectMake(0, 0, width, height);
CGContextClipToRect(context, rect);
[svgView drawToContext:context withRect:rect];
} else {
node.hidden = false;
[node.layer renderInContext:context];
node.hidden = true;
}
__block CGRect bounds = CGRectNull;
[self traverseSubviews:^(RNSVGView *node) {
if ([node isKindOfClass:[RNSVGMask class]] || [node isKindOfClass:[RNSVGClipPath class]]) {
// no-op
} else if ([node isKindOfClass:[RNSVGNode class]]) {
RNSVGNode *svgNode = (RNSVGNode *)node;
if (svgNode.display && [@"none" isEqualToString:svgNode.display]) {
return YES;
}];
CGPathRef path = [self getPath:context];
[self setHitArea:path];
if (!CGRectEqualToRect(bounds, CGRectNull)) {
self.clientRect = bounds;
self.fillBounds = CGPathGetBoundingBox(path);
self.strokeBounds = CGPathGetBoundingBox(self.strokePath);
self.pathBounds = CGRectUnion(self.fillBounds, self.strokeBounds);
}
if (svgNode.responsible && !self.svgView.responsible) {
self.svgView.responsible = YES;
}
CGAffineTransform current = CGContextGetCTM(context);
CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM);
if ([node isKindOfClass:[RNSVGRenderable class]]) {
[(RNSVGRenderable *)node mergeProperties:self];
}
self.ctm = svgToClientTransform;
self.screenCTM = current;
[svgNode renderTo:context rect:rect];
CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms);
CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
CGPoint center = CGPointApplyAffineTransform(mid, transform);
CGRect nodeRect = svgNode.clientRect;
if (!CGRectIsEmpty(nodeRect)) {
bounds = CGRectUnion(bounds, nodeRect);
}
self.bounds = bounds;
if (!isnan(center.x) && !isnan(center.y)) {
self.center = center;
}
self.frame = bounds;
if ([node isKindOfClass:[RNSVGRenderable class]]) {
[(RNSVGRenderable *)node resetProperties];
}
} else if ([node isKindOfClass:[RNSVGSvgView class]]) {
RNSVGSvgView *svgView = (RNSVGSvgView *)node;
CGFloat width = [self relativeOnWidth:svgView.bbWidth];
CGFloat height = [self relativeOnHeight:svgView.bbHeight];
CGRect rect = CGRectMake(0, 0, width, height);
CGContextClipToRect(context, rect);
[svgView drawToContext:context withRect:rect];
} else {
node.hidden = false;
[node.layer renderInContext:context];
node.hidden = true;
}
[self popGlyphContext];
return YES;
}];
CGPathRef path = [self getPath:context];
[self setHitArea:path];
if (!CGRectEqualToRect(bounds, CGRectNull)) {
self.clientRect = bounds;
self.fillBounds = CGPathGetBoundingBox(path);
self.strokeBounds = CGPathGetBoundingBox(self.strokePath);
self.pathBounds = CGRectUnion(self.fillBounds, self.strokeBounds);
CGAffineTransform current = CGContextGetCTM(context);
CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM);
self.ctm = svgToClientTransform;
self.screenCTM = current;
CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms);
CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
CGPoint center = CGPointApplyAffineTransform(mid, transform);
self.bounds = bounds;
if (!isnan(center.x) && !isnan(center.y)) {
self.center = center;
}
self.frame = bounds;
}
[self popGlyphContext];
}
- (void)drawRect:(CGRect)rect
{
[self invalidate];
[self invalidate];
}
- (void)setX:(RNSVGLength *)x
{
if ([x isEqualTo:_x]) {
return;
}
if ([x isEqualTo:_x]) {
return;
}
_x = x;
[self invalidate];
_x = x;
[self invalidate];
}
- (void)setY:(RNSVGLength *)y
{
if ([y isEqualTo:_y]) {
return;
}
if ([y isEqualTo:_y]) {
return;
}
_y = y;
[self invalidate];
_y = y;
[self invalidate];
}
- (void)setForeignObjectwidth:(RNSVGLength *)foreignObjectwidth
{
if ([foreignObjectwidth isEqualTo:_foreignObjectwidth]) {
return;
}
if ([foreignObjectwidth isEqualTo:_foreignObjectwidth]) {
return;
}
_foreignObjectwidth = foreignObjectwidth;
[self invalidate];
_foreignObjectwidth = foreignObjectwidth;
[self invalidate];
}
- (void)setForeignObjectheight:(RNSVGLength *)foreignObjectheight
{
if ([foreignObjectheight isEqualTo:_foreignObjectheight]) {
return;
}
if ([foreignObjectheight isEqualTo:_foreignObjectheight]) {
return;
}
_foreignObjectheight = foreignObjectheight;
[self invalidate];
_foreignObjectheight = foreignObjectheight;
[self invalidate];
}
@end

View File

@@ -10,11 +10,11 @@
#import "RNSVGUIKit.h"
#import "RNSVGContainer.h"
#import "RNSVGCGFCRule.h"
#import "RNSVGSvgView.h"
#import "RNSVGPath.h"
#import "RNSVGContainer.h"
#import "RNSVGGlyphContext.h"
#import "RNSVGPath.h"
#import "RNSVGSvgView.h"
@interface RNSVGGroup : RNSVGPath <RNSVGContainer>

View File

@@ -10,18 +10,16 @@
#import "RNSVGClipPath.h"
#import "RNSVGMask.h"
#ifdef RN_FABRIC_ENABLED
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import "RCTFabricComponentsPlugins.h"
#import "RCTConversions.h"
#import <react/renderer/components/view/conversions.h>
#import "RCTConversions.h"
#import "RCTFabricComponentsPlugins.h"
#import "RNSVGFabricConversions.h"
#endif // RN_FABRIC_ENABLED
@implementation RNSVGGroup
{
RNSVGGlyphContext *_glyphContext;
@implementation RNSVGGroup {
RNSVGGlyphContext *_glyphContext;
}
#ifdef RN_FABRIC_ENABLED
@@ -45,251 +43,251 @@ using namespace facebook::react;
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &newProps = *std::static_pointer_cast<const RNSVGGroupProps>(props);
const auto &newProps = *std::static_pointer_cast<const RNSVGGroupProps>(props);
setCommonGroupProps(newProps, self);
setCommonGroupProps(newProps, self);
}
- (void)prepareForRecycle
{
[super prepareForRecycle];
_font = nil;
_glyphContext = nil;
[super prepareForRecycle];
_font = nil;
_glyphContext = nil;
}
#endif // RN_FABRIC_ENABLED
- (void)setFont:(NSDictionary*)font
- (void)setFont:(NSDictionary *)font
{
if (font == _font) {
return;
}
if (font == _font) {
return;
}
[self invalidate];
_font = font;
[self invalidate];
_font = font;
}
- (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect
{
[self clip:context];
[self setupGlyphContext:context];
[self renderGroupTo:context rect:rect];
[self clip:context];
[self setupGlyphContext:context];
[self renderGroupTo:context rect:rect];
}
- (void)renderGroupTo:(CGContextRef)context rect:(CGRect)rect
{
[self pushGlyphContext];
[self pushGlyphContext];
__block CGRect bounds = CGRectNull;
[self traverseSubviews:^(RNSVGView *node) {
if ([node isKindOfClass:[RNSVGMask class]] || [node isKindOfClass:[RNSVGClipPath class]]) {
// no-op
} else if ([node isKindOfClass:[RNSVGNode class]]) {
RNSVGNode* svgNode = (RNSVGNode*)node;
if (svgNode.display && [@"none" isEqualToString:svgNode.display]) {
return YES;
}
if (svgNode.responsible && !self.svgView.responsible) {
self.svgView.responsible = YES;
}
if ([node isKindOfClass:[RNSVGRenderable class]]) {
[(RNSVGRenderable*)node mergeProperties:self];
}
[svgNode renderTo:context rect:rect];
CGRect nodeRect = svgNode.clientRect;
if (!CGRectIsEmpty(nodeRect)) {
bounds = CGRectUnion(bounds, nodeRect);
}
if ([node isKindOfClass:[RNSVGRenderable class]]) {
[(RNSVGRenderable*)node resetProperties];
}
} else if ([node isKindOfClass:[RNSVGSvgView class]]) {
RNSVGSvgView* svgView = (RNSVGSvgView*)node;
CGFloat width = [self relativeOnWidth:svgView.bbWidth];
CGFloat height = [self relativeOnHeight:svgView.bbHeight];
CGRect rect = CGRectMake(0, 0, width, height);
CGContextClipToRect(context, rect);
[svgView drawToContext:context withRect:rect];
} else {
[node drawRect:rect];
}
__block CGRect bounds = CGRectNull;
[self traverseSubviews:^(RNSVGView *node) {
if ([node isKindOfClass:[RNSVGMask class]] || [node isKindOfClass:[RNSVGClipPath class]]) {
// no-op
} else if ([node isKindOfClass:[RNSVGNode class]]) {
RNSVGNode *svgNode = (RNSVGNode *)node;
if (svgNode.display && [@"none" isEqualToString:svgNode.display]) {
return YES;
}];
CGPathRef path = [self getPath:context];
[self setHitArea:path];
if (!CGRectEqualToRect(bounds, CGRectNull)) {
self.clientRect = bounds;
self.fillBounds = CGPathGetBoundingBox(path);
self.strokeBounds = CGPathGetBoundingBox(self.strokePath);
self.pathBounds = CGRectUnion(self.fillBounds, self.strokeBounds);
}
if (svgNode.responsible && !self.svgView.responsible) {
self.svgView.responsible = YES;
}
CGAffineTransform current = CGContextGetCTM(context);
CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM);
if ([node isKindOfClass:[RNSVGRenderable class]]) {
[(RNSVGRenderable *)node mergeProperties:self];
}
self.ctm = svgToClientTransform;
self.screenCTM = current;
[svgNode renderTo:context rect:rect];
CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms);
CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
CGPoint center = CGPointApplyAffineTransform(mid, transform);
CGRect nodeRect = svgNode.clientRect;
if (!CGRectIsEmpty(nodeRect)) {
bounds = CGRectUnion(bounds, nodeRect);
}
self.bounds = bounds;
if (!isnan(center.x) && !isnan(center.y)) {
self.center = center;
}
self.frame = bounds;
if ([node isKindOfClass:[RNSVGRenderable class]]) {
[(RNSVGRenderable *)node resetProperties];
}
} else if ([node isKindOfClass:[RNSVGSvgView class]]) {
RNSVGSvgView *svgView = (RNSVGSvgView *)node;
CGFloat width = [self relativeOnWidth:svgView.bbWidth];
CGFloat height = [self relativeOnHeight:svgView.bbHeight];
CGRect rect = CGRectMake(0, 0, width, height);
CGContextClipToRect(context, rect);
[svgView drawToContext:context withRect:rect];
} else {
[node drawRect:rect];
}
[self popGlyphContext];
return YES;
}];
CGPathRef path = [self getPath:context];
[self setHitArea:path];
if (!CGRectEqualToRect(bounds, CGRectNull)) {
self.clientRect = bounds;
self.fillBounds = CGPathGetBoundingBox(path);
self.strokeBounds = CGPathGetBoundingBox(self.strokePath);
self.pathBounds = CGRectUnion(self.fillBounds, self.strokeBounds);
CGAffineTransform current = CGContextGetCTM(context);
CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM);
self.ctm = svgToClientTransform;
self.screenCTM = current;
CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms);
CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
CGPoint center = CGPointApplyAffineTransform(mid, transform);
self.bounds = bounds;
if (!isnan(center.x) && !isnan(center.y)) {
self.center = center;
}
self.frame = bounds;
}
[self popGlyphContext];
}
- (void)setupGlyphContext:(CGContextRef)context
{
CGRect clipBounds = CGContextGetClipBoundingBox(context);
clipBounds = CGRectApplyAffineTransform(clipBounds, self.matrix);
clipBounds = CGRectApplyAffineTransform(clipBounds, self.transforms);
CGFloat width = CGRectGetWidth(clipBounds);
CGFloat height = CGRectGetHeight(clipBounds);
CGRect clipBounds = CGContextGetClipBoundingBox(context);
clipBounds = CGRectApplyAffineTransform(clipBounds, self.matrix);
clipBounds = CGRectApplyAffineTransform(clipBounds, self.transforms);
CGFloat width = CGRectGetWidth(clipBounds);
CGFloat height = CGRectGetHeight(clipBounds);
_glyphContext = [[RNSVGGlyphContext alloc] initWithWidth:width
height:height];
_glyphContext = [[RNSVGGlyphContext alloc] initWithWidth:width height:height];
}
- (RNSVGGlyphContext *)getGlyphContext
{
return _glyphContext;
return _glyphContext;
}
- (void)pushGlyphContext
{
__typeof__(self) __weak weakSelf = self;
[[self.textRoot getGlyphContext] pushContext:weakSelf font:self.font];
__typeof__(self) __weak weakSelf = self;
[[self.textRoot getGlyphContext] pushContext:weakSelf font:self.font];
}
- (void)popGlyphContext
{
[[self.textRoot getGlyphContext] popContext];
[[self.textRoot getGlyphContext] popContext];
}
- (void)renderPathTo:(CGContextRef)context rect:(CGRect)rect
{
[super renderLayerTo:context rect:rect];
[super renderLayerTo:context rect:rect];
}
- (CGPathRef)getPath:(CGContextRef)context
{
CGPathRef cached = self.path;
if (cached) {
return cached;
}
CGMutablePathRef __block path = CGPathCreateMutable();
[self traverseSubviews:^(RNSVGNode *node) {
if ([node isKindOfClass:[RNSVGNode class]] && ![node isKindOfClass:[RNSVGMask class]]) {
CGAffineTransform transform = CGAffineTransformConcat(node.matrix, node.transforms);
CGPathAddPath(path, &transform, [node getPath:context]);
CGPathAddPath(path, &transform, [node markerPath]);
node.dirty = false;
}
return YES;
}];
cached = CGPathRetain((CGPathRef)CFAutorelease(path));
self.path = cached;
CGPathRef cached = self.path;
if (cached) {
return cached;
}
CGMutablePathRef __block path = CGPathCreateMutable();
[self traverseSubviews:^(RNSVGNode *node) {
if ([node isKindOfClass:[RNSVGNode class]] && ![node isKindOfClass:[RNSVGMask class]]) {
CGAffineTransform transform = CGAffineTransformConcat(node.matrix, node.transforms);
CGPathAddPath(path, &transform, [node getPath:context]);
CGPathAddPath(path, &transform, [node markerPath]);
node.dirty = false;
}
return YES;
}];
cached = CGPathRetain((CGPathRef)CFAutorelease(path));
self.path = cached;
return cached;
}
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix);
transformed = CGPointApplyAffineTransform(transformed, self.invTransform);
if (!CGRectContainsPoint(self.pathBounds, transformed)) {
return nil;
}
if (self.clipPath) {
RNSVGClipPath *clipNode = (RNSVGClipPath*)[self.svgView getDefinedClipPath:self.clipPath];
if ([clipNode isSimpleClipPath]) {
CGPathRef clipPath = [self getClipPath];
if (clipPath && !CGPathContainsPoint(clipPath, nil, transformed, clipNode.clipRule == kRNSVGCGFCRuleEvenodd)) {
return nil;
}
} else {
RNSVGRenderable *clipGroup = (RNSVGRenderable*)clipNode;
if (![clipGroup hitTest:transformed withEvent:event]) {
return nil;
}
}
}
if (!event) {
NSPredicate *const anyActive = [NSPredicate predicateWithFormat:@"self isKindOfClass: %@ AND active == TRUE", [RNSVGNode class]];
NSArray *const filtered = [self.subviews filteredArrayUsingPredicate:anyActive];
if ([filtered count] != 0) {
return [filtered.lastObject hitTest:transformed withEvent:event];
}
}
for (RNSVGView *node in [self.subviews reverseObjectEnumerator]) {
if ([node isKindOfClass:[RNSVGNode class]]) {
if ([node isKindOfClass:[RNSVGMask class]]) {
continue;
}
RNSVGNode* svgNode = (RNSVGNode*)node;
if (event) {
svgNode.active = NO;
}
RNSVGPlatformView *hitChild = [svgNode hitTest:transformed withEvent:event];
if (hitChild) {
svgNode.active = YES;
return (svgNode.responsible || (svgNode != hitChild)) ? hitChild : self;
}
} else if ([node isKindOfClass:[RNSVGSvgView class]]) {
RNSVGSvgView* svgView = (RNSVGSvgView*)node;
RNSVGPlatformView *hitChild = [svgView hitTest:transformed withEvent:event];
if (hitChild) {
return hitChild;
}
}
}
RNSVGPlatformView *hitSelf = [super hitTest:transformed withEvent:event];
if (hitSelf) {
return hitSelf;
}
CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix);
transformed = CGPointApplyAffineTransform(transformed, self.invTransform);
if (!CGRectContainsPoint(self.pathBounds, transformed)) {
return nil;
}
if (self.clipPath) {
RNSVGClipPath *clipNode = (RNSVGClipPath *)[self.svgView getDefinedClipPath:self.clipPath];
if ([clipNode isSimpleClipPath]) {
CGPathRef clipPath = [self getClipPath];
if (clipPath && !CGPathContainsPoint(clipPath, nil, transformed, clipNode.clipRule == kRNSVGCGFCRuleEvenodd)) {
return nil;
}
} else {
RNSVGRenderable *clipGroup = (RNSVGRenderable *)clipNode;
if (![clipGroup hitTest:transformed withEvent:event]) {
return nil;
}
}
}
if (!event) {
NSPredicate *const anyActive =
[NSPredicate predicateWithFormat:@"self isKindOfClass: %@ AND active == TRUE", [RNSVGNode class]];
NSArray *const filtered = [self.subviews filteredArrayUsingPredicate:anyActive];
if ([filtered count] != 0) {
return [filtered.lastObject hitTest:transformed withEvent:event];
}
}
for (RNSVGView *node in [self.subviews reverseObjectEnumerator]) {
if ([node isKindOfClass:[RNSVGNode class]]) {
if ([node isKindOfClass:[RNSVGMask class]]) {
continue;
}
RNSVGNode *svgNode = (RNSVGNode *)node;
if (event) {
svgNode.active = NO;
}
RNSVGPlatformView *hitChild = [svgNode hitTest:transformed withEvent:event];
if (hitChild) {
svgNode.active = YES;
return (svgNode.responsible || (svgNode != hitChild)) ? hitChild : self;
}
} else if ([node isKindOfClass:[RNSVGSvgView class]]) {
RNSVGSvgView *svgView = (RNSVGSvgView *)node;
RNSVGPlatformView *hitChild = [svgView hitTest:transformed withEvent:event];
if (hitChild) {
return hitChild;
}
}
}
RNSVGPlatformView *hitSelf = [super hitTest:transformed withEvent:event];
if (hitSelf) {
return hitSelf;
}
return nil;
}
- (void)parseReference
{
self.dirty = false;
if (self.name) {
__typeof__(self) __weak weakSelf = self;
[self.svgView defineTemplate:weakSelf templateName:self.name];
}
self.dirty = false;
if (self.name) {
__typeof__(self) __weak weakSelf = self;
[self.svgView defineTemplate:weakSelf templateName:self.name];
}
[self traverseSubviews:^(RNSVGNode *node) {
if ([node isKindOfClass:[RNSVGNode class]]) {
[node parseReference];
}
return YES;
}];
[self traverseSubviews:^(RNSVGNode *node) {
if ([node isKindOfClass:[RNSVGNode class]]) {
[node parseReference];
}
return YES;
}];
}
- (void)resetProperties
{
[self traverseSubviews:^(__kindof RNSVGNode *node) {
if ([node isKindOfClass:[RNSVGRenderable class]]) {
[(RNSVGRenderable*)node resetProperties];
}
return YES;
}];
[self traverseSubviews:^(__kindof RNSVGNode *node) {
if ([node isKindOfClass:[RNSVGRenderable class]]) {
[(RNSVGRenderable *)node resetProperties];
}
return YES;
}];
}
@end

View File

@@ -9,9 +9,9 @@
#import <Foundation/Foundation.h>
#import <React/RCTBridge.h>
#import "RNSVGLength.h"
#import "RNSVGRenderable.h"
#import "RNSVGVBMOS.h"
#import "RNSVGLength.h"
#import <React/RCTImageSource.h>
@@ -19,10 +19,10 @@
@property (nonatomic, weak) RCTBridge *bridge;
@property (nonatomic, assign) id src;
@property (nonatomic, strong) RNSVGLength* x;
@property (nonatomic, strong) RNSVGLength* y;
@property (nonatomic, strong) RNSVGLength* imagewidth;
@property (nonatomic, strong) RNSVGLength* imageheight;
@property (nonatomic, strong) RNSVGLength *x;
@property (nonatomic, strong) RNSVGLength *y;
@property (nonatomic, strong) RNSVGLength *imagewidth;
@property (nonatomic, strong) RNSVGLength *imageheight;
@property (nonatomic, strong) NSString *align;
@property (nonatomic, assign) RNSVGVBMOS meetOrSlice;

View File

@@ -15,25 +15,25 @@
#else
#import <React/RCTImageURLLoader.h>
#import <React/RCTImageShadowView.h>
#import <React/RCTImageView.h>
#import <React/RCTImageLoaderProtocol.h>
#import <React/RCTImageShadowView.h>
#import <React/RCTImageURLLoader.h>
#import <React/RCTImageView.h>
#endif // RN_FABRIC_ENABLED
#import <React/RCTLog.h>
#import "RNSVGViewBox.h"
#import "RCTBridge.h"
#import "RNSVGViewBox.h"
#ifdef RN_FABRIC_ENABLED
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import "RCTFabricComponentsPlugins.h"
#import "RCTConversions.h"
#import <react/renderer/components/view/conversions.h>
#import "RNSVGFabricConversions.h"
#import "RCTConversions.h"
#import "RCTFabricComponentsPlugins.h"
#import "RCTImagePrimitivesConversions.h"
#import "RCTImageSource.h"
#import "RNSVGFabricConversions.h"
// Some RN private method hacking below similar to how it is done in RNScreens:
// https://github.com/software-mansion/react-native-screens/blob/90e548739f35b5ded2524a9d6410033fc233f586/ios/RNSScreenStackHeaderConfig.mm#L30
@@ -43,11 +43,10 @@
#endif // RN_FABRIC_ENABLED
@implementation RNSVGImage
{
CGImageRef _image;
CGSize _imageSize;
RCTImageLoaderCancellationBlock _reloadImageCancellationBlock;
@implementation RNSVGImage {
CGImageRef _image;
CGSize _imageSize;
RCTImageLoaderCancellationBlock _reloadImageCancellationBlock;
}
#ifdef RN_FABRIC_ENABLED
using namespace facebook::react;
@@ -71,104 +70,106 @@ using namespace facebook::react;
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &newProps = *std::static_pointer_cast<const RNSVGImageProps>(props);
const auto &oldImageProps = *std::static_pointer_cast<const RNSVGImageProps>(oldProps);
const auto &oldImageProps = *std::static_pointer_cast<const RNSVGImageProps>(oldProps);
self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)];
self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)];
if (RCTNSStringFromStringNilIfEmpty(newProps.imageheight)) {
self.imageheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.imageheight)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.imagewidth)) {
self.imagewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.imagewidth)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
self.imageheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
self.imagewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
}
if (oldProps == nullptr || oldImageProps.src != newProps.src) {
// TODO: make it the same as in e.g. slider
NSURLRequest *request = NSURLRequestFromImageSource(newProps.src);
CGSize size = RCTCGSizeFromSize(newProps.src.size);
CGFloat scale = newProps.src.scale;
RCTImageSource *imageSource = [[RCTImageSource alloc] initWithURLRequest:request size:size scale:scale];
[self setImageSrc:imageSource request:request];
}
self.align = RCTNSStringFromStringNilIfEmpty(newProps.align);
self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice);
self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)];
self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)];
if (RCTNSStringFromStringNilIfEmpty(newProps.imageheight)) {
self.imageheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.imageheight)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.imagewidth)) {
self.imagewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.imagewidth)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
self.imageheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
self.imagewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
}
setCommonRenderableProps(newProps, self);
if (oldProps == nullptr || oldImageProps.src != newProps.src) {
// TODO: make it the same as in e.g. slider
NSURLRequest *request = NSURLRequestFromImageSource(newProps.src);
CGSize size = RCTCGSizeFromSize(newProps.src.size);
CGFloat scale = newProps.src.scale;
RCTImageSource *imageSource = [[RCTImageSource alloc] initWithURLRequest:request size:size scale:scale];
[self setImageSrc:imageSource request:request];
}
self.align = RCTNSStringFromStringNilIfEmpty(newProps.align);
self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice);
setCommonRenderableProps(newProps, self);
}
- (void)prepareForRecycle
{
[super prepareForRecycle];
_x = nil;
_y = nil;
_imageheight = nil;
_imagewidth = nil;
_src = nil;
_align = nil;
_meetOrSlice = kRNSVGVBMOSMeet;
if (_image) {
CGImageRelease(_image);
}
_image = nil;
_imageSize = CGSizeZero;
_reloadImageCancellationBlock = nil;
[super prepareForRecycle];
_x = nil;
_y = nil;
_imageheight = nil;
_imagewidth = nil;
_src = nil;
_align = nil;
_meetOrSlice = kRNSVGVBMOSMeet;
if (_image) {
CGImageRelease(_image);
}
_image = nil;
_imageSize = CGSizeZero;
_reloadImageCancellationBlock = nil;
}
#endif // RN_FABRIC_ENABLED
- (void)setSrc:(id)src
{
if (src == _src) {
return;
}
_src = src;
CGImageRelease(_image);
_image = nil;
RCTImageSource *source = [RCTConvert RCTImageSource:src];
if (source.size.width != 0 && source.size.height != 0) {
_imageSize = source.size;
} else {
_imageSize = CGSizeMake(0, 0);
}
if (src == _src) {
return;
}
_src = src;
CGImageRelease(_image);
_image = nil;
RCTImageSource *source = [RCTConvert RCTImageSource:src];
if (source.size.width != 0 && source.size.height != 0) {
_imageSize = source.size;
} else {
_imageSize = CGSizeMake(0, 0);
}
RCTImageLoaderCancellationBlock previousCancellationBlock = _reloadImageCancellationBlock;
if (previousCancellationBlock) {
previousCancellationBlock();
_reloadImageCancellationBlock = nil;
}
RCTImageLoaderCancellationBlock previousCancellationBlock = _reloadImageCancellationBlock;
if (previousCancellationBlock) {
previousCancellationBlock();
_reloadImageCancellationBlock = nil;
}
_reloadImageCancellationBlock = [[self.bridge moduleForName:@"ImageLoader"] loadImageWithURLRequest:[RCTConvert NSURLRequest:src] callback:^(NSError *error, UIImage *image) {
dispatch_async(dispatch_get_main_queue(), ^{
self->_image = CGImageRetain(image.CGImage);
self->_imageSize = CGSizeMake(CGImageGetWidth(self->_image), CGImageGetHeight(self->_image));
[self invalidate];
});
}];
_reloadImageCancellationBlock = [[self.bridge moduleForName:@"ImageLoader"]
loadImageWithURLRequest:[RCTConvert NSURLRequest:src]
callback:^(NSError *error, UIImage *image) {
dispatch_async(dispatch_get_main_queue(), ^{
self->_image = CGImageRetain(image.CGImage);
self->_imageSize = CGSizeMake(CGImageGetWidth(self->_image), CGImageGetHeight(self->_image));
[self invalidate];
});
}];
}
- (void)setImageSrc:(RCTImageSource *)source request:(NSURLRequest *)request
{
CGImageRelease(_image);
_image = nil;
if (source.size.width != 0 && source.size.height != 0) {
_imageSize = source.size;
} else {
_imageSize = CGSizeMake(0, 0);
}
CGImageRelease(_image);
_image = nil;
if (source.size.width != 0 && source.size.height != 0) {
_imageSize = source.size;
} else {
_imageSize = CGSizeMake(0, 0);
}
RCTImageLoaderCancellationBlock previousCancellationBlock = _reloadImageCancellationBlock;
if (previousCancellationBlock) {
previousCancellationBlock();
_reloadImageCancellationBlock = nil;
}
RCTImageLoaderCancellationBlock previousCancellationBlock = _reloadImageCancellationBlock;
if (previousCancellationBlock) {
previousCancellationBlock();
_reloadImageCancellationBlock = nil;
}
_reloadImageCancellationBlock = [[
_reloadImageCancellationBlock = [[
#ifdef RN_FABRIC_ENABLED
[RCTBridge currentBridge]
#else
@@ -185,130 +186,133 @@ using namespace facebook::react;
- (void)setX:(RNSVGLength *)x
{
if ([x isEqualTo:_x]) {
return;
}
[self invalidate];
_x = x;
if ([x isEqualTo:_x]) {
return;
}
[self invalidate];
_x = x;
}
- (void)setY:(RNSVGLength *)y
{
if ([y isEqualTo:_y]) {
return;
}
[self invalidate];
_y = y;
if ([y isEqualTo:_y]) {
return;
}
[self invalidate];
_y = y;
}
- (void)setImagewidth:(RNSVGLength *)width
{
if ([width isEqualTo:_imagewidth]) {
return;
}
[self invalidate];
_imagewidth = width;
if ([width isEqualTo:_imagewidth]) {
return;
}
[self invalidate];
_imagewidth = width;
}
- (void)setImageheight:(RNSVGLength *)height
{
if ([height isEqualTo:_imageheight]) {
return;
}
[self invalidate];
_imageheight = height;
if ([height isEqualTo:_imageheight]) {
return;
}
[self invalidate];
_imageheight = height;
}
- (void)setAlign:(NSString *)align
{
if ([align isEqualToString:_align]) {
return;
}
[self invalidate];
_align = align;
if ([align isEqualToString:_align]) {
return;
}
[self invalidate];
_align = align;
}
- (void)setMeetOrSlice:(RNSVGVBMOS)meetOrSlice
{
if (meetOrSlice == _meetOrSlice) {
return;
}
[self invalidate];
_meetOrSlice = meetOrSlice;
if (meetOrSlice == _meetOrSlice) {
return;
}
[self invalidate];
_meetOrSlice = meetOrSlice;
}
- (void)dealloc
{
CGImageRelease(_image);
CGImageRelease(_image);
}
- (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect
{
if (CGSizeEqualToSize(CGSizeZero, _imageSize)) {
return;
}
CGContextSaveGState(context);
if (CGSizeEqualToSize(CGSizeZero, _imageSize)) {
return;
}
CGContextSaveGState(context);
// add hit area
CGRect hitArea = [self getHitArea];
CGPathRef hitAreaPath = CGPathCreateWithRect(hitArea, nil);
[self setHitArea:hitAreaPath];
CGPathRelease(hitAreaPath);
self.pathBounds = hitArea;
self.fillBounds = hitArea;
self.strokeBounds = hitArea;
// add hit area
CGRect hitArea = [self getHitArea];
CGPathRef hitAreaPath = CGPathCreateWithRect(hitArea, nil);
[self setHitArea:hitAreaPath];
CGPathRelease(hitAreaPath);
self.pathBounds = hitArea;
self.fillBounds = hitArea;
self.strokeBounds = hitArea;
// apply viewBox transform on Image render.
CGRect imageBounds = CGRectMake(0, 0, _imageSize.width, _imageSize.height);
CGAffineTransform viewbox = [RNSVGViewBox getTransform:imageBounds eRect:hitArea align:self.align meetOrSlice:self.meetOrSlice];
// apply viewBox transform on Image render.
CGRect imageBounds = CGRectMake(0, 0, _imageSize.width, _imageSize.height);
CGAffineTransform viewbox = [RNSVGViewBox getTransform:imageBounds
eRect:hitArea
align:self.align
meetOrSlice:self.meetOrSlice];
[self clip:context];
CGContextClipToRect(context, hitArea);
CGContextConcatCTM(context, viewbox);
CGContextTranslateCTM(context, 0, imageBounds.size.height);
CGContextScaleCTM(context, 1, -1);
CGContextDrawImage(context, imageBounds, _image);
CGContextRestoreGState(context);
[self clip:context];
CGContextClipToRect(context, hitArea);
CGContextConcatCTM(context, viewbox);
CGContextTranslateCTM(context, 0, imageBounds.size.height);
CGContextScaleCTM(context, 1, -1);
CGContextDrawImage(context, imageBounds, _image);
CGContextRestoreGState(context);
CGRect bounds = hitArea;
self.clientRect = bounds;
CGRect bounds = hitArea;
self.clientRect = bounds;
CGAffineTransform current = CGContextGetCTM(context);
CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM);
CGAffineTransform current = CGContextGetCTM(context);
CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM);
self.ctm = svgToClientTransform;
self.screenCTM = current;
self.ctm = svgToClientTransform;
self.screenCTM = current;
CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms);
CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
CGPoint center = CGPointApplyAffineTransform(mid, transform);
CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms);
CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
CGPoint center = CGPointApplyAffineTransform(mid, transform);
self.bounds = bounds;
if (!isnan(center.x) && !isnan(center.y)) {
self.center = center;
}
self.frame = bounds;
self.bounds = bounds;
if (!isnan(center.x) && !isnan(center.y)) {
self.center = center;
}
self.frame = bounds;
}
- (CGRect)getHitArea
{
CGFloat x = [self relativeOnWidth:self.x];
CGFloat y = [self relativeOnHeight:self.y];
CGFloat width = [self relativeOnWidth:self.imagewidth];
CGFloat height = [self relativeOnHeight:self.imageheight];
if (width == 0) {
width = _imageSize.width;
}
if (height == 0) {
height = _imageSize.height;
}
CGFloat x = [self relativeOnWidth:self.x];
CGFloat y = [self relativeOnHeight:self.y];
CGFloat width = [self relativeOnWidth:self.imagewidth];
CGFloat height = [self relativeOnHeight:self.imageheight];
if (width == 0) {
width = _imageSize.width;
}
if (height == 0) {
height = _imageSize.height;
}
return CGRectMake(x, y, width, height);
return CGRectMake(x, y, width, height);
}
- (CGPathRef)getPath:(CGContextRef)context
{
return (CGPathRef)CFAutorelease(CGPathCreateWithRect([self getHitArea], nil));
return (CGPathRef)CFAutorelease(CGPathCreateWithRect([self getHitArea], nil));
}
@end

View File

@@ -6,8 +6,8 @@
* LICENSE file in the root directory of this source tree.
*/
#import "RNSVGNode.h"
#import "RNSVGLength.h"
#import "RNSVGNode.h"
@interface RNSVGLinearGradient : RNSVGNode

View File

@@ -6,14 +6,14 @@
* LICENSE file in the root directory of this source tree.
*/
#import "RNSVGLinearGradient.h"
#import "RNSVGPainter.h"
#import "RNSVGBrushType.h"
#import "RNSVGPainter.h"
#ifdef RN_FABRIC_ENABLED
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import "RCTFabricComponentsPlugins.h"
#import "RCTConversions.h"
#import <react/renderer/components/view/conversions.h>
#import "RCTConversions.h"
#import "RCTFabricComponentsPlugins.h"
#import "RNSVGFabricConversions.h"
#endif // RN_FABRIC_ENABLED
@@ -40,133 +40,139 @@ using namespace facebook::react;
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &newProps = *std::static_pointer_cast<const RNSVGLinearGradientProps>(props);
const auto &newProps = *std::static_pointer_cast<const RNSVGLinearGradientProps>(props);
self.x1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x1)];
self.y1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y1)];
self.x2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x2)];
self.y2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y2)];
if (newProps.gradient.size() > 0) {
NSMutableArray<NSNumber *> *gradientArray = [NSMutableArray new];
for (auto number : newProps.gradient) {
[gradientArray addObject:[NSNumber numberWithDouble:number]];
}
self.gradient = gradientArray;
self.x1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x1)];
self.y1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y1)];
self.x2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x2)];
self.y2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y2)];
if (newProps.gradient.size() > 0) {
NSMutableArray<NSNumber *> *gradientArray = [NSMutableArray new];
for (auto number : newProps.gradient) {
[gradientArray addObject:[NSNumber numberWithDouble:number]];
}
self.gradientUnits = newProps.gradientUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
if (newProps.gradientTransform.size() == 6) {
self.gradientTransform = CGAffineTransformMake(newProps.gradientTransform.at(0), newProps.gradientTransform.at(1), newProps.gradientTransform.at(2), newProps.gradientTransform.at(3), newProps.gradientTransform.at(4), newProps.gradientTransform.at(5));
}
setCommonNodeProps(newProps, self);
self.gradient = gradientArray;
}
self.gradientUnits = newProps.gradientUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
if (newProps.gradientTransform.size() == 6) {
self.gradientTransform = CGAffineTransformMake(
newProps.gradientTransform.at(0),
newProps.gradientTransform.at(1),
newProps.gradientTransform.at(2),
newProps.gradientTransform.at(3),
newProps.gradientTransform.at(4),
newProps.gradientTransform.at(5));
}
setCommonNodeProps(newProps, self);
}
- (void)prepareForRecycle
{
[super prepareForRecycle];
_x1 = nil;
_y1 = nil;
_x2 = nil;
_y2 = nil;
_gradient = nil;
_gradientUnits = kRNSVGUnitsObjectBoundingBox;
_gradientTransform = CGAffineTransformIdentity;
[super prepareForRecycle];
_x1 = nil;
_y1 = nil;
_x2 = nil;
_y2 = nil;
_gradient = nil;
_gradientUnits = kRNSVGUnitsObjectBoundingBox;
_gradientTransform = CGAffineTransformIdentity;
}
#endif // RN_FABRIC_ENABLED
- (instancetype)init
{
if (self = [super init]) {
_gradientTransform = CGAffineTransformIdentity;
}
return self;
if (self = [super init]) {
_gradientTransform = CGAffineTransformIdentity;
}
return self;
}
- (void)setX1:(RNSVGLength *)x1
{
if ([x1 isEqualTo:_x1]) {
return;
}
if ([x1 isEqualTo:_x1]) {
return;
}
_x1 = x1;
[self invalidate];
_x1 = x1;
[self invalidate];
}
- (void)setY1:(RNSVGLength *)y1
{
if ([y1 isEqualTo:_y1]) {
return;
}
if ([y1 isEqualTo:_y1]) {
return;
}
_y1 = y1;
[self invalidate];
_y1 = y1;
[self invalidate];
}
- (void)setX2:(RNSVGLength *)x2
{
if ([x2 isEqualTo:_x2]) {
return;
}
if ([x2 isEqualTo:_x2]) {
return;
}
_x2 = x2;
[self invalidate];
_x2 = x2;
[self invalidate];
}
- (void)setY2:(RNSVGLength *)y2
{
if ([y2 isEqualTo:_y2]) {
return;
}
if ([y2 isEqualTo:_y2]) {
return;
}
_y2 = y2;
[self invalidate];
_y2 = y2;
[self invalidate];
}
- (void)setGradient:(NSArray<NSNumber *> *)gradient
{
if (gradient == _gradient) {
return;
}
if (gradient == _gradient) {
return;
}
_gradient = gradient;
[self invalidate];
_gradient = gradient;
[self invalidate];
}
- (void)setGradientUnits:(RNSVGUnits)gradientUnits
{
if (gradientUnits == _gradientUnits) {
return;
}
if (gradientUnits == _gradientUnits) {
return;
}
_gradientUnits = gradientUnits;
[self invalidate];
_gradientUnits = gradientUnits;
[self invalidate];
}
- (void)setGradientTransform:(CGAffineTransform)gradientTransform
{
_gradientTransform = gradientTransform;
[self invalidate];
_gradientTransform = gradientTransform;
[self invalidate];
}
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
return nil;
return nil;
}
- (void)parseReference
{
self.dirty = false;
NSArray<RNSVGLength *> *points = @[self.x1, self.y1, self.x2, self.y2];
RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points];
[painter setUnits:self.gradientUnits];
[painter setTransform:self.gradientTransform];
[painter setLinearGradientColors:self.gradient];
self.dirty = false;
NSArray<RNSVGLength *> *points = @[ self.x1, self.y1, self.x2, self.y2 ];
RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points];
[painter setUnits:self.gradientUnits];
[painter setTransform:self.gradientTransform];
[painter setLinearGradientColors:self.gradient];
if (self.gradientUnits == kRNSVGUnitsUserSpaceOnUse) {
[painter setUserSpaceBoundingBox:[self.svgView getContextBounds]];
}
if (self.gradientUnits == kRNSVGUnitsUserSpaceOnUse) {
[painter setUserSpaceBoundingBox:[self.svgView getContextBounds]];
}
[self.svgView definePainter:painter painterName:self.name];
[self.svgView definePainter:painter painterName:self.name];
}
@end

View File

@@ -19,6 +19,9 @@
@property (nonatomic, strong) NSString *align;
@property (nonatomic, assign) RNSVGVBMOS meetOrSlice;
- (void)renderMarker:(CGContextRef)context rect:(CGRect)rect position:(RNSVGMarkerPosition*)position strokeWidth:(CGFloat)strokeWidth;
- (void)renderMarker:(CGContextRef)context
rect:(CGRect)rect
position:(RNSVGMarkerPosition *)position
strokeWidth:(CGFloat)strokeWidth;
@end

View File

@@ -6,16 +6,16 @@
* LICENSE file in the root directory of this source tree.
*/
#import "RNSVGMarker.h"
#import "RNSVGPainter.h"
#import "RNSVGBrushType.h"
#import "RNSVGNode.h"
#import "RNSVGPainter.h"
#import "RNSVGViewBox.h"
#ifdef RN_FABRIC_ENABLED
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import "RCTFabricComponentsPlugins.h"
#import "RCTConversions.h"
#import <react/renderer/components/view/conversions.h>
#import "RCTConversions.h"
#import "RCTFabricComponentsPlugins.h"
#import "RNSVGFabricConversions.h"
#endif // RN_FABRIC_ENABLED
@@ -42,230 +42,234 @@ using namespace facebook::react;
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &newProps = *std::static_pointer_cast<const RNSVGMarkerProps>(props);
self.refX = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.refX)];
self.refY = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.refY)];
self.markerHeight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.markerHeight)];
self.markerWidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.markerWidth)];
self.markerUnits = RCTNSStringFromStringNilIfEmpty(newProps.markerUnits);
self.orient = RCTNSStringFromStringNilIfEmpty(newProps.orient);
const auto &newProps = *std::static_pointer_cast<const RNSVGMarkerProps>(props);
self.minX = newProps.minX;
self.minY = newProps.minY;
self.vbWidth = newProps.vbWidth;
self.vbHeight = newProps.vbHeight;
self.align = RCTNSStringFromStringNilIfEmpty(newProps.align);
self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice);
self.refX = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.refX)];
self.refY = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.refY)];
self.markerHeight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.markerHeight)];
self.markerWidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.markerWidth)];
self.markerUnits = RCTNSStringFromStringNilIfEmpty(newProps.markerUnits);
self.orient = RCTNSStringFromStringNilIfEmpty(newProps.orient);
setCommonGroupProps(newProps, self);
self.minX = newProps.minX;
self.minY = newProps.minY;
self.vbWidth = newProps.vbWidth;
self.vbHeight = newProps.vbHeight;
self.align = RCTNSStringFromStringNilIfEmpty(newProps.align);
self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice);
setCommonGroupProps(newProps, self);
}
- (void)prepareForRecycle
{
[super prepareForRecycle];
_refX = nil;
_refY = nil;
_markerHeight = nil;
_markerWidth = nil;
_markerUnits = nil;
_orient = nil;
_minX = 0;
_minY = 0;
_vbWidth = 0;
_vbHeight = 0;
_align = nil;
_meetOrSlice = kRNSVGVBMOSMeet;
[super prepareForRecycle];
_refX = nil;
_refY = nil;
_markerHeight = nil;
_markerWidth = nil;
_markerUnits = nil;
_orient = nil;
_minX = 0;
_minY = 0;
_vbWidth = 0;
_vbHeight = 0;
_align = nil;
_meetOrSlice = kRNSVGVBMOSMeet;
}
#endif // RN_FABRIC_ENABLED
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
return nil;
return nil;
}
- (void)parseReference
{
self.dirty = false;
[self.svgView defineMarker:self markerName:self.name];
[self traverseSubviews:^(RNSVGNode *node) {
if ([node isKindOfClass:[RNSVGNode class]]) {
[node parseReference];
}
return YES;
}];
self.dirty = false;
[self.svgView defineMarker:self markerName:self.name];
[self traverseSubviews:^(RNSVGNode *node) {
if ([node isKindOfClass:[RNSVGNode class]]) {
[node parseReference];
}
return YES;
}];
}
- (void)setX:(RNSVGLength *)refX
{
if ([refX isEqualTo:_refX]) {
return;
}
if ([refX isEqualTo:_refX]) {
return;
}
_refX = refX;
[self invalidate];
_refX = refX;
[self invalidate];
}
- (void)setY:(RNSVGLength *)refY
{
if ([refY isEqualTo:_refY]) {
return;
}
if ([refY isEqualTo:_refY]) {
return;
}
_refY = refY;
[self invalidate];
_refY = refY;
[self invalidate];
}
- (void)setMarkerWidth:(RNSVGLength *)markerWidth
{
if ([markerWidth isEqualTo:_markerWidth]) {
return;
}
if ([markerWidth isEqualTo:_markerWidth]) {
return;
}
_markerWidth = markerWidth;
[self invalidate];
_markerWidth = markerWidth;
[self invalidate];
}
- (void)setMarkerHeight:(RNSVGLength *)markerHeight
{
if ([markerHeight isEqualTo:_markerHeight]) {
return;
}
if ([markerHeight isEqualTo:_markerHeight]) {
return;
}
_markerHeight = markerHeight;
[self invalidate];
_markerHeight = markerHeight;
[self invalidate];
}
- (void)setMarkerUnits:(NSString *)markerUnits
{
if ([_markerUnits isEqualToString:markerUnits]) {
return;
}
if ([_markerUnits isEqualToString:markerUnits]) {
return;
}
_markerUnits = markerUnits;
[self invalidate];
_markerUnits = markerUnits;
[self invalidate];
}
- (void)setOrient:(NSString *)orient
{
if ([orient isEqualToString:_orient]) {
return;
}
if ([orient isEqualToString:_orient]) {
return;
}
[self invalidate];
_orient = orient;
[self invalidate];
_orient = orient;
}
- (void)setMinX:(CGFloat)minX
{
if (minX == _minX) {
return;
}
if (minX == _minX) {
return;
}
[self invalidate];
_minX = minX;
[self invalidate];
_minX = minX;
}
- (void)setMinY:(CGFloat)minY
{
if (minY == _minY) {
return;
}
if (minY == _minY) {
return;
}
[self invalidate];
_minY = minY;
[self invalidate];
_minY = minY;
}
- (void)setVbWidth:(CGFloat)vbWidth
{
if (vbWidth == _vbWidth) {
return;
}
if (vbWidth == _vbWidth) {
return;
}
[self invalidate];
_vbWidth = vbWidth;
[self invalidate];
_vbWidth = vbWidth;
}
- (void)setVbHeight:(CGFloat)vbHeight
{
if (_vbHeight == vbHeight) {
return;
}
if (_vbHeight == vbHeight) {
return;
}
[self invalidate];
_vbHeight = vbHeight;
[self invalidate];
_vbHeight = vbHeight;
}
- (void)setAlign:(NSString *)align
{
if ([align isEqualToString:_align]) {
return;
}
if ([align isEqualToString:_align]) {
return;
}
[self invalidate];
_align = align;
[self invalidate];
_align = align;
}
- (void)setMeetOrSlice:(RNSVGVBMOS)meetOrSlice
{
if (meetOrSlice == _meetOrSlice) {
return;
}
if (meetOrSlice == _meetOrSlice) {
return;
}
[self invalidate];
_meetOrSlice = meetOrSlice;
[self invalidate];
_meetOrSlice = meetOrSlice;
}
static CGFloat RNSVG_degToRad = (CGFloat)M_PI / 180;
double deg2rad(CGFloat deg) {
return deg * RNSVG_degToRad;
double deg2rad(CGFloat deg)
{
return deg * RNSVG_degToRad;
}
- (void)renderMarker:(CGContextRef)context rect:(CGRect)rect position:(RNSVGMarkerPosition*)position strokeWidth:(CGFloat)strokeWidth
- (void)renderMarker:(CGContextRef)context
rect:(CGRect)rect
position:(RNSVGMarkerPosition *)position
strokeWidth:(CGFloat)strokeWidth
{
CGContextSaveGState(context);
CGContextSaveGState(context);
CGPoint origin = [position origin];
CGAffineTransform transform = CGAffineTransformMakeTranslation(origin.x, origin.y);
CGPoint origin = [position origin];
CGAffineTransform transform = CGAffineTransformMakeTranslation(origin.x, origin.y);
float markerAngle = [@"auto" isEqualToString:_orient] ? -1 : [_orient doubleValue];
float angle = 180 + (markerAngle == -1 ? [position angle] : markerAngle);
float rad = deg2rad(angle);
transform = CGAffineTransformRotate(transform, rad);
float markerAngle = [@"auto" isEqualToString:_orient] ? -1 : [_orient doubleValue];
float angle = 180 + (markerAngle == -1 ? [position angle] : markerAngle);
float rad = deg2rad(angle);
transform = CGAffineTransformRotate(transform, rad);
bool useStrokeWidth = [@"strokeWidth" isEqualToString:_markerUnits];
if (useStrokeWidth) {
transform = CGAffineTransformScale(transform, strokeWidth, strokeWidth);
}
bool useStrokeWidth = [@"strokeWidth" isEqualToString:_markerUnits];
if (useStrokeWidth) {
transform = CGAffineTransformScale(transform, strokeWidth, strokeWidth);
}
CGFloat width = [self relativeOnWidth:self.markerWidth];
CGFloat height = [self relativeOnHeight:self.markerHeight];
CGRect eRect = CGRectMake(0, 0, width, height);
if (self.align) {
CGAffineTransform viewBoxTransform = [RNSVGViewBox getTransform:CGRectMake(self.minX, self.minY, self.vbWidth, self.vbHeight)
eRect:eRect
align:self.align
meetOrSlice:self.meetOrSlice];
transform = CGAffineTransformScale(transform, viewBoxTransform.a, viewBoxTransform.d);
}
CGFloat width = [self relativeOnWidth:self.markerWidth];
CGFloat height = [self relativeOnHeight:self.markerHeight];
CGRect eRect = CGRectMake(0, 0, width, height);
if (self.align) {
CGAffineTransform viewBoxTransform =
[RNSVGViewBox getTransform:CGRectMake(self.minX, self.minY, self.vbWidth, self.vbHeight)
eRect:eRect
align:self.align
meetOrSlice:self.meetOrSlice];
transform = CGAffineTransformScale(transform, viewBoxTransform.a, viewBoxTransform.d);
}
CGFloat x = [self relativeOnWidth:self.refX];
CGFloat y = [self relativeOnHeight:self.refY];
transform = CGAffineTransformTranslate(transform, -x, -y);
CGFloat x = [self relativeOnWidth:self.refX];
CGFloat y = [self relativeOnHeight:self.refY];
transform = CGAffineTransformTranslate(transform, -x, -y);
self.transform = transform;
CGContextConcatCTM(context, transform);
self.transform = transform;
CGContextConcatCTM(context, transform);
[self renderGroupTo:context rect:eRect];
[self renderGroupTo:context rect:eRect];
CGContextRestoreGState(context);
CGContextRestoreGState(context);
}
@end
#ifdef RN_FABRIC_ENABLED
Class<RCTComponentViewProtocol> RNSVGMarkerCls(void)
{

View File

@@ -6,16 +6,15 @@
* LICENSE file in the root directory of this source tree.
*/
#import "RNSVGMask.h"
#import "RNSVGPainter.h"
#import "RNSVGBrushType.h"
#import "RNSVGNode.h"
#import "RNSVGPainter.h"
#ifdef RN_FABRIC_ENABLED
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import "RCTFabricComponentsPlugins.h"
#import "RCTConversions.h"
#import <react/renderer/components/view/conversions.h>
#import "RCTConversions.h"
#import "RCTFabricComponentsPlugins.h"
#import "RNSVGFabricConversions.h"
#endif // RN_FABRIC_ENABLED
@@ -42,119 +41,125 @@ using namespace facebook::react;
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &newProps = *std::static_pointer_cast<const RNSVGMaskProps>(props);
self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)];
self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)];
if (RCTNSStringFromStringNilIfEmpty(newProps.maskheight)) {
self.maskheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.maskheight)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.maskwidth)) {
self.maskwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.maskwidth)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
self.maskheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
self.maskwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
}
self.maskUnits = newProps.maskUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
self.maskContentUnits = newProps.maskUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
if (newProps.maskTransform.size() == 6) {
self.maskTransform = CGAffineTransformMake(newProps.maskTransform.at(0), newProps.maskTransform.at(1), newProps.maskTransform.at(2), newProps.maskTransform.at(3), newProps.maskTransform.at(4), newProps.maskTransform.at(5));
}
setCommonGroupProps(newProps, self);
const auto &newProps = *std::static_pointer_cast<const RNSVGMaskProps>(props);
self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)];
self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)];
if (RCTNSStringFromStringNilIfEmpty(newProps.maskheight)) {
self.maskheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.maskheight)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.maskwidth)) {
self.maskwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.maskwidth)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
self.maskheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
self.maskwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
}
self.maskUnits = newProps.maskUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
self.maskContentUnits = newProps.maskUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
if (newProps.maskTransform.size() == 6) {
self.maskTransform = CGAffineTransformMake(
newProps.maskTransform.at(0),
newProps.maskTransform.at(1),
newProps.maskTransform.at(2),
newProps.maskTransform.at(3),
newProps.maskTransform.at(4),
newProps.maskTransform.at(5));
}
setCommonGroupProps(newProps, self);
}
- (void)prepareForRecycle
{
[super prepareForRecycle];
_x = nil;
_y = nil;
_maskheight = nil;
_maskwidth = nil;
_maskUnits = kRNSVGUnitsObjectBoundingBox;
_maskContentUnits = kRNSVGUnitsObjectBoundingBox;
_maskTransform = CGAffineTransformIdentity;
[super prepareForRecycle];
_x = nil;
_y = nil;
_maskheight = nil;
_maskwidth = nil;
_maskUnits = kRNSVGUnitsObjectBoundingBox;
_maskContentUnits = kRNSVGUnitsObjectBoundingBox;
_maskTransform = CGAffineTransformIdentity;
}
#endif // RN_FABRIC_ENABLED
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
return nil;
return nil;
}
- (void)parseReference
{
self.dirty = false;
[self.svgView defineMask:self maskName:self.name];
self.dirty = false;
[self.svgView defineMask:self maskName:self.name];
}
- (void)setX:(RNSVGLength *)x
{
if ([x isEqualTo:_x]) {
return;
}
_x = x;
[self invalidate];
if ([x isEqualTo:_x]) {
return;
}
_x = x;
[self invalidate];
}
- (void)setY:(RNSVGLength *)y
{
if ([y isEqualTo:_y]) {
return;
}
_y = y;
[self invalidate];
if ([y isEqualTo:_y]) {
return;
}
_y = y;
[self invalidate];
}
- (void)setMaskwidth:(RNSVGLength *)maskwidth
{
if ([maskwidth isEqualTo:_maskwidth]) {
return;
}
_maskwidth = maskwidth;
[self invalidate];
if ([maskwidth isEqualTo:_maskwidth]) {
return;
}
_maskwidth = maskwidth;
[self invalidate];
}
- (void)setMaskheight:(RNSVGLength *)maskheight
{
if ([maskheight isEqualTo:_maskheight]) {
return;
}
_maskheight = maskheight;
[self invalidate];
if ([maskheight isEqualTo:_maskheight]) {
return;
}
_maskheight = maskheight;
[self invalidate];
}
- (void)setMaskUnits:(RNSVGUnits)maskUnits
{
if (maskUnits == _maskUnits) {
return;
}
_maskUnits = maskUnits;
[self invalidate];
if (maskUnits == _maskUnits) {
return;
}
_maskUnits = maskUnits;
[self invalidate];
}
- (void)setMaskContentUnits:(RNSVGUnits)maskContentUnits
{
if (maskContentUnits == _maskContentUnits) {
return;
}
_maskContentUnits = maskContentUnits;
[self invalidate];
if (maskContentUnits == _maskContentUnits) {
return;
}
_maskContentUnits = maskContentUnits;
[self invalidate];
}
- (void)setMaskTransform:(CGAffineTransform)maskTransform
{
_maskTransform = maskTransform;
[self invalidate];
_maskTransform = maskTransform;
[self invalidate];
}
@end

View File

@@ -10,15 +10,14 @@
#ifdef RN_FABRIC_ENABLED
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import "RCTFabricComponentsPlugins.h"
#import "RCTConversions.h"
#import <react/renderer/components/view/conversions.h>
#import "RCTConversions.h"
#import "RCTFabricComponentsPlugins.h"
#import "RNSVGFabricConversions.h"
#endif // RN_FABRIC_ENABLED
@implementation RNSVGPath
{
CGPathRef _path;
@implementation RNSVGPath {
CGPathRef _path;
}
#ifdef RN_FABRIC_ENABLED
@@ -42,43 +41,43 @@ using namespace facebook::react;
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &newProps = *std::static_pointer_cast<const RNSVGPathProps>(props);
self.d = [[RNSVGPathParser alloc] initWithPathString: RCTNSStringFromString(newProps.d)];
setCommonRenderableProps(newProps, self);
const auto &newProps = *std::static_pointer_cast<const RNSVGPathProps>(props);
self.d = [[RNSVGPathParser alloc] initWithPathString:RCTNSStringFromString(newProps.d)];
setCommonRenderableProps(newProps, self);
}
- (void)prepareForRecycle
{
[super prepareForRecycle];
if (_path) {
CGPathRelease(_path);
}
_path = nil;
_d = nil;
[super prepareForRecycle];
if (_path) {
CGPathRelease(_path);
}
_path = nil;
_d = nil;
}
#endif // RN_FABRIC_ENABLED
- (void)setD:(RNSVGPathParser *)d
{
if (d == _d) {
return;
}
if (d == _d) {
return;
}
[self invalidate];
_d = d;
CGPathRelease(_path);
_path = CGPathRetain([d getPath]);
[self invalidate];
_d = d;
CGPathRelease(_path);
_path = CGPathRetain([d getPath]);
}
- (CGPathRef)getPath:(CGContextRef)context
{
return _path;
return _path;
}
- (void)dealloc
{
CGPathRelease(_path);
CGPathRelease(_path);
}
@end

View File

@@ -6,15 +6,15 @@
* LICENSE file in the root directory of this source tree.
*/
#import "RNSVGPattern.h"
#import "RNSVGPainter.h"
#import "RNSVGBrushType.h"
#import "RNSVGNode.h"
#import "RNSVGPainter.h"
#ifdef RN_FABRIC_ENABLED
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import "RCTFabricComponentsPlugins.h"
#import "RCTConversions.h"
#import <react/renderer/components/view/conversions.h>
#import "RCTConversions.h"
#import "RCTFabricComponentsPlugins.h"
#import "RNSVGFabricConversions.h"
#endif // RN_FABRIC_ENABLED
@@ -41,211 +41,217 @@ using namespace facebook::react;
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &newProps = *std::static_pointer_cast<const RNSVGPatternProps>(props);
const auto &newProps = *std::static_pointer_cast<const RNSVGPatternProps>(props);
self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)];
self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)];
if (RCTNSStringFromStringNilIfEmpty(newProps.patternheight)) {
self.patternheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.patternheight)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.patternwidth)) {
self.patternwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.patternwidth)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
self.patternheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
self.patternwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
}
self.patternUnits = newProps.patternUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
self.patternContentUnits = newProps.patternUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
if (newProps.patternTransform.size() == 6) {
self.patternTransform = CGAffineTransformMake(newProps.patternTransform.at(0), newProps.patternTransform.at(1), newProps.patternTransform.at(2), newProps.patternTransform.at(3), newProps.patternTransform.at(4), newProps.patternTransform.at(5));
}
self.minX = newProps.minX;
self.minY = newProps.minY;
self.vbWidth = newProps.vbWidth;
self.vbHeight = newProps.vbHeight;
self.align = RCTNSStringFromStringNilIfEmpty(newProps.align);
self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice);
setCommonGroupProps(newProps, self);
self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)];
self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)];
if (RCTNSStringFromStringNilIfEmpty(newProps.patternheight)) {
self.patternheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.patternheight)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.patternwidth)) {
self.patternwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.patternwidth)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
self.patternheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
self.patternwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
}
self.patternUnits = newProps.patternUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
self.patternContentUnits = newProps.patternUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
if (newProps.patternTransform.size() == 6) {
self.patternTransform = CGAffineTransformMake(
newProps.patternTransform.at(0),
newProps.patternTransform.at(1),
newProps.patternTransform.at(2),
newProps.patternTransform.at(3),
newProps.patternTransform.at(4),
newProps.patternTransform.at(5));
}
self.minX = newProps.minX;
self.minY = newProps.minY;
self.vbWidth = newProps.vbWidth;
self.vbHeight = newProps.vbHeight;
self.align = RCTNSStringFromStringNilIfEmpty(newProps.align);
self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice);
setCommonGroupProps(newProps, self);
}
- (void)prepareForRecycle
{
[super prepareForRecycle];
_x = nil;
_y = nil;
_patternheight = nil;
_patternwidth = nil;
_patternUnits = kRNSVGUnitsObjectBoundingBox;
_patternContentUnits = kRNSVGUnitsObjectBoundingBox;
_patternTransform = CGAffineTransformIdentity;
_minX = 0;
_minY = 0;
_vbWidth = 0;
_vbHeight = 0;
_align = nil;
_meetOrSlice = kRNSVGVBMOSMeet;
[super prepareForRecycle];
_x = nil;
_y = nil;
_patternheight = nil;
_patternwidth = nil;
_patternUnits = kRNSVGUnitsObjectBoundingBox;
_patternContentUnits = kRNSVGUnitsObjectBoundingBox;
_patternTransform = CGAffineTransformIdentity;
_minX = 0;
_minY = 0;
_vbWidth = 0;
_vbHeight = 0;
_align = nil;
_meetOrSlice = kRNSVGVBMOSMeet;
}
#endif // RN_FABRIC_ENABLED
- (instancetype)init
{
if (self = [super init]) {
_patternTransform = CGAffineTransformIdentity;
}
return self;
if (self = [super init]) {
_patternTransform = CGAffineTransformIdentity;
}
return self;
}
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
return nil;
return nil;
}
- (void)parseReference
{
self.dirty = false;
NSArray<RNSVGLength *> *points = @[self.x, self.y, self.patternwidth, self.patternheight];
RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points];
[painter setUnits:self.patternUnits];
[painter setContentUnits:self.patternContentUnits];
[painter setTransform:self.patternTransform];
[painter setPattern:self];
self.dirty = false;
NSArray<RNSVGLength *> *points = @[ self.x, self.y, self.patternwidth, self.patternheight ];
RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points];
[painter setUnits:self.patternUnits];
[painter setContentUnits:self.patternContentUnits];
[painter setTransform:self.patternTransform];
[painter setPattern:self];
if (self.patternUnits == kRNSVGUnitsUserSpaceOnUse || self.patternContentUnits == kRNSVGUnitsUserSpaceOnUse) {
[painter setUserSpaceBoundingBox:[self.svgView getContextBounds]];
}
if (self.patternUnits == kRNSVGUnitsUserSpaceOnUse || self.patternContentUnits == kRNSVGUnitsUserSpaceOnUse) {
[painter setUserSpaceBoundingBox:[self.svgView getContextBounds]];
}
[self.svgView definePainter:painter painterName:self.name];
[self.svgView definePainter:painter painterName:self.name];
}
- (void)setX:(RNSVGLength *)x
{
if ([x isEqualTo:_x]) {
return;
}
if ([x isEqualTo:_x]) {
return;
}
_x = x;
[self invalidate];
_x = x;
[self invalidate];
}
- (void)setY:(RNSVGLength *)y
{
if ([y isEqualTo:_y]) {
return;
}
if ([y isEqualTo:_y]) {
return;
}
_y = y;
[self invalidate];
_y = y;
[self invalidate];
}
- (void)setPatternwidth:(RNSVGLength *)patternwidth
{
if ([patternwidth isEqualTo:_patternwidth]) {
return;
}
if ([patternwidth isEqualTo:_patternwidth]) {
return;
}
_patternwidth = patternwidth;
[self invalidate];
_patternwidth = patternwidth;
[self invalidate];
}
- (void)setPatternheight:(RNSVGLength *)patternheight
{
if ([patternheight isEqualTo:_patternheight]) {
return;
}
if ([patternheight isEqualTo:_patternheight]) {
return;
}
_patternheight = patternheight;
[self invalidate];
_patternheight = patternheight;
[self invalidate];
}
- (void)setPatternUnits:(RNSVGUnits)patternUnits
{
if (patternUnits == _patternUnits) {
return;
}
if (patternUnits == _patternUnits) {
return;
}
_patternUnits = patternUnits;
[self invalidate];
_patternUnits = patternUnits;
[self invalidate];
}
- (void)setPatternContentUnits:(RNSVGUnits)patternContentUnits
{
if (patternContentUnits == _patternContentUnits) {
return;
}
if (patternContentUnits == _patternContentUnits) {
return;
}
_patternContentUnits = patternContentUnits;
[self invalidate];
_patternContentUnits = patternContentUnits;
[self invalidate];
}
- (void)setPatternTransform:(CGAffineTransform)patternTransform
{
_patternTransform = patternTransform;
[self invalidate];
_patternTransform = patternTransform;
[self invalidate];
}
- (void)setMinX:(CGFloat)minX
{
if (minX == _minX) {
return;
}
if (minX == _minX) {
return;
}
[self invalidate];
_minX = minX;
[self invalidate];
_minX = minX;
}
- (void)setMinY:(CGFloat)minY
{
if (minY == _minY) {
return;
}
if (minY == _minY) {
return;
}
[self invalidate];
_minY = minY;
[self invalidate];
_minY = minY;
}
- (void)setVbWidth:(CGFloat)vbWidth
{
if (vbWidth == _vbWidth) {
return;
}
if (vbWidth == _vbWidth) {
return;
}
[self invalidate];
_vbWidth = vbWidth;
[self invalidate];
_vbWidth = vbWidth;
}
- (void)setVbHeight:(CGFloat)vbHeight
{
if (_vbHeight == vbHeight) {
return;
}
if (_vbHeight == vbHeight) {
return;
}
[self invalidate];
_vbHeight = vbHeight;
[self invalidate];
_vbHeight = vbHeight;
}
- (void)setAlign:(NSString *)align
{
if ([align isEqualToString:_align]) {
return;
}
if ([align isEqualToString:_align]) {
return;
}
[self invalidate];
_align = align;
[self invalidate];
_align = align;
}
- (void)setMeetOrSlice:(RNSVGVBMOS)meetOrSlice
{
if (meetOrSlice == _meetOrSlice) {
return;
}
if (meetOrSlice == _meetOrSlice) {
return;
}
[self invalidate];
_meetOrSlice = meetOrSlice;
[self invalidate];
_meetOrSlice = meetOrSlice;
}
@end

View File

@@ -6,8 +6,8 @@
* LICENSE file in the root directory of this source tree.
*/
#import "RNSVGNode.h"
#import "RNSVGLength.h"
#import "RNSVGNode.h"
@interface RNSVGRadialGradient : RNSVGNode

View File

@@ -9,9 +9,9 @@
#ifdef RN_FABRIC_ENABLED
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import "RCTFabricComponentsPlugins.h"
#import "RCTConversions.h"
#import <react/renderer/components/view/conversions.h>
#import "RCTConversions.h"
#import "RCTFabricComponentsPlugins.h"
#import "RNSVGFabricConversions.h"
#endif // RN_FABRIC_ENABLED
@@ -38,156 +38,162 @@ using namespace facebook::react;
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &newProps = *std::static_pointer_cast<const RNSVGRadialGradientProps>(props);
self.fx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.fx)];
self.fy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.fy)];
self.cx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cx)];
self.cy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cy)];
self.rx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rx)];
self.ry = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.ry)];
if (newProps.gradient.size() > 0) {
NSMutableArray<NSNumber *> *gradientArray = [NSMutableArray new];
for (auto number : newProps.gradient) {
[gradientArray addObject:[NSNumber numberWithDouble:number]];
}
self.gradient = gradientArray;
}
self.gradientUnits = newProps.gradientUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
if (newProps.gradientTransform.size() == 6) {
self.gradientTransform = CGAffineTransformMake(newProps.gradientTransform.at(0), newProps.gradientTransform.at(1), newProps.gradientTransform.at(2), newProps.gradientTransform.at(3), newProps.gradientTransform.at(4), newProps.gradientTransform.at(5));
}
const auto &newProps = *std::static_pointer_cast<const RNSVGRadialGradientProps>(props);
setCommonNodeProps(newProps, self);
self.fx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.fx)];
self.fy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.fy)];
self.cx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cx)];
self.cy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cy)];
self.rx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rx)];
self.ry = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.ry)];
if (newProps.gradient.size() > 0) {
NSMutableArray<NSNumber *> *gradientArray = [NSMutableArray new];
for (auto number : newProps.gradient) {
[gradientArray addObject:[NSNumber numberWithDouble:number]];
}
self.gradient = gradientArray;
}
self.gradientUnits = newProps.gradientUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
if (newProps.gradientTransform.size() == 6) {
self.gradientTransform = CGAffineTransformMake(
newProps.gradientTransform.at(0),
newProps.gradientTransform.at(1),
newProps.gradientTransform.at(2),
newProps.gradientTransform.at(3),
newProps.gradientTransform.at(4),
newProps.gradientTransform.at(5));
}
setCommonNodeProps(newProps, self);
}
- (void)prepareForRecycle
{
[super prepareForRecycle];
_fx = nil;
_fy = nil;
_cx = nil;
_cy = nil;
_rx = nil;
_ry = nil;
_gradient = nil;
_gradientUnits = kRNSVGUnitsObjectBoundingBox;
_gradientTransform = CGAffineTransformIdentity;
[super prepareForRecycle];
_fx = nil;
_fy = nil;
_cx = nil;
_cy = nil;
_rx = nil;
_ry = nil;
_gradient = nil;
_gradientUnits = kRNSVGUnitsObjectBoundingBox;
_gradientTransform = CGAffineTransformIdentity;
}
#endif // RN_FABRIC_ENABLED
- (instancetype)init
{
if (self = [super init]) {
_gradientTransform = CGAffineTransformIdentity;
}
return self;
if (self = [super init]) {
_gradientTransform = CGAffineTransformIdentity;
}
return self;
}
- (void)setFx:(RNSVGLength *)fx
{
if ([fx isEqualTo:_fx]) {
return;
}
if ([fx isEqualTo:_fx]) {
return;
}
_fx = fx;
[self invalidate];
_fx = fx;
[self invalidate];
}
- (void)setFy:(RNSVGLength *)fy
{
if ([fy isEqualTo:_fy]) {
return;
}
if ([fy isEqualTo:_fy]) {
return;
}
_fy = fy;
[self invalidate];
_fy = fy;
[self invalidate];
}
- (void)setRx:(RNSVGLength *)rx
{
if ([rx isEqualTo:_rx]) {
return;
}
if ([rx isEqualTo:_rx]) {
return;
}
_rx = rx;
[self invalidate];
_rx = rx;
[self invalidate];
}
- (void)setRy:(RNSVGLength *)ry
{
if ([ry isEqualTo:_ry]) {
return;
}
if ([ry isEqualTo:_ry]) {
return;
}
_ry = ry;
[self invalidate];
_ry = ry;
[self invalidate];
}
- (void)setCx:(RNSVGLength *)cx
{
if ([cx isEqualTo:_cx]) {
return;
}
if ([cx isEqualTo:_cx]) {
return;
}
_cx = cx;
[self invalidate];
_cx = cx;
[self invalidate];
}
- (void)setCy:(RNSVGLength *)cy
{
if ([cy isEqualTo:_cy]) {
return;
}
if ([cy isEqualTo:_cy]) {
return;
}
_cy = cy;
[self invalidate];
_cy = cy;
[self invalidate];
}
- (void)setGradient:(NSArray<NSNumber *> *)gradient
{
if (gradient == _gradient) {
return;
}
if (gradient == _gradient) {
return;
}
_gradient = gradient;
[self invalidate];
_gradient = gradient;
[self invalidate];
}
- (void)setGradientUnits:(RNSVGUnits)gradientUnits
{
if (gradientUnits == _gradientUnits) {
return;
}
if (gradientUnits == _gradientUnits) {
return;
}
_gradientUnits = gradientUnits;
[self invalidate];
_gradientUnits = gradientUnits;
[self invalidate];
}
- (void)setGradientTransform:(CGAffineTransform)gradientTransform
{
_gradientTransform = gradientTransform;
[self invalidate];
_gradientTransform = gradientTransform;
[self invalidate];
}
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
return nil;
return nil;
}
- (void)parseReference
{
self.dirty = false;
NSArray<RNSVGLength *> *points = @[self.fx, self.fy, self.rx, self.ry, self.cx, self.cy];
RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points];
[painter setUnits:self.gradientUnits];
[painter setTransform:self.gradientTransform];
[painter setRadialGradientColors:self.gradient];
self.dirty = false;
NSArray<RNSVGLength *> *points = @[ self.fx, self.fy, self.rx, self.ry, self.cx, self.cy ];
RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points];
[painter setUnits:self.gradientUnits];
[painter setTransform:self.gradientTransform];
[painter setRadialGradientColors:self.gradient];
if (self.gradientUnits == kRNSVGUnitsUserSpaceOnUse) {
[painter setUserSpaceBoundingBox:[self.svgView getContextBounds]];
}
if (self.gradientUnits == kRNSVGUnitsUserSpaceOnUse) {
[painter setUserSpaceBoundingBox:[self.svgView getContextBounds]];
}
[self.svgView definePainter:painter painterName:self.name];
[self.svgView definePainter:painter painterName:self.name];
}
@end

View File

@@ -8,8 +8,8 @@
#import "RNSVGUIKit.h"
#import "RNSVGPainter.h"
#import "RNSVGContainer.h"
#import "RNSVGPainter.h"
#import "RNSVGVBMOS.h"
#ifdef RN_FABRIC_ENABLED
@@ -20,9 +20,9 @@
@interface RNSVGSvgView :
#ifdef RN_FABRIC_ENABLED
RCTViewComponentView <RNSVGContainer>
RCTViewComponentView <RNSVGContainer>
#else
RNSVGView <RNSVGContainer>
RNSVGView <RNSVGContainer>
#endif // RN_FABRIC_ENABLED
@property (nonatomic, strong) RNSVGLength *bbWidth;

View File

@@ -7,27 +7,26 @@
*/
#import "RNSVGSvgView.h"
#import "RNSVGViewBox.h"
#import "RNSVGNode.h"
#import <React/RCTLog.h>
#import "RNSVGNode.h"
#import "RNSVGViewBox.h"
#ifdef RN_FABRIC_ENABLED
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import "RCTFabricComponentsPlugins.h"
#import "RCTConversions.h"
#import <react/renderer/components/view/conversions.h>
#import "RCTConversions.h"
#import "RCTFabricComponentsPlugins.h"
#import "RNSVGFabricConversions.h"
#endif // RN_FABRIC_ENABLED
@implementation RNSVGSvgView
{
NSMutableDictionary<NSString *, RNSVGNode *> *_clipPaths;
NSMutableDictionary<NSString *, RNSVGNode *> *_templates;
NSMutableDictionary<NSString *, RNSVGPainter *> *_painters;
NSMutableDictionary<NSString *, RNSVGNode *> *_markers;
NSMutableDictionary<NSString *, RNSVGNode *> *_masks;
CGAffineTransform _invviewBoxTransform;
bool rendered;
@implementation RNSVGSvgView {
NSMutableDictionary<NSString *, RNSVGNode *> *_clipPaths;
NSMutableDictionary<NSString *, RNSVGNode *> *_templates;
NSMutableDictionary<NSString *, RNSVGPainter *> *_painters;
NSMutableDictionary<NSString *, RNSVGNode *> *_markers;
NSMutableDictionary<NSString *, RNSVGNode *> *_masks;
CGAffineTransform _invviewBoxTransform;
bool rendered;
}
#ifdef RN_FABRIC_ENABLED
@@ -36,21 +35,21 @@ using namespace facebook::react;
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
if (self = [super initWithFrame:frame]) {
#if !TARGET_OS_OSX // Not available on macOS
// This is necessary to ensure that [self setNeedsDisplay] actually triggers
// a redraw when our parent transitions between hidden and visible.
self.contentMode = UIViewContentModeRedraw;
// This is necessary to ensure that [self setNeedsDisplay] actually triggers
// a redraw when our parent transitions between hidden and visible.
self.contentMode = UIViewContentModeRedraw;
#endif // TARGET_OS_OSX
rendered = false;
rendered = false;
#ifdef RN_FABRIC_ENABLED
static const auto defaultProps = std::make_shared<const RNSVGSvgViewProps>();
_props = defaultProps;
// TODO: think if we can do it better
self.opaque = NO;
static const auto defaultProps = std::make_shared<const RNSVGSvgViewProps>();
_props = defaultProps;
// TODO: think if we can do it better
self.opaque = NO;
#endif // RN_FABRIC_ENABLED
}
return self;
}
return self;
}
#ifdef RN_FABRIC_ENABLED
@@ -63,379 +62,374 @@ using namespace facebook::react;
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &newProps = *std::static_pointer_cast<const RNSVGSvgViewProps>(props);
self.minX = newProps.minX;
self.minY = newProps.minY;
self.vbWidth = newProps.vbWidth;
self.vbHeight = newProps.vbHeight;
self.bbWidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.bbWidth)];
self.bbHeight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.bbHeight)];
self.align = RCTNSStringFromStringNilIfEmpty(newProps.align);
self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice);
if (RCTUIColorFromSharedColor(newProps.tintColor)) {
self.tintColor = RCTUIColorFromSharedColor(newProps.tintColor);
}
if (RCTUIColorFromSharedColor(newProps.color)) {
self.tintColor = RCTUIColorFromSharedColor(newProps.color);
}
}
const auto &newProps = *std::static_pointer_cast<const RNSVGSvgViewProps>(props);
self.minX = newProps.minX;
self.minY = newProps.minY;
self.vbWidth = newProps.vbWidth;
self.vbHeight = newProps.vbHeight;
self.bbWidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.bbWidth)];
self.bbHeight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.bbHeight)];
self.align = RCTNSStringFromStringNilIfEmpty(newProps.align);
self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice);
if (RCTUIColorFromSharedColor(newProps.tintColor)) {
self.tintColor = RCTUIColorFromSharedColor(newProps.tintColor);
}
if (RCTUIColorFromSharedColor(newProps.color)) {
self.tintColor = RCTUIColorFromSharedColor(newProps.color);
}
}
- (void)prepareForRecycle
{
[super prepareForRecycle];
_minX = 0;
_minY = 0;
_vbWidth = 0;
_vbHeight = 0;
_bbWidth = 0;
_bbHeight = 0;
_align = nil;
_meetOrSlice = kRNSVGVBMOSMeet;
_responsible = NO;
_active = NO;
_boundingBox = CGRectZero;
_initialCTM = CGAffineTransformIdentity;
_invInitialCTM = CGAffineTransformIdentity;
_viewBoxTransform = CGAffineTransformIdentity;
_clipPaths = nil;
_templates = nil;
_painters = nil;
_markers = nil;
_masks = nil;
_invviewBoxTransform = CGAffineTransformIdentity;
rendered = NO;
[super prepareForRecycle];
_minX = 0;
_minY = 0;
_vbWidth = 0;
_vbHeight = 0;
_bbWidth = 0;
_bbHeight = 0;
_align = nil;
_meetOrSlice = kRNSVGVBMOSMeet;
_responsible = NO;
_active = NO;
_boundingBox = CGRectZero;
_initialCTM = CGAffineTransformIdentity;
_invInitialCTM = CGAffineTransformIdentity;
_viewBoxTransform = CGAffineTransformIdentity;
_clipPaths = nil;
_templates = nil;
_painters = nil;
_markers = nil;
_masks = nil;
_invviewBoxTransform = CGAffineTransformIdentity;
rendered = NO;
}
#endif // RN_FABRIC_ENABLED
- (void)insertReactSubview:(RNSVGView *)subview atIndex:(NSInteger)atIndex
{
[super insertReactSubview:subview atIndex:atIndex];
[self insertSubview:subview atIndex:atIndex];
[self invalidate];
[super insertReactSubview:subview atIndex:atIndex];
[self insertSubview:subview atIndex:atIndex];
[self invalidate];
}
- (void)removeReactSubview:(RNSVGView *)subview
{
[super removeReactSubview:subview];
[self invalidate];
[super removeReactSubview:subview];
[self invalidate];
}
- (void)didUpdateReactSubviews
{
// Do nothing, as subviews are inserted by insertReactSubview:
// Do nothing, as subviews are inserted by insertReactSubview:
}
- (void)clearChildCache
{
if (!rendered) {
return;
}
rendered = false;
for (__kindof RNSVGNode *node in self.subviews) {
if ([node isKindOfClass:[RNSVGNode class]]) {
[node clearChildCache];
}
if (!rendered) {
return;
}
rendered = false;
for (__kindof RNSVGNode *node in self.subviews) {
if ([node isKindOfClass:[RNSVGNode class]]) {
[node clearChildCache];
}
}
}
- (void)invalidate
{
RNSVGPlatformView* parent = self.superview;
if ([parent isKindOfClass:[RNSVGNode class]]) {
if (!rendered) {
return;
}
RNSVGNode* svgNode = (RNSVGNode*)parent;
[svgNode invalidate];
rendered = false;
return;
RNSVGPlatformView *parent = self.superview;
if ([parent isKindOfClass:[RNSVGNode class]]) {
if (!rendered) {
return;
}
[self setNeedsDisplay];
RNSVGNode *svgNode = (RNSVGNode *)parent;
[svgNode invalidate];
rendered = false;
return;
}
[self setNeedsDisplay];
}
- (void)tintColorDidChange
{
[self invalidate];
[self clearChildCache];
[self invalidate];
[self clearChildCache];
}
- (void)setMinX:(CGFloat)minX
{
if (minX == _minX) {
return;
}
if (minX == _minX) {
return;
}
[self invalidate];
[self clearChildCache];
_minX = minX;
[self invalidate];
[self clearChildCache];
_minX = minX;
}
- (void)setMinY:(CGFloat)minY
{
if (minY == _minY) {
return;
}
if (minY == _minY) {
return;
}
[self invalidate];
[self clearChildCache];
_minY = minY;
[self invalidate];
[self clearChildCache];
_minY = minY;
}
- (void)setVbWidth:(CGFloat)vbWidth
{
if (vbWidth == _vbWidth) {
return;
}
if (vbWidth == _vbWidth) {
return;
}
[self invalidate];
[self clearChildCache];
_vbWidth = vbWidth;
[self invalidate];
[self clearChildCache];
_vbWidth = vbWidth;
}
- (void)setVbHeight:(CGFloat)vbHeight
{
if (_vbHeight == vbHeight) {
return;
}
if (_vbHeight == vbHeight) {
return;
}
[self invalidate];
[self clearChildCache];
_vbHeight = vbHeight;
[self invalidate];
[self clearChildCache];
_vbHeight = vbHeight;
}
- (void)setBbWidth:(RNSVGLength *)bbWidth
{
if ([bbWidth isEqualTo:_bbWidth]) {
return;
}
if ([bbWidth isEqualTo:_bbWidth]) {
return;
}
[self invalidate];
[self clearChildCache];
_bbWidth = bbWidth;
[self invalidate];
[self clearChildCache];
_bbWidth = bbWidth;
}
- (void)setBbHeight:(RNSVGLength *)bbHeight
{
if ([bbHeight isEqualTo:_bbHeight]) {
return;
}
if ([bbHeight isEqualTo:_bbHeight]) {
return;
}
[self invalidate];
[self clearChildCache];
_bbHeight = bbHeight;
[self invalidate];
[self clearChildCache];
_bbHeight = bbHeight;
}
- (void)setAlign:(NSString *)align
{
if ([align isEqualToString:_align]) {
return;
}
if ([align isEqualToString:_align]) {
return;
}
[self invalidate];
[self clearChildCache];
_align = align;
[self invalidate];
[self clearChildCache];
_align = align;
}
- (void)setMeetOrSlice:(RNSVGVBMOS)meetOrSlice
{
if (meetOrSlice == _meetOrSlice) {
return;
}
if (meetOrSlice == _meetOrSlice) {
return;
}
[self invalidate];
[self clearChildCache];
_meetOrSlice = meetOrSlice;
[self invalidate];
[self clearChildCache];
_meetOrSlice = meetOrSlice;
}
- (void)drawToContext:(CGContextRef)context withRect:(CGRect)rect {
rendered = true;
self.initialCTM = CGContextGetCTM(context);
self.invInitialCTM = CGAffineTransformInvert(self.initialCTM);
if (self.align) {
CGRect tRect = CGRectMake(self.minX, self.minY, self.vbWidth, self.vbHeight);
_viewBoxTransform = [RNSVGViewBox getTransform:tRect
eRect:rect
align:self.align
meetOrSlice:self.meetOrSlice];
_invviewBoxTransform = CGAffineTransformInvert(_viewBoxTransform);
CGContextConcatCTM(context, _viewBoxTransform);
} else {
_viewBoxTransform = CGAffineTransformIdentity;
_invviewBoxTransform = CGAffineTransformIdentity;
}
- (void)drawToContext:(CGContextRef)context withRect:(CGRect)rect
{
rendered = true;
self.initialCTM = CGContextGetCTM(context);
self.invInitialCTM = CGAffineTransformInvert(self.initialCTM);
if (self.align) {
CGRect tRect = CGRectMake(self.minX, self.minY, self.vbWidth, self.vbHeight);
_viewBoxTransform = [RNSVGViewBox getTransform:tRect eRect:rect align:self.align meetOrSlice:self.meetOrSlice];
_invviewBoxTransform = CGAffineTransformInvert(_viewBoxTransform);
CGContextConcatCTM(context, _viewBoxTransform);
} else {
_viewBoxTransform = CGAffineTransformIdentity;
_invviewBoxTransform = CGAffineTransformIdentity;
}
for (RNSVGView *node in self.subviews) {
if ([node isKindOfClass:[RNSVGNode class]]) {
RNSVGNode *svg = (RNSVGNode *)node;
[svg renderTo:context
rect:rect];
} else {
[node drawRect:rect];
}
for (RNSVGView *node in self.subviews) {
if ([node isKindOfClass:[RNSVGNode class]]) {
RNSVGNode *svg = (RNSVGNode *)node;
[svg renderTo:context rect:rect];
} else {
[node drawRect:rect];
}
}
}
- (void)drawRect:(CGRect)rect
{
RNSVGPlatformView* parent = self.superview;
if ([parent isKindOfClass:[RNSVGNode class]]) {
return;
RNSVGPlatformView *parent = self.superview;
if ([parent isKindOfClass:[RNSVGNode class]]) {
return;
}
rendered = true;
_clipPaths = nil;
_templates = nil;
_painters = nil;
_boundingBox = rect;
CGContextRef context = UIGraphicsGetCurrentContext();
for (RNSVGPlatformView *node in self.subviews) {
if ([node isKindOfClass:[RNSVGNode class]]) {
RNSVGNode *svg = (RNSVGNode *)node;
if (svg.responsible && !self.responsible) {
self.responsible = YES;
}
[svg parseReference];
}
rendered = true;
_clipPaths = nil;
_templates = nil;
_painters = nil;
_boundingBox = rect;
CGContextRef context = UIGraphicsGetCurrentContext();
}
for (RNSVGPlatformView *node in self.subviews) {
if ([node isKindOfClass:[RNSVGNode class]]) {
RNSVGNode *svg = (RNSVGNode *)node;
if (svg.responsible && !self.responsible) {
self.responsible = YES;
}
[svg parseReference];
}
}
[self drawToContext:context withRect:rect];
[self drawToContext:context withRect:rect];
}
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
CGPoint transformed = point;
if (self.align) {
transformed = CGPointApplyAffineTransform(transformed, _invviewBoxTransform);
CGPoint transformed = point;
if (self.align) {
transformed = CGPointApplyAffineTransform(transformed, _invviewBoxTransform);
}
for (RNSVGNode *node in [self.subviews reverseObjectEnumerator]) {
if (![node isKindOfClass:[RNSVGNode class]]) {
continue;
}
for (RNSVGNode *node in [self.subviews reverseObjectEnumerator]) {
if (![node isKindOfClass:[RNSVGNode class]]) {
continue;
}
if (event) {
node.active = NO;
}
RNSVGPlatformView *hitChild = [node hitTest:transformed withEvent:event];
if (hitChild) {
node.active = YES;
return (node.responsible || (node != hitChild)) ? hitChild : self;
}
if (event) {
node.active = NO;
}
return nil;
RNSVGPlatformView *hitChild = [node hitTest:transformed withEvent:event];
if (hitChild) {
node.active = YES;
return (node.responsible || (node != hitChild)) ? hitChild : self;
}
}
return nil;
}
- (NSString *)getDataURL
{
UIGraphicsBeginImageContextWithOptions(_boundingBox.size, NO, 0);
[self clearChildCache];
[self drawRect:_boundingBox];
[self clearChildCache];
[self invalidate];
NSData *imageData = UIImagePNGRepresentation(UIGraphicsGetImageFromCurrentImageContext());
NSString *base64 = [imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
UIGraphicsEndImageContext();
return base64;
UIGraphicsBeginImageContextWithOptions(_boundingBox.size, NO, 0);
[self clearChildCache];
[self drawRect:_boundingBox];
[self clearChildCache];
[self invalidate];
NSData *imageData = UIImagePNGRepresentation(UIGraphicsGetImageFromCurrentImageContext());
NSString *base64 = [imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
UIGraphicsEndImageContext();
return base64;
}
- (NSString *)getDataURLwithBounds:(CGRect)bounds
{
UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 1);
[self clearChildCache];
[self drawRect:bounds];
[self clearChildCache];
[self invalidate];
NSData *imageData = UIImagePNGRepresentation(UIGraphicsGetImageFromCurrentImageContext());
NSString *base64 = [imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
UIGraphicsEndImageContext();
return base64;
UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 1);
[self clearChildCache];
[self drawRect:bounds];
[self clearChildCache];
[self invalidate];
NSData *imageData = UIImagePNGRepresentation(UIGraphicsGetImageFromCurrentImageContext());
NSString *base64 = [imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
UIGraphicsEndImageContext();
return base64;
}
- (void)reactSetInheritedBackgroundColor:(RNSVGColor *)inheritedBackgroundColor
{
self.backgroundColor = inheritedBackgroundColor;
self.backgroundColor = inheritedBackgroundColor;
}
- (void)defineClipPath:(__kindof RNSVGNode *)clipPath clipPathName:(NSString *)clipPathName
{
if (!_clipPaths) {
_clipPaths = [[NSMutableDictionary alloc] init];
}
[_clipPaths setObject:clipPath forKey:clipPathName];
if (!_clipPaths) {
_clipPaths = [[NSMutableDictionary alloc] init];
}
[_clipPaths setObject:clipPath forKey:clipPathName];
}
- (RNSVGNode *)getDefinedClipPath:(NSString *)clipPathName
{
return _clipPaths ? [_clipPaths objectForKey:clipPathName] : nil;
return _clipPaths ? [_clipPaths objectForKey:clipPathName] : nil;
}
- (void)defineTemplate:(RNSVGNode *)definedTemplate templateName:(NSString *)templateName
{
if (!_templates) {
_templates = [[NSMutableDictionary alloc] init];
}
[_templates setObject:definedTemplate forKey:templateName];
if (!_templates) {
_templates = [[NSMutableDictionary alloc] init];
}
[_templates setObject:definedTemplate forKey:templateName];
}
- (RNSVGNode *)getDefinedTemplate:(NSString *)templateName
{
return _templates ? [_templates objectForKey:templateName] : nil;
return _templates ? [_templates objectForKey:templateName] : nil;
}
- (void)definePainter:(RNSVGPainter *)painter painterName:(NSString *)painterName
{
if (!_painters) {
_painters = [[NSMutableDictionary alloc] init];
}
[_painters setObject:painter forKey:painterName];
if (!_painters) {
_painters = [[NSMutableDictionary alloc] init];
}
[_painters setObject:painter forKey:painterName];
}
- (RNSVGPainter *)getDefinedPainter:(NSString *)painterName;
{
return _painters ? [_painters objectForKey:painterName] : nil;
return _painters ? [_painters objectForKey:painterName] : nil;
}
- (void)defineMarker:(RNSVGNode *)marker markerName:(NSString *)markerName
{
if (!_markers) {
_markers = [[NSMutableDictionary alloc] init];
}
[_markers setObject:marker forKey:markerName];
if (!_markers) {
_markers = [[NSMutableDictionary alloc] init];
}
[_markers setObject:marker forKey:markerName];
}
- (RNSVGNode *)getDefinedMarker:(NSString *)markerName;
{
return _markers ? [_markers objectForKey:markerName] : nil;
return _markers ? [_markers objectForKey:markerName] : nil;
}
- (void)defineMask:(RNSVGNode *)mask maskName:(NSString *)maskName
{
if (!_masks) {
_masks = [[NSMutableDictionary alloc] init];
}
[_masks setObject:mask forKey:maskName];
if (!_masks) {
_masks = [[NSMutableDictionary alloc] init];
}
[_masks setObject:mask forKey:maskName];
}
- (RNSVGNode *)getDefinedMask:(NSString *)maskName;
{
return _masks ? [_masks objectForKey:maskName] : nil;
return _masks ? [_masks objectForKey:maskName] : nil;
}
- (CGRect)getContextBounds
{
return CGContextGetClipBoundingBox(UIGraphicsGetCurrentContext());
return CGContextGetClipBoundingBox(UIGraphicsGetCurrentContext());
}
- (CGAffineTransform)getViewBoxTransform
{
return _viewBoxTransform;
return _viewBoxTransform;
}
@end

View File

@@ -10,9 +10,9 @@
#ifdef RN_FABRIC_ENABLED
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import "RCTFabricComponentsPlugins.h"
#import "RCTConversions.h"
#import <react/renderer/components/view/conversions.h>
#import "RCTConversions.h"
#import "RCTFabricComponentsPlugins.h"
#import "RNSVGFabricConversions.h"
#endif // RN_FABRIC_ENABLED
@@ -39,110 +39,110 @@ using namespace facebook::react;
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &newProps = *std::static_pointer_cast<const RNSVGSymbolProps>(props);
const auto &newProps = *std::static_pointer_cast<const RNSVGSymbolProps>(props);
self.minX = newProps.minX;
self.minY = newProps.minY;
self.vbWidth = newProps.vbWidth;
self.vbHeight = newProps.vbHeight;
self.align = RCTNSStringFromStringNilIfEmpty(newProps.align);
self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice);
setCommonGroupProps(newProps, self);
self.minX = newProps.minX;
self.minY = newProps.minY;
self.vbWidth = newProps.vbWidth;
self.vbHeight = newProps.vbHeight;
self.align = RCTNSStringFromStringNilIfEmpty(newProps.align);
self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice);
setCommonGroupProps(newProps, self);
}
- (void)prepareForRecycle
{
[super prepareForRecycle];
_minX = 0;
_minY = 0;
_vbWidth = 0;
_vbHeight = 0;
_align = nil;
_meetOrSlice = kRNSVGVBMOSMeet;
[super prepareForRecycle];
_minX = 0;
_minY = 0;
_vbWidth = 0;
_vbHeight = 0;
_align = nil;
_meetOrSlice = kRNSVGVBMOSMeet;
}
#endif // RN_FABRIC_ENABLED
- (void)setMinX:(CGFloat)minX
{
if (minX == _minX) {
return;
}
[self invalidate];
_minX = minX;
if (minX == _minX) {
return;
}
[self invalidate];
_minX = minX;
}
- (void)setMinY:(CGFloat)minY
{
if (minY == _minY) {
return;
}
[self invalidate];
_minY = minY;
if (minY == _minY) {
return;
}
[self invalidate];
_minY = minY;
}
- (void)setVbWidth:(CGFloat)vbWidth
{
if (vbWidth == _vbWidth) {
return;
}
[self invalidate];
_vbWidth = vbWidth;
if (vbWidth == _vbWidth) {
return;
}
[self invalidate];
_vbWidth = vbWidth;
}
- (void)setVbHeight:(CGFloat)vbHeight
{
if (_vbHeight == vbHeight) {
return;
}
[self invalidate];
_vbHeight = vbHeight;
if (_vbHeight == vbHeight) {
return;
}
[self invalidate];
_vbHeight = vbHeight;
}
- (void)setAlign:(NSString *)align
{
if ([align isEqualToString:_align]) {
return;
}
[self invalidate];
_align = align;
if ([align isEqualToString:_align]) {
return;
}
[self invalidate];
_align = align;
}
- (void)setMeetOrSlice:(RNSVGVBMOS)meetOrSlice
{
if (meetOrSlice == _meetOrSlice) {
return;
}
[self invalidate];
_meetOrSlice = meetOrSlice;
if (meetOrSlice == _meetOrSlice) {
return;
}
[self invalidate];
_meetOrSlice = meetOrSlice;
}
- (void)renderTo:(CGContextRef)context rect:(CGRect)rect
{
self.dirty = false;
// Do not render Symbol
self.dirty = false;
// Do not render Symbol
}
- (void)renderSymbolTo:(CGContextRef)context width:(CGFloat)width height:(CGFloat)height
{
CGRect eRect = CGRectMake(0, 0, width, height);
if (self.align) {
CGAffineTransform viewBoxTransform = [RNSVGViewBox getTransform:CGRectMake(self.minX, self.minY, self.vbWidth, self.vbHeight)
eRect:eRect
align:self.align
meetOrSlice:self.meetOrSlice];
CGContextConcatCTM(context, viewBoxTransform);
}
[self renderGroupTo:context rect:eRect];
CGRect eRect = CGRectMake(0, 0, width, height);
if (self.align) {
CGAffineTransform viewBoxTransform =
[RNSVGViewBox getTransform:CGRectMake(self.minX, self.minY, self.vbWidth, self.vbHeight)
eRect:eRect
align:self.align
meetOrSlice:self.meetOrSlice];
CGContextConcatCTM(context, viewBoxTransform);
}
[self renderGroupTo:context rect:eRect];
}
@end

View File

@@ -6,8 +6,8 @@
* LICENSE file in the root directory of this source tree.
*/
#import "RNSVGRenderable.h"
#import "RNSVGLength.h"
#import "RNSVGRenderable.h"
/**
* RNSVG defination are implemented as abstract UIViews for all elements inside Defs.

View File

@@ -6,14 +6,14 @@
* LICENSE file in the root directory of this source tree.
*/
#import "RNSVGUse.h"
#import "RNSVGSymbol.h"
#import <React/RCTLog.h>
#import "RNSVGSymbol.h"
#ifdef RN_FABRIC_ENABLED
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import "RCTFabricComponentsPlugins.h"
#import "RCTConversions.h"
#import <react/renderer/components/view/conversions.h>
#import "RCTConversions.h"
#import "RCTFabricComponentsPlugins.h"
#import "RNSVGFabricConversions.h"
#endif // RN_FABRIC_ENABLED
@@ -40,166 +40,171 @@ using namespace facebook::react;
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &newProps = *std::static_pointer_cast<const RNSVGUseProps>(props);
const auto &newProps = *std::static_pointer_cast<const RNSVGUseProps>(props);
self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)];
self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)];
if (RCTNSStringFromStringNilIfEmpty(newProps.useheight)) {
self.useheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.useheight)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.usewidth)) {
self.usewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.usewidth)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
self.useheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
self.usewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
}
self.href = RCTNSStringFromStringNilIfEmpty(newProps.href);
self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)];
self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)];
if (RCTNSStringFromStringNilIfEmpty(newProps.useheight)) {
self.useheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.useheight)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.usewidth)) {
self.usewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.usewidth)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
self.useheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
self.usewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
}
self.href = RCTNSStringFromStringNilIfEmpty(newProps.href);
setCommonRenderableProps(newProps, self);
setCommonRenderableProps(newProps, self);
}
- (void)prepareForRecycle
{
[super prepareForRecycle];
_x = nil;
_y = nil;
_useheight = nil;
_usewidth = nil;
_href = nil;
[super prepareForRecycle];
_x = nil;
_y = nil;
_useheight = nil;
_usewidth = nil;
_href = nil;
}
#endif // RN_FABRIC_ENABLED
- (void)setHref:(NSString *)href
{
if ([href isEqualToString:_href]) {
return;
}
if ([href isEqualToString:_href]) {
return;
}
[self invalidate];
_href = href;
[self invalidate];
_href = href;
}
- (void)setX:(RNSVGLength *)x
{
if ([x isEqualTo:_x]) {
return;
}
if ([x isEqualTo:_x]) {
return;
}
[self invalidate];
_x = x;
[self invalidate];
_x = x;
}
- (void)setY:(RNSVGLength *)y
{
if ([y isEqualTo:_y]) {
return;
}
if ([y isEqualTo:_y]) {
return;
}
[self invalidate];
_y = y;
[self invalidate];
_y = y;
}
- (void)setUsewidth:(RNSVGLength *)usewidth
{
if ([usewidth isEqualTo:_usewidth]) {
return;
}
if ([usewidth isEqualTo:_usewidth]) {
return;
}
[self invalidate];
_usewidth = usewidth;
[self invalidate];
_usewidth = usewidth;
}
- (void)setUseheight:(RNSVGLength *)useheight
{
if ([useheight isEqualTo:_useheight]) {
return;
}
if ([useheight isEqualTo:_useheight]) {
return;
}
[self invalidate];
_useheight = useheight;
[self invalidate];
_useheight = useheight;
}
- (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect
{
CGContextTranslateCTM(context, [self relativeOnWidth:self.x], [self relativeOnHeight:self.y]);
RNSVGNode* definedTemplate = [self.svgView getDefinedTemplate:self.href];
if (definedTemplate) {
[self beginTransparencyLayer:context];
[self clip:context];
CGContextTranslateCTM(context, [self relativeOnWidth:self.x], [self relativeOnHeight:self.y]);
RNSVGNode *definedTemplate = [self.svgView getDefinedTemplate:self.href];
if (definedTemplate) {
[self beginTransparencyLayer:context];
[self clip:context];
if ([definedTemplate isKindOfClass:[RNSVGRenderable class]]) {
[(RNSVGRenderable*)definedTemplate mergeProperties:self];
}
if ([definedTemplate isKindOfClass:[RNSVGRenderable class]]) {
[(RNSVGRenderable *)definedTemplate mergeProperties:self];
}
if ([definedTemplate class] == [RNSVGSymbol class]) {
RNSVGSymbol *symbol = (RNSVGSymbol*)definedTemplate;
[symbol renderSymbolTo:context width:[self relativeOnWidth:self.usewidth] height:[self relativeOnHeight:self.useheight]];
} else {
[definedTemplate renderTo:context rect:rect];
}
if ([definedTemplate isKindOfClass:[RNSVGRenderable class]]) {
[(RNSVGRenderable*)definedTemplate resetProperties];
}
[self endTransparencyLayer:context];
} else if (self.href) {
// TODO: calling yellow box here
RCTLogWarn(@"`Use` element expected a pre-defined svg template as `href` prop, template named: %@ is not defined.", self.href);
return;
if ([definedTemplate class] == [RNSVGSymbol class]) {
RNSVGSymbol *symbol = (RNSVGSymbol *)definedTemplate;
[symbol renderSymbolTo:context
width:[self relativeOnWidth:self.usewidth]
height:[self relativeOnHeight:self.useheight]];
} else {
return;
[definedTemplate renderTo:context rect:rect];
}
CGRect bounds = definedTemplate.clientRect;
self.clientRect = bounds;
CGAffineTransform current = CGContextGetCTM(context);
CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM);
self.ctm = svgToClientTransform;
self.screenCTM = current;
CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms);
CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
CGPoint center = CGPointApplyAffineTransform(mid, transform);
self.bounds = bounds;
if (!isnan(center.x) && !isnan(center.y)) {
self.center = center;
if ([definedTemplate isKindOfClass:[RNSVGRenderable class]]) {
[(RNSVGRenderable *)definedTemplate resetProperties];
}
self.frame = bounds;
[self endTransparencyLayer:context];
} else if (self.href) {
// TODO: calling yellow box here
RCTLogWarn(
@"`Use` element expected a pre-defined svg template as `href` prop, template named: %@ is not defined.",
self.href);
return;
} else {
return;
}
CGRect bounds = definedTemplate.clientRect;
self.clientRect = bounds;
CGAffineTransform current = CGContextGetCTM(context);
CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM);
self.ctm = svgToClientTransform;
self.screenCTM = current;
CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms);
CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
CGPoint center = CGPointApplyAffineTransform(mid, transform);
self.bounds = bounds;
if (!isnan(center.x) && !isnan(center.y)) {
self.center = center;
}
self.frame = bounds;
}
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix);
transformed = CGPointApplyAffineTransform(transformed, self.invTransform);
RNSVGNode const* definedTemplate = [self.svgView getDefinedTemplate:self.href];
if (event) {
self.active = NO;
} else if (self.active) {
return self;
}
RNSVGPlatformView const* hitChild = [definedTemplate hitTest:transformed withEvent:event];
if (hitChild) {
self.active = YES;
return self;
}
return nil;
}
- (CGPathRef)getPath: (CGContextRef)context
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
CGAffineTransform transform = CGAffineTransformMakeTranslation([self relativeOnWidth:self.x], [self relativeOnHeight:self.y]);
RNSVGNode const* definedTemplate = [self.svgView getDefinedTemplate:self.href];
if (!definedTemplate) {
return nil;
}
CGPathRef path = [definedTemplate getPath:context];
return CGPathCreateCopyByTransformingPath(path, &transform);
CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix);
transformed = CGPointApplyAffineTransform(transformed, self.invTransform);
RNSVGNode const *definedTemplate = [self.svgView getDefinedTemplate:self.href];
if (event) {
self.active = NO;
} else if (self.active) {
return self;
}
RNSVGPlatformView const *hitChild = [definedTemplate hitTest:transformed withEvent:event];
if (hitChild) {
self.active = YES;
return self;
}
return nil;
}
- (CGPathRef)getPath:(CGContextRef)context
{
CGAffineTransform transform =
CGAffineTransformMakeTranslation([self relativeOnWidth:self.x], [self relativeOnHeight:self.y]);
RNSVGNode const *definedTemplate = [self.svgView getDefinedTemplate:self.href];
if (!definedTemplate) {
return nil;
}
CGPathRef path = [definedTemplate getPath:context];
return CGPathCreateCopyByTransformingPath(path, &transform);
}
@end

View File

@@ -6,10 +6,11 @@
* LICENSE file in the root directory of this source tree.
*/
#import "RNSVGSvgView.h"
#import <React/UIView+React.h>
#import <React/RCTPointerEvents.h>
#import "RNSVGCGFCRule.h"
#import "RNSVGSvgView.h"
#import <React/RCTPointerEvents.h>
#import <React/UIView+React.h>
#ifdef RN_FABRIC_ENABLED
#import <React/RCTViewComponentView.h>
@@ -24,9 +25,9 @@
@interface RNSVGNode :
#ifdef RN_FABRIC_ENABLED
RCTViewComponentView
RCTViewComponentView
#else
RNSVGView
RNSVGView
#endif // RN_FABRIC_ENABLED
/*
N[1/Sqrt[2], 36]
@@ -73,7 +74,6 @@ extern CGFloat const RNSVG_DEFAULT_FONT_SIZE;
@property (nonatomic, assign) CGRect markerBounds;
@property (nonatomic, copy) RCTDirectEventBlock onLayout;
/**
* RNSVGSvgView which ownes current RNSVGNode
*/
@@ -111,7 +111,7 @@ extern CGFloat const RNSVG_DEFAULT_FONT_SIZE;
/**
* getPath will return the path inside node as a ClipPath.
*/
- (CGPathRef)getPath:(CGContextRef) context;
- (CGPathRef)getPath:(CGContextRef)context;
- (CGFloat)relativeOnWidthString:(NSString *)length;

File diff suppressed because it is too large Load Diff

View File

@@ -12,8 +12,8 @@
#import "RNSVGBrush.h"
#import "RNSVGCGFCRule.h"
#import "RNSVGNode.h"
#import "RNSVGLength.h"
#import "RNSVGNode.h"
#import "RNSVGVectorEffect.h"
@interface RNSVGRenderable : RNSVGNode

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
// Most (if not all) of this file could probably go away once react-native-macos's version of RCTUIKit.h makes its way upstream.
// https://github.com/microsoft/react-native-macos/issues/242
// Most (if not all) of this file could probably go away once react-native-macos's version of RCTUIKit.h makes its way
// upstream. https://github.com/microsoft/react-native-macos/issues/242
#if !TARGET_OS_OSX
@@ -12,9 +12,9 @@
#else // TARGET_OS_OSX [
// Due to name mangling, calling c-style functions from .mm files will fail, therefore we need to wrap them with extern "C"
// so they are handled correctly. We also need to have imports positioned in a correct way,
// so that this extern "C" wrapper is used before the functions from RCTUIKit are used.
// Due to name mangling, calling c-style functions from .mm files will fail, therefore we need to wrap them with extern
// "C" so they are handled correctly. We also need to have imports positioned in a correct way, so that this extern "C"
// wrapper is used before the functions from RCTUIKit are used.
#ifdef __cplusplus
extern "C" {
#endif
@@ -37,7 +37,8 @@ extern "C" {
@end
// TODO: These could probably be a part of react-native-macos
// See https://github.com/microsoft/react-native-macos/issues/658 and https://github.com/microsoft/react-native-macos/issues/659
// See https://github.com/microsoft/react-native-macos/issues/658 and
// https://github.com/microsoft/react-native-macos/issues/659
@interface NSImage (RNSVGMacOSExtensions)
@property (readonly) CGImageRef CGImage;
@end

View File

@@ -1,86 +1,83 @@
#import "RNSVGUIKit.h"
@implementation RNSVGView
{
NSColor *_tintColor;
@implementation RNSVGView {
NSColor *_tintColor;
}
- (CGPoint)center
{
NSRect frameRect = self.frame;
CGFloat xCenter = frameRect.origin.x + frameRect.size.width / 2;
CGFloat yCenter = frameRect.origin.y + frameRect.size.height / 2;
return CGPointMake(xCenter, yCenter);
NSRect frameRect = self.frame;
CGFloat xCenter = frameRect.origin.x + frameRect.size.width / 2;
CGFloat yCenter = frameRect.origin.y + frameRect.size.height / 2;
return CGPointMake(xCenter, yCenter);
}
- (void)setCenter:(CGPoint)point
{
NSRect frameRect = self.frame;
CGFloat xOrigin = frameRect.origin.x - frameRect.size.width / 2;
CGFloat yOrigin = frameRect.origin.y - frameRect.size.height / 2;
self.frame = CGRectMake(xOrigin, yOrigin, frameRect.size.width, frameRect.size.height);
NSRect frameRect = self.frame;
CGFloat xOrigin = frameRect.origin.x - frameRect.size.width / 2;
CGFloat yOrigin = frameRect.origin.y - frameRect.size.height / 2;
self.frame = CGRectMake(xOrigin, yOrigin, frameRect.size.width, frameRect.size.height);
}
- (NSColor *)tintColor
{
if (_tintColor != nil) {
return _tintColor;
}
if (_tintColor != nil) {
return _tintColor;
}
// To mimic iOS's tintColor, we crawl up the view hierarchy until either:
// (a) we find a valid color
// (b) we reach a view that isn't an RNSVGView
NSView *parentView = [self superview];
if ([parentView isKindOfClass:[RNSVGView class]]) {
return [(RNSVGView *)parentView tintColor];
} else {
return [NSColor controlAccentColor];
}
// To mimic iOS's tintColor, we crawl up the view hierarchy until either:
// (a) we find a valid color
// (b) we reach a view that isn't an RNSVGView
NSView *parentView = [self superview];
if ([parentView isKindOfClass:[RNSVGView class]]) {
return [(RNSVGView *)parentView tintColor];
} else {
return [NSColor controlAccentColor];
}
}
- (void)setTintColor:(NSColor *)tintColor
{
_tintColor = tintColor;
[self setNeedsDisplay:YES];
_tintColor = tintColor;
[self setNeedsDisplay:YES];
}
@end
@implementation NSImage (RNSVGMacOSExtensions)
- (CGImageRef) CGImage
- (CGImageRef)CGImage
{
return [self CGImageForProposedRect:NULL context:NULL hints:NULL];
return [self CGImageForProposedRect:NULL context:NULL hints:NULL];
}
@end
@implementation NSValue (RNSVGMacOSExtensions)
+ (NSValue *)valueWithCGAffineTransform:(CGAffineTransform)transform
{
return [NSValue valueWithBytes:&transform objCType:@encode(CGAffineTransform)];
return [NSValue valueWithBytes:&transform objCType:@encode(CGAffineTransform)];
}
+ (NSValue *)valueWithCGPoint:(CGPoint)point
{
return [NSValue valueWithBytes:&point objCType:@encode(CGPoint)];
return [NSValue valueWithBytes:&point objCType:@encode(CGPoint)];
}
- (CGAffineTransform)CGAffineTransformValue
{
CGAffineTransform value;
[self getValue:&value];
return value;
CGAffineTransform value;
[self getValue:&value];
return value;
}
- (CGPoint)CGPointValue
{
CGPoint value;
[self getValue:&value];
return value;
CGPoint value;
[self getValue:&value];
return value;
}
@end

View File

@@ -12,9 +12,8 @@
@interface RNSVGCircle : RNSVGRenderable
@property (nonatomic, strong) RNSVGLength* cx;
@property (nonatomic, strong) RNSVGLength* cy;
@property (nonatomic, strong) RNSVGLength* r;
@property (nonatomic, strong) RNSVGLength *cx;
@property (nonatomic, strong) RNSVGLength *cy;
@property (nonatomic, strong) RNSVGLength *r;
@end

View File

@@ -11,9 +11,9 @@
#ifdef RN_FABRIC_ENABLED
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import "RCTFabricComponentsPlugins.h"
#import "RCTConversions.h"
#import <react/renderer/components/view/conversions.h>
#import "RCTConversions.h"
#import "RCTFabricComponentsPlugins.h"
#import "RNSVGFabricConversions.h"
#endif // RN_FABRIC_ENABLED
@@ -40,59 +40,59 @@ using namespace facebook::react;
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &newProps = *std::static_pointer_cast<const RNSVGCircleProps>(props);
const auto &newProps = *std::static_pointer_cast<const RNSVGCircleProps>(props);
self.cx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cx)];
self.cy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cy)];
self.r = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.r)];
setCommonRenderableProps(newProps, self);
self.cx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cx)];
self.cy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cy)];
self.r = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.r)];
setCommonRenderableProps(newProps, self);
}
- (void)prepareForRecycle
{
[super prepareForRecycle];
_cx = nil;
_cy = nil;
_r = nil;
[super prepareForRecycle];
_cx = nil;
_cy = nil;
_r = nil;
}
#endif // RN_FABRIC_ENABLED
- (void)setCx:(RNSVGLength *)cx
{
if ([cx isEqualTo:_cx]) {
return;
}
[self invalidate];
_cx = cx;
if ([cx isEqualTo:_cx]) {
return;
}
[self invalidate];
_cx = cx;
}
- (void)setCy:(RNSVGLength *)cy
{
if ([cy isEqualTo:_cy]) {
return;
}
[self invalidate];
_cy = cy;
if ([cy isEqualTo:_cy]) {
return;
}
[self invalidate];
_cy = cy;
}
- (void)setR:(RNSVGLength *)r
{
if ([r isEqualTo:_r]) {
return;
}
[self invalidate];
_r = r;
if ([r isEqualTo:_r]) {
return;
}
[self invalidate];
_r = r;
}
- (CGPathRef)getPath:(CGContextRef)context
{
CGMutablePathRef path = CGPathCreateMutable();
CGFloat cx = [self relativeOnWidth:self.cx];
CGFloat cy = [self relativeOnHeight:self.cy];
CGFloat r = [self relativeOnOther:self.r];
CGPathAddArc(path, nil, cx, cy, r, 0, 2 * (CGFloat)M_PI, NO);
return (CGPathRef)CFAutorelease(path);
CGMutablePathRef path = CGPathCreateMutable();
CGFloat cx = [self relativeOnWidth:self.cx];
CGFloat cy = [self relativeOnHeight:self.cy];
CGFloat r = [self relativeOnOther:self.r];
CGPathAddArc(path, nil, cx, cy, r, 0, 2 * (CGFloat)M_PI, NO);
return (CGPathRef)CFAutorelease(path);
}
@end

View File

@@ -11,8 +11,8 @@
#import "RNSVGPath.h"
@interface RNSVGEllipse : RNSVGRenderable
@property (nonatomic, strong) RNSVGLength* cx;
@property (nonatomic, strong) RNSVGLength* cy;
@property (nonatomic, strong) RNSVGLength* rx;
@property (nonatomic, strong) RNSVGLength* ry;
@property (nonatomic, strong) RNSVGLength *cx;
@property (nonatomic, strong) RNSVGLength *cy;
@property (nonatomic, strong) RNSVGLength *rx;
@property (nonatomic, strong) RNSVGLength *ry;
@end

View File

@@ -11,9 +11,9 @@
#ifdef RN_FABRIC_ENABLED
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import "RCTFabricComponentsPlugins.h"
#import "RCTConversions.h"
#import <react/renderer/components/view/conversions.h>
#import "RCTConversions.h"
#import "RCTFabricComponentsPlugins.h"
#import "RNSVGFabricConversions.h"
#endif // RN_FABRIC_ENABLED
@@ -40,71 +40,71 @@ using namespace facebook::react;
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &newProps = *std::static_pointer_cast<const RNSVGEllipseProps>(props);
const auto &newProps = *std::static_pointer_cast<const RNSVGEllipseProps>(props);
self.cx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cx)];
self.cy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cy)];
self.rx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rx)];
self.ry = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.ry)];
self.cx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cx)];
self.cy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cy)];
self.rx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rx)];
self.ry = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.ry)];
setCommonRenderableProps(newProps, self);
setCommonRenderableProps(newProps, self);
}
- (void)prepareForRecycle
{
[super prepareForRecycle];
_cx = nil;
_cy = nil;
_rx = nil;
_ry = nil;
[super prepareForRecycle];
_cx = nil;
_cy = nil;
_rx = nil;
_ry = nil;
}
#endif // RN_FABRIC_ENABLED
- (void)setCx:(RNSVGLength *)cx
{
if ([cx isEqualTo:_cx]) {
return;
}
[self invalidate];
_cx = cx;
if ([cx isEqualTo:_cx]) {
return;
}
[self invalidate];
_cx = cx;
}
- (void)setCy:(RNSVGLength *)cy
{
if ([cy isEqualTo:_cy]) {
return;
}
[self invalidate];
_cy = cy;
if ([cy isEqualTo:_cy]) {
return;
}
[self invalidate];
_cy = cy;
}
- (void)setRx:(RNSVGLength *)rx
{
if ([rx isEqualTo:_rx]) {
return;
}
[self invalidate];
_rx = rx;
if ([rx isEqualTo:_rx]) {
return;
}
[self invalidate];
_rx = rx;
}
- (void)setRy:(RNSVGLength *)ry
{
if ([ry isEqualTo:_ry]) {
return;
}
[self invalidate];
_ry = ry;
if ([ry isEqualTo:_ry]) {
return;
}
[self invalidate];
_ry = ry;
}
- (CGPathRef)getPath:(CGContextRef)context
{
CGMutablePathRef path = CGPathCreateMutable();
CGFloat cx = [self relativeOnWidth:self.cx];
CGFloat cy = [self relativeOnHeight:self.cy];
CGFloat rx = [self relativeOnWidth:self.rx];
CGFloat ry = [self relativeOnHeight:self.ry];
CGPathAddEllipseInRect(path, nil, CGRectMake(cx - rx, cy - ry, rx * 2, ry * 2));
return (CGPathRef)CFAutorelease(path);
CGMutablePathRef path = CGPathCreateMutable();
CGFloat cx = [self relativeOnWidth:self.cx];
CGFloat cy = [self relativeOnHeight:self.cy];
CGFloat rx = [self relativeOnWidth:self.rx];
CGFloat ry = [self relativeOnHeight:self.ry];
CGPathAddEllipseInRect(path, nil, CGRectMake(cx - rx, cy - ry, rx * 2, ry * 2));
return (CGPathRef)CFAutorelease(path);
}
@end

View File

@@ -11,8 +11,8 @@
#import "RNSVGPath.h"
@interface RNSVGLine : RNSVGRenderable
@property (nonatomic, strong) RNSVGLength* x1;
@property (nonatomic, strong) RNSVGLength* y1;
@property (nonatomic, strong) RNSVGLength* x2;
@property (nonatomic, strong) RNSVGLength* y2;
@property (nonatomic, strong) RNSVGLength *x1;
@property (nonatomic, strong) RNSVGLength *y1;
@property (nonatomic, strong) RNSVGLength *x2;
@property (nonatomic, strong) RNSVGLength *y2;
@end

View File

@@ -11,9 +11,9 @@
#ifdef RN_FABRIC_ENABLED
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import "RCTFabricComponentsPlugins.h"
#import "RCTConversions.h"
#import <react/renderer/components/view/conversions.h>
#import "RCTConversions.h"
#import "RCTFabricComponentsPlugins.h"
#import "RNSVGFabricConversions.h"
#endif // RN_FABRIC_ENABLED
@@ -40,73 +40,73 @@ using namespace facebook::react;
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &newProps = *std::static_pointer_cast<const RNSVGLineProps>(props);
const auto &newProps = *std::static_pointer_cast<const RNSVGLineProps>(props);
self.x1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x1)];
self.y1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y1)];
self.x2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x2)];
self.y2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y2)];
self.x1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x1)];
self.y1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y1)];
self.x2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x2)];
self.y2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y2)];
setCommonRenderableProps(newProps, self);
setCommonRenderableProps(newProps, self);
}
- (void)prepareForRecycle
{
[super prepareForRecycle];
_x1 = nil;
_y1 = nil;
_x2 = nil;
_y2 = nil;
[super prepareForRecycle];
_x1 = nil;
_y1 = nil;
_x2 = nil;
_y2 = nil;
}
#endif // RN_FABRIC_ENABLED
- (void)setX1:(RNSVGLength *)x1
{
if ([x1 isEqualTo:_x1]) {
return;
}
[self invalidate];
_x1 = x1;
if ([x1 isEqualTo:_x1]) {
return;
}
[self invalidate];
_x1 = x1;
}
- (void)setY1:(RNSVGLength *)y1
{
if ([y1 isEqualTo:_y1]) {
return;
}
[self invalidate];
_y1 = y1;
if ([y1 isEqualTo:_y1]) {
return;
}
[self invalidate];
_y1 = y1;
}
- (void)setX2:(RNSVGLength *)x2
{
if ([x2 isEqualTo:_x2]) {
return;
}
[self invalidate];
_x2 = x2;
if ([x2 isEqualTo:_x2]) {
return;
}
[self invalidate];
_x2 = x2;
}
- (void)setY2:(RNSVGLength *)y2
{
if ([y2 isEqualTo:_y2]) {
return;
}
[self invalidate];
_y2 = y2;
if ([y2 isEqualTo:_y2]) {
return;
}
[self invalidate];
_y2 = y2;
}
- (CGPathRef)getPath:(CGContextRef)context
{
CGMutablePathRef path = CGPathCreateMutable();
CGFloat x1 = [self relativeOnWidth:self.x1];
CGFloat y1 = [self relativeOnHeight:self.y1];
CGFloat x2 = [self relativeOnWidth:self.x2];
CGFloat y2 = [self relativeOnHeight:self.y2];
CGPathMoveToPoint(path, nil, x1, y1);
CGPathAddLineToPoint(path, nil, x2, y2);
return (CGPathRef)CFAutorelease(path);
CGMutablePathRef path = CGPathCreateMutable();
CGFloat x1 = [self relativeOnWidth:self.x1];
CGFloat y1 = [self relativeOnHeight:self.y1];
CGFloat x2 = [self relativeOnWidth:self.x2];
CGFloat y2 = [self relativeOnHeight:self.y2];
CGPathMoveToPoint(path, nil, x1, y1);
CGPathAddLineToPoint(path, nil, x2, y2);
return (CGPathRef)CFAutorelease(path);
}
@end

View File

@@ -12,11 +12,11 @@
@interface RNSVGRect : RNSVGRenderable
@property (nonatomic, strong) RNSVGLength* x;
@property (nonatomic, strong) RNSVGLength* y;
@property (nonatomic, strong) RNSVGLength* rectwidth;
@property (nonatomic, strong) RNSVGLength* rectheight;
@property (nonatomic, strong) RNSVGLength* rx;
@property (nonatomic, strong) RNSVGLength* ry;
@property (nonatomic, strong) RNSVGLength *x;
@property (nonatomic, strong) RNSVGLength *y;
@property (nonatomic, strong) RNSVGLength *rectwidth;
@property (nonatomic, strong) RNSVGLength *rectheight;
@property (nonatomic, strong) RNSVGLength *rx;
@property (nonatomic, strong) RNSVGLength *ry;
@end

View File

@@ -11,9 +11,9 @@
#ifdef RN_FABRIC_ENABLED
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import "RCTFabricComponentsPlugins.h"
#import "RCTConversions.h"
#import <react/renderer/components/view/conversions.h>
#import "RCTConversions.h"
#import "RCTFabricComponentsPlugins.h"
#import "RNSVGFabricConversions.h"
#endif // RN_FABRIC_ENABLED
@@ -40,132 +40,132 @@ using namespace facebook::react;
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &newProps = *std::static_pointer_cast<const RNSVGRectProps>(props);
const auto &newProps = *std::static_pointer_cast<const RNSVGRectProps>(props);
self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)];
self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)];
if (RCTNSStringFromStringNilIfEmpty(newProps.rectheight)) {
self.rectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rectheight)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.rectwidth)) {
self.rectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rectwidth)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
self.rectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
self.rectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
}
self.rx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rx)];
self.ry = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.ry)];
self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)];
self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)];
if (RCTNSStringFromStringNilIfEmpty(newProps.rectheight)) {
self.rectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rectheight)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.rectwidth)) {
self.rectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rectwidth)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
self.rectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
}
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
self.rectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
}
self.rx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rx)];
self.ry = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.ry)];
setCommonRenderableProps(newProps, self);
setCommonRenderableProps(newProps, self);
}
- (void)prepareForRecycle
{
[super prepareForRecycle];
_x = nil;
_y = nil;
_rectwidth = nil;
_rectheight = nil;
_rx = nil;
_ry = nil;
[super prepareForRecycle];
_x = nil;
_y = nil;
_rectwidth = nil;
_rectheight = nil;
_rx = nil;
_ry = nil;
}
#endif // RN_FABRIC_ENABLED
- (void)setX:(RNSVGLength *)x
{
if ([x isEqualTo:_x]) {
return;
}
[self invalidate];
_x = x;
if ([x isEqualTo:_x]) {
return;
}
[self invalidate];
_x = x;
}
- (void)setY:(RNSVGLength *)y
{
if ([y isEqualTo:_y]) {
return;
}
[self invalidate];
_y = y;
if ([y isEqualTo:_y]) {
return;
}
[self invalidate];
_y = y;
}
- (void)setRectwidth:(RNSVGLength *)rectwidth
{
if ([rectwidth isEqualTo:_rectwidth]) {
return;
}
[self invalidate];
_rectwidth = rectwidth;
if ([rectwidth isEqualTo:_rectwidth]) {
return;
}
[self invalidate];
_rectwidth = rectwidth;
}
- (void)setRectheight:(RNSVGLength *)rectheight
{
if ([rectheight isEqualTo:_rectheight]) {
return;
}
[self invalidate];
_rectheight = rectheight;
if ([rectheight isEqualTo:_rectheight]) {
return;
}
[self invalidate];
_rectheight = rectheight;
}
- (void)setRx:(RNSVGLength *)rx
{
if ([rx isEqualTo:_rx]) {
return;
}
[self invalidate];
_rx = rx;
if ([rx isEqualTo:_rx]) {
return;
}
[self invalidate];
_rx = rx;
}
- (void)setRy:(RNSVGLength *)ry
{
if ([ry isEqualTo:_ry]) {
return;
}
[self invalidate];
_ry = ry;
if ([ry isEqualTo:_ry]) {
return;
}
[self invalidate];
_ry = ry;
}
- (CGPathRef)getPath:(CGContextRef)context
{
CGMutablePathRef path = CGPathCreateMutable();
CGFloat x = [self relativeOnWidth:self.x];
CGFloat y = [self relativeOnHeight:self.y];
CGFloat width = [self relativeOnWidth:self.rectwidth];
CGFloat height = [self relativeOnHeight:self.rectheight];
CGMutablePathRef path = CGPathCreateMutable();
CGFloat x = [self relativeOnWidth:self.x];
CGFloat y = [self relativeOnHeight:self.y];
CGFloat width = [self relativeOnWidth:self.rectwidth];
CGFloat height = [self relativeOnHeight:self.rectheight];
if (self.rx != nil || self.ry != nil) {
CGFloat rx = 0;
CGFloat ry = 0;
if (self.rx == nil) {
ry = [self relativeOnHeight:self.ry];
rx = ry;
} else if (self.ry == nil) {
rx = [self relativeOnWidth:self.rx];
ry = rx;
} else {
rx = [self relativeOnWidth:self.rx];
ry = [self relativeOnHeight:self.ry];
}
if (rx > width / 2) {
rx = width / 2;
}
if (ry > height / 2) {
ry = height / 2;
}
CGPathAddRoundedRect(path, nil, CGRectMake(x, y, width, height), rx, ry);
if (self.rx != nil || self.ry != nil) {
CGFloat rx = 0;
CGFloat ry = 0;
if (self.rx == nil) {
ry = [self relativeOnHeight:self.ry];
rx = ry;
} else if (self.ry == nil) {
rx = [self relativeOnWidth:self.rx];
ry = rx;
} else {
CGPathAddRect(path, nil, CGRectMake(x, y, width, height));
rx = [self relativeOnWidth:self.rx];
ry = [self relativeOnHeight:self.ry];
}
return (CGPathRef)CFAutorelease(path);
if (rx > width / 2) {
rx = width / 2;
}
if (ry > height / 2) {
ry = height / 2;
}
CGPathAddRoundedRect(path, nil, CGRectMake(x, y, width, height), rx, ry);
} else {
CGPathAddRect(path, nil, CGRectMake(x, y, width, height));
}
return (CGPathRef)CFAutorelease(path);
}
@end

View File

@@ -2,35 +2,33 @@
#import "RNSVGUIKit.h"
#import "RNSVGTextProperties.h"
#import "RNSVGPropHelper.h"
#import "RNSVGTextProperties.h"
@interface RNSVGFontData : NSObject {
@public
CGFloat fontSize;
NSString * fontSize_;
NSString *fontFamily;
enum RNSVGFontStyle fontStyle;
NSDictionary * fontData;
enum RNSVGFontWeight fontWeight;
int absoluteFontWeight;
NSString *fontFeatureSettings;
enum RNSVGFontVariantLigatures fontVariantLigatures;
enum RNSVGTextAnchor textAnchor;
enum RNSVGTextDecoration textDecoration;
CGFloat kerning;
CGFloat wordSpacing;
CGFloat letterSpacing;
bool manualKerning;
@public
CGFloat fontSize;
NSString *fontSize_;
NSString *fontFamily;
enum RNSVGFontStyle fontStyle;
NSDictionary *fontData;
enum RNSVGFontWeight fontWeight;
int absoluteFontWeight;
NSString *fontFeatureSettings;
enum RNSVGFontVariantLigatures fontVariantLigatures;
enum RNSVGTextAnchor textAnchor;
enum RNSVGTextDecoration textDecoration;
CGFloat kerning;
CGFloat wordSpacing;
CGFloat letterSpacing;
bool manualKerning;
}
+ (instancetype)Defaults;
+ (CGFloat)toAbsoluteWithNSString:(NSString *)string
fontSize:(CGFloat)fontSize;
+ (CGFloat)toAbsoluteWithNSString:(NSString *)string fontSize:(CGFloat)fontSize;
+ (instancetype)initWithNSDictionary:(NSDictionary *)font
parent:(RNSVGFontData *)parent;
+ (instancetype)initWithNSDictionary:(NSDictionary *)font parent:(RNSVGFontData *)parent;
@end

View File

@@ -1,7 +1,7 @@
#import "RNSVGFontData.h"
#import "RNSVGNode.h"
#import "RNSVGPropHelper.h"
#import "RNSVGTextProperties.h"
#import "RNSVGNode.h"
#define RNSVG_DEFAULT_KERNING 0.0
#define RNSVG_DEFAULT_WORD_SPACING 0.0
@@ -23,178 +23,174 @@ RNSVGFontData *RNSVGFontData_Defaults;
@implementation RNSVGFontData
+ (instancetype)Defaults {
if (!RNSVGFontData_Defaults) {
RNSVGFontData *self = [RNSVGFontData alloc];
self->fontData = nil;
self->fontFamily = @"";
self->fontStyle = RNSVGFontStyleNormal;
self->fontWeight = RNSVGFontWeightNormal;
self->absoluteFontWeight = 400;
self->fontFeatureSettings = @"";
self->fontVariantLigatures = RNSVGFontVariantLigaturesNormal;
self->textAnchor = RNSVGTextAnchorStart;
self->textDecoration = RNSVGTextDecorationNone;
self->manualKerning = false;
self->kerning = RNSVG_DEFAULT_KERNING;
self->fontSize = RNSVG_DEFAULT_FONT_SIZE;
self->wordSpacing = RNSVG_DEFAULT_WORD_SPACING;
self->letterSpacing = RNSVG_DEFAULT_LETTER_SPACING;
RNSVGFontData_Defaults = self;
}
return RNSVGFontData_Defaults;
+ (instancetype)Defaults
{
if (!RNSVGFontData_Defaults) {
RNSVGFontData *self = [RNSVGFontData alloc];
self->fontData = nil;
self->fontFamily = @"";
self->fontStyle = RNSVGFontStyleNormal;
self->fontWeight = RNSVGFontWeightNormal;
self->absoluteFontWeight = 400;
self->fontFeatureSettings = @"";
self->fontVariantLigatures = RNSVGFontVariantLigaturesNormal;
self->textAnchor = RNSVGTextAnchorStart;
self->textDecoration = RNSVGTextDecorationNone;
self->manualKerning = false;
self->kerning = RNSVG_DEFAULT_KERNING;
self->fontSize = RNSVG_DEFAULT_FONT_SIZE;
self->wordSpacing = RNSVG_DEFAULT_WORD_SPACING;
self->letterSpacing = RNSVG_DEFAULT_LETTER_SPACING;
RNSVGFontData_Defaults = self;
}
return RNSVGFontData_Defaults;
}
+ (CGFloat)toAbsoluteWithNSString:(NSString *)string
fontSize:(CGFloat)fontSize {
return [RNSVGPropHelper fromRelativeWithNSString:string
relative:0
fontSize:fontSize];
+ (CGFloat)toAbsoluteWithNSString:(NSString *)string fontSize:(CGFloat)fontSize
{
return [RNSVGPropHelper fromRelativeWithNSString:string relative:0 fontSize:fontSize];
}
- (void)setInheritedWeight:(RNSVGFontData*) parent {
absoluteFontWeight = parent->absoluteFontWeight;
fontWeight = parent->fontWeight;
- (void)setInheritedWeight:(RNSVGFontData *)parent
{
absoluteFontWeight = parent->absoluteFontWeight;
fontWeight = parent->fontWeight;
}
RNSVGFontWeight nearestFontWeight(int absoluteFontWeight) {
return RNSVGFontWeights[(int)round(absoluteFontWeight / 100.0)];
RNSVGFontWeight nearestFontWeight(int absoluteFontWeight)
{
return RNSVGFontWeights[(int)round(absoluteFontWeight / 100.0)];
}
- (void)handleNumericWeight:(RNSVGFontData*)parent weight:(double)weight {
long roundWeight = round(weight);
if (roundWeight >= 1 && roundWeight <= 1000) {
absoluteFontWeight = (int)roundWeight;
fontWeight = nearestFontWeight(absoluteFontWeight);
} else {
[self setInheritedWeight:parent];
}
- (void)handleNumericWeight:(RNSVGFontData *)parent weight:(double)weight
{
long roundWeight = round(weight);
if (roundWeight >= 1 && roundWeight <= 1000) {
absoluteFontWeight = (int)roundWeight;
fontWeight = nearestFontWeight(absoluteFontWeight);
} else {
[self setInheritedWeight:parent];
}
}
// https://drafts.csswg.org/css-fonts-4/#relative-weights
int AbsoluteFontWeight(RNSVGFontWeight fontWeight, RNSVGFontData* parent) {
if (fontWeight == RNSVGFontWeightBolder) {
return bolder(parent->absoluteFontWeight);
} else if (fontWeight == RNSVGFontWeightLighter) {
return lighter(parent->absoluteFontWeight);
} else {
return RNSVGAbsoluteFontWeights[fontWeight];
}
int AbsoluteFontWeight(RNSVGFontWeight fontWeight, RNSVGFontData *parent)
{
if (fontWeight == RNSVGFontWeightBolder) {
return bolder(parent->absoluteFontWeight);
} else if (fontWeight == RNSVGFontWeightLighter) {
return lighter(parent->absoluteFontWeight);
} else {
return RNSVGAbsoluteFontWeights[fontWeight];
}
}
int bolder(int inherited) {
if (inherited < 350) {
return 400;
} else if (inherited < 550) {
return 700;
} else if (inherited < 900) {
return 900;
} else {
return inherited;
}
int bolder(int inherited)
{
if (inherited < 350) {
return 400;
} else if (inherited < 550) {
return 700;
} else if (inherited < 900) {
return 900;
} else {
return inherited;
}
}
int lighter(int inherited) {
if (inherited < 100) {
return inherited;
} else if (inherited < 550) {
return 100;
} else if (inherited < 750) {
return 400;
} else {
return 700;
}
int lighter(int inherited)
{
if (inherited < 100) {
return inherited;
} else if (inherited < 550) {
return 100;
} else if (inherited < 750) {
return 400;
} else {
return 700;
}
}
+ (instancetype)initWithNSDictionary:(NSDictionary *)font
parent:(RNSVGFontData *)parent {
RNSVGFontData *data = [RNSVGFontData alloc];
CGFloat parentFontSize = parent->fontSize;
if ([font objectForKey:FONT_SIZE]) {
id fontSize = [font objectForKey:FONT_SIZE];
if ([fontSize isKindOfClass:NSNumber.class]) {
NSNumber* fs = fontSize;
data->fontSize = (CGFloat)[fs doubleValue];
} else {
data->fontSize = [RNSVGPropHelper fromRelativeWithNSString:fontSize
relative:parentFontSize
fontSize:parentFontSize];
}
}
else {
data->fontSize = parentFontSize;
}
if ([font objectForKey:FONT_WEIGHT]) {
id fontWeight = [font objectForKey:FONT_WEIGHT];
if ([fontWeight isKindOfClass:NSNumber.class]) {
[data handleNumericWeight:parent weight:[fontWeight doubleValue]];
} else {
NSString* weight = fontWeight;
NSInteger fw = RNSVGFontWeightFromString(weight);
if (fw != -1) {
data->absoluteFontWeight = AbsoluteFontWeight((RNSVGFontWeight)fw, parent);
data->fontWeight = nearestFontWeight(data->absoluteFontWeight);
} else if ([weight length] != 0) {
[data handleNumericWeight:parent weight:[weight doubleValue]];
} else {
[data setInheritedWeight:parent];
}
}
+ (instancetype)initWithNSDictionary:(NSDictionary *)font parent:(RNSVGFontData *)parent
{
RNSVGFontData *data = [RNSVGFontData alloc];
CGFloat parentFontSize = parent->fontSize;
if ([font objectForKey:FONT_SIZE]) {
id fontSize = [font objectForKey:FONT_SIZE];
if ([fontSize isKindOfClass:NSNumber.class]) {
NSNumber *fs = fontSize;
data->fontSize = (CGFloat)[fs doubleValue];
} else {
data->fontSize = [RNSVGPropHelper fromRelativeWithNSString:fontSize
relative:parentFontSize
fontSize:parentFontSize];
}
} else {
data->fontSize = parentFontSize;
}
if ([font objectForKey:FONT_WEIGHT]) {
id fontWeight = [font objectForKey:FONT_WEIGHT];
if ([fontWeight isKindOfClass:NSNumber.class]) {
[data handleNumericWeight:parent weight:[fontWeight doubleValue]];
} else {
NSString *weight = fontWeight;
NSInteger fw = RNSVGFontWeightFromString(weight);
if (fw != -1) {
data->absoluteFontWeight = AbsoluteFontWeight((RNSVGFontWeight)fw, parent);
data->fontWeight = nearestFontWeight(data->absoluteFontWeight);
} else if ([weight length] != 0) {
[data handleNumericWeight:parent weight:[weight doubleValue]];
} else {
[data setInheritedWeight:parent];
}
}
} else {
[data setInheritedWeight:parent];
}
data->fontData = [font objectForKey:FONT_DATA] ? [font objectForKey:FONT_DATA] : parent->fontData;
data->fontFamily = [font objectForKey:FONT_FAMILY] ? [font objectForKey:FONT_FAMILY] : parent->fontFamily;
NSString* style = [font objectForKey:FONT_STYLE];
data->fontStyle = style ? RNSVGFontStyleFromString(style) : parent->fontStyle;
NSString* feature = [font objectForKey:FONT_FEATURE_SETTINGS];
data->fontFeatureSettings = feature ? [font objectForKey:FONT_FEATURE_SETTINGS] : parent->fontFeatureSettings;
NSString* variant = [font objectForKey:FONT_VARIANT_LIGATURES];
data->fontVariantLigatures = variant ? RNSVGFontVariantLigaturesFromString(variant) : parent->fontVariantLigatures;
NSString* anchor = [font objectForKey:TEXT_ANCHOR];
data->textAnchor = anchor ? RNSVGTextAnchorFromString(anchor) : parent->textAnchor;
NSString* decoration = [font objectForKey:TEXT_DECORATION];
data->textDecoration = decoration ? RNSVGTextDecorationFromString(decoration) : parent->textDecoration;
data->fontData = [font objectForKey:FONT_DATA] ? [font objectForKey:FONT_DATA] : parent->fontData;
data->fontFamily = [font objectForKey:FONT_FAMILY] ? [font objectForKey:FONT_FAMILY] : parent->fontFamily;
NSString *style = [font objectForKey:FONT_STYLE];
data->fontStyle = style ? RNSVGFontStyleFromString(style) : parent->fontStyle;
NSString *feature = [font objectForKey:FONT_FEATURE_SETTINGS];
data->fontFeatureSettings = feature ? [font objectForKey:FONT_FEATURE_SETTINGS] : parent->fontFeatureSettings;
NSString *variant = [font objectForKey:FONT_VARIANT_LIGATURES];
data->fontVariantLigatures = variant ? RNSVGFontVariantLigaturesFromString(variant) : parent->fontVariantLigatures;
NSString *anchor = [font objectForKey:TEXT_ANCHOR];
data->textAnchor = anchor ? RNSVGTextAnchorFromString(anchor) : parent->textAnchor;
NSString *decoration = [font objectForKey:TEXT_DECORATION];
data->textDecoration = decoration ? RNSVGTextDecorationFromString(decoration) : parent->textDecoration;
CGFloat fontSize = data->fontSize;
id kerning = [font objectForKey:KERNING];
data->manualKerning = (kerning || parent->manualKerning );
if ([kerning isKindOfClass:NSNumber.class]) {
NSNumber* kern = kerning;
data->kerning = (CGFloat)[kern doubleValue];
} else {
data->kerning = kerning ?
[RNSVGFontData toAbsoluteWithNSString:kerning
fontSize:fontSize]
: parent->kerning;
}
CGFloat fontSize = data->fontSize;
id kerning = [font objectForKey:KERNING];
data->manualKerning = (kerning || parent->manualKerning);
if ([kerning isKindOfClass:NSNumber.class]) {
NSNumber *kern = kerning;
data->kerning = (CGFloat)[kern doubleValue];
} else {
data->kerning = kerning ? [RNSVGFontData toAbsoluteWithNSString:kerning fontSize:fontSize] : parent->kerning;
}
id wordSpacing = [font objectForKey:WORD_SPACING];
if ([wordSpacing isKindOfClass:NSNumber.class]) {
NSNumber* ws = wordSpacing;
data->wordSpacing = (CGFloat)[ws doubleValue];
} else {
data->wordSpacing = wordSpacing ?
[RNSVGFontData toAbsoluteWithNSString:wordSpacing
fontSize:fontSize]
: parent->wordSpacing;
}
id wordSpacing = [font objectForKey:WORD_SPACING];
if ([wordSpacing isKindOfClass:NSNumber.class]) {
NSNumber *ws = wordSpacing;
data->wordSpacing = (CGFloat)[ws doubleValue];
} else {
data->wordSpacing =
wordSpacing ? [RNSVGFontData toAbsoluteWithNSString:wordSpacing fontSize:fontSize] : parent->wordSpacing;
}
id letterSpacing = [font objectForKey:LETTER_SPACING];
if ([letterSpacing isKindOfClass:NSNumber.class]) {
NSNumber* ls = letterSpacing;
data->wordSpacing = (CGFloat)[ls doubleValue];
} else {
data->letterSpacing = letterSpacing ?
[RNSVGFontData toAbsoluteWithNSString:letterSpacing
fontSize:fontSize]
: parent->letterSpacing;
}
id letterSpacing = [font objectForKey:LETTER_SPACING];
if ([letterSpacing isKindOfClass:NSNumber.class]) {
NSNumber *ls = letterSpacing;
data->wordSpacing = (CGFloat)[ls doubleValue];
} else {
data->letterSpacing =
letterSpacing ? [RNSVGFontData toAbsoluteWithNSString:letterSpacing fontSize:fontSize] : parent->letterSpacing;
}
return data;
return data;
}
@end

View File

@@ -1,5 +1,5 @@
#import <React/UIView+React.h>
#import <CoreText/CoreText.h>
#import <React/UIView+React.h>
#import "RNSVGFontData.h"
@class RNSVGText;
@@ -10,8 +10,7 @@
- (CTFontRef)getGlyphFont;
- (instancetype)initWithWidth:(CGFloat)width
height:(CGFloat)height;
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;
- (RNSVGFontData *)getFont;
@@ -33,17 +32,16 @@
- (void)popContext;
- (void)pushContext:(RNSVGText*)node
font:(NSDictionary*)font
x:(NSArray<RNSVGLength*>*)x
y:(NSArray<RNSVGLength*>*)y
deltaX:(NSArray<RNSVGLength*>*)deltaX
deltaY:(NSArray<RNSVGLength*>*)deltaY
rotate:(NSArray<RNSVGLength*>*)rotate;
- (void)pushContext:(RNSVGText *)node
font:(NSDictionary *)font
x:(NSArray<RNSVGLength *> *)x
y:(NSArray<RNSVGLength *> *)y
deltaX:(NSArray<RNSVGLength *> *)deltaX
deltaY:(NSArray<RNSVGLength *> *)deltaY
rotate:(NSArray<RNSVGLength *> *)rotate;
- (void)pushContext:(RNSVGGroup*)node
font:(NSDictionary *)font;
- (void)pushContext:(RNSVGGroup *)node font:(NSDictionary *)font;
- (NSArray*)getFontContext;
- (NSArray *)getFontContext;
@end

View File

@@ -1,414 +1,414 @@
#import "RNSVGGlyphContext.h"
#import <React/RCTFont.h>
#import "RNSVGFontData.h"
#import "RNSVGNode.h"
#import "RNSVGPropHelper.h"
#import "RNSVGFontData.h"
#import "RNSVGText.h"
// https://www.w3.org/TR/SVG/text.html#TSpanElement
@interface RNSVGGlyphContext () {
@public
// Current stack (one per node push/pop)
NSMutableArray *mFontContext_;
@public
// Current stack (one per node push/pop)
NSMutableArray *mFontContext_;
// Unique input attribute lists (only added if node sets a value)
NSMutableArray<NSArray<RNSVGLength*>*> *mXsContext_;
NSMutableArray<NSArray<RNSVGLength*>*> *mYsContext_;
NSMutableArray<NSArray<RNSVGLength*>*> *mDXsContext_;
NSMutableArray<NSArray<RNSVGLength*>*> *mDYsContext_;
NSMutableArray<NSArray<RNSVGLength*>*> *mRsContext_;
// Unique input attribute lists (only added if node sets a value)
NSMutableArray<NSArray<RNSVGLength *> *> *mXsContext_;
NSMutableArray<NSArray<RNSVGLength *> *> *mYsContext_;
NSMutableArray<NSArray<RNSVGLength *> *> *mDXsContext_;
NSMutableArray<NSArray<RNSVGLength *> *> *mDYsContext_;
NSMutableArray<NSArray<RNSVGLength *> *> *mRsContext_;
// Unique index into attribute list (one per unique list)
NSMutableArray<NSNumber*> *mXIndices_;
NSMutableArray<NSNumber*> *mYIndices_;
NSMutableArray<NSNumber*> *mDXIndices_;
NSMutableArray<NSNumber*> *mDYIndices_;
NSMutableArray<NSNumber*> *mRIndices_;
// Unique index into attribute list (one per unique list)
NSMutableArray<NSNumber *> *mXIndices_;
NSMutableArray<NSNumber *> *mYIndices_;
NSMutableArray<NSNumber *> *mDXIndices_;
NSMutableArray<NSNumber *> *mDYIndices_;
NSMutableArray<NSNumber *> *mRIndices_;
// Index of unique context used (one per node push/pop)
NSMutableArray<NSNumber*> *mXsIndices_;
NSMutableArray<NSNumber*> *mYsIndices_;
NSMutableArray<NSNumber*> *mDXsIndices_;
NSMutableArray<NSNumber*> *mDYsIndices_;
NSMutableArray<NSNumber*> *mRsIndices_;
// Index of unique context used (one per node push/pop)
NSMutableArray<NSNumber *> *mXsIndices_;
NSMutableArray<NSNumber *> *mYsIndices_;
NSMutableArray<NSNumber *> *mDXsIndices_;
NSMutableArray<NSNumber *> *mDYsIndices_;
NSMutableArray<NSNumber *> *mRsIndices_;
// Calculated on push context, percentage and em length depends on parent font size
CGFloat mFontSize_;
RNSVGFontData *topFont_;
// Calculated on push context, percentage and em length depends on parent font size
CGFloat mFontSize_;
RNSVGFontData *topFont_;
// Current accumulated values
// https://www.w3.org/TR/SVG/types.html#DataTypeCoordinate
// <coordinate> syntax is the same as that for <length>
CGFloat mX_;
CGFloat mY_;
// Current accumulated values
// https://www.w3.org/TR/SVG/types.html#DataTypeCoordinate
// <coordinate> syntax is the same as that for <length>
CGFloat mX_;
CGFloat mY_;
// https://www.w3.org/TR/SVG/types.html#Length
CGFloat mDX_;
CGFloat mDY_;
// https://www.w3.org/TR/SVG/types.html#Length
CGFloat mDX_;
CGFloat mDY_;
// Current <list-of-coordinates> SVGLengthList
// https://www.w3.org/TR/SVG/types.html#InterfaceSVGLengthList
// https://www.w3.org/TR/SVG/types.html#DataTypeCoordinates
// Current <list-of-coordinates> SVGLengthList
// https://www.w3.org/TR/SVG/types.html#InterfaceSVGLengthList
// https://www.w3.org/TR/SVG/types.html#DataTypeCoordinates
// https://www.w3.org/TR/SVG/text.html#TSpanElementXAttribute
NSArray<RNSVGLength*> *mXs_;
// https://www.w3.org/TR/SVG/text.html#TSpanElementXAttribute
NSArray<RNSVGLength *> *mXs_;
// https://www.w3.org/TR/SVG/text.html#TSpanElementYAttribute
NSArray<RNSVGLength*> *mYs_;
// https://www.w3.org/TR/SVG/text.html#TSpanElementYAttribute
NSArray<RNSVGLength *> *mYs_;
// Current <list-of-lengths> SVGLengthList
// https://www.w3.org/TR/SVG/types.html#DataTypeLengths
// Current <list-of-lengths> SVGLengthList
// https://www.w3.org/TR/SVG/types.html#DataTypeLengths
// https://www.w3.org/TR/SVG/text.html#TSpanElementDXAttribute
NSArray<RNSVGLength*> *mDXs_;
// https://www.w3.org/TR/SVG/text.html#TSpanElementDXAttribute
NSArray<RNSVGLength *> *mDXs_;
// https://www.w3.org/TR/SVG/text.html#TSpanElementDYAttribute
NSArray<RNSVGLength*> *mDYs_;
// https://www.w3.org/TR/SVG/text.html#TSpanElementDYAttribute
NSArray<RNSVGLength *> *mDYs_;
// Current <list-of-numbers> SVGLengthList
// https://www.w3.org/TR/SVG/types.html#DataTypeNumbers
// Current <list-of-numbers> SVGLengthList
// https://www.w3.org/TR/SVG/types.html#DataTypeNumbers
// https://www.w3.org/TR/SVG/text.html#TSpanElementRotateAttribute
NSArray<RNSVGLength*> *mRs_;
// https://www.w3.org/TR/SVG/text.html#TSpanElementRotateAttribute
NSArray<RNSVGLength *> *mRs_;
// Current attribute list index
long mXsIndex_;
long mYsIndex_;
long mDXsIndex_;
long mDYsIndex_;
long mRsIndex_;
// Current attribute list index
long mXsIndex_;
long mYsIndex_;
long mDXsIndex_;
long mDYsIndex_;
long mRsIndex_;
// Current value index in current attribute list
long mXIndex_;
long mYIndex_;
long mDXIndex_;
long mDYIndex_;
long mRIndex_;
// Current value index in current attribute list
long mXIndex_;
long mYIndex_;
long mDXIndex_;
long mDYIndex_;
long mRIndex_;
// Top index of stack
long mTop_;
// Top index of stack
long mTop_;
// Constructor parameters
CGFloat mWidth_;
CGFloat mHeight_;
// Constructor parameters
CGFloat mWidth_;
CGFloat mHeight_;
}
- (void)pushContext:(RNSVGText*)node
font:(NSDictionary*)font
x:(NSArray<RNSVGLength*>*)x
y:(NSArray<RNSVGLength*>*)y
deltaX:(NSArray<RNSVGLength*>*)deltaX
deltaY:(NSArray<RNSVGLength*>*)deltaY
rotate:(NSArray<RNSVGLength*>*)rotate;
- (void)pushContext:(RNSVGText *)node
font:(NSDictionary *)font
x:(NSArray<RNSVGLength *> *)x
y:(NSArray<RNSVGLength *> *)y
deltaX:(NSArray<RNSVGLength *> *)deltaX
deltaY:(NSArray<RNSVGLength *> *)deltaY
rotate:(NSArray<RNSVGLength *> *)rotate;
- (void)pushContext:(RNSVGGroup*)node
font:(NSDictionary *)font;
- (void)pushContext:(RNSVGGroup *)node font:(NSDictionary *)font;
@end
@implementation RNSVGGlyphContext
- (NSArray*)getFontContext {
return mFontContext_;
- (NSArray *)getFontContext
{
return mFontContext_;
}
- (CTFontRef)getGlyphFont
{
CGFloat size = topFont_->fontSize;
NSString *fontFamily = topFont_->fontFamily;
NSString *fontStyle = RNSVGFontStyleStrings[topFont_->fontStyle];
NSString *fontWeight = RNSVGFontWeightStrings[topFont_->fontWeight];
UIFont *font = [RCTFont updateFont:nil
withFamily:[fontFamily isEqualToString:@""] ? nil : fontFamily
size:@(isnan(size) ? 0 : size)
weight:fontWeight
style:fontStyle
variant:nil
scaleMultiplier:1.0];
CTFontRef ref = (__bridge CTFontRef)font;
CGFloat size = topFont_->fontSize;
NSString *fontFamily = topFont_->fontFamily;
NSString *fontStyle = RNSVGFontStyleStrings[topFont_->fontStyle];
NSString *fontWeight = RNSVGFontWeightStrings[topFont_->fontWeight];
UIFont *font = [RCTFont updateFont:nil
withFamily:[fontFamily isEqualToString:@""] ? nil : fontFamily
size:@(isnan(size) ? 0 : size)
weight:fontWeight
style:fontStyle
variant:nil
scaleMultiplier:1.0];
CTFontRef ref = (__bridge CTFontRef)font;
double weight = topFont_->absoluteFontWeight;
if (weight == 400) {
return ref;
double weight = topFont_->absoluteFontWeight;
if (weight == 400) {
return ref;
}
CFArrayRef cgAxes = CTFontCopyVariationAxes(ref);
if (cgAxes == 0) {
return ref;
}
CFIndex cgAxisCount = CFArrayGetCount(cgAxes);
CFNumberRef wght_id = 0;
for (CFIndex i = 0; i < cgAxisCount; ++i) {
CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes, i);
if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
continue;
}
CFArrayRef cgAxes = CTFontCopyVariationAxes(ref);
if (cgAxes == 0) {
return ref;
CFDictionaryRef cgAxisDict = (CFDictionaryRef)cgAxis;
CFTypeRef axisName = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisNameKey);
if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
continue;
}
CFIndex cgAxisCount = CFArrayGetCount(cgAxes);
CFNumberRef wght_id = 0;
for (CFIndex i = 0; i < cgAxisCount; ++i) {
CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes, i);
if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
continue;
}
CFDictionaryRef cgAxisDict = (CFDictionaryRef)cgAxis;
CFTypeRef axisName = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisNameKey);
if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
continue;
}
CFStringRef axisNameString = (CFStringRef)axisName;
NSString *axisNameNSString = (__bridge NSString *)(axisNameString);
if (![@"Weight" isEqualToString:axisNameNSString] && ![@"Size" isEqualToString:axisNameNSString]) {
continue;
}
CFTypeRef axisMinValue = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisMinimumValueKey);
if (axisMinValue && CFGetTypeID(axisMinValue) == CFNumberGetTypeID()) {
CFNumberRef axisMinValueNumber = (CFNumberRef)axisMinValue;
double axisMinValueDouble;
if (CFNumberGetValue(axisMinValueNumber, kCFNumberDoubleType, &axisMinValueDouble))
{
weight = fmax(axisMinValueDouble, weight);
}
}
CFTypeRef axisMaxValue = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisMaximumValueKey);
if (axisMaxValue && CFGetTypeID(axisMaxValue) == CFNumberGetTypeID()) {
CFNumberRef axisMaxValueNumber = (CFNumberRef)axisMaxValue;
double axisMaxValueDouble;
if (CFNumberGetValue(axisMaxValueNumber, kCFNumberDoubleType, &axisMaxValueDouble))
{
weight = fmin(axisMaxValueDouble, weight);
}
}
CFTypeRef axisId = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisIdentifierKey);
if (!axisId || CFGetTypeID(axisId) != CFNumberGetTypeID()) {
continue;
}
wght_id = (CFNumberRef)axisId;
break;
CFStringRef axisNameString = (CFStringRef)axisName;
NSString *axisNameNSString = (__bridge NSString *)(axisNameString);
if (![@"Weight" isEqualToString:axisNameNSString] && ![@"Size" isEqualToString:axisNameNSString]) {
continue;
}
if (wght_id == 0) {
return ref;
CFTypeRef axisMinValue = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisMinimumValueKey);
if (axisMinValue && CFGetTypeID(axisMinValue) == CFNumberGetTypeID()) {
CFNumberRef axisMinValueNumber = (CFNumberRef)axisMinValue;
double axisMinValueDouble;
if (CFNumberGetValue(axisMinValueNumber, kCFNumberDoubleType, &axisMinValueDouble)) {
weight = fmax(axisMinValueDouble, weight);
}
}
UIFontDescriptor *uifd = font.fontDescriptor;
CTFontDescriptorRef ctfd = (__bridge CTFontDescriptorRef)(uifd);
CTFontDescriptorRef newfd = CTFontDescriptorCreateCopyWithVariation(ctfd, wght_id, (CGFloat)weight);
CTFontRef newfont = CTFontCreateCopyWithAttributes(ref, size, nil, newfd);
CFRelease(newfd);
CFTypeRef axisMaxValue = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisMaximumValueKey);
if (axisMaxValue && CFGetTypeID(axisMaxValue) == CFNumberGetTypeID()) {
CFNumberRef axisMaxValueNumber = (CFNumberRef)axisMaxValue;
double axisMaxValueDouble;
if (CFNumberGetValue(axisMaxValueNumber, kCFNumberDoubleType, &axisMaxValueDouble)) {
weight = fmin(axisMaxValueDouble, weight);
}
}
return (CTFontRef)CFAutorelease(newfont);
CFTypeRef axisId = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisIdentifierKey);
if (!axisId || CFGetTypeID(axisId) != CFNumberGetTypeID()) {
continue;
}
wght_id = (CFNumberRef)axisId;
break;
}
if (wght_id == 0) {
return ref;
}
UIFontDescriptor *uifd = font.fontDescriptor;
CTFontDescriptorRef ctfd = (__bridge CTFontDescriptorRef)(uifd);
CTFontDescriptorRef newfd = CTFontDescriptorCreateCopyWithVariation(ctfd, wght_id, (CGFloat)weight);
CTFontRef newfont = CTFontCreateCopyWithAttributes(ref, size, nil, newfd);
CFRelease(newfd);
return (CTFontRef)CFAutorelease(newfont);
}
- (void)pushIndices
{
[self->mXsIndices_ addObject:[NSNumber numberWithLong:self->mXsIndex_]];
[self->mYsIndices_ addObject:[NSNumber numberWithLong:self->mYsIndex_]];
[self->mDXsIndices_ addObject:[NSNumber numberWithLong:self->mDXsIndex_]];
[self->mDYsIndices_ addObject:[NSNumber numberWithLong:self->mDYsIndex_]];
[self->mRsIndices_ addObject:[NSNumber numberWithLong:self->mRsIndex_]];
[self->mXsIndices_ addObject:[NSNumber numberWithLong:self->mXsIndex_]];
[self->mYsIndices_ addObject:[NSNumber numberWithLong:self->mYsIndex_]];
[self->mDXsIndices_ addObject:[NSNumber numberWithLong:self->mDXsIndex_]];
[self->mDYsIndices_ addObject:[NSNumber numberWithLong:self->mDYsIndex_]];
[self->mRsIndices_ addObject:[NSNumber numberWithLong:self->mRsIndex_]];
}
- (instancetype)initWithWidth:(CGFloat)width
height:(CGFloat)height {
self = [super init];
self->mFontContext_ = [[NSMutableArray alloc]init];
self->mXsContext_ = [[NSMutableArray alloc]init];
self->mYsContext_ = [[NSMutableArray alloc]init];
self->mDXsContext_ = [[NSMutableArray alloc]init];
self->mDYsContext_ = [[NSMutableArray alloc]init];
self->mRsContext_ = [[NSMutableArray alloc]init];
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height
{
self = [super init];
self->mFontContext_ = [[NSMutableArray alloc] init];
self->mXsContext_ = [[NSMutableArray alloc] init];
self->mYsContext_ = [[NSMutableArray alloc] init];
self->mDXsContext_ = [[NSMutableArray alloc] init];
self->mDYsContext_ = [[NSMutableArray alloc] init];
self->mRsContext_ = [[NSMutableArray alloc] init];
self->mXIndices_ = [[NSMutableArray alloc]init];
self->mYIndices_ = [[NSMutableArray alloc]init];
self->mDXIndices_ = [[NSMutableArray alloc]init];
self->mDYIndices_ = [[NSMutableArray alloc]init];
self->mRIndices_ = [[NSMutableArray alloc]init];
self->mXIndices_ = [[NSMutableArray alloc] init];
self->mYIndices_ = [[NSMutableArray alloc] init];
self->mDXIndices_ = [[NSMutableArray alloc] init];
self->mDYIndices_ = [[NSMutableArray alloc] init];
self->mRIndices_ = [[NSMutableArray alloc] init];
self->mXsIndices_ = [[NSMutableArray alloc]init];
self->mYsIndices_ = [[NSMutableArray alloc]init];
self->mDXsIndices_ = [[NSMutableArray alloc]init];
self->mDYsIndices_ = [[NSMutableArray alloc]init];
self->mRsIndices_ = [[NSMutableArray alloc]init];
self->mXsIndices_ = [[NSMutableArray alloc] init];
self->mYsIndices_ = [[NSMutableArray alloc] init];
self->mDXsIndices_ = [[NSMutableArray alloc] init];
self->mDYsIndices_ = [[NSMutableArray alloc] init];
self->mRsIndices_ = [[NSMutableArray alloc] init];
self->mFontSize_ = RNSVGFontData_DEFAULT_FONT_SIZE;
self->topFont_ = [RNSVGFontData Defaults];
self->mFontSize_ = RNSVGFontData_DEFAULT_FONT_SIZE;
self->topFont_ = [RNSVGFontData Defaults];
self->mXs_ = [[NSArray alloc]init];
self->mYs_ = [[NSArray alloc]init];
self->mDXs_ = [[NSArray alloc]init];
self->mDYs_ = [[NSArray alloc]init];
self->mRs_ = [[NSArray alloc]initWithObjects:[RNSVGLength lengthWithNumber:0], nil];
self->mXs_ = [[NSArray alloc] init];
self->mYs_ = [[NSArray alloc] init];
self->mDXs_ = [[NSArray alloc] init];
self->mDYs_ = [[NSArray alloc] init];
self->mRs_ = [[NSArray alloc] initWithObjects:[RNSVGLength lengthWithNumber:0], nil];
self->mXIndex_ = -1;
self->mYIndex_ = -1;
self->mDXIndex_ = -1;
self->mDYIndex_ = -1;
self->mRIndex_ = -1;
self->mXIndex_ = -1;
self->mYIndex_ = -1;
self->mDXIndex_ = -1;
self->mDYIndex_ = -1;
self->mRIndex_ = -1;
self->mWidth_ = width;
self->mHeight_ = height;
self->mWidth_ = width;
self->mHeight_ = height;
[self->mXsContext_ addObject:self->mXs_];
[self->mYsContext_ addObject:self->mYs_];
[self->mDXsContext_ addObject:self->mDXs_];
[self->mDYsContext_ addObject:self->mDYs_];
[self->mRsContext_ addObject:self->mRs_];
[self->mXsContext_ addObject:self->mXs_];
[self->mYsContext_ addObject:self->mYs_];
[self->mDXsContext_ addObject:self->mDXs_];
[self->mDYsContext_ addObject:self->mDYs_];
[self->mRsContext_ addObject:self->mRs_];
[self->mXIndices_ addObject:[NSNumber numberWithLong:self->mXIndex_]];
[self->mYIndices_ addObject:[NSNumber numberWithLong:self->mYIndex_]];
[self->mDXIndices_ addObject:[NSNumber numberWithLong:self->mDXIndex_]];
[self->mDYIndices_ addObject:[NSNumber numberWithLong:self->mDYIndex_]];
[self->mRIndices_ addObject:[NSNumber numberWithLong:self->mRIndex_]];
[self->mXIndices_ addObject:[NSNumber numberWithLong:self->mXIndex_]];
[self->mYIndices_ addObject:[NSNumber numberWithLong:self->mYIndex_]];
[self->mDXIndices_ addObject:[NSNumber numberWithLong:self->mDXIndex_]];
[self->mDYIndices_ addObject:[NSNumber numberWithLong:self->mDYIndex_]];
[self->mRIndices_ addObject:[NSNumber numberWithLong:self->mRIndex_]];
[self->mFontContext_ addObject:self->topFont_];
[self pushIndices];
return self;
[self->mFontContext_ addObject:self->topFont_];
[self pushIndices];
return self;
}
- (RNSVGFontData *)getFont {
return topFont_;
- (RNSVGFontData *)getFont
{
return topFont_;
}
- (RNSVGFontData *)getTopOrParentFont:(RNSVGGroup *)child
{
if (self->mTop_ > 0) {
return self->topFont_;
} else {
RNSVGGroup *parentRoot = [child getParentTextRoot];
RNSVGFontData *Defaults = [RNSVGFontData Defaults];
while (parentRoot != nil) {
RNSVGFontData *map = [[parentRoot getGlyphContext] getFont];
if (map != Defaults) {
return map;
}
parentRoot = [parentRoot getParentTextRoot];
}
return Defaults;
if (self->mTop_ > 0) {
return self->topFont_;
} else {
RNSVGGroup *parentRoot = [child getParentTextRoot];
RNSVGFontData *Defaults = [RNSVGFontData Defaults];
while (parentRoot != nil) {
RNSVGFontData *map = [[parentRoot getGlyphContext] getFont];
if (map != Defaults) {
return map;
}
parentRoot = [parentRoot getParentTextRoot];
}
return Defaults;
}
}
- (void)pushNode:(RNSVGGroup *)node andFont:(NSDictionary *)font
{
RNSVGFontData *parent = [self getTopOrParentFont:node];
self->mTop_++;
if (font == nil) {
[self->mFontContext_ addObject:parent];
return;
}
RNSVGFontData *data = [RNSVGFontData initWithNSDictionary:font
parent:parent];
self->mFontSize_ = data->fontSize;
[self->mFontContext_ addObject:data];
self->topFont_ = data;
RNSVGFontData *parent = [self getTopOrParentFont:node];
self->mTop_++;
if (font == nil) {
[self->mFontContext_ addObject:parent];
return;
}
RNSVGFontData *data = [RNSVGFontData initWithNSDictionary:font parent:parent];
self->mFontSize_ = data->fontSize;
[self->mFontContext_ addObject:data];
self->topFont_ = data;
}
- (void)pushContext:(RNSVGGroup*)node
font:(NSDictionary*)font {
[self pushNode:node andFont:font];
[self pushIndices];
- (void)pushContext:(RNSVGGroup *)node font:(NSDictionary *)font
{
[self pushNode:node andFont:font];
[self pushIndices];
}
- (void)pushContext:(RNSVGText*)node
font:(NSDictionary*)font
x:(NSArray<RNSVGLength*>*)x
y:(NSArray<RNSVGLength*>*)y
deltaX:(NSArray<RNSVGLength*>*)deltaX
deltaY:(NSArray<RNSVGLength*>*)deltaY
rotate:(NSArray<RNSVGLength*>*)rotate {
[self pushNode:(RNSVGGroup*)node andFont:font];
if (x != nil && [x count] != 0) {
mXsIndex_++;
mXIndex_ = -1;
[mXIndices_ addObject:[NSNumber numberWithLong:mXIndex_]];
mXs_ = x;
[mXsContext_ addObject:mXs_];
}
if (y != nil && [y count] != 0) {
mYsIndex_++;
mYIndex_ = -1;
[mYIndices_ addObject:[NSNumber numberWithLong:mYIndex_]];
mYs_ = y;
[mYsContext_ addObject:mYs_];
}
if (deltaX != nil && [deltaX count] != 0) {
mDXsIndex_++;
mDXIndex_ = -1;
[mDXIndices_ addObject:[NSNumber numberWithLong:mDXIndex_]];
mDXs_ = deltaX;
[mDXsContext_ addObject:mDXs_];
}
if (deltaY != nil && [deltaY count] != 0) {
mDYsIndex_++;
mDYIndex_ = -1;
[mDYIndices_ addObject:[NSNumber numberWithLong:mDYIndex_]];
mDYs_ = deltaY;
[mDYsContext_ addObject:mDYs_];
}
if (rotate != nil && [rotate count] != 0) {
mRsIndex_++;
mRIndex_ = -1;
[mRIndices_ addObject:[NSNumber numberWithLong:mRIndex_]];
mRs_ = rotate;
[mRsContext_ addObject:mRs_];
}
[self pushIndices];
- (void)pushContext:(RNSVGText *)node
font:(NSDictionary *)font
x:(NSArray<RNSVGLength *> *)x
y:(NSArray<RNSVGLength *> *)y
deltaX:(NSArray<RNSVGLength *> *)deltaX
deltaY:(NSArray<RNSVGLength *> *)deltaY
rotate:(NSArray<RNSVGLength *> *)rotate
{
[self pushNode:(RNSVGGroup *)node andFont:font];
if (x != nil && [x count] != 0) {
mXsIndex_++;
mXIndex_ = -1;
[mXIndices_ addObject:[NSNumber numberWithLong:mXIndex_]];
mXs_ = x;
[mXsContext_ addObject:mXs_];
}
if (y != nil && [y count] != 0) {
mYsIndex_++;
mYIndex_ = -1;
[mYIndices_ addObject:[NSNumber numberWithLong:mYIndex_]];
mYs_ = y;
[mYsContext_ addObject:mYs_];
}
if (deltaX != nil && [deltaX count] != 0) {
mDXsIndex_++;
mDXIndex_ = -1;
[mDXIndices_ addObject:[NSNumber numberWithLong:mDXIndex_]];
mDXs_ = deltaX;
[mDXsContext_ addObject:mDXs_];
}
if (deltaY != nil && [deltaY count] != 0) {
mDYsIndex_++;
mDYIndex_ = -1;
[mDYIndices_ addObject:[NSNumber numberWithLong:mDYIndex_]];
mDYs_ = deltaY;
[mDYsContext_ addObject:mDYs_];
}
if (rotate != nil && [rotate count] != 0) {
mRsIndex_++;
mRIndex_ = -1;
[mRIndices_ addObject:[NSNumber numberWithLong:mRIndex_]];
mRs_ = rotate;
[mRsContext_ addObject:mRs_];
}
[self pushIndices];
}
- (void)popContext {
[mFontContext_ removeLastObject];
[mXsIndices_ removeLastObject];
[mYsIndices_ removeLastObject];
[mDXsIndices_ removeLastObject];
[mDYsIndices_ removeLastObject];
[mRsIndices_ removeLastObject];
- (void)popContext
{
[mFontContext_ removeLastObject];
[mXsIndices_ removeLastObject];
[mYsIndices_ removeLastObject];
[mDXsIndices_ removeLastObject];
[mDYsIndices_ removeLastObject];
[mRsIndices_ removeLastObject];
mTop_--;
mTop_--;
long x = mXsIndex_;
long y = mYsIndex_;
long dx = mDXsIndex_;
long dy = mDYsIndex_;
long r = mRsIndex_;
long x = mXsIndex_;
long y = mYsIndex_;
long dx = mDXsIndex_;
long dy = mDYsIndex_;
long r = mRsIndex_;
topFont_ = [mFontContext_ lastObject];
topFont_ = [mFontContext_ lastObject];
mXsIndex_ = [[mXsIndices_ lastObject] longValue];
mYsIndex_ = [[mYsIndices_ lastObject] longValue];
mDXsIndex_ = [[mDXsIndices_ lastObject] longValue];
mDYsIndex_ = [[mDYsIndices_ lastObject] longValue];
mRsIndex_ = [[mRsIndices_ lastObject] longValue];
mXsIndex_ = [[mXsIndices_ lastObject] longValue];
mYsIndex_ = [[mYsIndices_ lastObject] longValue];
mDXsIndex_ = [[mDXsIndices_ lastObject] longValue];
mDYsIndex_ = [[mDYsIndices_ lastObject] longValue];
mRsIndex_ = [[mRsIndices_ lastObject] longValue];
if (x != mXsIndex_) {
[mXsContext_ removeObjectAtIndex:x];
mXs_ = [mXsContext_ objectAtIndex:mXsIndex_];
mXIndex_ = [[mXIndices_ objectAtIndex:mXsIndex_] longValue];
}
if (y != mYsIndex_) {
[mYsContext_ removeObjectAtIndex:y];
mYs_ = [mYsContext_ objectAtIndex:mYsIndex_];
mYIndex_ = [[mYIndices_ objectAtIndex:mYsIndex_] longValue];
}
if (dx != mDXsIndex_) {
[mDXsContext_ removeObjectAtIndex:dx];
mDXs_ = [mDXsContext_ objectAtIndex:mDXsIndex_];
mDXIndex_ = [[mDXIndices_ objectAtIndex:mDXsIndex_] longValue];
}
if (dy != mDYsIndex_) {
[mDYsContext_ removeObjectAtIndex:dy];
mDYs_ = [mDYsContext_ objectAtIndex:mDYsIndex_];
mDYIndex_ = [[mDYIndices_ objectAtIndex:mDYsIndex_] longValue];
}
if (r != mRsIndex_) {
[mRsContext_ removeObjectAtIndex:r];
mRs_ = [mRsContext_ objectAtIndex:mRsIndex_];
mRIndex_ = [[mRIndices_ objectAtIndex:mRsIndex_] longValue];
}
if (x != mXsIndex_) {
[mXsContext_ removeObjectAtIndex:x];
mXs_ = [mXsContext_ objectAtIndex:mXsIndex_];
mXIndex_ = [[mXIndices_ objectAtIndex:mXsIndex_] longValue];
}
if (y != mYsIndex_) {
[mYsContext_ removeObjectAtIndex:y];
mYs_ = [mYsContext_ objectAtIndex:mYsIndex_];
mYIndex_ = [[mYIndices_ objectAtIndex:mYsIndex_] longValue];
}
if (dx != mDXsIndex_) {
[mDXsContext_ removeObjectAtIndex:dx];
mDXs_ = [mDXsContext_ objectAtIndex:mDXsIndex_];
mDXIndex_ = [[mDXIndices_ objectAtIndex:mDXsIndex_] longValue];
}
if (dy != mDYsIndex_) {
[mDYsContext_ removeObjectAtIndex:dy];
mDYs_ = [mDYsContext_ objectAtIndex:mDYsIndex_];
mDYIndex_ = [[mDYIndices_ objectAtIndex:mDYsIndex_] longValue];
}
if (r != mRsIndex_) {
[mRsContext_ removeObjectAtIndex:r];
mRs_ = [mRsContext_ objectAtIndex:mRsIndex_];
mRIndex_ = [[mRIndices_ objectAtIndex:mRsIndex_] longValue];
}
}
+ (void)incrementIndices:(NSMutableArray *)indices topIndex:(long)topIndex
{
for (long index = topIndex; index >= 0; index--) {
long xIndex = [[indices objectAtIndex:index] longValue];
[indices setObject:[NSNumber numberWithLong:xIndex + 1] atIndexedSubscript:index];
}
for (long index = topIndex; index >= 0; index--) {
long xIndex = [[indices objectAtIndex:index] longValue];
[indices setObject:[NSNumber numberWithLong:xIndex + 1] atIndexedSubscript:index];
}
}
// https://www.w3.org/TR/SVG11/text.html#FontSizeProperty
@@ -442,84 +442,84 @@
* Except for any additional information provided in this specification,
* the normative definition of the property is in CSS2 ([CSS2], section 15.2.4).
*/
- (CGFloat)getFontSize {
return mFontSize_;
- (CGFloat)getFontSize
{
return mFontSize_;
}
- (CGFloat)nextXWithDouble:(CGFloat)advance {
[RNSVGGlyphContext incrementIndices:mXIndices_ topIndex:mXsIndex_];
long nextIndex = mXIndex_ + 1;
if (nextIndex < [mXs_ count]) {
mDX_ = 0;
mXIndex_ = nextIndex;
RNSVGLength *length = [mXs_ objectAtIndex:nextIndex];
mX_ = [RNSVGPropHelper fromRelative:length
relative:mWidth_
fontSize:mFontSize_];
}
mX_ += advance;
return mX_;
- (CGFloat)nextXWithDouble:(CGFloat)advance
{
[RNSVGGlyphContext incrementIndices:mXIndices_ topIndex:mXsIndex_];
long nextIndex = mXIndex_ + 1;
if (nextIndex < [mXs_ count]) {
mDX_ = 0;
mXIndex_ = nextIndex;
RNSVGLength *length = [mXs_ objectAtIndex:nextIndex];
mX_ = [RNSVGPropHelper fromRelative:length relative:mWidth_ fontSize:mFontSize_];
}
mX_ += advance;
return mX_;
}
- (CGFloat)nextY {
[RNSVGGlyphContext incrementIndices:mYIndices_ topIndex:mYsIndex_];
long nextIndex = mYIndex_ + 1;
if (nextIndex < [mYs_ count]) {
mDY_ = 0;
mYIndex_ = nextIndex;
RNSVGLength *length = [mYs_ objectAtIndex:nextIndex];
mY_ = [RNSVGPropHelper fromRelative:length
relative:mHeight_
fontSize:mFontSize_];
}
return mY_;
- (CGFloat)nextY
{
[RNSVGGlyphContext incrementIndices:mYIndices_ topIndex:mYsIndex_];
long nextIndex = mYIndex_ + 1;
if (nextIndex < [mYs_ count]) {
mDY_ = 0;
mYIndex_ = nextIndex;
RNSVGLength *length = [mYs_ objectAtIndex:nextIndex];
mY_ = [RNSVGPropHelper fromRelative:length relative:mHeight_ fontSize:mFontSize_];
}
return mY_;
}
- (CGFloat)nextDeltaX {
[RNSVGGlyphContext incrementIndices:mDXIndices_ topIndex:mDXsIndex_];
long nextIndex = mDXIndex_ + 1;
if (nextIndex < [mDXs_ count]) {
mDXIndex_ = nextIndex;
RNSVGLength *length = [mDXs_ objectAtIndex:nextIndex];
CGFloat val = [RNSVGPropHelper fromRelative:length
relative:mWidth_
fontSize:mFontSize_];
mDX_ += val;
}
return mDX_;
- (CGFloat)nextDeltaX
{
[RNSVGGlyphContext incrementIndices:mDXIndices_ topIndex:mDXsIndex_];
long nextIndex = mDXIndex_ + 1;
if (nextIndex < [mDXs_ count]) {
mDXIndex_ = nextIndex;
RNSVGLength *length = [mDXs_ objectAtIndex:nextIndex];
CGFloat val = [RNSVGPropHelper fromRelative:length relative:mWidth_ fontSize:mFontSize_];
mDX_ += val;
}
return mDX_;
}
- (CGFloat)nextDeltaY {
[RNSVGGlyphContext incrementIndices:mDYIndices_ topIndex:mDYsIndex_];
long nextIndex = mDYIndex_ + 1;
if (nextIndex < [mDYs_ count]) {
mDYIndex_ = nextIndex;
RNSVGLength *length = [mDYs_ objectAtIndex:nextIndex];
CGFloat val = [RNSVGPropHelper fromRelative:length
relative:mHeight_
fontSize:mFontSize_];
mDY_ += val;
}
return mDY_;
- (CGFloat)nextDeltaY
{
[RNSVGGlyphContext incrementIndices:mDYIndices_ topIndex:mDYsIndex_];
long nextIndex = mDYIndex_ + 1;
if (nextIndex < [mDYs_ count]) {
mDYIndex_ = nextIndex;
RNSVGLength *length = [mDYs_ objectAtIndex:nextIndex];
CGFloat val = [RNSVGPropHelper fromRelative:length relative:mHeight_ fontSize:mFontSize_];
mDY_ += val;
}
return mDY_;
}
- (CGFloat)nextRotation {
[RNSVGGlyphContext incrementIndices:mRIndices_ topIndex:mRsIndex_];
long nextIndex = mRIndex_ + 1;
long count = [mRs_ count];
if (nextIndex < count) {
mRIndex_ = nextIndex;
} else {
mRIndex_ = count - 1;
}
return [mRs_[mRIndex_] value];
- (CGFloat)nextRotation
{
[RNSVGGlyphContext incrementIndices:mRIndices_ topIndex:mRsIndex_];
long nextIndex = mRIndex_ + 1;
long count = [mRs_ count];
if (nextIndex < count) {
mRIndex_ = nextIndex;
} else {
mRIndex_ = count - 1;
}
return [mRs_[mRIndex_] value];
}
- (CGFloat)getWidth {
return mWidth_;
- (CGFloat)getWidth
{
return mWidth_;
}
- (CGFloat)getHeight {
return mHeight_;
- (CGFloat)getHeight
{
return mHeight_;
}
@end

Some files were not shown because too many files have changed in this diff Show More