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 run: yarn
- name: Build - name: Build
run: yarn bob run: yarn bob
- name: Lint - name: Test and lint
run: yarn lint run: yarn test
- name: Tests
run: yarn jest
- name: Build Example App - name: Build Example App
working-directory: Example/ working-directory: Example/
run: yarn && yarn tsc 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 CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662 DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
FBLazyVector: bcdeff523be9f87a135b7c6fde8736db94904716 FBLazyVector: bcdeff523be9f87a135b7c6fde8736db94904716
FBReactNativeSpec: 226f8b0f1a2e736a49301883ee34bca88cdc24f6 FBReactNativeSpec: 0c3f104f594b34d7b3a923cd12e03b0d4e12eaf5
Flipper: 26fc4b7382499f1281eb8cb921e5c3ad6de91fe0 Flipper: 26fc4b7382499f1281eb8cb921e5c3ad6de91fe0
Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c
Flipper-DoubleConversion: 57ffbe81ef95306cc9e69c4aa3aeeeeb58a6a28c Flipper-DoubleConversion: 57ffbe81ef95306cc9e69c4aa3aeeeeb58a6a28c

View File

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

View File

@@ -10,6 +10,7 @@ buildscript {
dependencies { dependencies {
classpath("com.android.tools.build:gradle:3.6.1") 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" apply plugin: "com.facebook.react"
} }
if (project == rootProject) {
apply from: 'spotless.gradle'
}
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
def safeExtGet(prop, fallback) { 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; package com.horcrux.svg;
import android.content.Context; import android.content.Context;
import android.view.ViewGroup;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.uimanager.FabricViewStateManager; 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.HasFabricViewStateManager;
import com.facebook.react.uimanager.FabricViewStateManager.StateUpdateCallback; import com.facebook.react.uimanager.FabricViewStateManager.StateUpdateCallback;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.views.view.ReactViewGroup; import com.facebook.react.views.view.ReactViewGroup;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public abstract class FabricEnabledViewGroup extends ReactViewGroup implements HasFabricViewStateManager { public abstract class FabricEnabledViewGroup extends ReactViewGroup
private final FabricViewStateManager mFabricViewStateManager = new FabricViewStateManager(); implements HasFabricViewStateManager {
private final FabricViewStateManager mFabricViewStateManager = new FabricViewStateManager();
@NotNull @NotNull
public FabricViewStateManager getFabricViewStateManager() { public FabricViewStateManager getFabricViewStateManager() {
return this.mFabricViewStateManager; return this.mFabricViewStateManager;
} }
protected final void updateScreenSizeFabric(int width, int height) { protected final void updateScreenSizeFabric(int width, int height) {
this.updateState(width, height); this.updateState(width, height);
} }
@UiThread @UiThread
public final void updateState(int width, int height) { public final void updateState(int width, int height) {
final float realWidth = PixelUtil.toDIPFromPixel((float)width); final float realWidth = PixelUtil.toDIPFromPixel((float) width);
final float realHeight = PixelUtil.toDIPFromPixel((float)height); final float realHeight = PixelUtil.toDIPFromPixel((float) height);
ReadableMap currentState = this.mFabricViewStateManager.getStateData(); ReadableMap currentState = this.mFabricViewStateManager.getStateData();
if (currentState != null) { if (currentState != null) {
float delta = 0.9F; float delta = 0.9F;
float stateFrameHeight = currentState.hasKey("frameHeight") ? (float)currentState.getDouble("frameHeight") : 0.0F; float stateFrameHeight =
float stateFrameWidth = currentState.hasKey("frameWidth") ? (float)currentState.getDouble("frameWidth") : 0.0F; currentState.hasKey("frameHeight") ? (float) currentState.getDouble("frameHeight") : 0.0F;
if (Math.abs(stateFrameWidth - realWidth) < delta && float stateFrameWidth =
Math.abs(stateFrameHeight - realHeight) < delta) { currentState.hasKey("frameWidth") ? (float) currentState.getDouble("frameWidth") : 0.0F;
return; if (Math.abs(stateFrameWidth - realWidth) < delta
} && Math.abs(stateFrameHeight - realHeight) < delta) {
return;
} }
}
this.mFabricViewStateManager.setState((StateUpdateCallback)(new StateUpdateCallback() { this.mFabricViewStateManager.setState(
public final WritableMap getStateUpdate() { (StateUpdateCallback)
WritableMap map = (WritableMap)(new WritableNativeMap()); (new StateUpdateCallback() {
map.putDouble("frameWidth", (double)realWidth); public final WritableMap getStateUpdate() {
map.putDouble("frameHeight", (double)realHeight); WritableMap map = (WritableMap) (new WritableNativeMap());
return map; map.putDouble("frameWidth", (double) realWidth);
} map.putDouble("frameHeight", (double) realHeight);
})); return map;
} }
}));
}
public FabricEnabledViewGroup(@Nullable ReactContext context) { public FabricEnabledViewGroup(@Nullable ReactContext context) {
super((Context)context); super((Context) context);
} }
} }

View File

@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.horcrux.svg; package com.horcrux.svg;
import android.graphics.Bitmap; import android.graphics.Bitmap;
@@ -19,215 +18,214 @@ import android.graphics.RadialGradient;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.RectF; import android.graphics.RectF;
import android.graphics.Shader; import android.graphics.Shader;
import com.facebook.common.logging.FLog; import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.common.ReactConstants; import com.facebook.react.common.ReactConstants;
class Brush { class Brush {
private final BrushType mType; private final BrushType mType;
private final SVGLength[] mPoints; private final SVGLength[] mPoints;
private ReadableArray mColors; private ReadableArray mColors;
private final boolean mUseObjectBoundingBox; private final boolean mUseObjectBoundingBox;
// TODO implement pattern units // TODO implement pattern units
@SuppressWarnings({"unused"}) @SuppressWarnings({"unused"})
private boolean mUseContentObjectBoundingBoxUnits; private boolean mUseContentObjectBoundingBoxUnits;
private Matrix mMatrix; private Matrix mMatrix;
private Rect mUserSpaceBoundingBox; private Rect mUserSpaceBoundingBox;
private PatternView mPattern; private PatternView mPattern;
Brush(BrushType type, SVGLength[] points, BrushUnits units) { Brush(BrushType type, SVGLength[] points, BrushUnits units) {
mType = type; mType = type;
mPoints = points; mPoints = points;
mUseObjectBoundingBox = units == BrushUnits.OBJECT_BOUNDING_BOX; 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) { return new RectF(x, y, x + width, y + height);
mUseContentObjectBoundingBoxUnits = units == BrushUnits.OBJECT_BOUNDING_BOX; }
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) { int size = mColors.size();
mPattern = pattern; 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 { if (mType == BrushType.LINEAR_GRADIENT) {
LINEAR_GRADIENT, double x1 = getVal(mPoints[0], width, scale, textSize) + offsetX;
RADIAL_GRADIENT, double y1 = getVal(mPoints[1], height, scale, textSize) + offsetY;
PATTERN double x2 = getVal(mPoints[2], width, scale, textSize) + offsetX;
} double y2 = getVal(mPoints[3], height, scale, textSize) + offsetY;
enum BrushUnits { Shader linearGradient =
OBJECT_BOUNDING_BOX, new LinearGradient(
USER_SPACE_ON_USE (float) x1,
} (float) y1,
(float) x2,
private static void parseGradientStops(ReadableArray value, int stopsCount, float[] stops, int[] stopsColors, float opacity) { (float) y2,
for (int i = 0; i < stopsCount; i++) { stopsColors,
int stopIndex = i * 2; stops,
stops[i] = (float) value.getDouble(stopIndex); Shader.TileMode.CLAMP);
int color = value.getInt(stopIndex + 1);
int alpha = color >>> 24; if (mMatrix != null) {
int combined = Math.round((float)alpha * opacity); Matrix m = new Matrix();
stopsColors[i] = combined << 24 | (color & 0x00ffffff); m.preConcat(mMatrix);
} linearGradient.setLocalMatrix(m);
} }
void setUserSpaceBoundingBox(Rect userSpaceBoundingBox) { paint.setShader(linearGradient);
mUserSpaceBoundingBox = userSpaceBoundingBox; } else if (mType == BrushType.RADIAL_GRADIENT) {
} double rx = getVal(mPoints[2], width, scale, textSize);
double ry = getVal(mPoints[3], height, scale, textSize);
void setGradientColors(ReadableArray colors) {
mColors = colors; double ratio = ry / rx;
}
double cx = getVal(mPoints[4], width, scale, textSize) + offsetX;
void setGradientTransform(Matrix matrix) { double cy = getVal(mPoints[5], height / ratio, scale, textSize) + offsetY / ratio;
mMatrix = matrix;
} // TODO: support focus point.
// double fx = PropHelper.fromRelative(mPoints[0], width, offsetX, scale);
private RectF getPaintRect(RectF pathBoundingBox) { // double fy = PropHelper.fromRelative(mPoints[1], height, offsetY, scale) / (ry / rx);
RectF rect = mUseObjectBoundingBox ? pathBoundingBox : new RectF(mUserSpaceBoundingBox);
float width = rect.width(); Shader radialGradient =
float height = rect.height(); new RadialGradient(
float x = 0f; (float) cx, (float) cy, (float) rx, stopsColors, stops, Shader.TileMode.CLAMP);
float y = 0f;
Matrix radialMatrix = new Matrix();
if (mUseObjectBoundingBox) { radialMatrix.preScale(1f, (float) ratio);
x = rect.left;
y = rect.top; if (mMatrix != null) {
} radialMatrix.preConcat(mMatrix);
}
return new RectF(x, y, x + width, y + height);
} radialGradient.setLocalMatrix(radialMatrix);
paint.setShader(radialGradient);
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);
}
} }
}
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.horcrux.svg; package com.horcrux.svg;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
@@ -14,76 +13,76 @@ import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Path; import android.graphics.Path;
import android.graphics.RectF; import android.graphics.RectF;
import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.annotations.ReactProp;
@SuppressLint("ViewConstructor") @SuppressLint("ViewConstructor")
class EllipseView extends RenderableView { class EllipseView extends RenderableView {
private SVGLength mCx; private SVGLength mCx;
private SVGLength mCy; private SVGLength mCy;
private SVGLength mRx; private SVGLength mRx;
private SVGLength mRy; private SVGLength mRy;
public EllipseView(ReactContext reactContext) { public EllipseView(ReactContext reactContext) {
super(reactContext); super(reactContext);
} }
@ReactProp(name = "cx") @ReactProp(name = "cx")
public void setCx(Dynamic cx) { public void setCx(Dynamic cx) {
mCx = SVGLength.from(cx); mCx = SVGLength.from(cx);
invalidate(); invalidate();
} }
public void setCx(String cx) { public void setCx(String cx) {
mCx = SVGLength.from(cx); mCx = SVGLength.from(cx);
invalidate(); invalidate();
} }
@ReactProp(name = "cy") @ReactProp(name = "cy")
public void setCy(Dynamic cy) { public void setCy(Dynamic cy) {
mCy = SVGLength.from(cy); mCy = SVGLength.from(cy);
invalidate(); invalidate();
} }
public void setCy(String cy) { public void setCy(String cy) {
mCy = SVGLength.from(cy); mCy = SVGLength.from(cy);
invalidate(); invalidate();
} }
@ReactProp(name = "rx") @ReactProp(name = "rx")
public void setRx(Dynamic rx) { public void setRx(Dynamic rx) {
mRx = SVGLength.from(rx); mRx = SVGLength.from(rx);
invalidate(); invalidate();
} }
public void setRx(String rx) { public void setRx(String rx) {
mRx = SVGLength.from(rx); mRx = SVGLength.from(rx);
invalidate(); invalidate();
} }
@ReactProp(name = "ry") @ReactProp(name = "ry")
public void setRy(Dynamic ry) { public void setRy(Dynamic ry) {
mRy = SVGLength.from(ry); mRy = SVGLength.from(ry);
invalidate(); invalidate();
} }
public void setRy(String ry) { public void setRy(String ry) {
mRy = SVGLength.from(ry); mRy = SVGLength.from(ry);
invalidate(); invalidate();
} }
@Override @Override
Path getPath(Canvas canvas, Paint paint) { Path getPath(Canvas canvas, Paint paint) {
Path path = new Path(); Path path = new Path();
double cx = relativeOnWidth(mCx); double cx = relativeOnWidth(mCx);
double cy = relativeOnHeight(mCy); double cy = relativeOnHeight(mCy);
double rx = relativeOnWidth(mRx); double rx = relativeOnWidth(mRx);
double ry = relativeOnHeight(mRy); double ry = relativeOnHeight(mRy);
RectF oval = new RectF((float) (cx - rx), (float) (cy - ry), (float) (cx + rx), (float) (cy + ry)); RectF oval =
path.addOval(oval, Path.Direction.CW); 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; 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_FAMILY;
import static com.facebook.react.uimanager.ViewProps.FONT_SIZE; 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_STYLE;
import static com.facebook.react.uimanager.ViewProps.FONT_WEIGHT; import static com.facebook.react.uimanager.ViewProps.FONT_WEIGHT;
import static com.horcrux.svg.TextProperties.*; import static com.horcrux.svg.TextProperties.*;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableType;
class FontData { 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[]{ private static final FontWeight[] WEIGHTS =
FontWeight.w100, new FontWeight[] {
FontWeight.w100, FontWeight.w100,
FontWeight.w200, FontWeight.w100,
FontWeight.w300, FontWeight.w200,
FontWeight.Normal, FontWeight.w300,
FontWeight.w500, FontWeight.Normal,
FontWeight.w600, FontWeight.w500,
FontWeight.Bold, FontWeight.w600,
FontWeight.w800, FontWeight.Bold,
FontWeight.w900, FontWeight.w800,
FontWeight.w900, FontWeight.w900,
FontWeight.w900,
}; };
static FontWeight nearestFontWeight(int absoluteFontWeight) { static FontWeight nearestFontWeight(int absoluteFontWeight) {
return WEIGHTS[Math.round(absoluteFontWeight / 100f)]; 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 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; // https://drafts.csswg.org/css-fonts-4/#relative-weights
private static final double DEFAULT_WORD_SPACING = 0d; static int from(FontWeight fontWeight, FontData parent) {
private static final double DEFAULT_LETTER_SPACING = 0d; if (fontWeight == FontWeight.Bolder) {
return bolder(parent.absoluteFontWeight);
private static final String KERNING = "kerning"; } else if (fontWeight == FontWeight.Lighter) {
private static final String FONT_DATA = "fontData"; return lighter(parent.absoluteFontWeight);
private static final String TEXT_ANCHOR = "textAnchor"; } else {
private static final String WORD_SPACING = "wordSpacing"; return absoluteFontWeights[fontWeight.ordinal()];
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) { private static int bolder(int inherited) {
ReadableType propType = font.getType(prop); if (inherited < 350) {
if (propType == ReadableType.Number) { return 400;
return font.getDouble(prop); } 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 { } else {
String string = font.getString(prop); setInheritedWeight(parent);
return PropHelper.fromRelative(
string,
relative,
scale,
fontSize
);
} }
}
} else {
setInheritedWeight(parent);
} }
private void setInheritedWeight(FontData parent) { fontData = font.hasKey(FONT_DATA) ? font.getMap(FONT_DATA) : parent.fontData;
absoluteFontWeight = parent.absoluteFontWeight;
fontWeight = parent.fontWeight;
}
private void handleNumericWeight(FontData parent, double number) { fontFamily = font.hasKey(FONT_FAMILY) ? font.getString(FONT_FAMILY) : parent.fontFamily;
long weight = Math.round(number); fontStyle =
if (weight >= 1 && weight <= 1000) { font.hasKey(FONT_STYLE) ? FontStyle.valueOf(font.getString(FONT_STYLE)) : parent.fontStyle;
absoluteFontWeight = (int)weight; fontFeatureSettings =
fontWeight = AbsoluteFontWeight.nearestFontWeight(absoluteFontWeight); font.hasKey(FONT_FEATURE_SETTINGS)
} else { ? font.getString(FONT_FEATURE_SETTINGS)
setInheritedWeight(parent); : 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) { textAnchor =
double parentFontSize = parent.fontSize; 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)) { final boolean hasKerning = font.hasKey(KERNING);
fontSize = toAbsolute(font, FONT_SIZE, 1, parentFontSize, parentFontSize); manualKerning = hasKerning || parent.manualKerning;
} else {
fontSize = parentFontSize;
}
if (font.hasKey(FONT_WEIGHT)) { // https://www.w3.org/TR/SVG11/text.html#SpacingProperties
ReadableType fontWeightType = font.getType(FONT_WEIGHT); // https://drafts.csswg.org/css-text-3/#spacing
if (fontWeightType == ReadableType.Number) { // calculated values for units in: kerning, word-spacing, and, letter-spacing.
handleNumericWeight(parent, font.getDouble(FONT_WEIGHT)); kerning = hasKerning ? toAbsolute(font, KERNING, scale, fontSize, 0) : parent.kerning;
} else { wordSpacing =
String string = font.getString(FONT_WEIGHT); font.hasKey(WORD_SPACING)
if (FontWeight.hasEnum(string)) { ? toAbsolute(font, WORD_SPACING, scale, fontSize, 0)
absoluteFontWeight = AbsoluteFontWeight.from(FontWeight.get(string), parent); : parent.wordSpacing;
fontWeight = AbsoluteFontWeight.nearestFontWeight(absoluteFontWeight); letterSpacing =
} else if (string != null) { font.hasKey(LETTER_SPACING)
handleNumericWeight(parent, Double.parseDouble(string)); ? toAbsolute(font, LETTER_SPACING, scale, fontSize, 0)
} else { : parent.letterSpacing;
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;
}
} }

View File

@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.horcrux.svg; package com.horcrux.svg;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
@@ -15,9 +14,7 @@ import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.RectF; import android.graphics.RectF;
import android.view.View; import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.annotations.ReactProp;
@@ -25,136 +22,136 @@ import com.facebook.react.uimanager.annotations.ReactProp;
@SuppressLint("ViewConstructor") @SuppressLint("ViewConstructor")
class ForeignObjectView extends GroupView { class ForeignObjectView extends GroupView {
SVGLength mX; SVGLength mX;
SVGLength mY; SVGLength mY;
SVGLength mW; SVGLength mW;
SVGLength mH; SVGLength mH;
public ForeignObjectView(ReactContext reactContext) { public ForeignObjectView(ReactContext reactContext) {
super(reactContext); super(reactContext);
} }
@Override @Override
void draw(Canvas canvas, Paint paint, float opacity) { void draw(Canvas canvas, Paint paint, float opacity) {
float x = (float)relativeOnWidth(mX); float x = (float) relativeOnWidth(mX);
float y = (float)relativeOnHeight(mY); float y = (float) relativeOnHeight(mY);
float w = (float)relativeOnWidth(mW); float w = (float) relativeOnWidth(mW);
float h = (float)relativeOnHeight(mH); float h = (float) relativeOnHeight(mH);
canvas.translate(x, y); canvas.translate(x, y);
canvas.clipRect(0, 0, w, h); canvas.clipRect(0, 0, w, h);
super.draw(canvas, paint, opacity); super.draw(canvas, paint, opacity);
} }
@Override @Override
public void onDescendantInvalidated(@NonNull View child, @NonNull View target) { public void onDescendantInvalidated(@NonNull View child, @NonNull View target) {
super.onDescendantInvalidated(child, target); super.onDescendantInvalidated(child, target);
invalidate(); invalidate();
} }
@ReactProp(name = "x") @ReactProp(name = "x")
public void setX(Dynamic x) { public void setX(Dynamic x) {
mX = SVGLength.from(x); mX = SVGLength.from(x);
invalidate(); invalidate();
} }
public void setX(String x) { public void setX(String x) {
mX = SVGLength.from(x); mX = SVGLength.from(x);
invalidate(); invalidate();
} }
@ReactProp(name = "y") @ReactProp(name = "y")
public void setY(Dynamic y) { public void setY(Dynamic y) {
mY = SVGLength.from(y); mY = SVGLength.from(y);
invalidate(); invalidate();
} }
public void setY(String y) { public void setY(String y) {
mY = SVGLength.from(y); mY = SVGLength.from(y);
invalidate(); invalidate();
} }
@ReactProp(name = "width") @ReactProp(name = "width")
public void setWidth(Dynamic width) { public void setWidth(Dynamic width) {
mW = SVGLength.from(width); mW = SVGLength.from(width);
invalidate(); invalidate();
} }
public void setWidth(String width) { public void setWidth(String width) {
mW = SVGLength.from(width); mW = SVGLength.from(width);
invalidate(); invalidate();
} }
@ReactProp(name = "height") @ReactProp(name = "height")
public void setHeight(Dynamic height) { public void setHeight(Dynamic height) {
mH = SVGLength.from(height); mH = SVGLength.from(height);
invalidate(); invalidate();
} }
public void setHeight(String height) { public void setHeight(String height) {
mH = SVGLength.from(height); mH = SVGLength.from(height);
invalidate(); invalidate();
} }
void drawGroup(final Canvas canvas, final Paint paint, final float opacity) { void drawGroup(final Canvas canvas, final Paint paint, final float opacity) {
pushGlyphContext(); pushGlyphContext();
final SvgView svg = getSvgView(); final SvgView svg = getSvgView();
final GroupView self = this; final GroupView self = this;
final RectF groupRect = new RectF(); final RectF groupRect = new RectF();
for (int i = 0; i < getChildCount(); i++) { for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i); View child = getChildAt(i);
if (child instanceof MaskView) { if (child instanceof MaskView) {
continue; continue;
} }
if (child instanceof VirtualView) { if (child instanceof VirtualView) {
VirtualView node = ((VirtualView)child); VirtualView node = ((VirtualView) child);
if ("none".equals(node.mDisplay)) { if ("none".equals(node.mDisplay)) {
continue; continue;
} }
if (node instanceof RenderableView) { if (node instanceof RenderableView) {
((RenderableView)node).mergeProperties(self); ((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);
}
} }
this.setClientRect(groupRect);
popGlyphContext();
}
// Enable rendering other native ancestor views in e.g. masks, but don't render them another time int count = node.saveAndSetupCanvas(canvas, mCTM);
Bitmap fakeBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); node.render(canvas, paint, opacity * mOpacity);
Canvas fake = new Canvas(fakeBitmap); RectF r = node.getClientRect();
if (r != null) {
groupRect.union(r);
}
@Override node.restoreCanvas(canvas, count);
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(fake);
}
protected boolean drawChild(Canvas canvas, View child, long drawingTime) { if (node instanceof RenderableView) {
return super.drawChild(fake, child, drawingTime); ((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. * LICENSE file in the root directory of this source tree.
*/ */
package com.horcrux.svg; package com.horcrux.svg;
import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableMap;
import java.util.ArrayList; import java.util.ArrayList;
import javax.annotation.Nullable; import javax.annotation.Nullable;
// https://www.w3.org/TR/SVG/text.html#TSpanElement // https://www.w3.org/TR/SVG/text.html#TSpanElement
class GlyphContext { class GlyphContext {
// Current stack (one per node push/pop) // Current stack (one per node push/pop)
final ArrayList<FontData> mFontContext = new ArrayList<>(); final ArrayList<FontData> mFontContext = new ArrayList<>();
// Unique input attribute lists (only added if node sets a value) // Unique input attribute lists (only added if node sets a value)
private final ArrayList<SVGLength[]> mXsContext = new ArrayList<>(); private final ArrayList<SVGLength[]> mXsContext = new ArrayList<>();
private final ArrayList<SVGLength[]> mYsContext = new ArrayList<>(); private final ArrayList<SVGLength[]> mYsContext = new ArrayList<>();
private final ArrayList<SVGLength[]> mDXsContext = new ArrayList<>(); private final ArrayList<SVGLength[]> mDXsContext = new ArrayList<>();
private final ArrayList<SVGLength[]> mDYsContext = new ArrayList<>(); private final ArrayList<SVGLength[]> mDYsContext = new ArrayList<>();
private final ArrayList<double[]> mRsContext = new ArrayList<>(); private final ArrayList<double[]> mRsContext = new ArrayList<>();
// Unique index into attribute list (one per unique list) // Unique index into attribute list (one per unique list)
private final ArrayList<Integer> mXIndices = new ArrayList<>(); private final ArrayList<Integer> mXIndices = new ArrayList<>();
private final ArrayList<Integer> mYIndices = new ArrayList<>(); private final ArrayList<Integer> mYIndices = new ArrayList<>();
private final ArrayList<Integer> mDXIndices = new ArrayList<>(); private final ArrayList<Integer> mDXIndices = new ArrayList<>();
private final ArrayList<Integer> mDYIndices = new ArrayList<>(); private final ArrayList<Integer> mDYIndices = new ArrayList<>();
private final ArrayList<Integer> mRIndices = new ArrayList<>(); private final ArrayList<Integer> mRIndices = new ArrayList<>();
// Index of unique context used (one per node push/pop) // Index of unique context used (one per node push/pop)
private final ArrayList<Integer> mXsIndices = new ArrayList<>(); private final ArrayList<Integer> mXsIndices = new ArrayList<>();
private final ArrayList<Integer> mYsIndices = new ArrayList<>(); private final ArrayList<Integer> mYsIndices = new ArrayList<>();
private final ArrayList<Integer> mDXsIndices = new ArrayList<>(); private final ArrayList<Integer> mDXsIndices = new ArrayList<>();
private final ArrayList<Integer> mDYsIndices = new ArrayList<>(); private final ArrayList<Integer> mDYsIndices = new ArrayList<>();
private final ArrayList<Integer> mRsIndices = new ArrayList<>(); private final ArrayList<Integer> mRsIndices = new ArrayList<>();
// Calculated on push context, percentage and em length depends on parent font size // Calculated on push context, percentage and em length depends on parent font size
private double mFontSize = FontData.DEFAULT_FONT_SIZE; private double mFontSize = FontData.DEFAULT_FONT_SIZE;
private FontData topFont = FontData.Defaults; private FontData topFont = FontData.Defaults;
// Current accumulated values // Current accumulated values
// https://www.w3.org/TR/SVG/types.html#DataTypeCoordinate // https://www.w3.org/TR/SVG/types.html#DataTypeCoordinate
// <coordinate> syntax is the same as that for <length> // <coordinate> syntax is the same as that for <length>
private double mX; private double mX;
private double mY; private double mY;
// https://www.w3.org/TR/SVG/types.html#Length // https://www.w3.org/TR/SVG/types.html#Length
private double mDX; private double mDX;
private double mDY; private double mDY;
// Current <list-of-coordinates> SVGLengthList // Current <list-of-coordinates> SVGLengthList
// https://www.w3.org/TR/SVG/types.html#InterfaceSVGLengthList // https://www.w3.org/TR/SVG/types.html#InterfaceSVGLengthList
// https://www.w3.org/TR/SVG/types.html#DataTypeCoordinates // https://www.w3.org/TR/SVG/types.html#DataTypeCoordinates
// https://www.w3.org/TR/SVG/text.html#TSpanElementXAttribute // https://www.w3.org/TR/SVG/text.html#TSpanElementXAttribute
private SVGLength[] mXs = new SVGLength[]{}; private SVGLength[] mXs = new SVGLength[] {};
// https://www.w3.org/TR/SVG/text.html#TSpanElementYAttribute // https://www.w3.org/TR/SVG/text.html#TSpanElementYAttribute
private SVGLength[] mYs = new SVGLength[]{}; private SVGLength[] mYs = new SVGLength[] {};
// Current <list-of-lengths> SVGLengthList // Current <list-of-lengths> SVGLengthList
// https://www.w3.org/TR/SVG/types.html#DataTypeLengths // https://www.w3.org/TR/SVG/types.html#DataTypeLengths
// https://www.w3.org/TR/SVG/text.html#TSpanElementDXAttribute // https://www.w3.org/TR/SVG/text.html#TSpanElementDXAttribute
private SVGLength[] mDXs = new SVGLength[]{}; private SVGLength[] mDXs = new SVGLength[] {};
// https://www.w3.org/TR/SVG/text.html#TSpanElementDYAttribute // https://www.w3.org/TR/SVG/text.html#TSpanElementDYAttribute
private SVGLength[] mDYs = new SVGLength[]{}; private SVGLength[] mDYs = new SVGLength[] {};
// Current <list-of-numbers> SVGLengthList // Current <list-of-numbers> SVGLengthList
// https://www.w3.org/TR/SVG/types.html#DataTypeNumbers // https://www.w3.org/TR/SVG/types.html#DataTypeNumbers
// https://www.w3.org/TR/SVG/text.html#TSpanElementRotateAttribute // https://www.w3.org/TR/SVG/text.html#TSpanElementRotateAttribute
private double[] mRs = new double[]{0}; private double[] mRs = new double[] {0};
// Current attribute list index // Current attribute list index
private int mXsIndex; private int mXsIndex;
private int mYsIndex; private int mYsIndex;
private int mDXsIndex; private int mDXsIndex;
private int mDYsIndex; private int mDYsIndex;
private int mRsIndex; private int mRsIndex;
// Current value index in current attribute list // Current value index in current attribute list
private int mXIndex = -1; private int mXIndex = -1;
private int mYIndex = -1; private int mYIndex = -1;
private int mDXIndex = -1; private int mDXIndex = -1;
private int mDYIndex = -1; private int mDYIndex = -1;
private int mRIndex = -1; private int mRIndex = -1;
// Top index of stack // Top index of stack
private int mTop; private int mTop;
// Constructor parameters // Constructor parameters
private final float mScale; private final float mScale;
private final float mWidth; private final float mWidth;
private final float mHeight; private final float mHeight;
private void pushIndices() { private void pushIndices() {
mXsIndices.add(mXsIndex); mXsIndices.add(mXsIndex);
mYsIndices.add(mYsIndex); mYsIndices.add(mYsIndex);
mDXsIndices.add(mDXsIndex); mDXsIndices.add(mDXsIndex);
mDYsIndices.add(mDYsIndex); mDYsIndices.add(mDYsIndex);
mRsIndices.add(mRsIndex); 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) { FontData data = new FontData(font, parent, mScale);
mScale = scale; mFontSize = data.fontSize;
mWidth = width; mFontContext.add(data);
mHeight = height; topFont = data;
}
mXsContext.add(mXs); void pushContext(GroupView node, @Nullable ReadableMap font) {
mYsContext.add(mYs); pushNodeAndFont(node, font);
mDXsContext.add(mDXs); pushIndices();
mDYsContext.add(mDYs); }
mRsContext.add(mRs);
mXIndices.add(mXIndex); private SVGLength[] getStringArrayFromReadableArray(ArrayList<SVGLength> readableArray) {
mYIndices.add(mYIndex); int size = readableArray.size();
mDXIndices.add(mDXIndex); SVGLength[] strings = new SVGLength[size];
mDYIndices.add(mDYIndex); for (int i = 0; i < size; i++) {
mRIndices.add(mRIndex); 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() { pushNodeAndFont(node, font);
mXsIndex = mYsIndex = mDXsIndex = mDYsIndex = mRsIndex = 0;
mXIndex = mYIndex = mDXIndex = mDYIndex = mRIndex = -1; if (x != null && x.size() != 0) {
mX = mY = mDX = mDY = 0; mXsIndex++;
mXIndex = -1;
mXIndices.add(mXIndex);
mXs = getStringArrayFromReadableArray(x);
mXsContext.add(mXs);
} }
FontData getFont() { if (y != null && y.size() != 0) {
return topFont; mYsIndex++;
mYIndex = -1;
mYIndices.add(mYIndex);
mYs = getStringArrayFromReadableArray(y);
mYsContext.add(mYs);
} }
private FontData getTopOrParentFont(GroupView child) { if (deltaX != null && deltaX.size() != 0) {
if (mTop > 0) { mDXsIndex++;
return topFont; mDXIndex = -1;
} else { mDXIndices.add(mDXIndex);
GroupView parentRoot = child.getParentTextRoot(); mDXs = getStringArrayFromReadableArray(deltaX);
mDXsContext.add(mDXs);
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) { if (deltaY != null && deltaY.size() != 0) {
FontData parent = getTopOrParentFont(node); mDYsIndex++;
mTop++; mDYIndex = -1;
mDYIndices.add(mDYIndex);
if (font == null) { mDYs = getStringArrayFromReadableArray(deltaY);
mFontContext.add(parent); mDYsContext.add(mDYs);
return;
}
FontData data = new FontData(font, parent, mScale);
mFontSize = data.fontSize;
mFontContext.add(data);
topFont = data;
} }
void pushContext(GroupView node, @Nullable ReadableMap font) { if (rotate != null && rotate.size() != 0) {
pushNodeAndFont(node, font); mRsIndex++;
pushIndices(); mRIndex = -1;
mRIndices.add(mRIndex);
mRs = getDoubleArrayFromReadableArray(rotate);
mRsContext.add(mRs);
} }
private SVGLength[] getStringArrayFromReadableArray(ArrayList<SVGLength> readableArray) { pushIndices();
int size = readableArray.size(); }
SVGLength[] strings = new SVGLength[size];
for (int i = 0; i < size; i++) { void popContext() {
strings[i] = readableArray.get(i); mFontContext.remove(mTop);
} mXsIndices.remove(mTop);
return strings; 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) { mX += advance;
int size = readableArray.size();
double[] doubles = new double[size]; return mX;
for (int i = 0; i < size; i++) { }
SVGLength length = readableArray.get(i);
doubles[i] = length.value; double nextY() {
} incrementIndices(mYIndices, mYsIndex);
return doubles;
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( return mY;
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();
}
pushNodeAndFont(node, font); double nextDeltaX() {
incrementIndices(mDXIndices, mDXsIndex);
if (x != null && x.size() != 0) { int nextIndex = mDXIndex + 1;
mXsIndex++; if (nextIndex < mDXs.length) {
mXIndex = -1; mDXIndex = nextIndex;
mXIndices.add(mXIndex); SVGLength string = mDXs[nextIndex];
mXs = getStringArrayFromReadableArray(x); double val = PropHelper.fromRelative(string, mWidth, 0, mScale, mFontSize);
mXsContext.add(mXs); mDX += val;
}
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();
} }
void popContext() { return mDX;
mFontContext.remove(mTop); }
mXsIndices.remove(mTop);
mYsIndices.remove(mTop);
mDXsIndices.remove(mTop);
mDYsIndices.remove(mTop);
mRsIndices.remove(mTop);
mTop--; double nextDeltaY() {
incrementIndices(mDYIndices, mDYsIndex);
int x = mXsIndex; int nextIndex = mDYIndex + 1;
int y = mYsIndex; if (nextIndex < mDYs.length) {
int dx = mDXsIndex; mDYIndex = nextIndex;
int dy = mDYsIndex; SVGLength string = mDYs[nextIndex];
int r = mRsIndex; double val = PropHelper.fromRelative(string, mHeight, 0, mScale, mFontSize);
mDY += val;
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) { return mDY;
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 double nextRotation() {
incrementIndices(mRIndices, mRsIndex);
/** mRIndex = Math.min(mRIndex + 1, mRs.length - 1);
* 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) { return mRs[mRIndex];
incrementIndices(mXIndices, mXsIndex); }
int nextIndex = mXIndex + 1; float getWidth() {
if (nextIndex < mXs.length) { return mWidth;
mDX = 0; }
mXIndex = nextIndex;
SVGLength string = mXs[nextIndex];
mX = PropHelper.fromRelative(string, mWidth, 0, mScale, mFontSize);
}
mX += advance; float getHeight() {
return mHeight;
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;
}
} }

View File

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

View File

@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.horcrux.svg; package com.horcrux.svg;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
@@ -19,268 +18,264 @@ import android.graphics.RectF;
import android.graphics.Region; import android.graphics.Region;
import android.os.Build; import android.os.Build;
import android.view.View; import android.view.View;
import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.views.view.ReactViewGroup;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@SuppressLint("ViewConstructor") @SuppressLint("ViewConstructor")
class GroupView extends RenderableView { class GroupView extends RenderableView {
@Nullable ReadableMap mFont; @Nullable ReadableMap mFont;
private GlyphContext mGlyphContext; private GlyphContext mGlyphContext;
public GroupView(ReactContext reactContext) { public GroupView(ReactContext reactContext) {
super(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);
} }
if (mTransform != null) {
@ReactProp(name = "font") mTransform.mapRect(clipBounds);
public void setFont(@Nullable ReadableMap font) {
mFont = font;
invalidate();
} }
mGlyphContext = new GlyphContext(mScale, clipBounds.width(), clipBounds.height());
}
void setupGlyphContext(Canvas canvas) { GlyphContext getGlyphContext() {
RectF clipBounds = new RectF(canvas.getClipBounds()); return mGlyphContext;
if (mMatrix != null) { }
mMatrix.mapRect(clipBounds);
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) { if (node instanceof RenderableView) {
mTransform.mapRect(clipBounds); ((RenderableView) node).mergeProperties(self);
}
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);
}
} }
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) { return mPath;
final Path path = new Path(); }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { Path getPath(final Canvas canvas, final Paint paint, final Region.Op op) {
final Path.Op pop = Path.Op.valueOf(op.name()); final Path path = new Path();
for (int i = 0; i < getChildCount(); i++) {
View node = getChildAt(i); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (node instanceof MaskView) { final Path.Op pop = Path.Op.valueOf(op.name());
continue; for (int i = 0; i < getChildCount(); i++) {
} View node = getChildAt(i);
if (node instanceof VirtualView) { if (node instanceof MaskView) {
VirtualView n = (VirtualView)node; continue;
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());
} }
if (node instanceof VirtualView) {
return path; 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 return path;
int hitTest(final float[] src) { }
if (!mInvertible || !mTransformInvertible) {
return -1;
}
float[] dst = new float[2]; @Override
mInvMatrix.mapPoints(dst, src); int hitTest(final float[] src) {
mInvTransform.mapPoints(dst); if (!mInvertible || !mTransformInvertible) {
return -1;
}
int x = Math.round(dst[0]); float[] dst = new float[2];
int y = Math.round(dst[1]); mInvMatrix.mapPoints(dst, src);
mInvTransform.mapPoints(dst);
Path clipPath = getClipPath(); int x = Math.round(dst[0]);
if (clipPath != null) { int y = Math.round(dst[1]);
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;
}
}
}
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; return -1;
}
} }
void saveDefinition() { for (int i = getChildCount() - 1; i >= 0; i--) {
if (mName != null) { View child = getChildAt(i);
getSvgView().defineTemplate(this, mName); if (child instanceof VirtualView) {
if (child instanceof MaskView) {
continue;
} }
for (int i = 0; i < getChildCount(); i++) { VirtualView node = (VirtualView) child;
View node = getChildAt(i);
if (node instanceof VirtualView) { int hitChild = node.hitTest(dst);
((VirtualView)node).saveDefinition(); 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 return -1;
void resetProperties() { }
for (int i = 0; i < getChildCount(); i++) {
View node = getChildAt(i); void saveDefinition() {
if (node instanceof RenderableView) { if (mName != null) {
((RenderableView)node).resetProperties(); 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. * LICENSE file in the root directory of this source tree.
*/ */
package com.horcrux.svg; package com.horcrux.svg;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
@@ -17,7 +16,6 @@ import android.graphics.Paint;
import android.graphics.Path; import android.graphics.Path;
import android.graphics.RectF; import android.graphics.RectF;
import android.net.Uri; import android.net.Uri;
import com.facebook.common.executors.UiThreadImmediateExecutorService; import com.facebook.common.executors.UiThreadImmediateExecutorService;
import com.facebook.common.logging.FLog; import com.facebook.common.logging.FLog;
import com.facebook.common.references.CloseableReference; 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.uimanager.annotations.ReactProp;
import com.facebook.react.views.imagehelper.ImageSource; import com.facebook.react.views.imagehelper.ImageSource;
import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper; import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@SuppressLint("ViewConstructor") @SuppressLint("ViewConstructor")
class ImageView extends RenderableView { class ImageView extends RenderableView {
private SVGLength mX; private SVGLength mX;
private SVGLength mY; private SVGLength mY;
private SVGLength mW; private SVGLength mW;
private SVGLength mH; private SVGLength mH;
private String uriString; private String uriString;
private int mImageWidth; private int mImageWidth;
private int mImageHeight; private int mImageHeight;
private String mAlign; private String mAlign;
private int mMeetOrSlice; private int mMeetOrSlice;
private final AtomicBoolean mLoading = new AtomicBoolean(false); private final AtomicBoolean mLoading = new AtomicBoolean(false);
public ImageView(ReactContext reactContext) { public ImageView(ReactContext reactContext) {
super(reactContext); super(reactContext);
} }
@ReactProp(name = "x") @ReactProp(name = "x")
public void setX(Dynamic x) { public void setX(Dynamic x) {
mX = SVGLength.from(x); mX = SVGLength.from(x);
invalidate(); invalidate();
} }
public void setX(String x) { public void setX(String x) {
mX = SVGLength.from(x); mX = SVGLength.from(x);
invalidate(); invalidate();
} }
@ReactProp(name = "y") @ReactProp(name = "y")
public void setY(Dynamic y) { public void setY(Dynamic y) {
mY = SVGLength.from(y); mY = SVGLength.from(y);
invalidate(); invalidate();
} }
public void setY(String y) { public void setY(String y) {
mY = SVGLength.from(y); mY = SVGLength.from(y);
invalidate(); invalidate();
} }
@ReactProp(name = "width") @ReactProp(name = "width")
public void setWidth(Dynamic width) { public void setWidth(Dynamic width) {
mW = SVGLength.from(width); mW = SVGLength.from(width);
invalidate(); invalidate();
} }
public void setWidth(String width) { public void setWidth(String width) {
mW = SVGLength.from(width); mW = SVGLength.from(width);
invalidate(); invalidate();
} }
@ReactProp(name = "height") @ReactProp(name = "height")
public void setHeight(Dynamic height) { public void setHeight(Dynamic height) {
mH = SVGLength.from(height); mH = SVGLength.from(height);
invalidate(); invalidate();
} }
public void setHeight(String height) { public void setHeight(String height) {
mH = SVGLength.from(height); mH = SVGLength.from(height);
invalidate(); invalidate();
} }
@ReactProp(name = "src")
public void setSrc(@Nullable ReadableMap src) {
if (src != null) {
uriString = src.getString("uri");
@ReactProp(name = "src") if (uriString == null || uriString.isEmpty()) {
public void setSrc(@Nullable ReadableMap src) { // TODO: give warning about this
if (src != null) { return;
uriString = src.getString("uri"); }
if (uriString == null || uriString.isEmpty()) { if (src.hasKey("width") && src.hasKey("height")) {
//TODO: give warning about this mImageWidth = src.getInt("width");
return; mImageHeight = src.getInt("height");
} } else {
mImageWidth = 0;
if (src.hasKey("width") && src.hasKey("height")) { mImageHeight = 0;
mImageWidth = src.getInt("width"); }
mImageHeight = src.getInt("height"); Uri mUri = Uri.parse(uriString);
} else { if (mUri.getScheme() == null) {
mImageWidth = 0; ResourceDrawableIdHelper.getInstance().getResourceDrawableUri(mContext, uriString);
mImageHeight = 0; }
}
Uri mUri = Uri.parse(uriString);
if (mUri.getScheme() == null) {
ResourceDrawableIdHelper.getInstance().getResourceDrawableUri(mContext, uriString);
}
}
} }
}
@ReactProp(name = "align") @ReactProp(name = "align")
public void setAlign(String align) { public void setAlign(String align) {
mAlign = align; mAlign = align;
invalidate(); 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") @Override
public void setMeetOrSlice(int meetOrSlice) { Path getPath(Canvas canvas, Paint paint) {
mMeetOrSlice = meetOrSlice; mPath = new Path();
invalidate(); mPath.addRect(getRect(), Path.Direction.CW);
} return mPath;
}
@Override private void loadBitmap(final ImagePipeline imagePipeline, final ImageRequest request) {
void draw(final Canvas canvas, final Paint paint, final float opacity) { mLoading.set(true);
if (!mLoading.get()) { final DataSource<CloseableReference<CloseableImage>> dataSource =
ImagePipeline imagePipeline = Fresco.getImagePipeline(); imagePipeline.fetchDecodedImage(request, mContext);
ImageSource imageSource = new ImageSource(mContext, uriString); BaseBitmapDataSubscriber subscriber =
ImageRequest request = ImageRequest.fromUri(imageSource.getUri()); new BaseBitmapDataSubscriber() {
boolean inMemoryCache = imagePipeline.isInBitmapMemoryCache(request); @Override
public void onNewResultImpl(Bitmap bitmap) {
if (inMemoryCache) { mLoading.set(false);
tryRenderFromBitmapCache(imagePipeline, request, canvas, paint, opacity * mOpacity); SvgView view = getSvgView();
} else { if (view != null) {
loadBitmap(imagePipeline, request); view.invalidate();
} }
} }
}
@Override @Override
Path getPath(Canvas canvas, Paint paint) { public void onFailureImpl(DataSource dataSource) {
mPath = new Path(); // No cleanup required here.
mPath.addRect(getRect(), Path.Direction.CW); // TODO: more details about this failure
return mPath; mLoading.set(false);
} FLog.w(
ReactConstants.TAG,
private void loadBitmap(final ImagePipeline imagePipeline, final ImageRequest request) { dataSource.getFailureCause(),
mLoading.set(true); "RNSVG: fetchDecodedImage failed!");
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!");
}
}; };
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 return new RectF((float) x, (float) y, (float) (x + w), (float) (y + h));
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)); 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) { RectF renderRect = getRect();
if (mImageWidth == 0 || mImageHeight == 0) { RectF vbRect = new RectF(0, 0, mImageWidth, mImageHeight);
mImageWidth = bitmap.getWidth(); Matrix transform = ViewBox.getTransform(vbRect, renderRect, mAlign, mMeetOrSlice);
mImageHeight = bitmap.getHeight(); transform.mapRect(vbRect);
}
RectF renderRect = getRect(); canvas.clipPath(getPath(canvas, paint));
RectF vbRect = new RectF(0, 0, mImageWidth, mImageHeight);
Matrix transform = ViewBox.getTransform(vbRect, renderRect, mAlign, mMeetOrSlice);
transform.mapRect(vbRect);
canvas.clipPath(getPath(canvas, paint)); Path clipPath = getClipPath(canvas, paint);
if (clipPath != null) {
Path clipPath = getClipPath(canvas, paint); canvas.clipPath(clipPath);
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);
} }
private void tryRenderFromBitmapCache(ImagePipeline imagePipeline, ImageRequest request, Canvas canvas, Paint paint, float opacity) { Paint alphaPaint = new Paint();
final DataSource<CloseableReference<CloseableImage>> dataSource alphaPaint.setAlpha((int) (opacity * 255));
= imagePipeline.fetchImageFromBitmapCache(request, mContext); canvas.drawBitmap(bitmap, null, vbRect, alphaPaint);
mCTM.mapRect(vbRect);
this.setClientRect(vbRect);
}
try { private void tryRenderFromBitmapCache(
final CloseableReference<CloseableImage> imageReference = dataSource.getResult(); ImagePipeline imagePipeline,
if (imageReference == null) { ImageRequest request,
return; Canvas canvas,
} Paint paint,
float opacity) {
final DataSource<CloseableReference<CloseableImage>> dataSource =
imagePipeline.fetchImageFromBitmapCache(request, mContext);
try { try {
CloseableImage closeableImage = imageReference.get(); final CloseableReference<CloseableImage> imageReference = dataSource.getResult();
if (!(closeableImage instanceof CloseableBitmap)) { if (imageReference == null) {
return; return;
} }
CloseableBitmap closeableBitmap = (CloseableBitmap) closeableImage; try {
final Bitmap bitmap = closeableBitmap.getUnderlyingBitmap(); CloseableImage closeableImage = imageReference.get();
if (!(closeableImage instanceof CloseableBitmap)) {
if (bitmap == null) { return;
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();
} }
}
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. * LICENSE file in the root directory of this source tree.
*/ */
package com.horcrux.svg; package com.horcrux.svg;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Path; import android.graphics.Path;
import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.annotations.ReactProp;
@SuppressLint("ViewConstructor") @SuppressLint("ViewConstructor")
class LineView extends RenderableView { class LineView extends RenderableView {
private SVGLength mX1; private SVGLength mX1;
private SVGLength mY1; private SVGLength mY1;
private SVGLength mX2; private SVGLength mX2;
private SVGLength mY2; private SVGLength mY2;
public LineView(ReactContext reactContext) { public LineView(ReactContext reactContext) {
super(reactContext); super(reactContext);
} }
@ReactProp(name = "x1") @ReactProp(name = "x1")
public void setX1(Dynamic x1) { public void setX1(Dynamic x1) {
mX1 = SVGLength.from(x1); mX1 = SVGLength.from(x1);
invalidate(); invalidate();
} }
public void setX1(String x1) { public void setX1(String x1) {
mX1 = SVGLength.from(x1); mX1 = SVGLength.from(x1);
invalidate(); invalidate();
} }
@ReactProp(name = "y1") @ReactProp(name = "y1")
public void setY1(Dynamic y1) { public void setY1(Dynamic y1) {
mY1 = SVGLength.from(y1); mY1 = SVGLength.from(y1);
invalidate(); invalidate();
} }
public void setY1(String y1) { public void setY1(String y1) {
mY1 = SVGLength.from(y1); mY1 = SVGLength.from(y1);
invalidate(); invalidate();
} }
@ReactProp(name = "x2") @ReactProp(name = "x2")
public void setX2(Dynamic x2) { public void setX2(Dynamic x2) {
mX2 = SVGLength.from(x2); mX2 = SVGLength.from(x2);
invalidate(); invalidate();
} }
public void setX2(String x2) { public void setX2(String x2) {
mX2 = SVGLength.from(x2); mX2 = SVGLength.from(x2);
invalidate(); invalidate();
} }
@ReactProp(name = "y2") @ReactProp(name = "y2")
public void setY2(Dynamic y2) { public void setY2(Dynamic y2) {
mY2 = SVGLength.from(y2); mY2 = SVGLength.from(y2);
invalidate(); invalidate();
} }
public void setY2(String y2) { public void setY2(String y2) {
mY2 = SVGLength.from(y2); mY2 = SVGLength.from(y2);
invalidate(); invalidate();
} }
@Override @Override
Path getPath(Canvas canvas, Paint paint) { Path getPath(Canvas canvas, Paint paint) {
Path path = new Path(); Path path = new Path();
double x1 = relativeOnWidth(mX1); double x1 = relativeOnWidth(mX1);
double y1 = relativeOnHeight(mY1); double y1 = relativeOnHeight(mY1);
double x2 = relativeOnWidth(mX2); double x2 = relativeOnWidth(mX2);
double y2 = relativeOnHeight(mY2); double y2 = relativeOnHeight(mY2);
path.moveTo((float) x1, (float) y1); path.moveTo((float) x1, (float) y1);
path.lineTo((float) x2, (float) y2); path.lineTo((float) x2, (float) y2);
return path; return path;
} }
} }

View File

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

View File

@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.horcrux.svg; package com.horcrux.svg;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
@@ -15,7 +14,6 @@ import android.graphics.Matrix;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.RectF; import android.graphics.RectF;
import android.view.View; import android.view.View;
import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.annotations.ReactProp;
@@ -23,167 +21,173 @@ import com.facebook.react.uimanager.annotations.ReactProp;
@SuppressLint("ViewConstructor") @SuppressLint("ViewConstructor")
class MarkerView extends GroupView { class MarkerView extends GroupView {
private SVGLength mRefX; private SVGLength mRefX;
private SVGLength mRefY; private SVGLength mRefY;
private SVGLength mMarkerWidth; private SVGLength mMarkerWidth;
private SVGLength mMarkerHeight; private SVGLength mMarkerHeight;
private String mMarkerUnits; private String mMarkerUnits;
private String mOrient; private String mOrient;
private float mMinX; private float mMinX;
private float mMinY; private float mMinY;
private float mVbWidth; private float mVbWidth;
private float mVbHeight; private float mVbHeight;
String mAlign; String mAlign;
int mMeetOrSlice; int mMeetOrSlice;
Matrix markerTransform = new Matrix(); Matrix markerTransform = new Matrix();
public MarkerView(ReactContext reactContext) { public MarkerView(ReactContext reactContext) {
super(reactContext); super(reactContext);
} }
@ReactProp(name = "refX") @ReactProp(name = "refX")
public void setRefX(Dynamic refX) { public void setRefX(Dynamic refX) {
mRefX = SVGLength.from(refX); mRefX = SVGLength.from(refX);
invalidate(); invalidate();
} }
public void setRefX(String refX) { public void setRefX(String refX) {
mRefX = SVGLength.from(refX); mRefX = SVGLength.from(refX);
invalidate(); invalidate();
} }
@ReactProp(name = "refY") @ReactProp(name = "refY")
public void setRefY(Dynamic refY) { public void setRefY(Dynamic refY) {
mRefY = SVGLength.from(refY); mRefY = SVGLength.from(refY);
invalidate(); invalidate();
} }
public void setRefY(String refY) { public void setRefY(String refY) {
mRefY = SVGLength.from(refY); mRefY = SVGLength.from(refY);
invalidate(); invalidate();
} }
@ReactProp(name = "markerWidth") @ReactProp(name = "markerWidth")
public void setMarkerWidth(Dynamic markerWidth) { public void setMarkerWidth(Dynamic markerWidth) {
mMarkerWidth = SVGLength.from(markerWidth); mMarkerWidth = SVGLength.from(markerWidth);
invalidate(); invalidate();
} }
public void setMarkerWidth(String markerWidth) { public void setMarkerWidth(String markerWidth) {
mMarkerWidth = SVGLength.from(markerWidth); mMarkerWidth = SVGLength.from(markerWidth);
invalidate(); invalidate();
} }
@ReactProp(name = "markerHeight") @ReactProp(name = "markerHeight")
public void setMarkerHeight(Dynamic markerHeight) { public void setMarkerHeight(Dynamic markerHeight) {
mMarkerHeight = SVGLength.from(markerHeight); mMarkerHeight = SVGLength.from(markerHeight);
invalidate(); invalidate();
} }
public void setMarkerHeight(String markerHeight) { public void setMarkerHeight(String markerHeight) {
mMarkerHeight = SVGLength.from(markerHeight); mMarkerHeight = SVGLength.from(markerHeight);
invalidate(); invalidate();
} }
@ReactProp(name = "markerUnits") @ReactProp(name = "markerUnits")
public void setMarkerUnits(String markerUnits) { public void setMarkerUnits(String markerUnits) {
mMarkerUnits = markerUnits; mMarkerUnits = markerUnits;
invalidate(); invalidate();
} }
@ReactProp(name = "orient") @ReactProp(name = "orient")
public void setOrient(String orient) { public void setOrient(String orient) {
mOrient = orient; mOrient = orient;
invalidate(); invalidate();
} }
@ReactProp(name = "minX") @ReactProp(name = "minX")
public void setMinX(float minX) { public void setMinX(float minX) {
mMinX = minX; mMinX = minX;
invalidate(); invalidate();
} }
@ReactProp(name = "minY") @ReactProp(name = "minY")
public void setMinY(float minY) { public void setMinY(float minY) {
mMinY = minY; mMinY = minY;
invalidate(); invalidate();
} }
@ReactProp(name = "vbWidth") @ReactProp(name = "vbWidth")
public void setVbWidth(float vbWidth) { public void setVbWidth(float vbWidth) {
mVbWidth = vbWidth; mVbWidth = vbWidth;
invalidate(); invalidate();
} }
@ReactProp(name = "vbHeight") @ReactProp(name = "vbHeight")
public void setVbHeight(float vbHeight) { public void setVbHeight(float vbHeight) {
mVbHeight = vbHeight; mVbHeight = vbHeight;
invalidate(); invalidate();
} }
@ReactProp(name = "align") @ReactProp(name = "align")
public void setAlign(String align) { public void setAlign(String align) {
mAlign = align; mAlign = align;
invalidate(); invalidate();
} }
@ReactProp(name = "meetOrSlice") @ReactProp(name = "meetOrSlice")
public void setMeetOrSlice(int meetOrSlice) { public void setMeetOrSlice(int meetOrSlice) {
mMeetOrSlice = meetOrSlice; mMeetOrSlice = meetOrSlice;
invalidate(); invalidate();
} }
@Override @Override
void saveDefinition() { void saveDefinition() {
if (mName != null) { if (mName != null) {
SvgView svg = getSvgView(); SvgView svg = getSvgView();
svg.defineMarker(this, mName); svg.defineMarker(this, mName);
for (int i = 0; i < getChildCount(); i++) { for (int i = 0; i < getChildCount(); i++) {
View node = getChildAt(i); View node = getChildAt(i);
if (node instanceof VirtualView) { if (node instanceof VirtualView) {
((VirtualView)node).saveDefinition(); ((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) { double width = relativeOnWidth(mMarkerWidth) / mScale;
int count = saveAndSetupCanvas(canvas, mCTM); double height = relativeOnHeight(mMarkerHeight) / mScale;
RectF eRect = new RectF(0, 0, (float) width, (float) height);
markerTransform.reset(); if (mAlign != null) {
Point origin = position.origin; RectF vbRect =
markerTransform.setTranslate((float)origin.x * mScale, (float)origin.y * mScale); new RectF(
mMinX * mScale,
double markerAngle = "auto".equals(mOrient) ? -1 : Double.parseDouble(mOrient); mMinY * mScale,
float degrees = 180 + (float) (markerAngle == -1 ? position.angle : markerAngle); (mMinX + mVbWidth) * mScale,
markerTransform.preRotate(degrees); (mMinY + mVbHeight) * mScale);
Matrix viewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice);
boolean useStrokeWidth = "strokeWidth".equals(mMarkerUnits); float[] values = new float[9];
if (useStrokeWidth) { viewBoxMatrix.getValues(values);
markerTransform.preScale(strokeWidth, strokeWidth); markerTransform.preScale(values[Matrix.MSCALE_X], values[Matrix.MSCALE_Y]);
}
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 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. * LICENSE file in the root directory of this source tree.
*/ */
package com.horcrux.svg; package com.horcrux.svg;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.graphics.Matrix; import android.graphics.Matrix;
import com.facebook.common.logging.FLog; import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.common.ReactConstants; import com.facebook.react.common.ReactConstants;
import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.annotations.ReactProp;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@SuppressLint("ViewConstructor") @SuppressLint("ViewConstructor")
class MaskView extends GroupView { class MaskView extends GroupView {
SVGLength mX; SVGLength mX;
SVGLength mY; SVGLength mY;
SVGLength mW; SVGLength mW;
SVGLength mH; SVGLength mH;
// TODO implement proper support for units // TODO implement proper support for units
@SuppressWarnings({"FieldCanBeLocal", "unused"}) @SuppressWarnings({"FieldCanBeLocal", "unused"})
private Brush.BrushUnits mMaskUnits; private Brush.BrushUnits mMaskUnits;
@SuppressWarnings({"FieldCanBeLocal", "unused"})
private Brush.BrushUnits mMaskContentUnits;
private static final float[] sRawMatrix = new float[]{ @SuppressWarnings({"FieldCanBeLocal", "unused"})
private Brush.BrushUnits mMaskContentUnits;
private static final float[] sRawMatrix =
new float[] {
1, 0, 0, 1, 0, 0,
0, 1, 0, 0, 1, 0,
0, 0, 1 0, 0, 1
}; };
private Matrix mMatrix = null; private Matrix mMatrix = null;
public MaskView(ReactContext reactContext) { public MaskView(ReactContext reactContext) {
super(reactContext); super(reactContext);
} }
@ReactProp(name = "x") @ReactProp(name = "x")
public void setX(Dynamic x) { public void setX(Dynamic x) {
mX = SVGLength.from(x); mX = SVGLength.from(x);
invalidate(); invalidate();
} }
public void setX(String x) { public void setX(String x) {
mX = SVGLength.from(x); mX = SVGLength.from(x);
invalidate(); invalidate();
} }
@ReactProp(name = "y") @ReactProp(name = "y")
public void setY(Dynamic y) { public void setY(Dynamic y) {
mY = SVGLength.from(y); mY = SVGLength.from(y);
invalidate(); invalidate();
} }
public void setY(String y) { public void setY(String y) {
mY = SVGLength.from(y); mY = SVGLength.from(y);
invalidate(); invalidate();
} }
@ReactProp(name = "width") @ReactProp(name = "width")
public void setWidth(Dynamic width) { public void setWidth(Dynamic width) {
mW = SVGLength.from(width); mW = SVGLength.from(width);
invalidate(); invalidate();
} }
public void setWidth(String width) { public void setWidth(String width) {
mW = SVGLength.from(width); mW = SVGLength.from(width);
invalidate(); invalidate();
} }
@ReactProp(name = "height") @ReactProp(name = "height")
public void setHeight(Dynamic height) { public void setHeight(Dynamic height) {
mH = SVGLength.from(height); mH = SVGLength.from(height);
invalidate(); invalidate();
} }
public void setHeight(String height) { public void setHeight(String height) {
mH = SVGLength.from(height); mH = SVGLength.from(height);
invalidate(); invalidate();
} }
@ReactProp(name = "maskUnits") @ReactProp(name = "maskUnits")
public void setMaskUnits(int maskUnits) { public void setMaskUnits(int maskUnits) {
switch (maskUnits) { switch (maskUnits) {
case 0: case 0:
mMaskUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX; mMaskUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
break; break;
case 1: case 1:
mMaskUnits = Brush.BrushUnits.USER_SPACE_ON_USE; mMaskUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
break; 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") invalidate();
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") @Override
public void setMaskTransform(@Nullable ReadableArray matrixArray) { void saveDefinition() {
if (matrixArray != null) { if (mName != null) {
int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale); SvgView svg = getSvgView();
if (matrixSize == 6) { svg.defineMask(this, mName);
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);
}
} }
}
} }

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. * LICENSE file in the root directory of this source tree.
*/ */
package com.horcrux.svg; package com.horcrux.svg;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Path; import android.graphics.Path;
import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.annotations.ReactProp;
@SuppressLint("ViewConstructor") @SuppressLint("ViewConstructor")
class PathView extends RenderableView { class PathView extends RenderableView {
private Path mPath; private Path mPath;
public PathView(ReactContext reactContext) { public PathView(ReactContext reactContext) {
super(reactContext); super(reactContext);
PathParser.mScale = mScale; PathParser.mScale = mScale;
mPath = new Path(); mPath = new Path();
} }
@ReactProp(name = "d") @ReactProp(name = "d")
public void setD(String d) { public void setD(String d) {
mPath = PathParser.parse(d); mPath = PathParser.parse(d);
elements = PathParser.elements; elements = PathParser.elements;
invalidate(); invalidate();
} }
@Override
Path getPath(Canvas canvas, Paint paint) {
return mPath;
}
@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. * LICENSE file in the root directory of this source tree.
*/ */
package com.horcrux.svg; package com.horcrux.svg;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.graphics.Matrix; import android.graphics.Matrix;
import android.graphics.RectF; import android.graphics.RectF;
import com.facebook.common.logging.FLog; import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.common.ReactConstants; import com.facebook.react.common.ReactConstants;
import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.annotations.ReactProp;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@SuppressLint("ViewConstructor") @SuppressLint("ViewConstructor")
class PatternView extends GroupView { class PatternView extends GroupView {
private SVGLength mX; private SVGLength mX;
private SVGLength mY; private SVGLength mY;
private SVGLength mW; private SVGLength mW;
private SVGLength mH; private SVGLength mH;
private Brush.BrushUnits mPatternUnits; private Brush.BrushUnits mPatternUnits;
private Brush.BrushUnits mPatternContentUnits; private Brush.BrushUnits mPatternContentUnits;
private float mMinX; private float mMinX;
private float mMinY; private float mMinY;
private float mVbWidth; private float mVbWidth;
private float mVbHeight; private float mVbHeight;
String mAlign; String mAlign;
int mMeetOrSlice; int mMeetOrSlice;
private static final float[] sRawMatrix = new float[]{ private static final float[] sRawMatrix =
new float[] {
1, 0, 0, 1, 0, 0,
0, 1, 0, 0, 1, 0,
0, 0, 1 0, 0, 1
}; };
private Matrix mMatrix = null; private Matrix mMatrix = null;
public PatternView(ReactContext reactContext) { public PatternView(ReactContext reactContext) {
super(reactContext); super(reactContext);
} }
@ReactProp(name = "x") @ReactProp(name = "x")
public void setX(Dynamic x) { public void setX(Dynamic x) {
mX = SVGLength.from(x); mX = SVGLength.from(x);
invalidate(); invalidate();
} }
public void setX(String x) { public void setX(String x) {
mX = SVGLength.from(x); mX = SVGLength.from(x);
invalidate(); invalidate();
} }
@ReactProp(name = "y") @ReactProp(name = "y")
public void setY(Dynamic y) { public void setY(Dynamic y) {
mY = SVGLength.from(y); mY = SVGLength.from(y);
invalidate(); invalidate();
} }
public void setY(String y) { public void setY(String y) {
mY = SVGLength.from(y); mY = SVGLength.from(y);
invalidate(); invalidate();
} }
@ReactProp(name = "width") @ReactProp(name = "width")
public void setWidth(Dynamic width) { public void setWidth(Dynamic width) {
mW = SVGLength.from(width); mW = SVGLength.from(width);
invalidate(); invalidate();
} }
public void setWidth(String width) { public void setWidth(String width) {
mW = SVGLength.from(width); mW = SVGLength.from(width);
invalidate(); invalidate();
} }
@ReactProp(name = "height")
public void setHeight(Dynamic height) { @ReactProp(name = "height")
mH = SVGLength.from(height); public void setHeight(Dynamic height) {
invalidate(); mH = SVGLength.from(height);
} invalidate();
}
public void setHeight(String height) { public void setHeight(String height) {
mH = SVGLength.from(height); mH = SVGLength.from(height);
invalidate(); invalidate();
} }
@ReactProp(name = "patternUnits") @ReactProp(name = "patternUnits")
public void setPatternUnits(int patternUnits) { public void setPatternUnits(int patternUnits) {
switch (patternUnits) { switch (patternUnits) {
case 0: case 0:
mPatternUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX; mPatternUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
break; break;
case 1: case 1:
mPatternUnits = Brush.BrushUnits.USER_SPACE_ON_USE; mPatternUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
break; 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") invalidate();
public void setPatternContentUnits(int patternContentUnits) { }
switch (patternContentUnits) {
case 0: @ReactProp(name = "minX")
mPatternContentUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX; public void setMinX(float minX) {
break; mMinX = minX;
case 1: invalidate();
mPatternContentUnits = Brush.BrushUnits.USER_SPACE_ON_USE; }
break;
} @ReactProp(name = "minY")
invalidate(); public void setMinY(float minY) {
} mMinY = minY;
invalidate();
@ReactProp(name = "patternTransform") }
public void setPatternTransform(@Nullable ReadableArray matrixArray) {
if (matrixArray != null) { @ReactProp(name = "vbWidth")
int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale); public void setVbWidth(float vbWidth) {
if (matrixSize == 6) { mVbWidth = vbWidth;
if (mMatrix == null) { invalidate();
mMatrix = new Matrix(); }
}
mMatrix.setValues(sRawMatrix); @ReactProp(name = "vbHeight")
} else if (matrixSize != -1) { public void setVbHeight(float vbHeight) {
FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6"); mVbHeight = vbHeight;
} invalidate();
} else { }
mMatrix = null;
} @ReactProp(name = "align")
public void setAlign(String align) {
invalidate(); mAlign = align;
} invalidate();
}
@ReactProp(name = "minX")
public void setMinX(float minX) { @ReactProp(name = "meetOrSlice")
mMinX = minX; public void setMeetOrSlice(int meetOrSlice) {
invalidate(); mMeetOrSlice = meetOrSlice;
} invalidate();
}
@ReactProp(name = "minY")
public void setMinY(float minY) { RectF getViewBox() {
mMinY = minY; return new RectF(
invalidate(); mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale);
} }
@ReactProp(name = "vbWidth") @Override
public void setVbWidth(float vbWidth) { void saveDefinition() {
mVbWidth = vbWidth; if (mName != null) {
invalidate(); SVGLength[] points = new SVGLength[] {mX, mY, mW, mH};
} Brush brush = new Brush(Brush.BrushType.PATTERN, points, mPatternUnits);
brush.setContentUnits(mPatternContentUnits);
@ReactProp(name = "vbHeight") brush.setPattern(this);
public void setVbHeight(float vbHeight) {
mVbHeight = vbHeight; if (mMatrix != null) {
invalidate(); brush.setGradientTransform(mMatrix);
} }
@ReactProp(name = "align") SvgView svg = getSvgView();
public void setAlign(String align) { if (mPatternUnits == Brush.BrushUnits.USER_SPACE_ON_USE
mAlign = align; || mPatternContentUnits == Brush.BrushUnits.USER_SPACE_ON_USE) {
invalidate(); brush.setUserSpaceBoundingBox(svg.getCanvasBounds());
} }
@ReactProp(name = "meetOrSlice") svg.defineBrush(brush, mName);
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. * LICENSE file in the root directory of this source tree.
*/ */
package com.horcrux.svg; package com.horcrux.svg;
import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableArray;
/** /** Contains static helper methods for accessing props. */
* Contains static helper methods for accessing props.
*/
class PropHelper { 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]}. * Converts given {@link ReadableArray} to a matrix data array, {@code float[6]}. Writes result to
* Writes result to the array passed in {@param into}. * the array passed in {@param into}. This method will write exactly six items to the output array
* This method will write exactly six items to the output array from the input array. * from the input array.
* *
* If the input array has a different size, then only the size is returned; * <p>If the input array has a different size, then only the size is returned; Does not check
* Does not check output array size. Ensure space for at least six elements. * output array size. Ensure space for at least six elements.
* *
* @param value input array * @param value input array
* @param sRawMatrix output matrix * @param sRawMatrix output matrix
* @param mScale current resolution scaling * @param mScale current resolution scaling
* @return size of input array * @return size of input array
*/ */
static int toMatrixData(ReadableArray value, float[] sRawMatrix, float mScale) { static int toMatrixData(ReadableArray value, float[] sRawMatrix, float mScale) {
int fromSize = value.size(); int fromSize = value.size();
if (fromSize != inputMatrixDataSize) { if (fromSize != inputMatrixDataSize) {
return fromSize; 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;
} }
/** sRawMatrix[0] = (float) value.getDouble(0);
* Converts length string into px / user units sRawMatrix[1] = (float) value.getDouble(2);
* in the current user coordinate system sRawMatrix[2] = (float) value.getDouble(4) * mScale;
* sRawMatrix[3] = (float) value.getDouble(1);
* @param length length string sRawMatrix[4] = (float) value.getDouble(3);
* @param relative relative size for percentages sRawMatrix[5] = (float) value.getDouble(5) * mScale;
* @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
unit relative to return inputMatrixDataSize;
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 ] * 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 unit relative to
https://www.w3.org/TR/css3-values/#absolute-lengths em font size of the element
https://drafts.csswg.org/css-cascade-4/#computed-value ex x-height of the elements font
https://drafts.csswg.org/css-fonts-3/#propdef-font-size ch width of the "0" (ZERO, U+0030) glyph in the elements font
https://drafts.csswg.org/css2/fonts.html#propdef-font-size rem font size of the root element
*/ vw 1% of viewports width
length = length.trim(); vh 1% of viewports height
int stringLength = length.length(); vmin 1% of viewports smaller dimension
int percentIndex = stringLength - 1; vmax 1% of viewports larger dimension
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 (lastTwo) { relative-size [ larger | smaller ]
case "px": absolute-size: [ xx-small | x-small | small | medium | large | x-large | xx-large ]
break;
case "em": https://www.w3.org/TR/css3-values/#relative-lengths
unit = fontSize; https://www.w3.org/TR/css3-values/#absolute-lengths
break; 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
"1pt" equals "1.25px" (and therefore 1.25 user units) */
"1pc" equals "15px" (and therefore 15 user units) length = length.trim();
"1mm" would be "3.543307px" (3.543307 user units) int stringLength = length.length();
"1cm" equals "35.43307px" (and therefore 35.43307 user units) int percentIndex = stringLength - 1;
"1in" equals "90px" (and therefore 90 user units) if (stringLength == 0 || length.equals("normal")) {
*/ return 0d;
} else if (length.codePointAt(percentIndex) == '%') {
case "pt": return Double.valueOf(length.substring(0, percentIndex)) / 100 * relative;
unit = 1.25d; } else {
break; int twoLetterUnitIndex = stringLength - 2;
if (twoLetterUnitIndex > 0) {
case "pc": String lastTwo = length.substring(twoLetterUnitIndex);
unit = 15; int end = twoLetterUnitIndex;
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;
double unit = 1; double unit = 1;
switch (unitType) {
case NUMBER:
case PX:
break;
case PERCENTAGE: switch (lastTwo) {
return value / 100 * relative + offset; case "px":
break;
case EMS: case "em":
unit = fontSize; unit = fontSize;
break; break;
case EXS:
unit = fontSize / 2;
break;
case CM: /*
unit = 35.43307; "1pt" equals "1.25px" (and therefore 1.25 user units)
break; "1pc" equals "15px" (and therefore 15 user units)
case MM: "1mm" would be "3.543307px" (3.543307 user units)
unit = 3.543307; "1cm" equals "35.43307px" (and therefore 35.43307 user units)
break; "1in" equals "90px" (and therefore 90 user units)
case IN: */
unit = 90;
break;
case PT:
unit = 1.25;
break;
case PC:
unit = 15;
break;
default: case "pt":
case UNKNOWN: unit = 1.25d;
return value * scale + offset; 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; import java.util.ArrayList;
enum RNSVGMarkerType { enum RNSVGMarkerType {
kStartMarker, kStartMarker,
kMidMarker, kMidMarker,
kEndMarker kEndMarker
} }
enum ElementType { enum ElementType {
kCGPathElementAddCurveToPoint, kCGPathElementAddCurveToPoint,
kCGPathElementAddQuadCurveToPoint, kCGPathElementAddQuadCurveToPoint,
kCGPathElementMoveToPoint, kCGPathElementMoveToPoint,
kCGPathElementAddLineToPoint, kCGPathElementAddLineToPoint,
kCGPathElementCloseSubpath kCGPathElementCloseSubpath
} }
class Point { class Point {
double x; double x;
double y; double y;
Point(double x, double y){
this.x = x; Point(double x, double y) {
this.y = y; this.x = x;
} this.y = y;
}
} }
class SegmentData { class SegmentData {
Point start_tangent; // Tangent in the start 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 end_tangent; // Tangent in the end point of the segment.
Point position; // The end point of the segment. Point position; // The end point of the segment.
} }
class RNSVGMarkerPosition { class RNSVGMarkerPosition {
static private ArrayList<RNSVGMarkerPosition> positions_; private static ArrayList<RNSVGMarkerPosition> positions_;
static private int element_index_; private static int element_index_;
static private Point origin_; private static Point origin_;
static private Point subpath_start_; private static Point subpath_start_;
static private Point in_slope_; private static Point in_slope_;
static private Point out_slope_; private static Point out_slope_;
@SuppressWarnings("unused")
static private boolean auto_start_reverse_; // TODO
RNSVGMarkerType type; @SuppressWarnings("unused")
Point origin; private static boolean auto_start_reverse_; // TODO
double angle;
private RNSVGMarkerPosition(RNSVGMarkerType type, Point origin, double angle) { RNSVGMarkerType type;
this.type = type; Point origin;
this.origin = origin; double angle;
this.angle = 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) { private static void PathIsDone() {
positions_ = new ArrayList<>(); double angle = CurrentAngle(RNSVGMarkerType.kEndMarker);
element_index_ = 0; positions_.add(new RNSVGMarkerPosition(RNSVGMarkerType.kEndMarker, origin_, angle));
origin_ = new Point(0, 0); }
subpath_start_ = new Point(0, 0);
for (PathElement e : elements) {
UpdateFromPathElement(e);
}
PathIsDone();
return positions_;
}
private static void PathIsDone() { private static double BisectingAngle(double in_angle, double out_angle) {
double angle = CurrentAngle(RNSVGMarkerType.kEndMarker); // WK193015: Prevent bugs due to angles being non-continuous.
positions_.add(new RNSVGMarkerPosition(RNSVGMarkerType.kEndMarker, origin_, angle)); 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) { private static double rad2deg(double rad) {
// WK193015: Prevent bugs due to angles being non-continuous. double RNSVG_radToDeg = 180 / Math.PI;
if (Math.abs(in_angle - out_angle) > 180) return rad * RNSVG_radToDeg;
in_angle += 360; }
return (in_angle + out_angle) / 2;
}
private static double rad2deg(double rad) { private static double SlopeAngleRadians(Point p) {
double RNSVG_radToDeg = 180 / Math.PI; return Math.atan2(p.y, p.x);
return rad * RNSVG_radToDeg; }
}
private static double SlopeAngleRadians(Point p) { private static double CurrentAngle(RNSVGMarkerType type) {
return Math.atan2(p.y, p.x); // 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) { private static Point subtract(Point p1, Point p2) {
// For details of this calculation, see: return new Point(p2.x - p1.x, p2.y - p1.y);
// 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) { private static boolean isZero(Point p) {
return new Point(p2.x - p1.x, p2.y - p1.y); return p.x == 0 && p.y == 0;
} }
private static boolean isZero(Point p) { private static void ComputeQuadTangents(SegmentData data, Point start, Point control, Point end) {
return p.x == 0 && p.y == 0; 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, private static SegmentData ExtractPathElementFeatures(PathElement element) {
Point start, SegmentData data = new SegmentData();
Point control, Point[] points = element.points;
Point end) { switch (element.type) {
data.start_tangent = subtract(control, start); case kCGPathElementAddCurveToPoint:
data.end_tangent = subtract(end, control); data.position = points[2];
if (isZero(data.start_tangent)) data.start_tangent = subtract(points[0], origin_);
data.start_tangent = data.end_tangent; data.end_tangent = subtract(points[2], points[1]);
else if (isZero(data.end_tangent)) if (isZero(data.start_tangent)) ComputeQuadTangents(data, points[0], points[1], points[2]);
data.end_tangent = data.start_tangent; 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) { private static void UpdateFromPathElement(PathElement element) {
SegmentData data = new SegmentData(); SegmentData segment_data = ExtractPathElementFeatures(element);
Point[] points = element.points; // First update the outgoing slope for the previous element.
switch (element.type) { out_slope_ = segment_data.start_tangent;
case kCGPathElementAddCurveToPoint: // Record the marker for the previous element.
data.position = points[2]; if (element_index_ > 0) {
data.start_tangent = subtract(points[0], origin_); RNSVGMarkerType marker_type =
data.end_tangent = subtract(points[2], points[1]); element_index_ == 1 ? RNSVGMarkerType.kStartMarker : RNSVGMarkerType.kMidMarker;
if (isZero(data.start_tangent)) double angle = CurrentAngle(marker_type);
ComputeQuadTangents(data, points[0], points[1], points[2]); positions_.add(new RNSVGMarkerPosition(marker_type, origin_, angle));
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_;
} }
// 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. * LICENSE file in the root directory of this source tree.
*/ */
package com.horcrux.svg; package com.horcrux.svg;
import static com.facebook.react.common.StandardCharsets.UTF_8;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Matrix; import android.graphics.Matrix;
import android.graphics.Path; import android.graphics.Path;
import android.graphics.PathMeasure; import android.graphics.PathMeasure;
import android.graphics.RectF; import android.graphics.RectF;
import android.graphics.Region; import android.graphics.Region;
import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext; 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.ReactMethod;
import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableMap;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import static com.facebook.react.common.StandardCharsets.UTF_8;
class RNSVGRenderableManager extends ReactContextBaseJavaModule { class RNSVGRenderableManager extends ReactContextBaseJavaModule {
RNSVGRenderableManager(ReactApplicationContext reactContext) { RNSVGRenderableManager(ReactApplicationContext reactContext) {
super(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 float scale = svg.mScale;
@Override float x = (float) options.getDouble("x") * scale;
public String getName() { float y = (float) options.getDouble("y") * scale;
return "RNSVGRenderableManager";
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") try {
@ReactMethod(isBlockingSynchronousMethod = true) svg.getPath(null, null);
public boolean isPointInFill(int tag, ReadableMap options) { } catch (NullPointerException e) {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); svg.invalidate();
if (svg == null) { return false;
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);
} }
String result = builder.toString();
float scale = svg.mScale; promise.resolve(result);
float x = (float) options.getDouble("x") * scale; } finally {
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;
}
try { try {
svg.getPath(null, null); stream.close();
} catch (NullPointerException e) { } catch (IOException ioe) {
svg.invalidate(); // ignore
return false;
} }
}
svg.initBounds(); } catch (Exception e) {
e.printStackTrace();
float scale = svg.mScale; promise.reject(e);
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") private static final int EOF = -1;
@ReactMethod(isBlockingSynchronousMethod = true) private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
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;
} }

View File

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

View File

@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.horcrux.svg; package com.horcrux.svg;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
@@ -15,128 +14,139 @@ import android.graphics.Paint;
import android.graphics.Path; import android.graphics.Path;
import android.graphics.RectF; import android.graphics.RectF;
import android.os.Build; import android.os.Build;
import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.annotations.ReactProp;
@SuppressLint("ViewConstructor") @SuppressLint("ViewConstructor")
class RectView extends RenderableView { class RectView extends RenderableView {
private SVGLength mX; private SVGLength mX;
private SVGLength mY; private SVGLength mY;
private SVGLength mW; private SVGLength mW;
private SVGLength mH; private SVGLength mH;
private SVGLength mRx; private SVGLength mRx;
private SVGLength mRy; private SVGLength mRy;
public RectView(ReactContext reactContext) { public RectView(ReactContext reactContext) {
super(reactContext); super(reactContext);
} }
@ReactProp(name = "x") @ReactProp(name = "x")
public void setX(Dynamic x) { public void setX(Dynamic x) {
mX = SVGLength.from(x); mX = SVGLength.from(x);
invalidate(); invalidate();
} }
public void setX(String x) { public void setX(String x) {
mX = SVGLength.from(x); mX = SVGLength.from(x);
invalidate(); invalidate();
} }
@ReactProp(name = "y") @ReactProp(name = "y")
public void setY(Dynamic y) { public void setY(Dynamic y) {
mY = SVGLength.from(y); mY = SVGLength.from(y);
invalidate(); invalidate();
} }
public void setY(String y) { public void setY(String y) {
mY = SVGLength.from(y); mY = SVGLength.from(y);
invalidate(); invalidate();
} }
@ReactProp(name = "width") @ReactProp(name = "width")
public void setWidth(Dynamic width) { public void setWidth(Dynamic width) {
mW = SVGLength.from(width); mW = SVGLength.from(width);
invalidate(); invalidate();
} }
public void setWidth(String width) { public void setWidth(String width) {
mW = SVGLength.from(width); mW = SVGLength.from(width);
invalidate(); invalidate();
} }
@ReactProp(name = "height") @ReactProp(name = "height")
public void setHeight(Dynamic height) { public void setHeight(Dynamic height) {
mH = SVGLength.from(height); mH = SVGLength.from(height);
invalidate(); invalidate();
} }
public void setHeight(String height) { public void setHeight(String height) {
mH = SVGLength.from(height); mH = SVGLength.from(height);
invalidate(); invalidate();
} }
@ReactProp(name = "rx") @ReactProp(name = "rx")
public void setRx(Dynamic rx) { public void setRx(Dynamic rx) {
mRx = SVGLength.from(rx); mRx = SVGLength.from(rx);
invalidate(); invalidate();
} }
public void setRx(String rx) { public void setRx(String rx) {
mRx = SVGLength.from(rx); mRx = SVGLength.from(rx);
invalidate(); invalidate();
} }
@ReactProp(name = "ry") @ReactProp(name = "ry")
public void setRy(Dynamic ry) { public void setRy(Dynamic ry) {
mRy = SVGLength.from(ry); mRy = SVGLength.from(ry);
invalidate(); invalidate();
} }
public void setRy(String ry) { public void setRy(String ry) {
mRy = SVGLength.from(ry); mRy = SVGLength.from(ry);
invalidate(); invalidate();
} }
@Override @Override
Path getPath(Canvas canvas, Paint paint) { Path getPath(Canvas canvas, Paint paint) {
Path path = new Path(); Path path = new Path();
double x = relativeOnWidth(mX); double x = relativeOnWidth(mX);
double y = relativeOnHeight(mY); double y = relativeOnHeight(mY);
double w = relativeOnWidth(mW); double w = relativeOnWidth(mW);
double h = relativeOnHeight(mH); double h = relativeOnHeight(mH);
if (mRx != null || mRy != null) { if (mRx != null || mRy != null) {
double rx = 0d; double rx = 0d;
double ry = 0d; double ry = 0d;
if (mRx == null) { if (mRx == null) {
ry = relativeOnHeight(mRy); ry = relativeOnHeight(mRy);
rx = ry; rx = ry;
} else if (mRy == null) { } else if (mRy == null) {
rx = relativeOnWidth(mRx); rx = relativeOnWidth(mRx);
ry = rx; ry = rx;
} else { } else {
rx = relativeOnWidth(mRx); rx = relativeOnWidth(mRx);
ry = relativeOnHeight(mRy); ry = relativeOnHeight(mRy);
} }
if (rx > w / 2) { if (rx > w / 2) {
rx = w / 2; rx = w / 2;
} }
if (ry > h / 2) { if (ry > h / 2) {
ry = h / 2; ry = h / 2;
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 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); path.addRoundRect(
} else { (float) x,
path.addRoundRect(new RectF((float) x, (float) y, (float) (x + w), (float) (y + h)), (float) rx, (float) ry, Path.Direction.CW); (float) y,
} (float) (x + w),
} else { (float) (y + h),
path.addRect((float) x, (float) y, (float) (x + w), (float) (y + h), Path.Direction.CW); (float) rx,
path.close(); // Ensure isSimplePath = false such that rect doesn't become represented using integers (float) ry,
} Path.Direction.CW);
return path; } 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.Dynamic;
import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableArray;
import java.util.ArrayList; import java.util.ArrayList;
class SVGLength { class SVGLength {
@@ -121,26 +120,29 @@ class SVGLength {
static ArrayList<SVGLength> arrayFrom(Dynamic dynamic) { static ArrayList<SVGLength> arrayFrom(Dynamic dynamic) {
switch (dynamic.getType()) { switch (dynamic.getType()) {
case Number: { case Number:
ArrayList<SVGLength> list = new ArrayList<>(1); {
list.add(new SVGLength(dynamic.asDouble())); ArrayList<SVGLength> list = new ArrayList<>(1);
return list; list.add(new SVGLength(dynamic.asDouble()));
} return list;
case Array: { }
ReadableArray arr = dynamic.asArray(); case Array:
int size = arr.size(); {
ArrayList<SVGLength> list = new ArrayList<>(size); ReadableArray arr = dynamic.asArray();
for (int i = 0; i < size; i++) { int size = arr.size();
Dynamic val = arr.getDynamic(i); ArrayList<SVGLength> list = new ArrayList<>(size);
list.add(from(val)); 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: default:
return null; return null;
} }

View File

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

View File

@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.horcrux.svg; package com.horcrux.svg;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
@@ -20,9 +19,6 @@ import android.graphics.Typeface;
import android.util.Base64; import android.util.Base64;
import android.view.View; import android.view.View;
import android.view.ViewParent; import android.view.ViewParent;
import androidx.annotation.NonNull;
import com.facebook.react.bridge.ColorPropConverter; import com.facebook.react.bridge.ColorPropConverter;
import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext; 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.ReactCompoundView;
import com.facebook.react.uimanager.ReactCompoundViewGroup; import com.facebook.react.uimanager.ReactCompoundViewGroup;
import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.views.view.ReactViewGroup;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; 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") @SuppressLint("ViewConstructor")
public class SvgView extends FabricEnabledViewGroup implements ReactCompoundView, ReactCompoundViewGroup { public class SvgView extends FabricEnabledViewGroup
implements ReactCompoundView, ReactCompoundViewGroup {
@Override @Override
public boolean interceptsTouchEvent(float touchX, float touchY) { public boolean interceptsTouchEvent(float touchX, float touchY) {
return true; return true;
}
@SuppressWarnings("unused")
public enum Events {
EVENT_DATA_URL("onDataURL");
private final String mName;
Events(final String name) {
mName = name;
} }
@SuppressWarnings("unused") @Nonnull
public enum Events { public String toString() {
EVENT_DATA_URL("onDataURL"); return mName;
private final String mName;
Events(final String name) {
mName = name;
}
@Nonnull
public String toString() {
return mName;
}
} }
}
private @Nullable Bitmap mBitmap; private @Nullable Bitmap mBitmap;
public SvgView(ReactContext reactContext) { public SvgView(ReactContext reactContext) {
super(reactContext); super(reactContext);
mScale = DisplayMetricsHolder.getScreenDisplayMetrics().density; mScale = DisplayMetricsHolder.getScreenDisplayMetrics().density;
// for some reason on Fabric the `onDraw` won't be called without it // for some reason on Fabric the `onDraw` won't be called without it
setWillNotDraw(false); setWillNotDraw(false);
} }
@Override @Override
public void setId(int id) { public void setId(int id) {
super.setId(id); super.setId(id);
SvgViewManager.setSvgView(id, this); SvgViewManager.setSvgView(id, this);
} }
@Override @Override
public void invalidate() { public void invalidate() {
super.invalidate(); super.invalidate();
ViewParent parent = getParent(); ViewParent parent = getParent();
if (parent instanceof VirtualView) { if (parent instanceof VirtualView) {
if (!mRendered) { if (!mRendered) {
return; 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;
} }
invalidate(); mRendered = false;
clearChildCache(); ((VirtualView) parent).getSvgView().invalidate();
return;
} }
if (mBitmap != null) {
mBitmap.recycle();
}
mBitmap = null;
}
public void setTintColor(@Nullable Integer tintColor) { @Override
mTintColor = tintColor; 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(); invalidate();
clearChildCache(); clearChildCache();
} }
@ReactProp(name = "minX") public void setTintColor(@Nullable Integer tintColor) {
public void setMinX(float minX) { mTintColor = tintColor;
mMinX = minX; invalidate();
invalidate(); clearChildCache();
clearChildCache(); }
}
@ReactProp(name = "minY") @ReactProp(name = "minX")
public void setMinY(float minY) { public void setMinX(float minX) {
mMinY = minY; mMinX = minX;
invalidate(); invalidate();
clearChildCache(); clearChildCache();
} }
@ReactProp(name = "vbWidth") @ReactProp(name = "minY")
public void setVbWidth(float vbWidth) { public void setMinY(float minY) {
mVbWidth = vbWidth; mMinY = minY;
invalidate(); invalidate();
clearChildCache(); clearChildCache();
} }
@ReactProp(name = "vbHeight") @ReactProp(name = "vbWidth")
public void setVbHeight(float vbHeight) { public void setVbWidth(float vbWidth) {
mVbHeight = vbHeight; mVbWidth = vbWidth;
invalidate(); invalidate();
clearChildCache(); clearChildCache();
} }
@ReactProp(name = "bbWidth") @ReactProp(name = "vbHeight")
public void setBbWidth(Dynamic bbWidth) { public void setVbHeight(float vbHeight) {
mbbWidth = SVGLength.from(bbWidth); mVbHeight = vbHeight;
invalidate(); invalidate();
clearChildCache(); clearChildCache();
} }
@ReactProp(name = "bbWidth")
public void setBbWidth(Dynamic bbWidth) {
mbbWidth = SVGLength.from(bbWidth);
invalidate();
clearChildCache();
}
public void setBbWidth(String bbWidth) { public void setBbWidth(String bbWidth) {
mbbWidth = SVGLength.from(bbWidth); mbbWidth = SVGLength.from(bbWidth);
@@ -238,12 +230,12 @@ public class SvgView extends FabricEnabledViewGroup implements ReactCompoundView
clearChildCache(); clearChildCache();
} }
@ReactProp(name = "bbHeight") @ReactProp(name = "bbHeight")
public void setBbHeight(Dynamic bbHeight) { public void setBbHeight(Dynamic bbHeight) {
mbbHeight = SVGLength.from(bbHeight); mbbHeight = SVGLength.from(bbHeight);
invalidate(); invalidate();
clearChildCache(); clearChildCache();
} }
public void setBbHeight(String bbHeight) { public void setBbHeight(String bbHeight) {
mbbHeight = SVGLength.from(bbHeight); mbbHeight = SVGLength.from(bbHeight);
@@ -251,202 +243,198 @@ public class SvgView extends FabricEnabledViewGroup implements ReactCompoundView
clearChildCache(); clearChildCache();
} }
@ReactProp(name = "align") @ReactProp(name = "align")
public void setAlign(String align) { public void setAlign(String align) {
mAlign = align; mAlign = align;
invalidate(); invalidate();
clearChildCache(); 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") final Paint paint = new Paint();
public void setMeetOrSlice(int meetOrSlice) {
mMeetOrSlice = meetOrSlice; paint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG | Paint.SUBPIXEL_TEXT_FLAG);
invalidate();
clearChildCache(); 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() { for (int i = 0; i < getChildCount(); i++) {
mRendered = true; View lNode = getChildAt(i);
float width = getWidth(); if (lNode instanceof VirtualView) {
float height = getHeight(); VirtualView node = (VirtualView) lNode;
boolean invalid = Float.isNaN(width) || Float.isNaN(height) || width < 1 || height < 1 || (Math.log10(width) + Math.log10(height) > 42); int count = node.saveAndSetupCanvas(canvas, mViewBoxMatrix);
if (invalid) { node.render(canvas, paint, 1f);
return null; 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)); private RectF getViewBox() {
return bitmap; 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() { float[] transformed = {touchX, touchY};
return mCanvas.getClipBounds(); 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) { return viewTag == -1 ? getId() : viewTag;
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);
}
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++) { void defineBrush(Brush brush, String brushRef) {
View node = getChildAt(i); mDefinedBrushes.put(brushRef, brush);
if (node instanceof VirtualView) { }
((VirtualView)node).saveDefinition();
}
}
for (int i = 0; i < getChildCount(); i++) { Brush getDefinedBrush(String brushRef) {
View lNode = getChildAt(i); return mDefinedBrushes.get(brushRef);
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) { void defineMask(VirtualView mask, String maskRef) {
mResponsible = true; mDefinedMasks.put(maskRef, mask);
} }
}
}
}
private RectF getViewBox() { VirtualView getDefinedMask(String maskRef) {
return new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale); return mDefinedMasks.get(maskRef);
} }
String toDataURL() { void defineMarker(VirtualView marker, String markerRef) {
Bitmap bitmap = Bitmap.createBitmap( mDefinedMarkers.put(markerRef, marker);
getWidth(), }
getHeight(),
Bitmap.Config.ARGB_8888);
clearChildCache(); VirtualView getDefinedMarker(String markerRef) {
drawChildren(new Canvas(bitmap)); return mDefinedMarkers.get(markerRef);
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);
}
} }

View File

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

View File

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

View File

@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.horcrux.svg; package com.horcrux.svg;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
@@ -14,72 +13,76 @@ import android.graphics.Canvas;
import android.graphics.Matrix; import android.graphics.Matrix;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.RectF; import android.graphics.RectF;
import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.annotations.ReactProp;
@SuppressLint("ViewConstructor") @SuppressLint("ViewConstructor")
class SymbolView extends GroupView { class SymbolView extends GroupView {
private float mMinX; private float mMinX;
private float mMinY; private float mMinY;
private float mVbWidth; private float mVbWidth;
private float mVbHeight; private float mVbHeight;
private String mAlign; private String mAlign;
private int mMeetOrSlice; private int mMeetOrSlice;
public SymbolView(ReactContext reactContext) { public SymbolView(ReactContext reactContext) {
super(reactContext); super(reactContext);
} }
@ReactProp(name = "minX") @ReactProp(name = "minX")
public void setMinX(float minX) { public void setMinX(float minX) {
mMinX = minX; mMinX = minX;
invalidate(); invalidate();
} }
@ReactProp(name = "minY") @ReactProp(name = "minY")
public void setMinY(float minY) { public void setMinY(float minY) {
mMinY = minY; mMinY = minY;
invalidate(); invalidate();
} }
@ReactProp(name = "vbWidth") @ReactProp(name = "vbWidth")
public void setVbWidth(float vbWidth) { public void setVbWidth(float vbWidth) {
mVbWidth = vbWidth; mVbWidth = vbWidth;
invalidate(); invalidate();
} }
@ReactProp(name = "vbHeight") @ReactProp(name = "vbHeight")
public void setVbHeight(float vbHeight) { public void setVbHeight(float vbHeight) {
mVbHeight = vbHeight; mVbHeight = vbHeight;
invalidate(); invalidate();
} }
@ReactProp(name = "align") @ReactProp(name = "align")
public void setAlign(String align) { public void setAlign(String align) {
mAlign = align; mAlign = align;
invalidate(); invalidate();
} }
@ReactProp(name = "meetOrSlice") @ReactProp(name = "meetOrSlice")
public void setMeetOrSlice(int meetOrSlice) { public void setMeetOrSlice(int meetOrSlice) {
mMeetOrSlice = meetOrSlice; mMeetOrSlice = meetOrSlice;
invalidate(); invalidate();
} }
@Override @Override
void draw(Canvas canvas, Paint paint, float opacity) { void draw(Canvas canvas, Paint paint, float opacity) {
saveDefinition(); saveDefinition();
} }
void drawSymbol(Canvas canvas, Paint paint, float opacity, float width, float height) { void drawSymbol(Canvas canvas, Paint paint, float opacity, float width, float height) {
if (mAlign != null) { if (mAlign != null) {
RectF vbRect = new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale); RectF vbRect =
RectF eRect = new RectF(0, 0, width, height); new RectF(
Matrix viewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice); mMinX * mScale,
canvas.concat(viewBoxMatrix); mMinY * mScale,
super.draw(canvas, paint, opacity); (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. * LICENSE file in the root directory of this source tree.
*/ */
package com.horcrux.svg; package com.horcrux.svg;
import static com.horcrux.svg.TextProperties.*;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Path; import android.graphics.Path;
import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.annotations.ReactProp;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static com.horcrux.svg.TextProperties.*;
@SuppressLint("ViewConstructor") @SuppressLint("ViewConstructor")
class TextPathView extends TextView { class TextPathView extends TextView {
private String mHref; private String mHref;
private TextPathSide mSide; private TextPathSide mSide;
private TextPathMidLine mMidLine; private TextPathMidLine mMidLine;
private @Nullable SVGLength mStartOffset; private @Nullable SVGLength mStartOffset;
private TextPathMethod mMethod = TextPathMethod.align; private TextPathMethod mMethod = TextPathMethod.align;
private TextPathSpacing mSpacing = TextPathSpacing.exact; private TextPathSpacing mSpacing = TextPathSpacing.exact;
public TextPathView(ReactContext reactContext) { public TextPathView(ReactContext reactContext) {
super(reactContext); super(reactContext);
} }
@ReactProp(name = "href") @ReactProp(name = "href")
public void setHref(String href) { public void setHref(String href) {
mHref = href; mHref = href;
invalidate(); invalidate();
} }
@ReactProp(name = "startOffset") @ReactProp(name = "startOffset")
public void setStartOffset(Dynamic startOffset) { public void setStartOffset(Dynamic startOffset) {
mStartOffset = SVGLength.from(startOffset); mStartOffset = SVGLength.from(startOffset);
invalidate(); invalidate();
} }
public void setStartOffset(String startOffset) { public void setStartOffset(String startOffset) {
mStartOffset = SVGLength.from(startOffset); mStartOffset = SVGLength.from(startOffset);
invalidate(); invalidate();
} }
@ReactProp(name = "method") @ReactProp(name = "method")
public void setMethod(@Nullable String method) { public void setMethod(@Nullable String method) {
mMethod = TextPathMethod.valueOf(method); mMethod = TextPathMethod.valueOf(method);
invalidate(); 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") RenderableView view = (RenderableView) template;
public void setSpacing(@Nullable String spacing) { return view.getPath(canvas, paint);
mSpacing = TextPathSpacing.valueOf(spacing); }
invalidate();
}
@ReactProp(name = "side") @Override
public void setSide(@Nullable String side) { Path getPath(Canvas canvas, Paint paint) {
mSide = TextPathSide.valueOf(side); return getGroupPath(canvas, paint);
invalidate(); }
}
@ReactProp(name = "midLine") @Override
public void setSharp(@Nullable String midLine) { void pushGlyphContext() {
mMidLine = TextPathMidLine.valueOf(midLine); // do nothing
invalidate(); }
}
@SuppressWarnings("unused") @Override
TextPathMethod getMethod() { void popGlyphContext() {
return mMethod; // do nothing
} }
@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
}
} }

View File

@@ -2,212 +2,218 @@ package com.horcrux.svg;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
class TextProperties { 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 SVG implementations may support the following aliases in order to support legacy content:
2.2.1. Alignment Point: alignment-baseline longhand
Name: alignment-baseline text-before-edge = text-top
Value: baseline | text-bottom | alphabetic | ideographic | middle | central | mathematical | text-top | bottom | center | top text-after-edge = text-bottom
Initial: baseline */
Applies to: inline-level boxes, flex items, grid items, table cells textBeforeEdge("text-before-edge"),
Inherited: no textAfterEdge("text-after-edge"),
Percentages: N/A // SVG 1.1
Media: visual beforeEdge("before-edge"),
Computed value: as specified afterEdge("after-edge"),
Canonical order: per grammar hanging("hanging"),
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 private final String alignment;
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; AlignmentBaseline(String alignment) {
this.alignment = 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;
}
} }
// 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") @SuppressWarnings("unused")
enum Direction { none
ltr, }
rtl
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 { static boolean hasEnum(String strVal) {
normal, return weightToEnum.containsKey(strVal);
@SuppressWarnings("unused")none
} }
enum FontStyle { static FontWeight get(String strVal) {
normal, return weightToEnum.get(strVal);
italic,
@SuppressWarnings("unused")oblique
} }
enum FontWeight { private static final Map<String, FontWeight> weightToEnum = new HashMap<>();
// 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; static {
FontWeight(String weight) { for (final FontWeight en : FontWeight.values()) {
this.weight = weight; weightToEnum.put(en.weight, en);
} }
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;
}
} }
enum TextAnchor @Nonnull
{ @Override
start, public String toString() {
middle, return weight;
end }
}
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 static TextDecoration getEnum(String strVal) {
{ if (!decorationToEnum.containsKey(strVal)) {
None("none"), throw new IllegalArgumentException("Unknown String Value: " + strVal);
Underline("underline"), }
Overline("overline"), return decorationToEnum.get(strVal);
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;
}
} }
enum TextLengthAdjust private static final Map<String, TextDecoration> decorationToEnum = new HashMap<>();
{
spacing, static {
spacingAndGlyphs for (final TextDecoration en : TextDecoration.values()) {
decorationToEnum.put(en.decoration, en);
}
} }
enum TextPathMethod { @Nonnull
align, @Override
@SuppressWarnings("unused")stretch public String toString() {
return decoration;
} }
}
/* enum TextLengthAdjust {
TODO suggest adding a compatibility mid-line rendering attribute to textPath, spacing,
for a chrome/firefox/opera/safari compatible sharp text path rendering, spacingAndGlyphs
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 { enum TextPathMethod {
@SuppressWarnings("unused")left, align,
right @SuppressWarnings("unused")
} stretch
}
enum TextPathSpacing { /*
@SuppressWarnings("unused")auto, TODO suggest adding a compatibility mid-line rendering attribute to textPath,
exact 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. * LICENSE file in the root directory of this source tree.
*/ */
package com.horcrux.svg; package com.horcrux.svg;
import static com.horcrux.svg.TextProperties.AlignmentBaseline;
import static com.horcrux.svg.TextProperties.TextLengthAdjust;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
@@ -16,292 +18,288 @@ import android.graphics.Path;
import android.graphics.Region; import android.graphics.Region;
import android.view.View; import android.view.View;
import android.view.ViewParent; import android.view.ViewParent;
import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.annotations.ReactProp;
import java.util.ArrayList; import java.util.ArrayList;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static com.horcrux.svg.TextProperties.AlignmentBaseline;
import static com.horcrux.svg.TextProperties.TextLengthAdjust;
@SuppressLint("ViewConstructor") @SuppressLint("ViewConstructor")
class TextView extends GroupView { class TextView extends GroupView {
SVGLength mInlineSize = null; SVGLength mInlineSize = null;
SVGLength mTextLength = null; SVGLength mTextLength = null;
private String mBaselineShift = null; private String mBaselineShift = null;
TextLengthAdjust mLengthAdjust = TextLengthAdjust.spacing; TextLengthAdjust mLengthAdjust = TextLengthAdjust.spacing;
private AlignmentBaseline mAlignmentBaseline; private AlignmentBaseline mAlignmentBaseline;
@Nullable private ArrayList<SVGLength> mPositionX; @Nullable private ArrayList<SVGLength> mPositionX;
@Nullable private ArrayList<SVGLength> mPositionY; @Nullable private ArrayList<SVGLength> mPositionY;
@Nullable private ArrayList<SVGLength> mRotate; @Nullable private ArrayList<SVGLength> mRotate;
@Nullable private ArrayList<SVGLength> mDeltaX; @Nullable private ArrayList<SVGLength> mDeltaX;
@Nullable private ArrayList<SVGLength> mDeltaY; @Nullable private ArrayList<SVGLength> mDeltaY;
double cachedAdvance = Double.NaN; double cachedAdvance = Double.NaN;
public TextView(ReactContext reactContext) { public TextView(ReactContext reactContext) {
super(reactContext); super(reactContext);
} }
@Override @Override
public void invalidate() { public void invalidate() {
if (mPath == null) { if (mPath == null) {
return; return;
}
super.invalidate();
getTextContainer().clearChildCache();
} }
super.invalidate();
getTextContainer().clearChildCache();
}
void clearCache() { void clearCache() {
cachedAdvance = Double.NaN; cachedAdvance = Double.NaN;
super.clearCache(); super.clearCache();
} }
@ReactProp(name = "inlineSize") @ReactProp(name = "inlineSize")
public void setInlineSize(Dynamic inlineSize) { public void setInlineSize(Dynamic inlineSize) {
mInlineSize = SVGLength.from(inlineSize); mInlineSize = SVGLength.from(inlineSize);
invalidate(); invalidate();
} }
public void setInlineSize(String inlineSize) { public void setInlineSize(String inlineSize) {
mInlineSize = SVGLength.from(inlineSize); mInlineSize = SVGLength.from(inlineSize);
invalidate(); invalidate();
} }
@ReactProp(name = "textLength") @ReactProp(name = "textLength")
public void setTextLength(Dynamic length) { public void setTextLength(Dynamic length) {
mTextLength = SVGLength.from(length); mTextLength = SVGLength.from(length);
invalidate(); invalidate();
} }
public void setTextLength(String length) { public void setTextLength(String length) {
mTextLength = SVGLength.from(length); mTextLength = SVGLength.from(length);
invalidate(); invalidate();
} }
@ReactProp(name = "lengthAdjust") @ReactProp(name = "lengthAdjust")
public void setLengthAdjust(@Nullable String adjustment) { public void setLengthAdjust(@Nullable String adjustment) {
mLengthAdjust = TextLengthAdjust.valueOf(adjustment); mLengthAdjust = TextLengthAdjust.valueOf(adjustment);
invalidate(); invalidate();
} }
@ReactProp(name = "alignmentBaseline") @ReactProp(name = "alignmentBaseline")
public void setMethod(@Nullable String alignment) { public void setMethod(@Nullable String alignment) {
mAlignmentBaseline = AlignmentBaseline.getEnum(alignment); mAlignmentBaseline = AlignmentBaseline.getEnum(alignment);
invalidate(); invalidate();
} }
@ReactProp(name = "baselineShift") @ReactProp(name = "baselineShift")
public void setBaselineShift(Dynamic baselineShift) { public void setBaselineShift(Dynamic baselineShift) {
mBaselineShift = SVGLength.toString(baselineShift); mBaselineShift = SVGLength.toString(baselineShift);
invalidate(); invalidate();
} }
public void setBaselineShift(String baselineShift) { public void setBaselineShift(String baselineShift) {
mBaselineShift = baselineShift; mBaselineShift = baselineShift;
invalidate(); invalidate();
} }
@ReactProp(name = "verticalAlign") @ReactProp(name = "verticalAlign")
public void setVerticalAlign(@Nullable String verticalAlign) { public void setVerticalAlign(@Nullable String verticalAlign) {
if (verticalAlign != null) { if (verticalAlign != null) {
verticalAlign = verticalAlign.trim(); verticalAlign = verticalAlign.trim();
int i = verticalAlign.lastIndexOf(' '); int i = verticalAlign.lastIndexOf(' ');
try { try {
mAlignmentBaseline = AlignmentBaseline.getEnum(verticalAlign.substring(i)); mAlignmentBaseline = AlignmentBaseline.getEnum(verticalAlign.substring(i));
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
mAlignmentBaseline = AlignmentBaseline.baseline; mAlignmentBaseline = AlignmentBaseline.baseline;
} }
try { try {
mBaselineShift = verticalAlign.substring(0, i); mBaselineShift = verticalAlign.substring(0, i);
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
mBaselineShift = null; mBaselineShift = null;
} }
} else { } else {
mAlignmentBaseline = AlignmentBaseline.baseline; mAlignmentBaseline = AlignmentBaseline.baseline;
mBaselineShift = null; mBaselineShift = null;
}
invalidate();
} }
invalidate();
}
@ReactProp(name = "rotate") @ReactProp(name = "rotate")
public void setRotate(Dynamic rotate) { public void setRotate(Dynamic rotate) {
mRotate = SVGLength.arrayFrom(rotate); mRotate = SVGLength.arrayFrom(rotate);
invalidate(); invalidate();
} }
public void setRotate(ReadableArray rotate) { public void setRotate(ReadableArray rotate) {
mRotate = SVGLength.arrayFrom(rotate); mRotate = SVGLength.arrayFrom(rotate);
invalidate(); invalidate();
} }
@ReactProp(name = "dx") @ReactProp(name = "dx")
public void setDeltaX(Dynamic deltaX) { public void setDeltaX(Dynamic deltaX) {
mDeltaX = SVGLength.arrayFrom(deltaX); mDeltaX = SVGLength.arrayFrom(deltaX);
invalidate(); invalidate();
} }
public void setDeltaX(ReadableArray deltaX) { public void setDeltaX(ReadableArray deltaX) {
mDeltaX = SVGLength.arrayFrom(deltaX); mDeltaX = SVGLength.arrayFrom(deltaX);
invalidate(); invalidate();
} }
@ReactProp(name = "dy") @ReactProp(name = "dy")
public void setDeltaY(Dynamic deltaY) { public void setDeltaY(Dynamic deltaY) {
mDeltaY = SVGLength.arrayFrom(deltaY); mDeltaY = SVGLength.arrayFrom(deltaY);
invalidate(); invalidate();
} }
public void setDeltaY(ReadableArray deltaY) { public void setDeltaY(ReadableArray deltaY) {
mDeltaY = SVGLength.arrayFrom(deltaY); mDeltaY = SVGLength.arrayFrom(deltaY);
invalidate(); invalidate();
} }
@ReactProp(name = "x") @ReactProp(name = "x")
public void setPositionX(Dynamic positionX) { public void setPositionX(Dynamic positionX) {
mPositionX = SVGLength.arrayFrom(positionX); mPositionX = SVGLength.arrayFrom(positionX);
invalidate(); invalidate();
} }
public void setPositionX(ReadableArray positionX) { public void setPositionX(ReadableArray positionX) {
mPositionX = SVGLength.arrayFrom(positionX); mPositionX = SVGLength.arrayFrom(positionX);
invalidate(); invalidate();
} }
@ReactProp(name = "y") @ReactProp(name = "y")
public void setPositionY(Dynamic positionY) { public void setPositionY(Dynamic positionY) {
mPositionY = SVGLength.arrayFrom(positionY); mPositionY = SVGLength.arrayFrom(positionY);
invalidate(); invalidate();
} }
public void setPositionY(ReadableArray positionY) { public void setPositionY(ReadableArray positionY) {
mPositionY = SVGLength.arrayFrom(positionY); mPositionY = SVGLength.arrayFrom(positionY);
invalidate(); invalidate();
} }
@Override @Override
void draw(Canvas canvas, Paint paint, float opacity) { void draw(Canvas canvas, Paint paint, float opacity) {
setupGlyphContext(canvas); setupGlyphContext(canvas);
clip(canvas, paint); clip(canvas, paint);
getGroupPath(canvas, paint); getGroupPath(canvas, paint);
pushGlyphContext(); pushGlyphContext();
drawGroup(canvas, paint, opacity); drawGroup(canvas, paint, opacity);
popGlyphContext(); popGlyphContext();
} }
@Override @Override
Path getPath(Canvas canvas, Paint paint) { Path getPath(Canvas canvas, Paint paint) {
if (mPath != null) { if (mPath != null) {
return mPath; 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); parent = parent.getParent();
return getGroupPath(canvas, paint); }
} }
if (mAlignmentBaseline == null) {
@Override mAlignmentBaseline = AlignmentBaseline.baseline;
Path getPath(Canvas canvas, Paint paint, Region.Op op) {
return getPath(canvas, paint);
} }
return mAlignmentBaseline;
}
AlignmentBaseline getAlignmentBaseline() { String getBaselineShift() {
if (mAlignmentBaseline == null) { if (mBaselineShift == null) {
ViewParent parent = this.getParent(); ViewParent parent = this.getParent();
while (parent != null) { while (parent != null) {
if (parent instanceof TextView) { if (parent instanceof TextView) {
TextView node = (TextView)parent; TextView node = (TextView) parent;
final AlignmentBaseline baseline = node.mAlignmentBaseline; final String baselineShift = node.mBaselineShift;
if (baseline != null) { if (baselineShift != null) {
mAlignmentBaseline = baseline; mBaselineShift = baselineShift;
return baseline; return baselineShift;
} }
}
parent = parent.getParent();
}
} }
if (mAlignmentBaseline == null) { parent = parent.getParent();
mAlignmentBaseline = AlignmentBaseline.baseline; }
}
return mAlignmentBaseline;
} }
return mBaselineShift;
}
String getBaselineShift() { Path getGroupPath(Canvas canvas, Paint paint) {
if (mBaselineShift == null) { if (mPath != null) {
ViewParent parent = this.getParent(); return mPath;
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;
} }
pushGlyphContext();
mPath = super.getPath(canvas, paint);
popGlyphContext();
Path getGroupPath(Canvas canvas, Paint paint) { return mPath;
if (mPath != null) { }
return mPath;
}
pushGlyphContext();
mPath = super.getPath(canvas, paint);
popGlyphContext();
return mPath; @Override
} void pushGlyphContext() {
boolean isTextNode = !(this instanceof TextPathView) && !(this instanceof TSpanView);
getTextRootGlyphContext()
.pushContext(isTextNode, this, mFont, mPositionX, mPositionY, mDeltaX, mDeltaY, mRotate);
}
@Override TextView getTextAnchorRoot() {
void pushGlyphContext() { GlyphContext gc = getTextRootGlyphContext();
boolean isTextNode = !(this instanceof TextPathView) && !(this instanceof TSpanView); ArrayList<FontData> font = gc.mFontContext;
getTextRootGlyphContext().pushContext(isTextNode, this, mFont, mPositionX, mPositionY, mDeltaX, mDeltaY, mRotate); TextView node = this;
} ViewParent parent = this.getParent();
for (int i = font.size() - 1; i >= 0; i--) {
TextView getTextAnchorRoot() { if (!(parent instanceof TextView)
GlyphContext gc = getTextRootGlyphContext(); || font.get(i).textAnchor == TextProperties.TextAnchor.start
ArrayList<FontData> font = gc.mFontContext; || node.mPositionX != null) {
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; return node;
}
node = (TextView) parent;
parent = node.getParent();
} }
return node;
}
double getSubtreeTextChunksTotalAdvance(Paint paint) { double getSubtreeTextChunksTotalAdvance(Paint paint) {
if (!Double.isNaN(cachedAdvance)) { if (!Double.isNaN(cachedAdvance)) {
return 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 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 getTextContainer() {
TextView node = this; TextView node = this;
ViewParent parent = this.getParent(); ViewParent parent = this.getParent();
while (parent instanceof TextView) { while (parent instanceof TextView) {
node = (TextView) parent; node = (TextView) parent;
parent = node.getParent(); parent = node.getParent();
}
return node;
} }
return node;
}
} }

View File

@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.horcrux.svg; package com.horcrux.svg;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
@@ -14,7 +13,6 @@ import android.graphics.Canvas;
import android.graphics.Matrix; import android.graphics.Matrix;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Path; import android.graphics.Path;
import com.facebook.common.logging.FLog; import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContext;
@@ -23,138 +21,151 @@ import com.facebook.react.uimanager.annotations.ReactProp;
@SuppressLint("ViewConstructor") @SuppressLint("ViewConstructor")
class UseView extends RenderableView { class UseView extends RenderableView {
private String mHref; private String mHref;
private SVGLength mX; private SVGLength mX;
private SVGLength mY; private SVGLength mY;
private SVGLength mW; private SVGLength mW;
private SVGLength mH; private SVGLength mH;
public UseView(ReactContext reactContext) { public UseView(ReactContext reactContext) {
super(reactContext); super(reactContext);
} }
@ReactProp(name = "href") @ReactProp(name = "href")
public void setHref(String href) { public void setHref(String href) {
mHref = href; mHref = href;
invalidate(); invalidate();
} }
@ReactProp(name = "x") @ReactProp(name = "x")
public void setX(Dynamic x) { public void setX(Dynamic x) {
mX = SVGLength.from(x); mX = SVGLength.from(x);
invalidate(); invalidate();
} }
public void setX(String x) { public void setX(String x) {
mX = SVGLength.from(x); mX = SVGLength.from(x);
invalidate(); invalidate();
} }
@ReactProp(name = "y") @ReactProp(name = "y")
public void setY(Dynamic y) { public void setY(Dynamic y) {
mY = SVGLength.from(y); mY = SVGLength.from(y);
invalidate(); invalidate();
} }
public void setY(String y) { public void setY(String y) {
mY = SVGLength.from(y); mY = SVGLength.from(y);
invalidate(); invalidate();
} }
@ReactProp(name = "width") @ReactProp(name = "width")
public void setWidth(Dynamic width) { public void setWidth(Dynamic width) {
mW = SVGLength.from(width); mW = SVGLength.from(width);
invalidate(); invalidate();
} }
public void setWidth(String width) { public void setWidth(String width) {
mW = SVGLength.from(width); mW = SVGLength.from(width);
invalidate(); invalidate();
} }
@ReactProp(name = "height") @ReactProp(name = "height")
public void setHeight(Dynamic height) { public void setHeight(Dynamic height) {
mH = SVGLength.from(height); mH = SVGLength.from(height);
invalidate(); invalidate();
} }
public void setHeight(String height) { public void setHeight(String height) {
mH = SVGLength.from(height); mH = SVGLength.from(height);
invalidate(); invalidate();
} }
@Override @Override
void draw(Canvas canvas, Paint paint, float opacity) { void draw(Canvas canvas, Paint paint, float opacity) {
VirtualView template = getSvgView().getDefinedTemplate(mHref); VirtualView template = getSvgView().getDefinedTemplate(mHref);
if (template == null) { if (template == null) {
FLog.w(ReactConstants.TAG, "`Use` element expected a pre-defined svg template as `href` prop, " + FLog.w(
"template named: " + mHref + " is not defined."); ReactConstants.TAG,
return; "`Use` element expected a pre-defined svg template as `href` prop, "
} + "template named: "
+ mHref
template.clearCache(); + " is not defined.");
canvas.translate((float) relativeOnWidth(mX), (float) relativeOnHeight(mY)); return;
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();
}
} }
@Override template.clearCache();
int hitTest(float[] src) { canvas.translate((float) relativeOnWidth(mX), (float) relativeOnHeight(mY));
if (!mInvertible || !mTransformInvertible) { if (template instanceof RenderableView) {
return -1; ((RenderableView) template).mergeProperties(this);
}
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 int count = template.saveAndSetupCanvas(canvas, mCTM);
Path getPath(Canvas canvas, Paint paint) { clip(canvas, paint);
VirtualView template = getSvgView().getDefinedTemplate(mHref);
if (template == null) { if (template instanceof SymbolView) {
FLog.w(ReactConstants.TAG, "`Use` element expected a pre-defined svg template as `href` prop, " + SymbolView symbol = (SymbolView) template;
"template named: " + mHref + " is not defined."); symbol.drawSymbol(
return null; canvas, paint, opacity, (float) relativeOnWidth(mW), (float) relativeOnHeight(mH));
} } else {
Path path = template.getPath(canvas, paint); template.draw(canvas, paint, opacity * mOpacity);
Path use = new Path();
Matrix m = new Matrix();
m.setTranslate((float) relativeOnWidth(mX), (float) relativeOnHeight(mY));
path.transform(m, use);
return use;
} }
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. * LICENSE file in the root directory of this source tree.
*/ */
package com.horcrux.svg; package com.horcrux.svg;
import android.graphics.Matrix; import android.graphics.Matrix;
@@ -14,90 +13,91 @@ import android.graphics.RectF;
class ViewBox { class ViewBox {
private static final int MOS_MEET = 0; private static final int MOS_MEET = 0;
private static final int MOS_SLICE = 1; private static final int MOS_SLICE = 1;
private static final int MOS_NONE = 2; private static final int MOS_NONE = 2;
static Matrix getTransform(RectF vbRect, RectF eRect, String align, int meetOrSlice) { static Matrix getTransform(RectF vbRect, RectF eRect, String align, int meetOrSlice) {
// based on https://svgwg.org/svg2-draft/coords.html#ComputingAViewportsTransform // 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. // Let vb-x, vb-y, vb-width, vb-height be the min-x, min-y, width and height values of the
double vbX = vbRect.left; // viewBox attribute respectively.
double vbY = vbRect.top; double vbX = vbRect.left;
double vbWidth = vbRect.width(); double vbY = vbRect.top;
double vbHeight = vbRect.height(); 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. // Let e-x, e-y, e-width, e-height be the position and size of the element respectively.
double eX = eRect.left; double eX = eRect.left;
double eY = eRect.top; double eY = eRect.top;
double eWidth = eRect.width(); double eWidth = eRect.width();
double eHeight = eRect.height(); double eHeight = eRect.height();
// Initialize scale-x to e-width/vb-width.
double scaleX = eWidth / vbWidth;
// Initialize scale-x to e-width/vb-width. // Initialize scale-y to e-height/vb-height.
double scaleX = eWidth / vbWidth; double scaleY = eHeight / vbHeight;
// Initialize scale-y to e-height/vb-height. // Initialize translate-x to e-x - (vb-x * scale-x).
double scaleY = eHeight / vbHeight; // 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). // If align is 'none'
// Initialize translate-y to e-y - (vb-y * scale-y). if (meetOrSlice == MOS_NONE) {
double translateX = eX - (vbX * scaleX); // Let scale be set the smaller value of scale-x and scale-y.
double translateY = eY - (vbY * scaleY); // Assign scale-x and scale-y to scale.
double scale = scaleX = scaleY = Math.min(scaleX, scaleY);
// If align is 'none' // If scale is greater than 1
if (meetOrSlice == MOS_NONE) { if (scale > 1) {
// Let scale be set the smaller value of scale-x and scale-y. // Minus translateX by (eWidth / scale - vbWidth) / 2
// Assign scale-x and scale-y to scale. // Minus translateY by (eHeight / scale - vbHeight) / 2
double scale = scaleX = scaleY = Math.min(scaleX, scaleY); 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 (!align.equals("none") && meetOrSlice == MOS_MEET) {
if (scale > 1) { scaleX = scaleY = Math.min(scaleX, scaleY);
// Minus translateX by (eWidth / scale - vbWidth) / 2 } else if (!align.equals("none") && meetOrSlice == MOS_SLICE) {
// Minus translateY by (eHeight / scale - vbHeight) / 2 scaleX = scaleY = Math.max(scaleX, scaleY);
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) { // If align contains 'xMid', add (e-width - vb-width * scale-x) / 2 to translate-x.
scaleX = scaleY = Math.min(scaleX, scaleY); if (align.contains("xMid")) {
} else if (!align.equals("none") && meetOrSlice == MOS_SLICE) { translateX += (eWidth - vbWidth * scaleX) / 2.0d;
scaleX = scaleY = Math.max(scaleX, scaleY); }
}
// If align contains 'xMid', add (e-width - vb-width * scale-x) / 2 to translate-x. // If align contains 'xMax', add (e-width - vb-width * scale-x) to translate-x.
if (align.contains("xMid")) { if (align.contains("xMax")) {
translateX += (eWidth - vbWidth * scaleX) / 2.0d; translateX += (eWidth - vbWidth * scaleX);
} }
// If align contains 'xMax', add (e-width - vb-width * scale-x) to translate-x. // If align contains 'yMid', add (e-height - vb-height * scale-y) / 2 to translate-y.
if (align.contains("xMax")) { if (align.contains("YMid")) {
translateX += (eWidth - vbWidth * scaleX); translateY += (eHeight - vbHeight * scaleY) / 2.0d;
} }
// If align contains 'yMid', add (e-height - vb-height * scale-y) / 2 to translate-y. // If align contains 'yMax', add (e-height - vb-height * scale-y) to translate-y.
if (align.contains("YMid")) { if (align.contains("YMax")) {
translateY += (eHeight - vbHeight * scaleY) / 2.0d; translateY += (eHeight - vbHeight * scaleY);
} }
// 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;
} }
// 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; package com.horcrux.svg;
import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContext;
import com.facebook.react.views.view.ReactViewGroup; import com.facebook.react.views.view.ReactViewGroup;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public abstract class FabricEnabledViewGroup extends ReactViewGroup { public abstract class FabricEnabledViewGroup extends ReactViewGroup {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -12,9 +12,9 @@
#ifdef RN_FABRIC_ENABLED #ifdef RN_FABRIC_ENABLED
#import <react/renderer/components/rnsvg/ComponentDescriptors.h> #import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import "RCTFabricComponentsPlugins.h"
#import "RCTConversions.h"
#import <react/renderer/components/view/conversions.h> #import <react/renderer/components/view/conversions.h>
#import "RCTConversions.h"
#import "RCTFabricComponentsPlugins.h"
#import "RNSVGFabricConversions.h" #import "RNSVGFabricConversions.h"
#endif // RN_FABRIC_ENABLED #endif // RN_FABRIC_ENABLED
@@ -41,177 +41,177 @@ using namespace facebook::react;
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps - (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.x = RCTNSStringFromStringNilIfEmpty(newProps.x)
self.y = RCTNSStringFromStringNilIfEmpty(newProps.y) ? [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)] : nil; ? [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)]
if (RCTNSStringFromStringNilIfEmpty(newProps.foreignObjectheight)) { : nil;
self.foreignObjectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.foreignObjectheight)]; self.y = RCTNSStringFromStringNilIfEmpty(newProps.y)
} ? [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)]
if (RCTNSStringFromStringNilIfEmpty(newProps.foreignObjectwidth)) { : nil;
self.foreignObjectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.foreignObjectwidth)]; if (RCTNSStringFromStringNilIfEmpty(newProps.foreignObjectheight)) {
} self.foreignObjectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.foreignObjectheight)];
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) { }
self.foreignObjectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)]; if (RCTNSStringFromStringNilIfEmpty(newProps.foreignObjectwidth)) {
} self.foreignObjectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.foreignObjectwidth)];
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) { }
self.foreignObjectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)]; 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 - (void)prepareForRecycle
{ {
[super prepareForRecycle]; [super prepareForRecycle];
_x = nil; _x = nil;
_y = nil; _y = nil;
_foreignObjectheight = nil; _foreignObjectheight = nil;
_foreignObjectwidth = nil; _foreignObjectwidth = nil;
} }
#endif // RN_FABRIC_ENABLED #endif // RN_FABRIC_ENABLED
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event - (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{ {
return nil; return nil;
} }
- (void)parseReference - (void)parseReference
{ {
self.dirty = false; self.dirty = false;
} }
- (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect - (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect
{ {
[self clip:context]; [self clip:context];
CGContextTranslateCTM(context, [self relativeOnWidth:self.x], [self relativeOnHeight:self.y]); CGContextTranslateCTM(context, [self relativeOnWidth:self.x], [self relativeOnHeight:self.y]);
CGRect clip = CGRectMake( CGRect clip = CGRectMake(
0, 0, 0, [self relativeOnWidth:self.foreignObjectwidth], [self relativeOnHeight:self.foreignObjectheight]);
0, CGContextClipToRect(context, clip);
[self relativeOnWidth:self.foreignObjectwidth], [super renderLayerTo:context rect:rect];
[self relativeOnHeight:self.foreignObjectheight]
);
CGContextClipToRect(context, clip);
[super renderLayerTo:context rect:rect];
} }
- (void)renderGroupTo:(CGContextRef)context rect:(CGRect)rect - (void)renderGroupTo:(CGContextRef)context rect:(CGRect)rect
{ {
[self pushGlyphContext]; [self pushGlyphContext];
__block CGRect bounds = CGRectNull; __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;
}
[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; return YES;
}]; }
CGPathRef path = [self getPath:context]; if (svgNode.responsible && !self.svgView.responsible) {
[self setHitArea:path]; self.svgView.responsible = YES;
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); if ([node isKindOfClass:[RNSVGRenderable class]]) {
CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM); [(RNSVGRenderable *)node mergeProperties:self];
}
self.ctm = svgToClientTransform; [svgNode renderTo:context rect:rect];
self.screenCTM = current;
CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms); CGRect nodeRect = svgNode.clientRect;
CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); if (!CGRectIsEmpty(nodeRect)) {
CGPoint center = CGPointApplyAffineTransform(mid, transform); bounds = CGRectUnion(bounds, nodeRect);
}
self.bounds = bounds; if ([node isKindOfClass:[RNSVGRenderable class]]) {
if (!isnan(center.x) && !isnan(center.y)) { [(RNSVGRenderable *)node resetProperties];
self.center = center; }
} } else if ([node isKindOfClass:[RNSVGSvgView class]]) {
self.frame = bounds; 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 - (void)drawRect:(CGRect)rect
{ {
[self invalidate]; [self invalidate];
} }
- (void)setX:(RNSVGLength *)x - (void)setX:(RNSVGLength *)x
{ {
if ([x isEqualTo:_x]) { if ([x isEqualTo:_x]) {
return; return;
} }
_x = x; _x = x;
[self invalidate]; [self invalidate];
} }
- (void)setY:(RNSVGLength *)y - (void)setY:(RNSVGLength *)y
{ {
if ([y isEqualTo:_y]) { if ([y isEqualTo:_y]) {
return; return;
} }
_y = y; _y = y;
[self invalidate]; [self invalidate];
} }
- (void)setForeignObjectwidth:(RNSVGLength *)foreignObjectwidth - (void)setForeignObjectwidth:(RNSVGLength *)foreignObjectwidth
{ {
if ([foreignObjectwidth isEqualTo:_foreignObjectwidth]) { if ([foreignObjectwidth isEqualTo:_foreignObjectwidth]) {
return; return;
} }
_foreignObjectwidth = foreignObjectwidth; _foreignObjectwidth = foreignObjectwidth;
[self invalidate]; [self invalidate];
} }
- (void)setForeignObjectheight:(RNSVGLength *)foreignObjectheight - (void)setForeignObjectheight:(RNSVGLength *)foreignObjectheight
{ {
if ([foreignObjectheight isEqualTo:_foreignObjectheight]) { if ([foreignObjectheight isEqualTo:_foreignObjectheight]) {
return; return;
} }
_foreignObjectheight = foreignObjectheight; _foreignObjectheight = foreignObjectheight;
[self invalidate]; [self invalidate];
} }
@end @end

View File

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

View File

@@ -10,18 +10,16 @@
#import "RNSVGClipPath.h" #import "RNSVGClipPath.h"
#import "RNSVGMask.h" #import "RNSVGMask.h"
#ifdef RN_FABRIC_ENABLED #ifdef RN_FABRIC_ENABLED
#import <react/renderer/components/rnsvg/ComponentDescriptors.h> #import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import "RCTFabricComponentsPlugins.h"
#import "RCTConversions.h"
#import <react/renderer/components/view/conversions.h> #import <react/renderer/components/view/conversions.h>
#import "RCTConversions.h"
#import "RCTFabricComponentsPlugins.h"
#import "RNSVGFabricConversions.h" #import "RNSVGFabricConversions.h"
#endif // RN_FABRIC_ENABLED #endif // RN_FABRIC_ENABLED
@implementation RNSVGGroup @implementation RNSVGGroup {
{ RNSVGGlyphContext *_glyphContext;
RNSVGGlyphContext *_glyphContext;
} }
#ifdef RN_FABRIC_ENABLED #ifdef RN_FABRIC_ENABLED
@@ -45,251 +43,251 @@ using namespace facebook::react;
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps - (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 - (void)prepareForRecycle
{ {
[super prepareForRecycle]; [super prepareForRecycle];
_font = nil; _font = nil;
_glyphContext = nil; _glyphContext = nil;
} }
#endif // RN_FABRIC_ENABLED #endif // RN_FABRIC_ENABLED
- (void)setFont:(NSDictionary*)font - (void)setFont:(NSDictionary *)font
{ {
if (font == _font) { if (font == _font) {
return; return;
} }
[self invalidate]; [self invalidate];
_font = font; _font = font;
} }
- (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect - (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect
{ {
[self clip:context]; [self clip:context];
[self setupGlyphContext:context]; [self setupGlyphContext:context];
[self renderGroupTo:context rect:rect]; [self renderGroupTo:context rect:rect];
} }
- (void)renderGroupTo:(CGContextRef)context rect:(CGRect)rect - (void)renderGroupTo:(CGContextRef)context rect:(CGRect)rect
{ {
[self pushGlyphContext]; [self pushGlyphContext];
__block CGRect bounds = CGRectNull; __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];
}
[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; return YES;
}]; }
CGPathRef path = [self getPath:context]; if (svgNode.responsible && !self.svgView.responsible) {
[self setHitArea:path]; self.svgView.responsible = YES;
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); if ([node isKindOfClass:[RNSVGRenderable class]]) {
CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM); [(RNSVGRenderable *)node mergeProperties:self];
}
self.ctm = svgToClientTransform; [svgNode renderTo:context rect:rect];
self.screenCTM = current;
CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms); CGRect nodeRect = svgNode.clientRect;
CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); if (!CGRectIsEmpty(nodeRect)) {
CGPoint center = CGPointApplyAffineTransform(mid, transform); bounds = CGRectUnion(bounds, nodeRect);
}
self.bounds = bounds; if ([node isKindOfClass:[RNSVGRenderable class]]) {
if (!isnan(center.x) && !isnan(center.y)) { [(RNSVGRenderable *)node resetProperties];
self.center = center; }
} } else if ([node isKindOfClass:[RNSVGSvgView class]]) {
self.frame = bounds; 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 - (void)setupGlyphContext:(CGContextRef)context
{ {
CGRect clipBounds = CGContextGetClipBoundingBox(context); CGRect clipBounds = CGContextGetClipBoundingBox(context);
clipBounds = CGRectApplyAffineTransform(clipBounds, self.matrix); clipBounds = CGRectApplyAffineTransform(clipBounds, self.matrix);
clipBounds = CGRectApplyAffineTransform(clipBounds, self.transforms); clipBounds = CGRectApplyAffineTransform(clipBounds, self.transforms);
CGFloat width = CGRectGetWidth(clipBounds); CGFloat width = CGRectGetWidth(clipBounds);
CGFloat height = CGRectGetHeight(clipBounds); CGFloat height = CGRectGetHeight(clipBounds);
_glyphContext = [[RNSVGGlyphContext alloc] initWithWidth:width _glyphContext = [[RNSVGGlyphContext alloc] initWithWidth:width height:height];
height:height];
} }
- (RNSVGGlyphContext *)getGlyphContext - (RNSVGGlyphContext *)getGlyphContext
{ {
return _glyphContext; return _glyphContext;
} }
- (void)pushGlyphContext - (void)pushGlyphContext
{ {
__typeof__(self) __weak weakSelf = self; __typeof__(self) __weak weakSelf = self;
[[self.textRoot getGlyphContext] pushContext:weakSelf font:self.font]; [[self.textRoot getGlyphContext] pushContext:weakSelf font:self.font];
} }
- (void)popGlyphContext - (void)popGlyphContext
{ {
[[self.textRoot getGlyphContext] popContext]; [[self.textRoot getGlyphContext] popContext];
} }
- (void)renderPathTo:(CGContextRef)context rect:(CGRect)rect - (void)renderPathTo:(CGContextRef)context rect:(CGRect)rect
{ {
[super renderLayerTo:context rect:rect]; [super renderLayerTo:context rect:rect];
} }
- (CGPathRef)getPath:(CGContextRef)context - (CGPathRef)getPath:(CGContextRef)context
{ {
CGPathRef cached = self.path; CGPathRef cached = self.path;
if (cached) { 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; 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 - (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{ {
CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix); CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix);
transformed = CGPointApplyAffineTransform(transformed, self.invTransform); 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;
}
if (!CGRectContainsPoint(self.pathBounds, transformed)) {
return nil; 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 - (void)parseReference
{ {
self.dirty = false; self.dirty = false;
if (self.name) { if (self.name) {
__typeof__(self) __weak weakSelf = self; __typeof__(self) __weak weakSelf = self;
[self.svgView defineTemplate:weakSelf templateName:self.name]; [self.svgView defineTemplate:weakSelf templateName:self.name];
} }
[self traverseSubviews:^(RNSVGNode *node) { [self traverseSubviews:^(RNSVGNode *node) {
if ([node isKindOfClass:[RNSVGNode class]]) { if ([node isKindOfClass:[RNSVGNode class]]) {
[node parseReference]; [node parseReference];
} }
return YES; return YES;
}]; }];
} }
- (void)resetProperties - (void)resetProperties
{ {
[self traverseSubviews:^(__kindof RNSVGNode *node) { [self traverseSubviews:^(__kindof RNSVGNode *node) {
if ([node isKindOfClass:[RNSVGRenderable class]]) { if ([node isKindOfClass:[RNSVGRenderable class]]) {
[(RNSVGRenderable*)node resetProperties]; [(RNSVGRenderable *)node resetProperties];
} }
return YES; return YES;
}]; }];
} }
@end @end

View File

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

View File

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

View File

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

View File

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

View File

@@ -19,6 +19,9 @@
@property (nonatomic, strong) NSString *align; @property (nonatomic, strong) NSString *align;
@property (nonatomic, assign) RNSVGVBMOS meetOrSlice; @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 @end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,8 +6,8 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
#import "RNSVGRenderable.h"
#import "RNSVGLength.h" #import "RNSVGLength.h"
#import "RNSVGRenderable.h"
/** /**
* RNSVG defination are implemented as abstract UIViews for all elements inside Defs. * 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. * LICENSE file in the root directory of this source tree.
*/ */
#import "RNSVGUse.h" #import "RNSVGUse.h"
#import "RNSVGSymbol.h"
#import <React/RCTLog.h> #import <React/RCTLog.h>
#import "RNSVGSymbol.h"
#ifdef RN_FABRIC_ENABLED #ifdef RN_FABRIC_ENABLED
#import <react/renderer/components/rnsvg/ComponentDescriptors.h> #import <react/renderer/components/rnsvg/ComponentDescriptors.h>
#import "RCTFabricComponentsPlugins.h"
#import "RCTConversions.h"
#import <react/renderer/components/view/conversions.h> #import <react/renderer/components/view/conversions.h>
#import "RCTConversions.h"
#import "RCTFabricComponentsPlugins.h"
#import "RNSVGFabricConversions.h" #import "RNSVGFabricConversions.h"
#endif // RN_FABRIC_ENABLED #endif // RN_FABRIC_ENABLED
@@ -40,166 +40,171 @@ using namespace facebook::react;
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps - (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.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)];
self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)]; self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)];
if (RCTNSStringFromStringNilIfEmpty(newProps.useheight)) { if (RCTNSStringFromStringNilIfEmpty(newProps.useheight)) {
self.useheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.useheight)]; self.useheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.useheight)];
} }
if (RCTNSStringFromStringNilIfEmpty(newProps.usewidth)) { if (RCTNSStringFromStringNilIfEmpty(newProps.usewidth)) {
self.usewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.usewidth)]; self.usewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.usewidth)];
} }
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) { if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
self.useheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)]; self.useheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
} }
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) { if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
self.usewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)]; self.usewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
} }
self.href = RCTNSStringFromStringNilIfEmpty(newProps.href); self.href = RCTNSStringFromStringNilIfEmpty(newProps.href);
setCommonRenderableProps(newProps, self); setCommonRenderableProps(newProps, self);
} }
- (void)prepareForRecycle - (void)prepareForRecycle
{ {
[super prepareForRecycle]; [super prepareForRecycle];
_x = nil; _x = nil;
_y = nil; _y = nil;
_useheight = nil; _useheight = nil;
_usewidth = nil; _usewidth = nil;
_href = nil; _href = nil;
} }
#endif // RN_FABRIC_ENABLED #endif // RN_FABRIC_ENABLED
- (void)setHref:(NSString *)href - (void)setHref:(NSString *)href
{ {
if ([href isEqualToString:_href]) { if ([href isEqualToString:_href]) {
return; return;
} }
[self invalidate]; [self invalidate];
_href = href; _href = href;
} }
- (void)setX:(RNSVGLength *)x - (void)setX:(RNSVGLength *)x
{ {
if ([x isEqualTo:_x]) { if ([x isEqualTo:_x]) {
return; return;
} }
[self invalidate]; [self invalidate];
_x = x; _x = x;
} }
- (void)setY:(RNSVGLength *)y - (void)setY:(RNSVGLength *)y
{ {
if ([y isEqualTo:_y]) { if ([y isEqualTo:_y]) {
return; return;
} }
[self invalidate]; [self invalidate];
_y = y; _y = y;
} }
- (void)setUsewidth:(RNSVGLength *)usewidth - (void)setUsewidth:(RNSVGLength *)usewidth
{ {
if ([usewidth isEqualTo:_usewidth]) { if ([usewidth isEqualTo:_usewidth]) {
return; return;
} }
[self invalidate]; [self invalidate];
_usewidth = usewidth; _usewidth = usewidth;
} }
- (void)setUseheight:(RNSVGLength *)useheight - (void)setUseheight:(RNSVGLength *)useheight
{ {
if ([useheight isEqualTo:_useheight]) { if ([useheight isEqualTo:_useheight]) {
return; return;
} }
[self invalidate]; [self invalidate];
_useheight = useheight; _useheight = useheight;
} }
- (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect - (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect
{ {
CGContextTranslateCTM(context, [self relativeOnWidth:self.x], [self relativeOnHeight:self.y]); CGContextTranslateCTM(context, [self relativeOnWidth:self.x], [self relativeOnHeight:self.y]);
RNSVGNode* definedTemplate = [self.svgView getDefinedTemplate:self.href]; RNSVGNode *definedTemplate = [self.svgView getDefinedTemplate:self.href];
if (definedTemplate) { if (definedTemplate) {
[self beginTransparencyLayer:context]; [self beginTransparencyLayer:context];
[self clip:context]; [self clip:context];
if ([definedTemplate isKindOfClass:[RNSVGRenderable class]]) { if ([definedTemplate isKindOfClass:[RNSVGRenderable class]]) {
[(RNSVGRenderable*)definedTemplate mergeProperties:self]; [(RNSVGRenderable *)definedTemplate mergeProperties:self];
} }
if ([definedTemplate class] == [RNSVGSymbol class]) { if ([definedTemplate class] == [RNSVGSymbol class]) {
RNSVGSymbol *symbol = (RNSVGSymbol*)definedTemplate; RNSVGSymbol *symbol = (RNSVGSymbol *)definedTemplate;
[symbol renderSymbolTo:context width:[self relativeOnWidth:self.usewidth] height:[self relativeOnHeight:self.useheight]]; [symbol renderSymbolTo:context
} else { width:[self relativeOnWidth:self.usewidth]
[definedTemplate renderTo:context rect:rect]; height:[self relativeOnHeight:self.useheight]];
}
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;
} else { } else {
return; [definedTemplate renderTo:context rect:rect];
} }
CGRect bounds = definedTemplate.clientRect;
self.clientRect = bounds;
CGAffineTransform current = CGContextGetCTM(context); if ([definedTemplate isKindOfClass:[RNSVGRenderable class]]) {
CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM); [(RNSVGRenderable *)definedTemplate resetProperties];
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 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 { - (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
{ {
CGAffineTransform transform = CGAffineTransformMakeTranslation([self relativeOnWidth:self.x], [self relativeOnHeight:self.y]); CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix);
RNSVGNode const* definedTemplate = [self.svgView getDefinedTemplate:self.href]; transformed = CGPointApplyAffineTransform(transformed, self.invTransform);
if (!definedTemplate) { RNSVGNode const *definedTemplate = [self.svgView getDefinedTemplate:self.href];
return nil; if (event) {
} self.active = NO;
CGPathRef path = [definedTemplate getPath:context]; } else if (self.active) {
return CGPathCreateCopyByTransformingPath(path, &transform); 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 @end

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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