mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-05 22:56:11 +00:00
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:
91
.clang-format
Normal file
91
.clang-format
Normal 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
|
||||
...
|
||||
6
.github/workflows/js-build-test.yml
vendored
6
.github/workflows/js-build-test.yml
vendored
@@ -25,10 +25,8 @@ jobs:
|
||||
run: yarn
|
||||
- name: Build
|
||||
run: yarn bob
|
||||
- name: Lint
|
||||
run: yarn lint
|
||||
- name: Tests
|
||||
run: yarn jest
|
||||
- name: Test and lint
|
||||
run: yarn test
|
||||
- name: Build Example App
|
||||
working-directory: Example/
|
||||
run: yarn && yarn tsc
|
||||
|
||||
4
.husky/pre-commit
Executable file
4
.husky/pre-commit
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
yarn lint-staged
|
||||
@@ -501,7 +501,7 @@ SPEC CHECKSUMS:
|
||||
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
||||
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
|
||||
FBLazyVector: bcdeff523be9f87a135b7c6fde8736db94904716
|
||||
FBReactNativeSpec: 226f8b0f1a2e736a49301883ee34bca88cdc24f6
|
||||
FBReactNativeSpec: 0c3f104f594b34d7b3a923cd12e03b0d4e12eaf5
|
||||
Flipper: 26fc4b7382499f1281eb8cb921e5c3ad6de91fe0
|
||||
Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c
|
||||
Flipper-DoubleConversion: 57ffbe81ef95306cc9e69c4aa3aeeeeb58a6a28c
|
||||
|
||||
26
USAGE.md
26
USAGE.md
@@ -105,14 +105,14 @@ export default function TestComponent() {
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<SvgUri
|
||||
width="100"
|
||||
height="100"
|
||||
uri="https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/ruby.svg"
|
||||
onError={onError}
|
||||
onLoad={onLoad}
|
||||
/>
|
||||
{loading && <ActivityIndicator size="large" color="#0000ff"/>}
|
||||
<SvgUri
|
||||
width="100"
|
||||
height="100"
|
||||
uri="https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/ruby.svg"
|
||||
onError={onError}
|
||||
onLoad={onLoad}
|
||||
/>
|
||||
{loading && <ActivityIndicator size="large" color="#0000ff" />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -129,16 +129,20 @@ import * as React from 'react';
|
||||
import { SvgUri } from 'react-native-svg';
|
||||
|
||||
export default () => {
|
||||
const [uri, setUri] = React.useState('https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/not_existing.svg')
|
||||
const [uri, setUri] = React.useState(
|
||||
'https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/not_existing.svg',
|
||||
);
|
||||
return (
|
||||
<SvgUri
|
||||
onError={() => setUri('https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/ruby.svg')}
|
||||
onError={() =>
|
||||
setUri('https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/ruby.svg')
|
||||
}
|
||||
width="100%"
|
||||
height="100%"
|
||||
uri={uri}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# Use with svg files
|
||||
|
||||
@@ -10,6 +10,7 @@ buildscript {
|
||||
|
||||
dependencies {
|
||||
classpath("com.android.tools.build:gradle:3.6.1")
|
||||
classpath "com.diffplug.spotless:spotless-plugin-gradle:5.15.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +27,10 @@ if (isNewArchitectureEnabled()) {
|
||||
apply plugin: "com.facebook.react"
|
||||
}
|
||||
|
||||
if (project == rootProject) {
|
||||
apply from: 'spotless.gradle'
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
def safeExtGet(prop, fallback) {
|
||||
|
||||
BIN
android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal 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
240
android/gradlew
vendored
Executable 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
91
android/gradlew.bat
vendored
Normal 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
9
android/spotless.gradle
Normal 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()
|
||||
}
|
||||
}
|
||||
@@ -1,59 +1,62 @@
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.UiThread;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.bridge.WritableNativeMap;
|
||||
import com.facebook.react.uimanager.FabricViewStateManager;
|
||||
import com.facebook.react.uimanager.PixelUtil;
|
||||
import com.facebook.react.uimanager.FabricViewStateManager.HasFabricViewStateManager;
|
||||
import com.facebook.react.uimanager.FabricViewStateManager.StateUpdateCallback;
|
||||
import com.facebook.react.uimanager.PixelUtil;
|
||||
import com.facebook.react.views.view.ReactViewGroup;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class FabricEnabledViewGroup extends ReactViewGroup implements HasFabricViewStateManager {
|
||||
private final FabricViewStateManager mFabricViewStateManager = new FabricViewStateManager();
|
||||
public abstract class FabricEnabledViewGroup extends ReactViewGroup
|
||||
implements HasFabricViewStateManager {
|
||||
private final FabricViewStateManager mFabricViewStateManager = new FabricViewStateManager();
|
||||
|
||||
@NotNull
|
||||
public FabricViewStateManager getFabricViewStateManager() {
|
||||
return this.mFabricViewStateManager;
|
||||
}
|
||||
@NotNull
|
||||
public FabricViewStateManager getFabricViewStateManager() {
|
||||
return this.mFabricViewStateManager;
|
||||
}
|
||||
|
||||
protected final void updateScreenSizeFabric(int width, int height) {
|
||||
this.updateState(width, height);
|
||||
}
|
||||
protected final void updateScreenSizeFabric(int width, int height) {
|
||||
this.updateState(width, height);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public final void updateState(int width, int height) {
|
||||
final float realWidth = PixelUtil.toDIPFromPixel((float)width);
|
||||
final float realHeight = PixelUtil.toDIPFromPixel((float)height);
|
||||
ReadableMap currentState = this.mFabricViewStateManager.getStateData();
|
||||
if (currentState != null) {
|
||||
float delta = 0.9F;
|
||||
float stateFrameHeight = currentState.hasKey("frameHeight") ? (float)currentState.getDouble("frameHeight") : 0.0F;
|
||||
float stateFrameWidth = currentState.hasKey("frameWidth") ? (float)currentState.getDouble("frameWidth") : 0.0F;
|
||||
if (Math.abs(stateFrameWidth - realWidth) < delta &&
|
||||
Math.abs(stateFrameHeight - realHeight) < delta) {
|
||||
return;
|
||||
}
|
||||
@UiThread
|
||||
public final void updateState(int width, int height) {
|
||||
final float realWidth = PixelUtil.toDIPFromPixel((float) width);
|
||||
final float realHeight = PixelUtil.toDIPFromPixel((float) height);
|
||||
ReadableMap currentState = this.mFabricViewStateManager.getStateData();
|
||||
if (currentState != null) {
|
||||
float delta = 0.9F;
|
||||
float stateFrameHeight =
|
||||
currentState.hasKey("frameHeight") ? (float) currentState.getDouble("frameHeight") : 0.0F;
|
||||
float stateFrameWidth =
|
||||
currentState.hasKey("frameWidth") ? (float) currentState.getDouble("frameWidth") : 0.0F;
|
||||
if (Math.abs(stateFrameWidth - realWidth) < delta
|
||||
&& Math.abs(stateFrameHeight - realHeight) < delta) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.mFabricViewStateManager.setState((StateUpdateCallback)(new StateUpdateCallback() {
|
||||
public final WritableMap getStateUpdate() {
|
||||
WritableMap map = (WritableMap)(new WritableNativeMap());
|
||||
map.putDouble("frameWidth", (double)realWidth);
|
||||
map.putDouble("frameHeight", (double)realHeight);
|
||||
return map;
|
||||
}
|
||||
}));
|
||||
}
|
||||
this.mFabricViewStateManager.setState(
|
||||
(StateUpdateCallback)
|
||||
(new StateUpdateCallback() {
|
||||
public final WritableMap getStateUpdate() {
|
||||
WritableMap map = (WritableMap) (new WritableNativeMap());
|
||||
map.putDouble("frameWidth", (double) realWidth);
|
||||
map.putDouble("frameHeight", (double) realHeight);
|
||||
return map;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public FabricEnabledViewGroup(@Nullable ReactContext context) {
|
||||
super((Context)context);
|
||||
}
|
||||
public FabricEnabledViewGroup(@Nullable ReactContext context) {
|
||||
super((Context) context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
@@ -19,215 +18,214 @@ import android.graphics.RadialGradient;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Shader;
|
||||
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
|
||||
class Brush {
|
||||
private final BrushType mType;
|
||||
private final SVGLength[] mPoints;
|
||||
private ReadableArray mColors;
|
||||
private final boolean mUseObjectBoundingBox;
|
||||
private final BrushType mType;
|
||||
private final SVGLength[] mPoints;
|
||||
private ReadableArray mColors;
|
||||
private final boolean mUseObjectBoundingBox;
|
||||
|
||||
// TODO implement pattern units
|
||||
@SuppressWarnings({"unused"})
|
||||
private boolean mUseContentObjectBoundingBoxUnits;
|
||||
// TODO implement pattern units
|
||||
@SuppressWarnings({"unused"})
|
||||
private boolean mUseContentObjectBoundingBoxUnits;
|
||||
|
||||
private Matrix mMatrix;
|
||||
private Rect mUserSpaceBoundingBox;
|
||||
private PatternView mPattern;
|
||||
private Matrix mMatrix;
|
||||
private Rect mUserSpaceBoundingBox;
|
||||
private PatternView mPattern;
|
||||
|
||||
Brush(BrushType type, SVGLength[] points, BrushUnits units) {
|
||||
mType = type;
|
||||
mPoints = points;
|
||||
mUseObjectBoundingBox = units == BrushUnits.OBJECT_BOUNDING_BOX;
|
||||
Brush(BrushType type, SVGLength[] points, BrushUnits units) {
|
||||
mType = type;
|
||||
mPoints = points;
|
||||
mUseObjectBoundingBox = units == BrushUnits.OBJECT_BOUNDING_BOX;
|
||||
}
|
||||
|
||||
void setContentUnits(BrushUnits units) {
|
||||
mUseContentObjectBoundingBoxUnits = units == BrushUnits.OBJECT_BOUNDING_BOX;
|
||||
}
|
||||
|
||||
void setPattern(PatternView pattern) {
|
||||
mPattern = pattern;
|
||||
}
|
||||
|
||||
enum BrushType {
|
||||
LINEAR_GRADIENT,
|
||||
RADIAL_GRADIENT,
|
||||
PATTERN
|
||||
}
|
||||
|
||||
enum BrushUnits {
|
||||
OBJECT_BOUNDING_BOX,
|
||||
USER_SPACE_ON_USE
|
||||
}
|
||||
|
||||
private static void parseGradientStops(
|
||||
ReadableArray value, int stopsCount, float[] stops, int[] stopsColors, float opacity) {
|
||||
for (int i = 0; i < stopsCount; i++) {
|
||||
int stopIndex = i * 2;
|
||||
stops[i] = (float) value.getDouble(stopIndex);
|
||||
int color = value.getInt(stopIndex + 1);
|
||||
int alpha = color >>> 24;
|
||||
int combined = Math.round((float) alpha * opacity);
|
||||
stopsColors[i] = combined << 24 | (color & 0x00ffffff);
|
||||
}
|
||||
}
|
||||
|
||||
void setUserSpaceBoundingBox(Rect userSpaceBoundingBox) {
|
||||
mUserSpaceBoundingBox = userSpaceBoundingBox;
|
||||
}
|
||||
|
||||
void setGradientColors(ReadableArray colors) {
|
||||
mColors = colors;
|
||||
}
|
||||
|
||||
void setGradientTransform(Matrix matrix) {
|
||||
mMatrix = matrix;
|
||||
}
|
||||
|
||||
private RectF getPaintRect(RectF pathBoundingBox) {
|
||||
RectF rect = mUseObjectBoundingBox ? pathBoundingBox : new RectF(mUserSpaceBoundingBox);
|
||||
float width = rect.width();
|
||||
float height = rect.height();
|
||||
float x = 0f;
|
||||
float y = 0f;
|
||||
|
||||
if (mUseObjectBoundingBox) {
|
||||
x = rect.left;
|
||||
y = rect.top;
|
||||
}
|
||||
|
||||
void setContentUnits(BrushUnits units) {
|
||||
mUseContentObjectBoundingBoxUnits = units == BrushUnits.OBJECT_BOUNDING_BOX;
|
||||
return new RectF(x, y, x + width, y + height);
|
||||
}
|
||||
|
||||
private double getVal(SVGLength length, double relative, float scale, float textSize) {
|
||||
return PropHelper.fromRelative(
|
||||
length,
|
||||
relative,
|
||||
0,
|
||||
mUseObjectBoundingBox && length.unit == SVGLength.UnitType.NUMBER ? relative : scale,
|
||||
textSize);
|
||||
}
|
||||
|
||||
void setupPaint(Paint paint, RectF pathBoundingBox, float scale, float opacity) {
|
||||
RectF rect = getPaintRect(pathBoundingBox);
|
||||
float width = rect.width();
|
||||
float height = rect.height();
|
||||
float offsetX = rect.left;
|
||||
float offsetY = rect.top;
|
||||
|
||||
float textSize = paint.getTextSize();
|
||||
if (mType == BrushType.PATTERN) {
|
||||
double x = getVal(mPoints[0], width, scale, textSize);
|
||||
double y = getVal(mPoints[1], height, scale, textSize);
|
||||
double w = getVal(mPoints[2], width, scale, textSize);
|
||||
double h = getVal(mPoints[3], height, scale, textSize);
|
||||
|
||||
if (!(w > 1 && h > 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Bitmap bitmap = Bitmap.createBitmap((int) w, (int) h, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
|
||||
RectF vbRect = mPattern.getViewBox();
|
||||
if (vbRect != null && vbRect.width() > 0 && vbRect.height() > 0) {
|
||||
RectF eRect = new RectF((float) x, (float) y, (float) w, (float) h);
|
||||
Matrix mViewBoxMatrix =
|
||||
ViewBox.getTransform(vbRect, eRect, mPattern.mAlign, mPattern.mMeetOrSlice);
|
||||
canvas.concat(mViewBoxMatrix);
|
||||
}
|
||||
|
||||
if (mUseContentObjectBoundingBoxUnits) {
|
||||
canvas.scale(width / scale, height / scale);
|
||||
}
|
||||
|
||||
mPattern.draw(canvas, new Paint(), opacity);
|
||||
|
||||
Matrix patternMatrix = new Matrix();
|
||||
if (mMatrix != null) {
|
||||
patternMatrix.preConcat(mMatrix);
|
||||
}
|
||||
|
||||
BitmapShader bitmapShader =
|
||||
new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
|
||||
bitmapShader.setLocalMatrix(patternMatrix);
|
||||
paint.setShader(bitmapShader);
|
||||
return;
|
||||
}
|
||||
|
||||
void setPattern(PatternView pattern) {
|
||||
mPattern = pattern;
|
||||
int size = mColors.size();
|
||||
if (size == 0) {
|
||||
FLog.w(ReactConstants.TAG, "Gradient contains no stops");
|
||||
return;
|
||||
}
|
||||
int stopsCount = size / 2;
|
||||
int[] stopsColors = new int[stopsCount];
|
||||
float[] stops = new float[stopsCount];
|
||||
parseGradientStops(mColors, stopsCount, stops, stopsColors, opacity);
|
||||
|
||||
if (stops.length == 1) {
|
||||
// Gradient with only one stop will make LinearGradient/RadialGradient
|
||||
// throw. It may happen when source SVG contains only one stop or
|
||||
// two stops at the same spot (see lib/extract/extractGradient.js).
|
||||
// Although it's mistake SVGs like this can be produced by vector
|
||||
// editors or other tools, so let's handle that gracefully.
|
||||
stopsColors = new int[] {stopsColors[0], stopsColors[0]};
|
||||
stops = new float[] {stops[0], stops[0]};
|
||||
FLog.w(ReactConstants.TAG, "Gradient contains only one stop");
|
||||
}
|
||||
|
||||
enum BrushType {
|
||||
LINEAR_GRADIENT,
|
||||
RADIAL_GRADIENT,
|
||||
PATTERN
|
||||
}
|
||||
|
||||
enum BrushUnits {
|
||||
OBJECT_BOUNDING_BOX,
|
||||
USER_SPACE_ON_USE
|
||||
}
|
||||
|
||||
private static void parseGradientStops(ReadableArray value, int stopsCount, float[] stops, int[] stopsColors, float opacity) {
|
||||
for (int i = 0; i < stopsCount; i++) {
|
||||
int stopIndex = i * 2;
|
||||
stops[i] = (float) value.getDouble(stopIndex);
|
||||
int color = value.getInt(stopIndex + 1);
|
||||
int alpha = color >>> 24;
|
||||
int combined = Math.round((float)alpha * opacity);
|
||||
stopsColors[i] = combined << 24 | (color & 0x00ffffff);
|
||||
}
|
||||
}
|
||||
|
||||
void setUserSpaceBoundingBox(Rect userSpaceBoundingBox) {
|
||||
mUserSpaceBoundingBox = userSpaceBoundingBox;
|
||||
}
|
||||
|
||||
void setGradientColors(ReadableArray colors) {
|
||||
mColors = colors;
|
||||
}
|
||||
|
||||
void setGradientTransform(Matrix matrix) {
|
||||
mMatrix = matrix;
|
||||
}
|
||||
|
||||
private RectF getPaintRect(RectF pathBoundingBox) {
|
||||
RectF rect = mUseObjectBoundingBox ? pathBoundingBox : new RectF(mUserSpaceBoundingBox);
|
||||
float width = rect.width();
|
||||
float height = rect.height();
|
||||
float x = 0f;
|
||||
float y = 0f;
|
||||
|
||||
if (mUseObjectBoundingBox) {
|
||||
x = rect.left;
|
||||
y = rect.top;
|
||||
}
|
||||
|
||||
return new RectF(x, y, x + width, y + height);
|
||||
}
|
||||
|
||||
private double getVal(SVGLength length, double relative, float scale, float textSize) {
|
||||
return PropHelper.fromRelative(length, relative, 0, mUseObjectBoundingBox &&
|
||||
length.unit == SVGLength.UnitType.NUMBER ? relative : scale, textSize);
|
||||
}
|
||||
|
||||
void setupPaint(Paint paint, RectF pathBoundingBox, float scale, float opacity) {
|
||||
RectF rect = getPaintRect(pathBoundingBox);
|
||||
float width = rect.width();
|
||||
float height = rect.height();
|
||||
float offsetX = rect.left;
|
||||
float offsetY = rect.top;
|
||||
|
||||
float textSize = paint.getTextSize();
|
||||
if (mType == BrushType.PATTERN) {
|
||||
double x = getVal(mPoints[0], width, scale, textSize);
|
||||
double y = getVal(mPoints[1], height, scale, textSize);
|
||||
double w = getVal(mPoints[2], width, scale, textSize);
|
||||
double h = getVal(mPoints[3], height, scale, textSize);
|
||||
|
||||
if (!(w > 1 && h > 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Bitmap bitmap = Bitmap.createBitmap(
|
||||
(int) w,
|
||||
(int) h,
|
||||
Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
|
||||
RectF vbRect = mPattern.getViewBox();
|
||||
if (vbRect != null && vbRect.width() > 0 && vbRect.height() > 0) {
|
||||
RectF eRect = new RectF((float) x, (float) y, (float) w, (float) h);
|
||||
Matrix mViewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mPattern.mAlign, mPattern.mMeetOrSlice);
|
||||
canvas.concat(mViewBoxMatrix);
|
||||
}
|
||||
|
||||
if (mUseContentObjectBoundingBoxUnits) {
|
||||
canvas.scale(width / scale, height / scale);
|
||||
}
|
||||
|
||||
mPattern.draw(canvas, new Paint(), opacity);
|
||||
|
||||
Matrix patternMatrix = new Matrix();
|
||||
if (mMatrix != null) {
|
||||
patternMatrix.preConcat(mMatrix);
|
||||
}
|
||||
|
||||
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
|
||||
bitmapShader.setLocalMatrix(patternMatrix);
|
||||
paint.setShader(bitmapShader);
|
||||
return;
|
||||
}
|
||||
|
||||
int size = mColors.size();
|
||||
if (size == 0) {
|
||||
FLog.w(ReactConstants.TAG, "Gradient contains no stops");
|
||||
return;
|
||||
}
|
||||
int stopsCount = size / 2;
|
||||
int[] stopsColors = new int[stopsCount];
|
||||
float[] stops = new float[stopsCount];
|
||||
parseGradientStops(mColors, stopsCount, stops, stopsColors, opacity);
|
||||
|
||||
if (stops.length == 1) {
|
||||
// Gradient with only one stop will make LinearGradient/RadialGradient
|
||||
// throw. It may happen when source SVG contains only one stop or
|
||||
// two stops at the same spot (see lib/extract/extractGradient.js).
|
||||
// Although it's mistake SVGs like this can be produced by vector
|
||||
// editors or other tools, so let's handle that gracefully.
|
||||
stopsColors = new int[] { stopsColors[0], stopsColors[0] };
|
||||
stops = new float[] { stops[0], stops[0] };
|
||||
FLog.w(ReactConstants.TAG, "Gradient contains only one stop");
|
||||
}
|
||||
|
||||
if (mType == BrushType.LINEAR_GRADIENT) {
|
||||
double x1 = getVal(mPoints[0], width, scale, textSize) + offsetX;
|
||||
double y1 = getVal(mPoints[1], height, scale, textSize) + offsetY;
|
||||
double x2 = getVal(mPoints[2], width, scale, textSize) + offsetX;
|
||||
double y2 = getVal(mPoints[3], height, scale, textSize) + offsetY;
|
||||
|
||||
Shader linearGradient = new LinearGradient(
|
||||
(float) x1,
|
||||
(float) y1,
|
||||
(float) x2,
|
||||
(float) y2,
|
||||
stopsColors,
|
||||
stops,
|
||||
Shader.TileMode.CLAMP);
|
||||
|
||||
if (mMatrix != null) {
|
||||
Matrix m = new Matrix();
|
||||
m.preConcat(mMatrix);
|
||||
linearGradient.setLocalMatrix(m);
|
||||
}
|
||||
|
||||
paint.setShader(linearGradient);
|
||||
} else if (mType == BrushType.RADIAL_GRADIENT) {
|
||||
double rx = getVal(mPoints[2], width, scale, textSize);
|
||||
double ry = getVal(mPoints[3], height, scale, textSize);
|
||||
|
||||
double ratio = ry / rx;
|
||||
|
||||
double cx = getVal(mPoints[4], width, scale, textSize) + offsetX;
|
||||
double cy = getVal(mPoints[5], height / ratio, scale, textSize) + offsetY / ratio;
|
||||
|
||||
// TODO: support focus point.
|
||||
//double fx = PropHelper.fromRelative(mPoints[0], width, offsetX, scale);
|
||||
//double fy = PropHelper.fromRelative(mPoints[1], height, offsetY, scale) / (ry / rx);
|
||||
|
||||
Shader radialGradient = new RadialGradient(
|
||||
(float) cx,
|
||||
(float) cy,
|
||||
(float) rx,
|
||||
stopsColors,
|
||||
stops,
|
||||
Shader.TileMode.CLAMP
|
||||
);
|
||||
|
||||
Matrix radialMatrix = new Matrix();
|
||||
radialMatrix.preScale(1f, (float) ratio);
|
||||
|
||||
if (mMatrix != null) {
|
||||
radialMatrix.preConcat(mMatrix);
|
||||
}
|
||||
|
||||
radialGradient.setLocalMatrix(radialMatrix);
|
||||
paint.setShader(radialGradient);
|
||||
}
|
||||
if (mType == BrushType.LINEAR_GRADIENT) {
|
||||
double x1 = getVal(mPoints[0], width, scale, textSize) + offsetX;
|
||||
double y1 = getVal(mPoints[1], height, scale, textSize) + offsetY;
|
||||
double x2 = getVal(mPoints[2], width, scale, textSize) + offsetX;
|
||||
double y2 = getVal(mPoints[3], height, scale, textSize) + offsetY;
|
||||
|
||||
Shader linearGradient =
|
||||
new LinearGradient(
|
||||
(float) x1,
|
||||
(float) y1,
|
||||
(float) x2,
|
||||
(float) y2,
|
||||
stopsColors,
|
||||
stops,
|
||||
Shader.TileMode.CLAMP);
|
||||
|
||||
if (mMatrix != null) {
|
||||
Matrix m = new Matrix();
|
||||
m.preConcat(mMatrix);
|
||||
linearGradient.setLocalMatrix(m);
|
||||
}
|
||||
|
||||
paint.setShader(linearGradient);
|
||||
} else if (mType == BrushType.RADIAL_GRADIENT) {
|
||||
double rx = getVal(mPoints[2], width, scale, textSize);
|
||||
double ry = getVal(mPoints[3], height, scale, textSize);
|
||||
|
||||
double ratio = ry / rx;
|
||||
|
||||
double cx = getVal(mPoints[4], width, scale, textSize) + offsetX;
|
||||
double cy = getVal(mPoints[5], height / ratio, scale, textSize) + offsetY / ratio;
|
||||
|
||||
// TODO: support focus point.
|
||||
// double fx = PropHelper.fromRelative(mPoints[0], width, offsetX, scale);
|
||||
// double fy = PropHelper.fromRelative(mPoints[1], height, offsetY, scale) / (ry / rx);
|
||||
|
||||
Shader radialGradient =
|
||||
new RadialGradient(
|
||||
(float) cx, (float) cy, (float) rx, stopsColors, stops, Shader.TileMode.CLAMP);
|
||||
|
||||
Matrix radialMatrix = new Matrix();
|
||||
radialMatrix.preScale(1f, (float) ratio);
|
||||
|
||||
if (mMatrix != null) {
|
||||
radialMatrix.preConcat(mMatrix);
|
||||
}
|
||||
|
||||
radialGradient.setLocalMatrix(radialMatrix);
|
||||
paint.setShader(radialGradient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,71 +6,68 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class CircleView extends RenderableView {
|
||||
private SVGLength mCx;
|
||||
private SVGLength mCy;
|
||||
private SVGLength mR;
|
||||
private SVGLength mCx;
|
||||
private SVGLength mCy;
|
||||
private SVGLength mR;
|
||||
|
||||
public CircleView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
public CircleView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@ReactProp(name = "cx")
|
||||
public void setCx(Dynamic cx) {
|
||||
mCx = SVGLength.from(cx);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "cx")
|
||||
public void setCx(Dynamic cx) {
|
||||
mCx = SVGLength.from(cx);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setCx(String cx) {
|
||||
mCx = SVGLength.from(cx);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "cy")
|
||||
public void setCy(Dynamic cy) {
|
||||
mCy = SVGLength.from(cy);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "cy")
|
||||
public void setCy(Dynamic cy) {
|
||||
mCy = SVGLength.from(cy);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setCy(String cy) {
|
||||
mCy = SVGLength.from(cy);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "r")
|
||||
public void setR(Dynamic r) {
|
||||
mR = SVGLength.from(r);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "r")
|
||||
public void setR(Dynamic r) {
|
||||
mR = SVGLength.from(r);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setR(String r) {
|
||||
mR = SVGLength.from(r);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
Path getPath(Canvas canvas, Paint paint) {
|
||||
Path path = new Path();
|
||||
@Override
|
||||
Path getPath(Canvas canvas, Paint paint) {
|
||||
Path path = new Path();
|
||||
|
||||
double cx = relativeOnWidth(mCx);
|
||||
double cy = relativeOnHeight(mCy);
|
||||
double r = relativeOnOther(mR);
|
||||
double cx = relativeOnWidth(mCx);
|
||||
double cy = relativeOnHeight(mCy);
|
||||
double r = relativeOnOther(mR);
|
||||
|
||||
path.addCircle((float) cx, (float) cy, (float) r, Path.Direction.CW);
|
||||
return path;
|
||||
}
|
||||
path.addCircle((float) cx, (float) cy, (float) r, Path.Direction.CW);
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,11 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
@@ -20,33 +18,35 @@ import com.facebook.react.common.ReactConstants;
|
||||
@SuppressLint("ViewConstructor")
|
||||
class ClipPathView extends GroupView {
|
||||
|
||||
public ClipPathView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
public ClipPathView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
void draw(Canvas canvas, Paint paint, float opacity) {
|
||||
FLog.w(ReactConstants.TAG, "RNSVG: ClipPath can't be drawn, it should be defined as a child component for `Defs` ");
|
||||
}
|
||||
@Override
|
||||
void draw(Canvas canvas, Paint paint, float opacity) {
|
||||
FLog.w(
|
||||
ReactConstants.TAG,
|
||||
"RNSVG: ClipPath can't be drawn, it should be defined as a child component for `Defs` ");
|
||||
}
|
||||
|
||||
@Override
|
||||
void saveDefinition() {
|
||||
getSvgView().defineClipPath(this, mName);
|
||||
}
|
||||
@Override
|
||||
void saveDefinition() {
|
||||
getSvgView().defineClipPath(this, mName);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isResponsible() {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
boolean isResponsible() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
int hitTest(float[] src) {
|
||||
return -1;
|
||||
}
|
||||
@Override
|
||||
int hitTest(float[] src) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
void mergeProperties(RenderableView target) {}
|
||||
@Override
|
||||
void mergeProperties(RenderableView target) {}
|
||||
|
||||
@Override
|
||||
void resetProperties() {}
|
||||
@Override
|
||||
void resetProperties() {}
|
||||
}
|
||||
|
||||
@@ -6,38 +6,36 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class DefinitionView extends VirtualView {
|
||||
|
||||
DefinitionView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
DefinitionView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@SuppressWarnings("EmptyMethod")
|
||||
void draw(Canvas canvas, Paint paint, float opacity) {}
|
||||
@SuppressWarnings("EmptyMethod")
|
||||
void draw(Canvas canvas, Paint paint, float opacity) {}
|
||||
|
||||
@Override
|
||||
boolean isResponsible() {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
boolean isResponsible() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
Path getPath(Canvas canvas, Paint paint) {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
Path getPath(Canvas canvas, Paint paint) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
int hitTest(float[] src) {
|
||||
return -1;
|
||||
}
|
||||
@Override
|
||||
int hitTest(float[] src) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,32 +6,30 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.view.View;
|
||||
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class DefsView extends DefinitionView {
|
||||
|
||||
public DefsView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
public DefsView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
void draw(Canvas canvas, Paint paint, float opacity) {}
|
||||
@Override
|
||||
void draw(Canvas canvas, Paint paint, float opacity) {}
|
||||
|
||||
void saveDefinition() {
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View child = getChildAt(i);
|
||||
if (child instanceof VirtualView) {
|
||||
((VirtualView)child).saveDefinition();
|
||||
}
|
||||
}
|
||||
void saveDefinition() {
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View child = getChildAt(i);
|
||||
if (child instanceof VirtualView) {
|
||||
((VirtualView) child).saveDefinition();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
@@ -14,76 +13,76 @@ import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.RectF;
|
||||
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class EllipseView extends RenderableView {
|
||||
private SVGLength mCx;
|
||||
private SVGLength mCy;
|
||||
private SVGLength mRx;
|
||||
private SVGLength mRy;
|
||||
private SVGLength mCx;
|
||||
private SVGLength mCy;
|
||||
private SVGLength mRx;
|
||||
private SVGLength mRy;
|
||||
|
||||
public EllipseView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
public EllipseView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@ReactProp(name = "cx")
|
||||
public void setCx(Dynamic cx) {
|
||||
mCx = SVGLength.from(cx);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "cx")
|
||||
public void setCx(Dynamic cx) {
|
||||
mCx = SVGLength.from(cx);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setCx(String cx) {
|
||||
mCx = SVGLength.from(cx);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "cy")
|
||||
public void setCy(Dynamic cy) {
|
||||
mCy = SVGLength.from(cy);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "cy")
|
||||
public void setCy(Dynamic cy) {
|
||||
mCy = SVGLength.from(cy);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setCy(String cy) {
|
||||
mCy = SVGLength.from(cy);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "rx")
|
||||
public void setRx(Dynamic rx) {
|
||||
mRx = SVGLength.from(rx);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "rx")
|
||||
public void setRx(Dynamic rx) {
|
||||
mRx = SVGLength.from(rx);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setRx(String rx) {
|
||||
mRx = SVGLength.from(rx);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "ry")
|
||||
public void setRy(Dynamic ry) {
|
||||
mRy = SVGLength.from(ry);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "ry")
|
||||
public void setRy(Dynamic ry) {
|
||||
mRy = SVGLength.from(ry);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setRy(String ry) {
|
||||
mRy = SVGLength.from(ry);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
Path getPath(Canvas canvas, Paint paint) {
|
||||
Path path = new Path();
|
||||
double cx = relativeOnWidth(mCx);
|
||||
double cy = relativeOnHeight(mCy);
|
||||
double rx = relativeOnWidth(mRx);
|
||||
double ry = relativeOnHeight(mRy);
|
||||
RectF oval = new RectF((float) (cx - rx), (float) (cy - ry), (float) (cx + rx), (float) (cy + ry));
|
||||
path.addOval(oval, Path.Direction.CW);
|
||||
@Override
|
||||
Path getPath(Canvas canvas, Paint paint) {
|
||||
Path path = new Path();
|
||||
double cx = relativeOnWidth(mCx);
|
||||
double cy = relativeOnHeight(mCy);
|
||||
double rx = relativeOnWidth(mRx);
|
||||
double ry = relativeOnHeight(mRy);
|
||||
RectF oval =
|
||||
new RectF((float) (cx - rx), (float) (cy - ry), (float) (cx + rx), (float) (cy + ry));
|
||||
path.addOval(oval, Path.Direction.CW);
|
||||
|
||||
return path;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,214 +1,232 @@
|
||||
package com.horcrux.svg;
|
||||
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.ReadableType;
|
||||
|
||||
import static com.facebook.react.uimanager.ViewProps.FONT_FAMILY;
|
||||
import static com.facebook.react.uimanager.ViewProps.FONT_SIZE;
|
||||
import static com.facebook.react.uimanager.ViewProps.FONT_STYLE;
|
||||
import static com.facebook.react.uimanager.ViewProps.FONT_WEIGHT;
|
||||
import static com.horcrux.svg.TextProperties.*;
|
||||
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.ReadableType;
|
||||
|
||||
class FontData {
|
||||
|
||||
static class AbsoluteFontWeight {
|
||||
static class AbsoluteFontWeight {
|
||||
|
||||
static final int normal = 400;
|
||||
static final int normal = 400;
|
||||
|
||||
private static final FontWeight[] WEIGHTS = new FontWeight[]{
|
||||
FontWeight.w100,
|
||||
FontWeight.w100,
|
||||
FontWeight.w200,
|
||||
FontWeight.w300,
|
||||
FontWeight.Normal,
|
||||
FontWeight.w500,
|
||||
FontWeight.w600,
|
||||
FontWeight.Bold,
|
||||
FontWeight.w800,
|
||||
FontWeight.w900,
|
||||
FontWeight.w900,
|
||||
private static final FontWeight[] WEIGHTS =
|
||||
new FontWeight[] {
|
||||
FontWeight.w100,
|
||||
FontWeight.w100,
|
||||
FontWeight.w200,
|
||||
FontWeight.w300,
|
||||
FontWeight.Normal,
|
||||
FontWeight.w500,
|
||||
FontWeight.w600,
|
||||
FontWeight.Bold,
|
||||
FontWeight.w800,
|
||||
FontWeight.w900,
|
||||
FontWeight.w900,
|
||||
};
|
||||
|
||||
static FontWeight nearestFontWeight(int absoluteFontWeight) {
|
||||
return WEIGHTS[Math.round(absoluteFontWeight / 100f)];
|
||||
}
|
||||
|
||||
private static final int[] absoluteFontWeights = new int[]{
|
||||
400, 700, 100, 200, 300, 400, 500, 600, 700, 800, 900
|
||||
};
|
||||
|
||||
// https://drafts.csswg.org/css-fonts-4/#relative-weights
|
||||
static int from(FontWeight fontWeight, FontData parent) {
|
||||
if (fontWeight == FontWeight.Bolder) {
|
||||
return bolder(parent.absoluteFontWeight);
|
||||
} else if (fontWeight == FontWeight.Lighter) {
|
||||
return lighter(parent.absoluteFontWeight);
|
||||
} else {
|
||||
return absoluteFontWeights[fontWeight.ordinal()];
|
||||
}
|
||||
}
|
||||
|
||||
private static int bolder(int inherited) {
|
||||
if (inherited < 350) {
|
||||
return 400;
|
||||
} else if (inherited < 550) {
|
||||
return 700;
|
||||
} else if (inherited < 900) {
|
||||
return 900;
|
||||
} else {
|
||||
return inherited;
|
||||
}
|
||||
}
|
||||
|
||||
private static int lighter(int inherited) {
|
||||
if (inherited < 100) {
|
||||
return inherited;
|
||||
} else if (inherited < 550) {
|
||||
return 100;
|
||||
} else if (inherited < 750) {
|
||||
return 400;
|
||||
} else {
|
||||
return 700;
|
||||
}
|
||||
}
|
||||
static FontWeight nearestFontWeight(int absoluteFontWeight) {
|
||||
return WEIGHTS[Math.round(absoluteFontWeight / 100f)];
|
||||
}
|
||||
|
||||
static final double DEFAULT_FONT_SIZE = 12d;
|
||||
private static final int[] absoluteFontWeights =
|
||||
new int[] {400, 700, 100, 200, 300, 400, 500, 600, 700, 800, 900};
|
||||
|
||||
private static final double DEFAULT_KERNING = 0d;
|
||||
private static final double DEFAULT_WORD_SPACING = 0d;
|
||||
private static final double DEFAULT_LETTER_SPACING = 0d;
|
||||
|
||||
private static final String KERNING = "kerning";
|
||||
private static final String FONT_DATA = "fontData";
|
||||
private static final String TEXT_ANCHOR = "textAnchor";
|
||||
private static final String WORD_SPACING = "wordSpacing";
|
||||
private static final String LETTER_SPACING = "letterSpacing";
|
||||
private static final String TEXT_DECORATION = "textDecoration";
|
||||
private static final String FONT_FEATURE_SETTINGS = "fontFeatureSettings";
|
||||
private static final String FONT_VARIATION_SETTINGS = "fontVariationSettings";
|
||||
private static final String FONT_VARIANT_LIGATURES = "fontVariantLigatures";
|
||||
|
||||
final double fontSize;
|
||||
final String fontFamily;
|
||||
final FontStyle fontStyle;
|
||||
final ReadableMap fontData;
|
||||
|
||||
FontWeight fontWeight;
|
||||
int absoluteFontWeight;
|
||||
|
||||
final String fontFeatureSettings;
|
||||
final String fontVariationSettings;
|
||||
final FontVariantLigatures fontVariantLigatures;
|
||||
|
||||
final TextAnchor textAnchor;
|
||||
private final TextDecoration textDecoration;
|
||||
|
||||
final double kerning;
|
||||
final double wordSpacing;
|
||||
final double letterSpacing;
|
||||
|
||||
final boolean manualKerning;
|
||||
|
||||
static final FontData Defaults = new FontData();
|
||||
|
||||
private FontData() {
|
||||
fontData = null;
|
||||
fontFamily = "";
|
||||
fontStyle = FontStyle.normal;
|
||||
fontWeight = FontWeight.Normal;
|
||||
absoluteFontWeight = AbsoluteFontWeight.normal;
|
||||
fontFeatureSettings = "";
|
||||
fontVariationSettings = "";
|
||||
fontVariantLigatures = FontVariantLigatures.normal;
|
||||
|
||||
textAnchor = TextAnchor.start;
|
||||
textDecoration = TextDecoration.None;
|
||||
|
||||
manualKerning = false;
|
||||
kerning = DEFAULT_KERNING;
|
||||
fontSize = DEFAULT_FONT_SIZE;
|
||||
wordSpacing = DEFAULT_WORD_SPACING;
|
||||
letterSpacing = DEFAULT_LETTER_SPACING;
|
||||
// https://drafts.csswg.org/css-fonts-4/#relative-weights
|
||||
static int from(FontWeight fontWeight, FontData parent) {
|
||||
if (fontWeight == FontWeight.Bolder) {
|
||||
return bolder(parent.absoluteFontWeight);
|
||||
} else if (fontWeight == FontWeight.Lighter) {
|
||||
return lighter(parent.absoluteFontWeight);
|
||||
} else {
|
||||
return absoluteFontWeights[fontWeight.ordinal()];
|
||||
}
|
||||
}
|
||||
|
||||
private double toAbsolute(ReadableMap font, String prop, double scale, double fontSize, double relative) {
|
||||
ReadableType propType = font.getType(prop);
|
||||
if (propType == ReadableType.Number) {
|
||||
return font.getDouble(prop);
|
||||
private static int bolder(int inherited) {
|
||||
if (inherited < 350) {
|
||||
return 400;
|
||||
} else if (inherited < 550) {
|
||||
return 700;
|
||||
} else if (inherited < 900) {
|
||||
return 900;
|
||||
} else {
|
||||
return inherited;
|
||||
}
|
||||
}
|
||||
|
||||
private static int lighter(int inherited) {
|
||||
if (inherited < 100) {
|
||||
return inherited;
|
||||
} else if (inherited < 550) {
|
||||
return 100;
|
||||
} else if (inherited < 750) {
|
||||
return 400;
|
||||
} else {
|
||||
return 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final double DEFAULT_FONT_SIZE = 12d;
|
||||
|
||||
private static final double DEFAULT_KERNING = 0d;
|
||||
private static final double DEFAULT_WORD_SPACING = 0d;
|
||||
private static final double DEFAULT_LETTER_SPACING = 0d;
|
||||
|
||||
private static final String KERNING = "kerning";
|
||||
private static final String FONT_DATA = "fontData";
|
||||
private static final String TEXT_ANCHOR = "textAnchor";
|
||||
private static final String WORD_SPACING = "wordSpacing";
|
||||
private static final String LETTER_SPACING = "letterSpacing";
|
||||
private static final String TEXT_DECORATION = "textDecoration";
|
||||
private static final String FONT_FEATURE_SETTINGS = "fontFeatureSettings";
|
||||
private static final String FONT_VARIATION_SETTINGS = "fontVariationSettings";
|
||||
private static final String FONT_VARIANT_LIGATURES = "fontVariantLigatures";
|
||||
|
||||
final double fontSize;
|
||||
final String fontFamily;
|
||||
final FontStyle fontStyle;
|
||||
final ReadableMap fontData;
|
||||
|
||||
FontWeight fontWeight;
|
||||
int absoluteFontWeight;
|
||||
|
||||
final String fontFeatureSettings;
|
||||
final String fontVariationSettings;
|
||||
final FontVariantLigatures fontVariantLigatures;
|
||||
|
||||
final TextAnchor textAnchor;
|
||||
private final TextDecoration textDecoration;
|
||||
|
||||
final double kerning;
|
||||
final double wordSpacing;
|
||||
final double letterSpacing;
|
||||
|
||||
final boolean manualKerning;
|
||||
|
||||
static final FontData Defaults = new FontData();
|
||||
|
||||
private FontData() {
|
||||
fontData = null;
|
||||
fontFamily = "";
|
||||
fontStyle = FontStyle.normal;
|
||||
fontWeight = FontWeight.Normal;
|
||||
absoluteFontWeight = AbsoluteFontWeight.normal;
|
||||
fontFeatureSettings = "";
|
||||
fontVariationSettings = "";
|
||||
fontVariantLigatures = FontVariantLigatures.normal;
|
||||
|
||||
textAnchor = TextAnchor.start;
|
||||
textDecoration = TextDecoration.None;
|
||||
|
||||
manualKerning = false;
|
||||
kerning = DEFAULT_KERNING;
|
||||
fontSize = DEFAULT_FONT_SIZE;
|
||||
wordSpacing = DEFAULT_WORD_SPACING;
|
||||
letterSpacing = DEFAULT_LETTER_SPACING;
|
||||
}
|
||||
|
||||
private double toAbsolute(
|
||||
ReadableMap font, String prop, double scale, double fontSize, double relative) {
|
||||
ReadableType propType = font.getType(prop);
|
||||
if (propType == ReadableType.Number) {
|
||||
return font.getDouble(prop);
|
||||
} else {
|
||||
String string = font.getString(prop);
|
||||
return PropHelper.fromRelative(string, relative, scale, fontSize);
|
||||
}
|
||||
}
|
||||
|
||||
private void setInheritedWeight(FontData parent) {
|
||||
absoluteFontWeight = parent.absoluteFontWeight;
|
||||
fontWeight = parent.fontWeight;
|
||||
}
|
||||
|
||||
private void handleNumericWeight(FontData parent, double number) {
|
||||
long weight = Math.round(number);
|
||||
if (weight >= 1 && weight <= 1000) {
|
||||
absoluteFontWeight = (int) weight;
|
||||
fontWeight = AbsoluteFontWeight.nearestFontWeight(absoluteFontWeight);
|
||||
} else {
|
||||
setInheritedWeight(parent);
|
||||
}
|
||||
}
|
||||
|
||||
FontData(ReadableMap font, FontData parent, double scale) {
|
||||
double parentFontSize = parent.fontSize;
|
||||
|
||||
if (font.hasKey(FONT_SIZE)) {
|
||||
fontSize = toAbsolute(font, FONT_SIZE, 1, parentFontSize, parentFontSize);
|
||||
} else {
|
||||
fontSize = parentFontSize;
|
||||
}
|
||||
|
||||
if (font.hasKey(FONT_WEIGHT)) {
|
||||
ReadableType fontWeightType = font.getType(FONT_WEIGHT);
|
||||
if (fontWeightType == ReadableType.Number) {
|
||||
handleNumericWeight(parent, font.getDouble(FONT_WEIGHT));
|
||||
} else {
|
||||
String string = font.getString(FONT_WEIGHT);
|
||||
if (FontWeight.hasEnum(string)) {
|
||||
absoluteFontWeight = AbsoluteFontWeight.from(FontWeight.get(string), parent);
|
||||
fontWeight = AbsoluteFontWeight.nearestFontWeight(absoluteFontWeight);
|
||||
} else if (string != null) {
|
||||
handleNumericWeight(parent, Double.parseDouble(string));
|
||||
} else {
|
||||
String string = font.getString(prop);
|
||||
return PropHelper.fromRelative(
|
||||
string,
|
||||
relative,
|
||||
scale,
|
||||
fontSize
|
||||
);
|
||||
setInheritedWeight(parent);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setInheritedWeight(parent);
|
||||
}
|
||||
|
||||
private void setInheritedWeight(FontData parent) {
|
||||
absoluteFontWeight = parent.absoluteFontWeight;
|
||||
fontWeight = parent.fontWeight;
|
||||
}
|
||||
fontData = font.hasKey(FONT_DATA) ? font.getMap(FONT_DATA) : parent.fontData;
|
||||
|
||||
private void handleNumericWeight(FontData parent, double number) {
|
||||
long weight = Math.round(number);
|
||||
if (weight >= 1 && weight <= 1000) {
|
||||
absoluteFontWeight = (int)weight;
|
||||
fontWeight = AbsoluteFontWeight.nearestFontWeight(absoluteFontWeight);
|
||||
} else {
|
||||
setInheritedWeight(parent);
|
||||
}
|
||||
}
|
||||
fontFamily = font.hasKey(FONT_FAMILY) ? font.getString(FONT_FAMILY) : parent.fontFamily;
|
||||
fontStyle =
|
||||
font.hasKey(FONT_STYLE) ? FontStyle.valueOf(font.getString(FONT_STYLE)) : parent.fontStyle;
|
||||
fontFeatureSettings =
|
||||
font.hasKey(FONT_FEATURE_SETTINGS)
|
||||
? font.getString(FONT_FEATURE_SETTINGS)
|
||||
: parent.fontFeatureSettings;
|
||||
fontVariationSettings =
|
||||
font.hasKey(FONT_VARIATION_SETTINGS)
|
||||
? font.getString(FONT_VARIATION_SETTINGS)
|
||||
: parent.fontVariationSettings;
|
||||
fontVariantLigatures =
|
||||
font.hasKey(FONT_VARIANT_LIGATURES)
|
||||
? FontVariantLigatures.valueOf(font.getString(FONT_VARIANT_LIGATURES))
|
||||
: parent.fontVariantLigatures;
|
||||
|
||||
FontData(ReadableMap font, FontData parent, double scale) {
|
||||
double parentFontSize = parent.fontSize;
|
||||
textAnchor =
|
||||
font.hasKey(TEXT_ANCHOR)
|
||||
? TextAnchor.valueOf(font.getString(TEXT_ANCHOR))
|
||||
: parent.textAnchor;
|
||||
textDecoration =
|
||||
font.hasKey(TEXT_DECORATION)
|
||||
? TextDecoration.getEnum(font.getString(TEXT_DECORATION))
|
||||
: parent.textDecoration;
|
||||
|
||||
if (font.hasKey(FONT_SIZE)) {
|
||||
fontSize = toAbsolute(font, FONT_SIZE, 1, parentFontSize, parentFontSize);
|
||||
} else {
|
||||
fontSize = parentFontSize;
|
||||
}
|
||||
final boolean hasKerning = font.hasKey(KERNING);
|
||||
manualKerning = hasKerning || parent.manualKerning;
|
||||
|
||||
if (font.hasKey(FONT_WEIGHT)) {
|
||||
ReadableType fontWeightType = font.getType(FONT_WEIGHT);
|
||||
if (fontWeightType == ReadableType.Number) {
|
||||
handleNumericWeight(parent, font.getDouble(FONT_WEIGHT));
|
||||
} else {
|
||||
String string = font.getString(FONT_WEIGHT);
|
||||
if (FontWeight.hasEnum(string)) {
|
||||
absoluteFontWeight = AbsoluteFontWeight.from(FontWeight.get(string), parent);
|
||||
fontWeight = AbsoluteFontWeight.nearestFontWeight(absoluteFontWeight);
|
||||
} else if (string != null) {
|
||||
handleNumericWeight(parent, Double.parseDouble(string));
|
||||
} else {
|
||||
setInheritedWeight(parent);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setInheritedWeight(parent);
|
||||
}
|
||||
|
||||
fontData = font.hasKey(FONT_DATA) ? font.getMap(FONT_DATA) : parent.fontData;
|
||||
|
||||
fontFamily = font.hasKey(FONT_FAMILY) ? font.getString(FONT_FAMILY) : parent.fontFamily;
|
||||
fontStyle = font.hasKey(FONT_STYLE) ? FontStyle.valueOf(font.getString(FONT_STYLE)) : parent.fontStyle;
|
||||
fontFeatureSettings = font.hasKey(FONT_FEATURE_SETTINGS) ? font.getString(FONT_FEATURE_SETTINGS) : parent.fontFeatureSettings;
|
||||
fontVariationSettings = font.hasKey(FONT_VARIATION_SETTINGS) ? font.getString(FONT_VARIATION_SETTINGS) : parent.fontVariationSettings;
|
||||
fontVariantLigatures = font.hasKey(FONT_VARIANT_LIGATURES) ? FontVariantLigatures.valueOf(font.getString(FONT_VARIANT_LIGATURES)) : parent.fontVariantLigatures;
|
||||
|
||||
textAnchor = font.hasKey(TEXT_ANCHOR) ? TextAnchor.valueOf(font.getString(TEXT_ANCHOR)) : parent.textAnchor;
|
||||
textDecoration = font.hasKey(TEXT_DECORATION) ? TextDecoration.getEnum(font.getString(TEXT_DECORATION)) : parent.textDecoration;
|
||||
|
||||
final boolean hasKerning = font.hasKey(KERNING);
|
||||
manualKerning = hasKerning || parent.manualKerning;
|
||||
|
||||
// https://www.w3.org/TR/SVG11/text.html#SpacingProperties
|
||||
// https://drafts.csswg.org/css-text-3/#spacing
|
||||
// calculated values for units in: kerning, word-spacing, and, letter-spacing.
|
||||
kerning = hasKerning ? toAbsolute(font, KERNING, scale, fontSize, 0) : parent.kerning;
|
||||
wordSpacing = font.hasKey(WORD_SPACING) ? toAbsolute(font, WORD_SPACING, scale, fontSize, 0) : parent.wordSpacing;
|
||||
letterSpacing = font.hasKey(LETTER_SPACING) ? toAbsolute(font, LETTER_SPACING, scale, fontSize, 0) : parent.letterSpacing;
|
||||
}
|
||||
// https://www.w3.org/TR/SVG11/text.html#SpacingProperties
|
||||
// https://drafts.csswg.org/css-text-3/#spacing
|
||||
// calculated values for units in: kerning, word-spacing, and, letter-spacing.
|
||||
kerning = hasKerning ? toAbsolute(font, KERNING, scale, fontSize, 0) : parent.kerning;
|
||||
wordSpacing =
|
||||
font.hasKey(WORD_SPACING)
|
||||
? toAbsolute(font, WORD_SPACING, scale, fontSize, 0)
|
||||
: parent.wordSpacing;
|
||||
letterSpacing =
|
||||
font.hasKey(LETTER_SPACING)
|
||||
? toAbsolute(font, LETTER_SPACING, scale, fontSize, 0)
|
||||
: parent.letterSpacing;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
@@ -15,9 +14,7 @@ import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
@@ -25,136 +22,136 @@ import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
@SuppressLint("ViewConstructor")
|
||||
class ForeignObjectView extends GroupView {
|
||||
|
||||
SVGLength mX;
|
||||
SVGLength mY;
|
||||
SVGLength mW;
|
||||
SVGLength mH;
|
||||
SVGLength mX;
|
||||
SVGLength mY;
|
||||
SVGLength mW;
|
||||
SVGLength mH;
|
||||
|
||||
public ForeignObjectView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
public ForeignObjectView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
void draw(Canvas canvas, Paint paint, float opacity) {
|
||||
float x = (float)relativeOnWidth(mX);
|
||||
float y = (float)relativeOnHeight(mY);
|
||||
float w = (float)relativeOnWidth(mW);
|
||||
float h = (float)relativeOnHeight(mH);
|
||||
canvas.translate(x, y);
|
||||
canvas.clipRect(0, 0, w, h);
|
||||
super.draw(canvas, paint, opacity);
|
||||
}
|
||||
@Override
|
||||
void draw(Canvas canvas, Paint paint, float opacity) {
|
||||
float x = (float) relativeOnWidth(mX);
|
||||
float y = (float) relativeOnHeight(mY);
|
||||
float w = (float) relativeOnWidth(mW);
|
||||
float h = (float) relativeOnHeight(mH);
|
||||
canvas.translate(x, y);
|
||||
canvas.clipRect(0, 0, w, h);
|
||||
super.draw(canvas, paint, opacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDescendantInvalidated(@NonNull View child, @NonNull View target) {
|
||||
super.onDescendantInvalidated(child, target);
|
||||
invalidate();
|
||||
}
|
||||
@Override
|
||||
public void onDescendantInvalidated(@NonNull View child, @NonNull View target) {
|
||||
super.onDescendantInvalidated(child, target);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "x")
|
||||
public void setX(Dynamic x) {
|
||||
mX = SVGLength.from(x);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "x")
|
||||
public void setX(Dynamic x) {
|
||||
mX = SVGLength.from(x);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setX(String x) {
|
||||
mX = SVGLength.from(x);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "y")
|
||||
public void setY(Dynamic y) {
|
||||
mY = SVGLength.from(y);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "y")
|
||||
public void setY(Dynamic y) {
|
||||
mY = SVGLength.from(y);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setY(String y) {
|
||||
mY = SVGLength.from(y);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "width")
|
||||
public void setWidth(Dynamic width) {
|
||||
mW = SVGLength.from(width);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "width")
|
||||
public void setWidth(Dynamic width) {
|
||||
mW = SVGLength.from(width);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setWidth(String width) {
|
||||
mW = SVGLength.from(width);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "height")
|
||||
public void setHeight(Dynamic height) {
|
||||
mH = SVGLength.from(height);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "height")
|
||||
public void setHeight(Dynamic height) {
|
||||
mH = SVGLength.from(height);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setHeight(String height) {
|
||||
mH = SVGLength.from(height);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void drawGroup(final Canvas canvas, final Paint paint, final float opacity) {
|
||||
pushGlyphContext();
|
||||
final SvgView svg = getSvgView();
|
||||
final GroupView self = this;
|
||||
final RectF groupRect = new RectF();
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View child = getChildAt(i);
|
||||
if (child instanceof MaskView) {
|
||||
continue;
|
||||
}
|
||||
if (child instanceof VirtualView) {
|
||||
VirtualView node = ((VirtualView)child);
|
||||
if ("none".equals(node.mDisplay)) {
|
||||
continue;
|
||||
}
|
||||
if (node instanceof RenderableView) {
|
||||
((RenderableView)node).mergeProperties(self);
|
||||
}
|
||||
|
||||
int count = node.saveAndSetupCanvas(canvas, mCTM);
|
||||
node.render(canvas, paint, opacity * mOpacity);
|
||||
RectF r = node.getClientRect();
|
||||
if (r != null) {
|
||||
groupRect.union(r);
|
||||
}
|
||||
|
||||
node.restoreCanvas(canvas, count);
|
||||
|
||||
if (node instanceof RenderableView) {
|
||||
((RenderableView)node).resetProperties();
|
||||
}
|
||||
|
||||
if (node.isResponsible()) {
|
||||
svg.enableTouchEvents();
|
||||
}
|
||||
} else if (child instanceof SvgView) {
|
||||
SvgView svgView = (SvgView)child;
|
||||
svgView.drawChildren(canvas);
|
||||
if (svgView.isResponsible()) {
|
||||
svg.enableTouchEvents();
|
||||
}
|
||||
} else {
|
||||
// Enable rendering other native ancestor views in e.g. masks
|
||||
child.draw(canvas);
|
||||
}
|
||||
void drawGroup(final Canvas canvas, final Paint paint, final float opacity) {
|
||||
pushGlyphContext();
|
||||
final SvgView svg = getSvgView();
|
||||
final GroupView self = this;
|
||||
final RectF groupRect = new RectF();
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View child = getChildAt(i);
|
||||
if (child instanceof MaskView) {
|
||||
continue;
|
||||
}
|
||||
if (child instanceof VirtualView) {
|
||||
VirtualView node = ((VirtualView) child);
|
||||
if ("none".equals(node.mDisplay)) {
|
||||
continue;
|
||||
}
|
||||
if (node instanceof RenderableView) {
|
||||
((RenderableView) node).mergeProperties(self);
|
||||
}
|
||||
this.setClientRect(groupRect);
|
||||
popGlyphContext();
|
||||
}
|
||||
|
||||
// Enable rendering other native ancestor views in e.g. masks, but don't render them another time
|
||||
Bitmap fakeBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
|
||||
Canvas fake = new Canvas(fakeBitmap);
|
||||
int count = node.saveAndSetupCanvas(canvas, mCTM);
|
||||
node.render(canvas, paint, opacity * mOpacity);
|
||||
RectF r = node.getClientRect();
|
||||
if (r != null) {
|
||||
groupRect.union(r);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
super.dispatchDraw(fake);
|
||||
}
|
||||
node.restoreCanvas(canvas, count);
|
||||
|
||||
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
||||
return super.drawChild(fake, child, drawingTime);
|
||||
if (node instanceof RenderableView) {
|
||||
((RenderableView) node).resetProperties();
|
||||
}
|
||||
|
||||
if (node.isResponsible()) {
|
||||
svg.enableTouchEvents();
|
||||
}
|
||||
} else if (child instanceof SvgView) {
|
||||
SvgView svgView = (SvgView) child;
|
||||
svgView.drawChildren(canvas);
|
||||
if (svgView.isResponsible()) {
|
||||
svg.enableTouchEvents();
|
||||
}
|
||||
} else {
|
||||
// Enable rendering other native ancestor views in e.g. masks
|
||||
child.draw(canvas);
|
||||
}
|
||||
}
|
||||
this.setClientRect(groupRect);
|
||||
popGlyphContext();
|
||||
}
|
||||
|
||||
// Enable rendering other native ancestor views in e.g. masks, but don't render them another time
|
||||
Bitmap fakeBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
|
||||
Canvas fake = new Canvas(fakeBitmap);
|
||||
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
super.dispatchDraw(fake);
|
||||
}
|
||||
|
||||
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
||||
return super.drawChild(fake, child, drawingTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,423 +6,410 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
// https://www.w3.org/TR/SVG/text.html#TSpanElement
|
||||
class GlyphContext {
|
||||
|
||||
// Current stack (one per node push/pop)
|
||||
final ArrayList<FontData> mFontContext = new ArrayList<>();
|
||||
// Current stack (one per node push/pop)
|
||||
final ArrayList<FontData> mFontContext = new ArrayList<>();
|
||||
|
||||
// Unique input attribute lists (only added if node sets a value)
|
||||
private final ArrayList<SVGLength[]> mXsContext = new ArrayList<>();
|
||||
private final ArrayList<SVGLength[]> mYsContext = new ArrayList<>();
|
||||
private final ArrayList<SVGLength[]> mDXsContext = new ArrayList<>();
|
||||
private final ArrayList<SVGLength[]> mDYsContext = new ArrayList<>();
|
||||
private final ArrayList<double[]> mRsContext = new ArrayList<>();
|
||||
// Unique input attribute lists (only added if node sets a value)
|
||||
private final ArrayList<SVGLength[]> mXsContext = new ArrayList<>();
|
||||
private final ArrayList<SVGLength[]> mYsContext = new ArrayList<>();
|
||||
private final ArrayList<SVGLength[]> mDXsContext = new ArrayList<>();
|
||||
private final ArrayList<SVGLength[]> mDYsContext = new ArrayList<>();
|
||||
private final ArrayList<double[]> mRsContext = new ArrayList<>();
|
||||
|
||||
// Unique index into attribute list (one per unique list)
|
||||
private final ArrayList<Integer> mXIndices = new ArrayList<>();
|
||||
private final ArrayList<Integer> mYIndices = new ArrayList<>();
|
||||
private final ArrayList<Integer> mDXIndices = new ArrayList<>();
|
||||
private final ArrayList<Integer> mDYIndices = new ArrayList<>();
|
||||
private final ArrayList<Integer> mRIndices = new ArrayList<>();
|
||||
// Unique index into attribute list (one per unique list)
|
||||
private final ArrayList<Integer> mXIndices = new ArrayList<>();
|
||||
private final ArrayList<Integer> mYIndices = new ArrayList<>();
|
||||
private final ArrayList<Integer> mDXIndices = new ArrayList<>();
|
||||
private final ArrayList<Integer> mDYIndices = new ArrayList<>();
|
||||
private final ArrayList<Integer> mRIndices = new ArrayList<>();
|
||||
|
||||
// Index of unique context used (one per node push/pop)
|
||||
private final ArrayList<Integer> mXsIndices = new ArrayList<>();
|
||||
private final ArrayList<Integer> mYsIndices = new ArrayList<>();
|
||||
private final ArrayList<Integer> mDXsIndices = new ArrayList<>();
|
||||
private final ArrayList<Integer> mDYsIndices = new ArrayList<>();
|
||||
private final ArrayList<Integer> mRsIndices = new ArrayList<>();
|
||||
// Index of unique context used (one per node push/pop)
|
||||
private final ArrayList<Integer> mXsIndices = new ArrayList<>();
|
||||
private final ArrayList<Integer> mYsIndices = new ArrayList<>();
|
||||
private final ArrayList<Integer> mDXsIndices = new ArrayList<>();
|
||||
private final ArrayList<Integer> mDYsIndices = new ArrayList<>();
|
||||
private final ArrayList<Integer> mRsIndices = new ArrayList<>();
|
||||
|
||||
// Calculated on push context, percentage and em length depends on parent font size
|
||||
private double mFontSize = FontData.DEFAULT_FONT_SIZE;
|
||||
private FontData topFont = FontData.Defaults;
|
||||
// Calculated on push context, percentage and em length depends on parent font size
|
||||
private double mFontSize = FontData.DEFAULT_FONT_SIZE;
|
||||
private FontData topFont = FontData.Defaults;
|
||||
|
||||
// Current accumulated values
|
||||
// https://www.w3.org/TR/SVG/types.html#DataTypeCoordinate
|
||||
// <coordinate> syntax is the same as that for <length>
|
||||
private double mX;
|
||||
private double mY;
|
||||
// Current accumulated values
|
||||
// https://www.w3.org/TR/SVG/types.html#DataTypeCoordinate
|
||||
// <coordinate> syntax is the same as that for <length>
|
||||
private double mX;
|
||||
private double mY;
|
||||
|
||||
// https://www.w3.org/TR/SVG/types.html#Length
|
||||
private double mDX;
|
||||
private double mDY;
|
||||
// https://www.w3.org/TR/SVG/types.html#Length
|
||||
private double mDX;
|
||||
private double mDY;
|
||||
|
||||
// Current <list-of-coordinates> SVGLengthList
|
||||
// https://www.w3.org/TR/SVG/types.html#InterfaceSVGLengthList
|
||||
// https://www.w3.org/TR/SVG/types.html#DataTypeCoordinates
|
||||
// Current <list-of-coordinates> SVGLengthList
|
||||
// https://www.w3.org/TR/SVG/types.html#InterfaceSVGLengthList
|
||||
// https://www.w3.org/TR/SVG/types.html#DataTypeCoordinates
|
||||
|
||||
// https://www.w3.org/TR/SVG/text.html#TSpanElementXAttribute
|
||||
private SVGLength[] mXs = new SVGLength[]{};
|
||||
// https://www.w3.org/TR/SVG/text.html#TSpanElementXAttribute
|
||||
private SVGLength[] mXs = new SVGLength[] {};
|
||||
|
||||
// https://www.w3.org/TR/SVG/text.html#TSpanElementYAttribute
|
||||
private SVGLength[] mYs = new SVGLength[]{};
|
||||
// https://www.w3.org/TR/SVG/text.html#TSpanElementYAttribute
|
||||
private SVGLength[] mYs = new SVGLength[] {};
|
||||
|
||||
// Current <list-of-lengths> SVGLengthList
|
||||
// https://www.w3.org/TR/SVG/types.html#DataTypeLengths
|
||||
// Current <list-of-lengths> SVGLengthList
|
||||
// https://www.w3.org/TR/SVG/types.html#DataTypeLengths
|
||||
|
||||
// https://www.w3.org/TR/SVG/text.html#TSpanElementDXAttribute
|
||||
private SVGLength[] mDXs = new SVGLength[]{};
|
||||
// https://www.w3.org/TR/SVG/text.html#TSpanElementDXAttribute
|
||||
private SVGLength[] mDXs = new SVGLength[] {};
|
||||
|
||||
// https://www.w3.org/TR/SVG/text.html#TSpanElementDYAttribute
|
||||
private SVGLength[] mDYs = new SVGLength[]{};
|
||||
// https://www.w3.org/TR/SVG/text.html#TSpanElementDYAttribute
|
||||
private SVGLength[] mDYs = new SVGLength[] {};
|
||||
|
||||
// Current <list-of-numbers> SVGLengthList
|
||||
// https://www.w3.org/TR/SVG/types.html#DataTypeNumbers
|
||||
// Current <list-of-numbers> SVGLengthList
|
||||
// https://www.w3.org/TR/SVG/types.html#DataTypeNumbers
|
||||
|
||||
// https://www.w3.org/TR/SVG/text.html#TSpanElementRotateAttribute
|
||||
private double[] mRs = new double[]{0};
|
||||
// https://www.w3.org/TR/SVG/text.html#TSpanElementRotateAttribute
|
||||
private double[] mRs = new double[] {0};
|
||||
|
||||
// Current attribute list index
|
||||
private int mXsIndex;
|
||||
private int mYsIndex;
|
||||
private int mDXsIndex;
|
||||
private int mDYsIndex;
|
||||
private int mRsIndex;
|
||||
// Current attribute list index
|
||||
private int mXsIndex;
|
||||
private int mYsIndex;
|
||||
private int mDXsIndex;
|
||||
private int mDYsIndex;
|
||||
private int mRsIndex;
|
||||
|
||||
// Current value index in current attribute list
|
||||
private int mXIndex = -1;
|
||||
private int mYIndex = -1;
|
||||
private int mDXIndex = -1;
|
||||
private int mDYIndex = -1;
|
||||
private int mRIndex = -1;
|
||||
// Current value index in current attribute list
|
||||
private int mXIndex = -1;
|
||||
private int mYIndex = -1;
|
||||
private int mDXIndex = -1;
|
||||
private int mDYIndex = -1;
|
||||
private int mRIndex = -1;
|
||||
|
||||
// Top index of stack
|
||||
private int mTop;
|
||||
// Top index of stack
|
||||
private int mTop;
|
||||
|
||||
// Constructor parameters
|
||||
private final float mScale;
|
||||
private final float mWidth;
|
||||
private final float mHeight;
|
||||
// Constructor parameters
|
||||
private final float mScale;
|
||||
private final float mWidth;
|
||||
private final float mHeight;
|
||||
|
||||
private void pushIndices() {
|
||||
mXsIndices.add(mXsIndex);
|
||||
mYsIndices.add(mYsIndex);
|
||||
mDXsIndices.add(mDXsIndex);
|
||||
mDYsIndices.add(mDYsIndex);
|
||||
mRsIndices.add(mRsIndex);
|
||||
private void pushIndices() {
|
||||
mXsIndices.add(mXsIndex);
|
||||
mYsIndices.add(mYsIndex);
|
||||
mDXsIndices.add(mDXsIndex);
|
||||
mDYsIndices.add(mDYsIndex);
|
||||
mRsIndices.add(mRsIndex);
|
||||
}
|
||||
|
||||
GlyphContext(float scale, float width, float height) {
|
||||
mScale = scale;
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
|
||||
mXsContext.add(mXs);
|
||||
mYsContext.add(mYs);
|
||||
mDXsContext.add(mDXs);
|
||||
mDYsContext.add(mDYs);
|
||||
mRsContext.add(mRs);
|
||||
|
||||
mXIndices.add(mXIndex);
|
||||
mYIndices.add(mYIndex);
|
||||
mDXIndices.add(mDXIndex);
|
||||
mDYIndices.add(mDYIndex);
|
||||
mRIndices.add(mRIndex);
|
||||
|
||||
mFontContext.add(topFont);
|
||||
|
||||
pushIndices();
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
mXsIndex = mYsIndex = mDXsIndex = mDYsIndex = mRsIndex = 0;
|
||||
mXIndex = mYIndex = mDXIndex = mDYIndex = mRIndex = -1;
|
||||
mX = mY = mDX = mDY = 0;
|
||||
}
|
||||
|
||||
FontData getFont() {
|
||||
return topFont;
|
||||
}
|
||||
|
||||
private FontData getTopOrParentFont(GroupView child) {
|
||||
if (mTop > 0) {
|
||||
return topFont;
|
||||
} else {
|
||||
GroupView parentRoot = child.getParentTextRoot();
|
||||
|
||||
while (parentRoot != null) {
|
||||
FontData map = parentRoot.getGlyphContext().getFont();
|
||||
if (map != FontData.Defaults) {
|
||||
return map;
|
||||
}
|
||||
parentRoot = parentRoot.getParentTextRoot();
|
||||
}
|
||||
|
||||
return FontData.Defaults;
|
||||
}
|
||||
}
|
||||
|
||||
private void pushNodeAndFont(GroupView node, @Nullable ReadableMap font) {
|
||||
FontData parent = getTopOrParentFont(node);
|
||||
mTop++;
|
||||
|
||||
if (font == null) {
|
||||
mFontContext.add(parent);
|
||||
return;
|
||||
}
|
||||
|
||||
GlyphContext(float scale, float width, float height) {
|
||||
mScale = scale;
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
FontData data = new FontData(font, parent, mScale);
|
||||
mFontSize = data.fontSize;
|
||||
mFontContext.add(data);
|
||||
topFont = data;
|
||||
}
|
||||
|
||||
mXsContext.add(mXs);
|
||||
mYsContext.add(mYs);
|
||||
mDXsContext.add(mDXs);
|
||||
mDYsContext.add(mDYs);
|
||||
mRsContext.add(mRs);
|
||||
void pushContext(GroupView node, @Nullable ReadableMap font) {
|
||||
pushNodeAndFont(node, font);
|
||||
pushIndices();
|
||||
}
|
||||
|
||||
mXIndices.add(mXIndex);
|
||||
mYIndices.add(mYIndex);
|
||||
mDXIndices.add(mDXIndex);
|
||||
mDYIndices.add(mDYIndex);
|
||||
mRIndices.add(mRIndex);
|
||||
private SVGLength[] getStringArrayFromReadableArray(ArrayList<SVGLength> readableArray) {
|
||||
int size = readableArray.size();
|
||||
SVGLength[] strings = new SVGLength[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
strings[i] = readableArray.get(i);
|
||||
}
|
||||
return strings;
|
||||
}
|
||||
|
||||
mFontContext.add(topFont);
|
||||
private double[] getDoubleArrayFromReadableArray(ArrayList<SVGLength> readableArray) {
|
||||
int size = readableArray.size();
|
||||
double[] doubles = new double[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
SVGLength length = readableArray.get(i);
|
||||
doubles[i] = length.value;
|
||||
}
|
||||
return doubles;
|
||||
}
|
||||
|
||||
pushIndices();
|
||||
void pushContext(
|
||||
boolean reset,
|
||||
TextView node,
|
||||
@Nullable ReadableMap font,
|
||||
@Nullable ArrayList<SVGLength> x,
|
||||
@Nullable ArrayList<SVGLength> y,
|
||||
@Nullable ArrayList<SVGLength> deltaX,
|
||||
@Nullable ArrayList<SVGLength> deltaY,
|
||||
@Nullable ArrayList<SVGLength> rotate) {
|
||||
if (reset) {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
mXsIndex = mYsIndex = mDXsIndex = mDYsIndex = mRsIndex = 0;
|
||||
mXIndex = mYIndex = mDXIndex = mDYIndex = mRIndex = -1;
|
||||
mX = mY = mDX = mDY = 0;
|
||||
pushNodeAndFont(node, font);
|
||||
|
||||
if (x != null && x.size() != 0) {
|
||||
mXsIndex++;
|
||||
mXIndex = -1;
|
||||
mXIndices.add(mXIndex);
|
||||
mXs = getStringArrayFromReadableArray(x);
|
||||
mXsContext.add(mXs);
|
||||
}
|
||||
|
||||
FontData getFont() {
|
||||
return topFont;
|
||||
if (y != null && y.size() != 0) {
|
||||
mYsIndex++;
|
||||
mYIndex = -1;
|
||||
mYIndices.add(mYIndex);
|
||||
mYs = getStringArrayFromReadableArray(y);
|
||||
mYsContext.add(mYs);
|
||||
}
|
||||
|
||||
private FontData getTopOrParentFont(GroupView child) {
|
||||
if (mTop > 0) {
|
||||
return topFont;
|
||||
} else {
|
||||
GroupView parentRoot = child.getParentTextRoot();
|
||||
|
||||
while (parentRoot != null) {
|
||||
FontData map = parentRoot.getGlyphContext().getFont();
|
||||
if (map != FontData.Defaults) {
|
||||
return map;
|
||||
}
|
||||
parentRoot = parentRoot.getParentTextRoot();
|
||||
}
|
||||
|
||||
return FontData.Defaults;
|
||||
}
|
||||
if (deltaX != null && deltaX.size() != 0) {
|
||||
mDXsIndex++;
|
||||
mDXIndex = -1;
|
||||
mDXIndices.add(mDXIndex);
|
||||
mDXs = getStringArrayFromReadableArray(deltaX);
|
||||
mDXsContext.add(mDXs);
|
||||
}
|
||||
|
||||
private void pushNodeAndFont(GroupView node, @Nullable ReadableMap font) {
|
||||
FontData parent = getTopOrParentFont(node);
|
||||
mTop++;
|
||||
|
||||
if (font == null) {
|
||||
mFontContext.add(parent);
|
||||
return;
|
||||
}
|
||||
|
||||
FontData data = new FontData(font, parent, mScale);
|
||||
mFontSize = data.fontSize;
|
||||
mFontContext.add(data);
|
||||
topFont = data;
|
||||
|
||||
if (deltaY != null && deltaY.size() != 0) {
|
||||
mDYsIndex++;
|
||||
mDYIndex = -1;
|
||||
mDYIndices.add(mDYIndex);
|
||||
mDYs = getStringArrayFromReadableArray(deltaY);
|
||||
mDYsContext.add(mDYs);
|
||||
}
|
||||
|
||||
void pushContext(GroupView node, @Nullable ReadableMap font) {
|
||||
pushNodeAndFont(node, font);
|
||||
pushIndices();
|
||||
if (rotate != null && rotate.size() != 0) {
|
||||
mRsIndex++;
|
||||
mRIndex = -1;
|
||||
mRIndices.add(mRIndex);
|
||||
mRs = getDoubleArrayFromReadableArray(rotate);
|
||||
mRsContext.add(mRs);
|
||||
}
|
||||
|
||||
private SVGLength[] getStringArrayFromReadableArray(ArrayList<SVGLength> readableArray) {
|
||||
int size = readableArray.size();
|
||||
SVGLength[] strings = new SVGLength[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
strings[i] = readableArray.get(i);
|
||||
}
|
||||
return strings;
|
||||
pushIndices();
|
||||
}
|
||||
|
||||
void popContext() {
|
||||
mFontContext.remove(mTop);
|
||||
mXsIndices.remove(mTop);
|
||||
mYsIndices.remove(mTop);
|
||||
mDXsIndices.remove(mTop);
|
||||
mDYsIndices.remove(mTop);
|
||||
mRsIndices.remove(mTop);
|
||||
|
||||
mTop--;
|
||||
|
||||
int x = mXsIndex;
|
||||
int y = mYsIndex;
|
||||
int dx = mDXsIndex;
|
||||
int dy = mDYsIndex;
|
||||
int r = mRsIndex;
|
||||
|
||||
topFont = mFontContext.get(mTop);
|
||||
mXsIndex = mXsIndices.get(mTop);
|
||||
mYsIndex = mYsIndices.get(mTop);
|
||||
mDXsIndex = mDXsIndices.get(mTop);
|
||||
mDYsIndex = mDYsIndices.get(mTop);
|
||||
mRsIndex = mRsIndices.get(mTop);
|
||||
|
||||
if (x != mXsIndex) {
|
||||
mXsContext.remove(x);
|
||||
mXs = mXsContext.get(mXsIndex);
|
||||
mXIndex = mXIndices.get(mXsIndex);
|
||||
}
|
||||
if (y != mYsIndex) {
|
||||
mYsContext.remove(y);
|
||||
mYs = mYsContext.get(mYsIndex);
|
||||
mYIndex = mYIndices.get(mYsIndex);
|
||||
}
|
||||
if (dx != mDXsIndex) {
|
||||
mDXsContext.remove(dx);
|
||||
mDXs = mDXsContext.get(mDXsIndex);
|
||||
mDXIndex = mDXIndices.get(mDXsIndex);
|
||||
}
|
||||
if (dy != mDYsIndex) {
|
||||
mDYsContext.remove(dy);
|
||||
mDYs = mDYsContext.get(mDYsIndex);
|
||||
mDYIndex = mDYIndices.get(mDYsIndex);
|
||||
}
|
||||
if (r != mRsIndex) {
|
||||
mRsContext.remove(r);
|
||||
mRs = mRsContext.get(mRsIndex);
|
||||
mRIndex = mRIndices.get(mRsIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void incrementIndices(ArrayList<Integer> indices, int topIndex) {
|
||||
for (int index = topIndex; index >= 0; index--) {
|
||||
int xIndex = indices.get(index);
|
||||
indices.set(index, xIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/SVG11/text.html#FontSizeProperty
|
||||
|
||||
/**
|
||||
* Get font size from context.
|
||||
*
|
||||
* <p>‘font-size’ Value: < absolute-size > | < relative-size > | < length > | < percentage > |
|
||||
* inherit Initial: medium Applies to: text content elements Inherited: yes, the computed value is
|
||||
* inherited Percentages: refer to parent element's font size Media: visual Animatable: yes
|
||||
*
|
||||
* <p>This property refers to the size of the font from baseline to baseline when multiple lines
|
||||
* of text are set solid in a multiline layout environment.
|
||||
*
|
||||
* <p>For SVG, if a < length > is provided without a unit identifier (e.g., an unqualified number
|
||||
* such as 128), the SVG user agent processes the < length > as a height value in the current user
|
||||
* coordinate system.
|
||||
*
|
||||
* <p>If a < length > is provided with one of the unit identifiers (e.g., 12pt or 10%), then the
|
||||
* SVG user agent converts the < length > into a corresponding value in the current user
|
||||
* coordinate system by applying the rules described in Units.
|
||||
*
|
||||
* <p>Except for any additional information provided in this specification, the normative
|
||||
* definition of the property is in CSS2 ([CSS2], section 15.2.4).
|
||||
*/
|
||||
double getFontSize() {
|
||||
return mFontSize;
|
||||
}
|
||||
|
||||
double nextX(double advance) {
|
||||
incrementIndices(mXIndices, mXsIndex);
|
||||
|
||||
int nextIndex = mXIndex + 1;
|
||||
if (nextIndex < mXs.length) {
|
||||
mDX = 0;
|
||||
mXIndex = nextIndex;
|
||||
SVGLength string = mXs[nextIndex];
|
||||
mX = PropHelper.fromRelative(string, mWidth, 0, mScale, mFontSize);
|
||||
}
|
||||
|
||||
private double[] getDoubleArrayFromReadableArray(ArrayList<SVGLength> readableArray) {
|
||||
int size = readableArray.size();
|
||||
double[] doubles = new double[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
SVGLength length = readableArray.get(i);
|
||||
doubles[i] = length.value;
|
||||
}
|
||||
return doubles;
|
||||
mX += advance;
|
||||
|
||||
return mX;
|
||||
}
|
||||
|
||||
double nextY() {
|
||||
incrementIndices(mYIndices, mYsIndex);
|
||||
|
||||
int nextIndex = mYIndex + 1;
|
||||
if (nextIndex < mYs.length) {
|
||||
mDY = 0;
|
||||
mYIndex = nextIndex;
|
||||
SVGLength string = mYs[nextIndex];
|
||||
mY = PropHelper.fromRelative(string, mHeight, 0, mScale, mFontSize);
|
||||
}
|
||||
|
||||
void pushContext(
|
||||
boolean reset,
|
||||
TextView node,
|
||||
@Nullable ReadableMap font,
|
||||
@Nullable ArrayList<SVGLength> x,
|
||||
@Nullable ArrayList<SVGLength> y,
|
||||
@Nullable ArrayList<SVGLength> deltaX,
|
||||
@Nullable ArrayList<SVGLength> deltaY,
|
||||
@Nullable ArrayList<SVGLength> rotate
|
||||
) {
|
||||
if (reset) {
|
||||
this.reset();
|
||||
}
|
||||
return mY;
|
||||
}
|
||||
|
||||
pushNodeAndFont(node, font);
|
||||
double nextDeltaX() {
|
||||
incrementIndices(mDXIndices, mDXsIndex);
|
||||
|
||||
if (x != null && x.size() != 0) {
|
||||
mXsIndex++;
|
||||
mXIndex = -1;
|
||||
mXIndices.add(mXIndex);
|
||||
mXs = getStringArrayFromReadableArray(x);
|
||||
mXsContext.add(mXs);
|
||||
}
|
||||
|
||||
if (y != null && y.size() != 0) {
|
||||
mYsIndex++;
|
||||
mYIndex = -1;
|
||||
mYIndices.add(mYIndex);
|
||||
mYs = getStringArrayFromReadableArray(y);
|
||||
mYsContext.add(mYs);
|
||||
}
|
||||
|
||||
if (deltaX != null && deltaX.size() != 0) {
|
||||
mDXsIndex++;
|
||||
mDXIndex = -1;
|
||||
mDXIndices.add(mDXIndex);
|
||||
mDXs = getStringArrayFromReadableArray(deltaX);
|
||||
mDXsContext.add(mDXs);
|
||||
}
|
||||
|
||||
if (deltaY != null && deltaY.size() != 0) {
|
||||
mDYsIndex++;
|
||||
mDYIndex = -1;
|
||||
mDYIndices.add(mDYIndex);
|
||||
mDYs = getStringArrayFromReadableArray(deltaY);
|
||||
mDYsContext.add(mDYs);
|
||||
}
|
||||
|
||||
if (rotate != null && rotate.size() != 0) {
|
||||
mRsIndex++;
|
||||
mRIndex = -1;
|
||||
mRIndices.add(mRIndex);
|
||||
mRs = getDoubleArrayFromReadableArray(rotate);
|
||||
mRsContext.add(mRs);
|
||||
}
|
||||
|
||||
pushIndices();
|
||||
int nextIndex = mDXIndex + 1;
|
||||
if (nextIndex < mDXs.length) {
|
||||
mDXIndex = nextIndex;
|
||||
SVGLength string = mDXs[nextIndex];
|
||||
double val = PropHelper.fromRelative(string, mWidth, 0, mScale, mFontSize);
|
||||
mDX += val;
|
||||
}
|
||||
|
||||
void popContext() {
|
||||
mFontContext.remove(mTop);
|
||||
mXsIndices.remove(mTop);
|
||||
mYsIndices.remove(mTop);
|
||||
mDXsIndices.remove(mTop);
|
||||
mDYsIndices.remove(mTop);
|
||||
mRsIndices.remove(mTop);
|
||||
return mDX;
|
||||
}
|
||||
|
||||
mTop--;
|
||||
double nextDeltaY() {
|
||||
incrementIndices(mDYIndices, mDYsIndex);
|
||||
|
||||
int x = mXsIndex;
|
||||
int y = mYsIndex;
|
||||
int dx = mDXsIndex;
|
||||
int dy = mDYsIndex;
|
||||
int r = mRsIndex;
|
||||
|
||||
topFont = mFontContext.get(mTop);
|
||||
mXsIndex = mXsIndices.get(mTop);
|
||||
mYsIndex = mYsIndices.get(mTop);
|
||||
mDXsIndex = mDXsIndices.get(mTop);
|
||||
mDYsIndex = mDYsIndices.get(mTop);
|
||||
mRsIndex = mRsIndices.get(mTop);
|
||||
|
||||
if (x != mXsIndex) {
|
||||
mXsContext.remove(x);
|
||||
mXs = mXsContext.get(mXsIndex);
|
||||
mXIndex = mXIndices.get(mXsIndex);
|
||||
}
|
||||
if (y != mYsIndex) {
|
||||
mYsContext.remove(y);
|
||||
mYs = mYsContext.get(mYsIndex);
|
||||
mYIndex = mYIndices.get(mYsIndex);
|
||||
}
|
||||
if (dx != mDXsIndex) {
|
||||
mDXsContext.remove(dx);
|
||||
mDXs = mDXsContext.get(mDXsIndex);
|
||||
mDXIndex = mDXIndices.get(mDXsIndex);
|
||||
}
|
||||
if (dy != mDYsIndex) {
|
||||
mDYsContext.remove(dy);
|
||||
mDYs = mDYsContext.get(mDYsIndex);
|
||||
mDYIndex = mDYIndices.get(mDYsIndex);
|
||||
}
|
||||
if (r != mRsIndex) {
|
||||
mRsContext.remove(r);
|
||||
mRs = mRsContext.get(mRsIndex);
|
||||
mRIndex = mRIndices.get(mRsIndex);
|
||||
}
|
||||
int nextIndex = mDYIndex + 1;
|
||||
if (nextIndex < mDYs.length) {
|
||||
mDYIndex = nextIndex;
|
||||
SVGLength string = mDYs[nextIndex];
|
||||
double val = PropHelper.fromRelative(string, mHeight, 0, mScale, mFontSize);
|
||||
mDY += val;
|
||||
}
|
||||
|
||||
private static void incrementIndices(ArrayList<Integer> indices, int topIndex) {
|
||||
for (int index = topIndex; index >= 0; index--) {
|
||||
int xIndex = indices.get(index);
|
||||
indices.set(index, xIndex + 1);
|
||||
}
|
||||
}
|
||||
return mDY;
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/SVG11/text.html#FontSizeProperty
|
||||
double nextRotation() {
|
||||
incrementIndices(mRIndices, mRsIndex);
|
||||
|
||||
/**
|
||||
* Get font size from context.
|
||||
* <p>
|
||||
* ‘font-size’
|
||||
* Value: < absolute-size > | < relative-size > | < length > | < percentage > | inherit
|
||||
* Initial: medium
|
||||
* Applies to: text content elements
|
||||
* Inherited: yes, the computed value is inherited
|
||||
* Percentages: refer to parent element's font size
|
||||
* Media: visual
|
||||
* Animatable: yes
|
||||
* <p>
|
||||
* This property refers to the size of the font from baseline to
|
||||
* baseline when multiple lines of text are set solid in a multiline
|
||||
* layout environment.
|
||||
* <p>
|
||||
* For SVG, if a < length > is provided without a unit identifier
|
||||
* (e.g., an unqualified number such as 128), the SVG user agent
|
||||
* processes the < length > as a height value in the current user
|
||||
* coordinate system.
|
||||
* <p>
|
||||
* If a < length > is provided with one of the unit identifiers
|
||||
* (e.g., 12pt or 10%), then the SVG user agent converts the
|
||||
* < length > into a corresponding value in the current user
|
||||
* coordinate system by applying the rules described in Units.
|
||||
* <p>
|
||||
* Except for any additional information provided in this specification,
|
||||
* the normative definition of the property is in CSS2 ([CSS2], section 15.2.4).
|
||||
*/
|
||||
double getFontSize() {
|
||||
return mFontSize;
|
||||
}
|
||||
mRIndex = Math.min(mRIndex + 1, mRs.length - 1);
|
||||
|
||||
double nextX(double advance) {
|
||||
incrementIndices(mXIndices, mXsIndex);
|
||||
return mRs[mRIndex];
|
||||
}
|
||||
|
||||
int nextIndex = mXIndex + 1;
|
||||
if (nextIndex < mXs.length) {
|
||||
mDX = 0;
|
||||
mXIndex = nextIndex;
|
||||
SVGLength string = mXs[nextIndex];
|
||||
mX = PropHelper.fromRelative(string, mWidth, 0, mScale, mFontSize);
|
||||
}
|
||||
float getWidth() {
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
mX += advance;
|
||||
|
||||
return mX;
|
||||
}
|
||||
|
||||
double nextY() {
|
||||
incrementIndices(mYIndices, mYsIndex);
|
||||
|
||||
int nextIndex = mYIndex + 1;
|
||||
if (nextIndex < mYs.length) {
|
||||
mDY = 0;
|
||||
mYIndex = nextIndex;
|
||||
SVGLength string = mYs[nextIndex];
|
||||
mY = PropHelper.fromRelative(string, mHeight, 0, mScale, mFontSize);
|
||||
}
|
||||
|
||||
return mY;
|
||||
}
|
||||
|
||||
double nextDeltaX() {
|
||||
incrementIndices(mDXIndices, mDXsIndex);
|
||||
|
||||
int nextIndex = mDXIndex + 1;
|
||||
if (nextIndex < mDXs.length) {
|
||||
mDXIndex = nextIndex;
|
||||
SVGLength string = mDXs[nextIndex];
|
||||
double val = PropHelper.fromRelative(string, mWidth, 0, mScale, mFontSize);
|
||||
mDX += val;
|
||||
}
|
||||
|
||||
return mDX;
|
||||
}
|
||||
|
||||
double nextDeltaY() {
|
||||
incrementIndices(mDYIndices, mDYsIndex);
|
||||
|
||||
int nextIndex = mDYIndex + 1;
|
||||
if (nextIndex < mDYs.length) {
|
||||
mDYIndex = nextIndex;
|
||||
SVGLength string = mDYs[nextIndex];
|
||||
double val = PropHelper.fromRelative(string, mHeight, 0, mScale, mFontSize);
|
||||
mDY += val;
|
||||
}
|
||||
|
||||
return mDY;
|
||||
}
|
||||
|
||||
double nextRotation() {
|
||||
incrementIndices(mRIndices, mRsIndex);
|
||||
|
||||
mRIndex = Math.min(mRIndex + 1, mRs.length - 1);
|
||||
|
||||
return mRs[mRIndex];
|
||||
}
|
||||
|
||||
float getWidth() {
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
float getHeight() {
|
||||
return mHeight;
|
||||
}
|
||||
float getHeight() {
|
||||
return mHeight;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,47 +2,46 @@ package com.horcrux.svg;
|
||||
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
class GlyphPathBag {
|
||||
private final ArrayList<Path> paths = new ArrayList<>();
|
||||
private final int[][] data = new int[256][];
|
||||
private final Paint paint;
|
||||
private final ArrayList<Path> paths = new ArrayList<>();
|
||||
private final int[][] data = new int[256][];
|
||||
private final Paint paint;
|
||||
|
||||
GlyphPathBag(Paint paint) {
|
||||
this.paint = paint;
|
||||
// Make indexed-by-one, to allow zero to represent non-cached
|
||||
paths.add(new Path());
|
||||
GlyphPathBag(Paint paint) {
|
||||
this.paint = paint;
|
||||
// Make indexed-by-one, to allow zero to represent non-cached
|
||||
paths.add(new Path());
|
||||
}
|
||||
|
||||
Path getOrCreateAndCache(char ch, String current) {
|
||||
int index = getIndex(ch);
|
||||
Path cached;
|
||||
|
||||
if (index != 0) {
|
||||
cached = paths.get(index);
|
||||
} else {
|
||||
cached = new Path();
|
||||
paint.getTextPath(current, 0, 1, 0, 0, cached);
|
||||
|
||||
int[] bin = data[ch >> 8];
|
||||
if (bin == null) {
|
||||
bin = data[ch >> 8] = new int[256];
|
||||
}
|
||||
bin[ch & 0xFF] = paths.size();
|
||||
|
||||
paths.add(cached);
|
||||
}
|
||||
|
||||
Path getOrCreateAndCache(char ch, String current) {
|
||||
int index = getIndex(ch);
|
||||
Path cached;
|
||||
Path glyph = new Path();
|
||||
glyph.addPath(cached);
|
||||
return glyph;
|
||||
}
|
||||
|
||||
if (index != 0) {
|
||||
cached = paths.get(index);
|
||||
} else {
|
||||
cached = new Path();
|
||||
paint.getTextPath(current, 0, 1, 0, 0, cached);
|
||||
|
||||
int[] bin = data[ch >> 8];
|
||||
if (bin == null) {
|
||||
bin = data[ch >> 8] = new int[256];
|
||||
}
|
||||
bin[ch & 0xFF] = paths.size();
|
||||
|
||||
paths.add(cached);
|
||||
}
|
||||
|
||||
Path glyph = new Path();
|
||||
glyph.addPath(cached);
|
||||
return glyph;
|
||||
}
|
||||
|
||||
private int getIndex(char ch) {
|
||||
int[] bin = data[ch >> 8];
|
||||
if (bin == null) return 0;
|
||||
return bin[ch & 0xFF];
|
||||
}
|
||||
private int getIndex(char ch) {
|
||||
int[] bin = data[ch >> 8];
|
||||
if (bin == null) return 0;
|
||||
return bin[ch & 0xFF];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
@@ -19,268 +18,264 @@ import android.graphics.RectF;
|
||||
import android.graphics.Region;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
import com.facebook.react.views.view.ReactViewGroup;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class GroupView extends RenderableView {
|
||||
@Nullable ReadableMap mFont;
|
||||
private GlyphContext mGlyphContext;
|
||||
@Nullable ReadableMap mFont;
|
||||
private GlyphContext mGlyphContext;
|
||||
|
||||
public GroupView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
public GroupView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@ReactProp(name = "font")
|
||||
public void setFont(@Nullable ReadableMap font) {
|
||||
mFont = font;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void setupGlyphContext(Canvas canvas) {
|
||||
RectF clipBounds = new RectF(canvas.getClipBounds());
|
||||
if (mMatrix != null) {
|
||||
mMatrix.mapRect(clipBounds);
|
||||
}
|
||||
|
||||
@ReactProp(name = "font")
|
||||
public void setFont(@Nullable ReadableMap font) {
|
||||
mFont = font;
|
||||
invalidate();
|
||||
if (mTransform != null) {
|
||||
mTransform.mapRect(clipBounds);
|
||||
}
|
||||
mGlyphContext = new GlyphContext(mScale, clipBounds.width(), clipBounds.height());
|
||||
}
|
||||
|
||||
void setupGlyphContext(Canvas canvas) {
|
||||
RectF clipBounds = new RectF(canvas.getClipBounds());
|
||||
if (mMatrix != null) {
|
||||
mMatrix.mapRect(clipBounds);
|
||||
GlyphContext getGlyphContext() {
|
||||
return mGlyphContext;
|
||||
}
|
||||
|
||||
private static <T> T requireNonNull(T obj) {
|
||||
if (obj == null) throw new NullPointerException();
|
||||
return obj;
|
||||
}
|
||||
|
||||
GlyphContext getTextRootGlyphContext() {
|
||||
return requireNonNull(getTextRoot()).getGlyphContext();
|
||||
}
|
||||
|
||||
void pushGlyphContext() {
|
||||
getTextRootGlyphContext().pushContext(this, mFont);
|
||||
}
|
||||
|
||||
void popGlyphContext() {
|
||||
getTextRootGlyphContext().popContext();
|
||||
}
|
||||
|
||||
void draw(final Canvas canvas, final Paint paint, final float opacity) {
|
||||
setupGlyphContext(canvas);
|
||||
clip(canvas, paint);
|
||||
drawGroup(canvas, paint, opacity);
|
||||
}
|
||||
|
||||
void drawGroup(final Canvas canvas, final Paint paint, final float opacity) {
|
||||
pushGlyphContext();
|
||||
final SvgView svg = getSvgView();
|
||||
final GroupView self = this;
|
||||
final RectF groupRect = new RectF();
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View child = getChildAt(i);
|
||||
if (child instanceof MaskView) {
|
||||
continue;
|
||||
}
|
||||
if (child instanceof VirtualView) {
|
||||
VirtualView node = ((VirtualView) child);
|
||||
if ("none".equals(node.mDisplay)) {
|
||||
continue;
|
||||
}
|
||||
if (mTransform != null) {
|
||||
mTransform.mapRect(clipBounds);
|
||||
}
|
||||
mGlyphContext = new GlyphContext(mScale, clipBounds.width(), clipBounds.height());
|
||||
}
|
||||
|
||||
GlyphContext getGlyphContext() {
|
||||
return mGlyphContext;
|
||||
}
|
||||
|
||||
private static <T> T requireNonNull(T obj) {
|
||||
if (obj == null)
|
||||
throw new NullPointerException();
|
||||
return obj;
|
||||
}
|
||||
|
||||
GlyphContext getTextRootGlyphContext() {
|
||||
return requireNonNull(getTextRoot()).getGlyphContext();
|
||||
}
|
||||
|
||||
void pushGlyphContext() {
|
||||
getTextRootGlyphContext().pushContext(this, mFont);
|
||||
}
|
||||
|
||||
void popGlyphContext() {
|
||||
getTextRootGlyphContext().popContext();
|
||||
}
|
||||
|
||||
void draw(final Canvas canvas, final Paint paint, final float opacity) {
|
||||
setupGlyphContext(canvas);
|
||||
clip(canvas, paint);
|
||||
drawGroup(canvas, paint, opacity);
|
||||
}
|
||||
|
||||
void drawGroup(final Canvas canvas, final Paint paint, final float opacity) {
|
||||
pushGlyphContext();
|
||||
final SvgView svg = getSvgView();
|
||||
final GroupView self = this;
|
||||
final RectF groupRect = new RectF();
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View child = getChildAt(i);
|
||||
if (child instanceof MaskView) {
|
||||
continue;
|
||||
}
|
||||
if (child instanceof VirtualView) {
|
||||
VirtualView node = ((VirtualView)child);
|
||||
if ("none".equals(node.mDisplay)) {
|
||||
continue;
|
||||
}
|
||||
if (node instanceof RenderableView) {
|
||||
((RenderableView)node).mergeProperties(self);
|
||||
}
|
||||
|
||||
int count = node.saveAndSetupCanvas(canvas, mCTM);
|
||||
node.render(canvas, paint, opacity * mOpacity);
|
||||
RectF r = node.getClientRect();
|
||||
if (r != null) {
|
||||
groupRect.union(r);
|
||||
}
|
||||
|
||||
node.restoreCanvas(canvas, count);
|
||||
|
||||
if (node instanceof RenderableView) {
|
||||
((RenderableView)node).resetProperties();
|
||||
}
|
||||
|
||||
if (node.isResponsible()) {
|
||||
svg.enableTouchEvents();
|
||||
}
|
||||
} else if (child instanceof SvgView) {
|
||||
SvgView svgView = (SvgView)child;
|
||||
svgView.drawChildren(canvas);
|
||||
if (svgView.isResponsible()) {
|
||||
svg.enableTouchEvents();
|
||||
}
|
||||
}
|
||||
}
|
||||
this.setClientRect(groupRect);
|
||||
popGlyphContext();
|
||||
}
|
||||
|
||||
void drawPath(Canvas canvas, Paint paint, float opacity) {
|
||||
super.draw(canvas, paint, opacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
Path getPath(final Canvas canvas, final Paint paint) {
|
||||
if (mPath != null) {
|
||||
return mPath;
|
||||
}
|
||||
mPath = new Path();
|
||||
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View node = getChildAt(i);
|
||||
if (node instanceof MaskView) {
|
||||
continue;
|
||||
}
|
||||
if (node instanceof VirtualView) {
|
||||
VirtualView n = (VirtualView)node;
|
||||
Matrix transform = n.mMatrix;
|
||||
mPath.addPath(n.getPath(canvas, paint), transform);
|
||||
}
|
||||
if (node instanceof RenderableView) {
|
||||
((RenderableView) node).mergeProperties(self);
|
||||
}
|
||||
|
||||
return mPath;
|
||||
int count = node.saveAndSetupCanvas(canvas, mCTM);
|
||||
node.render(canvas, paint, opacity * mOpacity);
|
||||
RectF r = node.getClientRect();
|
||||
if (r != null) {
|
||||
groupRect.union(r);
|
||||
}
|
||||
|
||||
node.restoreCanvas(canvas, count);
|
||||
|
||||
if (node instanceof RenderableView) {
|
||||
((RenderableView) node).resetProperties();
|
||||
}
|
||||
|
||||
if (node.isResponsible()) {
|
||||
svg.enableTouchEvents();
|
||||
}
|
||||
} else if (child instanceof SvgView) {
|
||||
SvgView svgView = (SvgView) child;
|
||||
svgView.drawChildren(canvas);
|
||||
if (svgView.isResponsible()) {
|
||||
svg.enableTouchEvents();
|
||||
}
|
||||
}
|
||||
}
|
||||
this.setClientRect(groupRect);
|
||||
popGlyphContext();
|
||||
}
|
||||
|
||||
void drawPath(Canvas canvas, Paint paint, float opacity) {
|
||||
super.draw(canvas, paint, opacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
Path getPath(final Canvas canvas, final Paint paint) {
|
||||
if (mPath != null) {
|
||||
return mPath;
|
||||
}
|
||||
mPath = new Path();
|
||||
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View node = getChildAt(i);
|
||||
if (node instanceof MaskView) {
|
||||
continue;
|
||||
}
|
||||
if (node instanceof VirtualView) {
|
||||
VirtualView n = (VirtualView) node;
|
||||
Matrix transform = n.mMatrix;
|
||||
mPath.addPath(n.getPath(canvas, paint), transform);
|
||||
}
|
||||
}
|
||||
|
||||
Path getPath(final Canvas canvas, final Paint paint, final Region.Op op) {
|
||||
final Path path = new Path();
|
||||
return mPath;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
final Path.Op pop = Path.Op.valueOf(op.name());
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View node = getChildAt(i);
|
||||
if (node instanceof MaskView) {
|
||||
continue;
|
||||
}
|
||||
if (node instanceof VirtualView) {
|
||||
VirtualView n = (VirtualView)node;
|
||||
Matrix transform = n.mMatrix;
|
||||
Path p2;
|
||||
if (n instanceof GroupView) {
|
||||
p2 = ((GroupView)n).getPath(canvas, paint, op);
|
||||
} else {
|
||||
p2 = n.getPath(canvas, paint);
|
||||
}
|
||||
p2.transform(transform);
|
||||
path.op(p2, pop);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Rect clipBounds = canvas.getClipBounds();
|
||||
final Region bounds = new Region(clipBounds);
|
||||
final Region r = new Region();
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View node = getChildAt(i);
|
||||
if (node instanceof MaskView) {
|
||||
continue;
|
||||
}
|
||||
if (node instanceof VirtualView) {
|
||||
VirtualView n = (VirtualView)node;
|
||||
Matrix transform = n.mMatrix;
|
||||
Path p2;
|
||||
if (n instanceof GroupView) {
|
||||
p2 = ((GroupView)n).getPath(canvas, paint, op);
|
||||
} else {
|
||||
p2 = n.getPath(canvas, paint);
|
||||
}
|
||||
if (transform != null) {
|
||||
p2.transform(transform);
|
||||
}
|
||||
Region r2 = new Region();
|
||||
r2.setPath(p2, bounds);
|
||||
r.op(r2, op);
|
||||
}
|
||||
}
|
||||
path.addPath(r.getBoundaryPath());
|
||||
Path getPath(final Canvas canvas, final Paint paint, final Region.Op op) {
|
||||
final Path path = new Path();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
final Path.Op pop = Path.Op.valueOf(op.name());
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View node = getChildAt(i);
|
||||
if (node instanceof MaskView) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return path;
|
||||
if (node instanceof VirtualView) {
|
||||
VirtualView n = (VirtualView) node;
|
||||
Matrix transform = n.mMatrix;
|
||||
Path p2;
|
||||
if (n instanceof GroupView) {
|
||||
p2 = ((GroupView) n).getPath(canvas, paint, op);
|
||||
} else {
|
||||
p2 = n.getPath(canvas, paint);
|
||||
}
|
||||
p2.transform(transform);
|
||||
path.op(p2, pop);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Rect clipBounds = canvas.getClipBounds();
|
||||
final Region bounds = new Region(clipBounds);
|
||||
final Region r = new Region();
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View node = getChildAt(i);
|
||||
if (node instanceof MaskView) {
|
||||
continue;
|
||||
}
|
||||
if (node instanceof VirtualView) {
|
||||
VirtualView n = (VirtualView) node;
|
||||
Matrix transform = n.mMatrix;
|
||||
Path p2;
|
||||
if (n instanceof GroupView) {
|
||||
p2 = ((GroupView) n).getPath(canvas, paint, op);
|
||||
} else {
|
||||
p2 = n.getPath(canvas, paint);
|
||||
}
|
||||
if (transform != null) {
|
||||
p2.transform(transform);
|
||||
}
|
||||
Region r2 = new Region();
|
||||
r2.setPath(p2, bounds);
|
||||
r.op(r2, op);
|
||||
}
|
||||
}
|
||||
path.addPath(r.getBoundaryPath());
|
||||
}
|
||||
|
||||
@Override
|
||||
int hitTest(final float[] src) {
|
||||
if (!mInvertible || !mTransformInvertible) {
|
||||
return -1;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
float[] dst = new float[2];
|
||||
mInvMatrix.mapPoints(dst, src);
|
||||
mInvTransform.mapPoints(dst);
|
||||
@Override
|
||||
int hitTest(final float[] src) {
|
||||
if (!mInvertible || !mTransformInvertible) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int x = Math.round(dst[0]);
|
||||
int y = Math.round(dst[1]);
|
||||
float[] dst = new float[2];
|
||||
mInvMatrix.mapPoints(dst, src);
|
||||
mInvTransform.mapPoints(dst);
|
||||
|
||||
Path clipPath = getClipPath();
|
||||
if (clipPath != null) {
|
||||
if (mClipRegionPath != clipPath) {
|
||||
mClipRegionPath = clipPath;
|
||||
mClipBounds = new RectF();
|
||||
clipPath.computeBounds(mClipBounds, true);
|
||||
mClipRegion = getRegion(clipPath, mClipBounds);
|
||||
}
|
||||
if (!mClipRegion.contains(x, y)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = getChildCount() - 1; i >= 0; i--) {
|
||||
View child = getChildAt(i);
|
||||
if (child instanceof VirtualView) {
|
||||
if (child instanceof MaskView) {
|
||||
continue;
|
||||
}
|
||||
|
||||
VirtualView node = (VirtualView) child;
|
||||
|
||||
int hitChild = node.hitTest(dst);
|
||||
if (hitChild != -1) {
|
||||
return (node.isResponsible() || hitChild != child.getId()) ? hitChild : getId();
|
||||
}
|
||||
} else if (child instanceof SvgView) {
|
||||
SvgView node = (SvgView) child;
|
||||
|
||||
int hitChild = node.reactTagForTouch(dst[0], dst[1]);
|
||||
if (hitChild != child.getId()) {
|
||||
return hitChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
int x = Math.round(dst[0]);
|
||||
int y = Math.round(dst[1]);
|
||||
|
||||
Path clipPath = getClipPath();
|
||||
if (clipPath != null) {
|
||||
if (mClipRegionPath != clipPath) {
|
||||
mClipRegionPath = clipPath;
|
||||
mClipBounds = new RectF();
|
||||
clipPath.computeBounds(mClipBounds, true);
|
||||
mClipRegion = getRegion(clipPath, mClipBounds);
|
||||
}
|
||||
if (!mClipRegion.contains(x, y)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void saveDefinition() {
|
||||
if (mName != null) {
|
||||
getSvgView().defineTemplate(this, mName);
|
||||
for (int i = getChildCount() - 1; i >= 0; i--) {
|
||||
View child = getChildAt(i);
|
||||
if (child instanceof VirtualView) {
|
||||
if (child instanceof MaskView) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View node = getChildAt(i);
|
||||
if (node instanceof VirtualView) {
|
||||
((VirtualView)node).saveDefinition();
|
||||
}
|
||||
VirtualView node = (VirtualView) child;
|
||||
|
||||
int hitChild = node.hitTest(dst);
|
||||
if (hitChild != -1) {
|
||||
return (node.isResponsible() || hitChild != child.getId()) ? hitChild : getId();
|
||||
}
|
||||
} else if (child instanceof SvgView) {
|
||||
SvgView node = (SvgView) child;
|
||||
|
||||
int hitChild = node.reactTagForTouch(dst[0], dst[1]);
|
||||
if (hitChild != child.getId()) {
|
||||
return hitChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void resetProperties() {
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View node = getChildAt(i);
|
||||
if (node instanceof RenderableView) {
|
||||
((RenderableView)node).resetProperties();
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void saveDefinition() {
|
||||
if (mName != null) {
|
||||
getSvgView().defineTemplate(this, mName);
|
||||
}
|
||||
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View node = getChildAt(i);
|
||||
if (node instanceof VirtualView) {
|
||||
((VirtualView) node).saveDefinition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void resetProperties() {
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View node = getChildAt(i);
|
||||
if (node instanceof RenderableView) {
|
||||
((RenderableView) node).resetProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
@@ -17,7 +16,6 @@ import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.RectF;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.facebook.common.executors.UiThreadImmediateExecutorService;
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.common.references.CloseableReference;
|
||||
@@ -35,235 +33,240 @@ import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
import com.facebook.react.views.imagehelper.ImageSource;
|
||||
import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class ImageView extends RenderableView {
|
||||
private SVGLength mX;
|
||||
private SVGLength mY;
|
||||
private SVGLength mW;
|
||||
private SVGLength mH;
|
||||
private String uriString;
|
||||
private int mImageWidth;
|
||||
private int mImageHeight;
|
||||
private String mAlign;
|
||||
private int mMeetOrSlice;
|
||||
private final AtomicBoolean mLoading = new AtomicBoolean(false);
|
||||
private SVGLength mX;
|
||||
private SVGLength mY;
|
||||
private SVGLength mW;
|
||||
private SVGLength mH;
|
||||
private String uriString;
|
||||
private int mImageWidth;
|
||||
private int mImageHeight;
|
||||
private String mAlign;
|
||||
private int mMeetOrSlice;
|
||||
private final AtomicBoolean mLoading = new AtomicBoolean(false);
|
||||
|
||||
public ImageView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
public ImageView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@ReactProp(name = "x")
|
||||
public void setX(Dynamic x) {
|
||||
mX = SVGLength.from(x);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "x")
|
||||
public void setX(Dynamic x) {
|
||||
mX = SVGLength.from(x);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setX(String x) {
|
||||
mX = SVGLength.from(x);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "y")
|
||||
public void setY(Dynamic y) {
|
||||
mY = SVGLength.from(y);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "y")
|
||||
public void setY(Dynamic y) {
|
||||
mY = SVGLength.from(y);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setY(String y) {
|
||||
mY = SVGLength.from(y);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "width")
|
||||
public void setWidth(Dynamic width) {
|
||||
mW = SVGLength.from(width);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "width")
|
||||
public void setWidth(Dynamic width) {
|
||||
mW = SVGLength.from(width);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setWidth(String width) {
|
||||
mW = SVGLength.from(width);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "height")
|
||||
public void setHeight(Dynamic height) {
|
||||
mH = SVGLength.from(height);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "height")
|
||||
public void setHeight(Dynamic height) {
|
||||
mH = SVGLength.from(height);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setHeight(String height) {
|
||||
mH = SVGLength.from(height);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "src")
|
||||
public void setSrc(@Nullable ReadableMap src) {
|
||||
if (src != null) {
|
||||
uriString = src.getString("uri");
|
||||
|
||||
@ReactProp(name = "src")
|
||||
public void setSrc(@Nullable ReadableMap src) {
|
||||
if (src != null) {
|
||||
uriString = src.getString("uri");
|
||||
if (uriString == null || uriString.isEmpty()) {
|
||||
// TODO: give warning about this
|
||||
return;
|
||||
}
|
||||
|
||||
if (uriString == null || uriString.isEmpty()) {
|
||||
//TODO: give warning about this
|
||||
return;
|
||||
}
|
||||
|
||||
if (src.hasKey("width") && src.hasKey("height")) {
|
||||
mImageWidth = src.getInt("width");
|
||||
mImageHeight = src.getInt("height");
|
||||
} else {
|
||||
mImageWidth = 0;
|
||||
mImageHeight = 0;
|
||||
}
|
||||
Uri mUri = Uri.parse(uriString);
|
||||
if (mUri.getScheme() == null) {
|
||||
ResourceDrawableIdHelper.getInstance().getResourceDrawableUri(mContext, uriString);
|
||||
}
|
||||
}
|
||||
if (src.hasKey("width") && src.hasKey("height")) {
|
||||
mImageWidth = src.getInt("width");
|
||||
mImageHeight = src.getInt("height");
|
||||
} else {
|
||||
mImageWidth = 0;
|
||||
mImageHeight = 0;
|
||||
}
|
||||
Uri mUri = Uri.parse(uriString);
|
||||
if (mUri.getScheme() == null) {
|
||||
ResourceDrawableIdHelper.getInstance().getResourceDrawableUri(mContext, uriString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactProp(name = "align")
|
||||
public void setAlign(String align) {
|
||||
mAlign = align;
|
||||
invalidate();
|
||||
@ReactProp(name = "align")
|
||||
public void setAlign(String align) {
|
||||
mAlign = align;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "meetOrSlice")
|
||||
public void setMeetOrSlice(int meetOrSlice) {
|
||||
mMeetOrSlice = meetOrSlice;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
void draw(final Canvas canvas, final Paint paint, final float opacity) {
|
||||
if (!mLoading.get()) {
|
||||
ImagePipeline imagePipeline = Fresco.getImagePipeline();
|
||||
ImageSource imageSource = new ImageSource(mContext, uriString);
|
||||
ImageRequest request = ImageRequest.fromUri(imageSource.getUri());
|
||||
boolean inMemoryCache = imagePipeline.isInBitmapMemoryCache(request);
|
||||
|
||||
if (inMemoryCache) {
|
||||
tryRenderFromBitmapCache(imagePipeline, request, canvas, paint, opacity * mOpacity);
|
||||
} else {
|
||||
loadBitmap(imagePipeline, request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactProp(name = "meetOrSlice")
|
||||
public void setMeetOrSlice(int meetOrSlice) {
|
||||
mMeetOrSlice = meetOrSlice;
|
||||
invalidate();
|
||||
}
|
||||
@Override
|
||||
Path getPath(Canvas canvas, Paint paint) {
|
||||
mPath = new Path();
|
||||
mPath.addRect(getRect(), Path.Direction.CW);
|
||||
return mPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
void draw(final Canvas canvas, final Paint paint, final float opacity) {
|
||||
if (!mLoading.get()) {
|
||||
ImagePipeline imagePipeline = Fresco.getImagePipeline();
|
||||
ImageSource imageSource = new ImageSource(mContext, uriString);
|
||||
ImageRequest request = ImageRequest.fromUri(imageSource.getUri());
|
||||
boolean inMemoryCache = imagePipeline.isInBitmapMemoryCache(request);
|
||||
|
||||
if (inMemoryCache) {
|
||||
tryRenderFromBitmapCache(imagePipeline, request, canvas, paint, opacity * mOpacity);
|
||||
} else {
|
||||
loadBitmap(imagePipeline, request);
|
||||
private void loadBitmap(final ImagePipeline imagePipeline, final ImageRequest request) {
|
||||
mLoading.set(true);
|
||||
final DataSource<CloseableReference<CloseableImage>> dataSource =
|
||||
imagePipeline.fetchDecodedImage(request, mContext);
|
||||
BaseBitmapDataSubscriber subscriber =
|
||||
new BaseBitmapDataSubscriber() {
|
||||
@Override
|
||||
public void onNewResultImpl(Bitmap bitmap) {
|
||||
mLoading.set(false);
|
||||
SvgView view = getSvgView();
|
||||
if (view != null) {
|
||||
view.invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
Path getPath(Canvas canvas, Paint paint) {
|
||||
mPath = new Path();
|
||||
mPath.addRect(getRect(), Path.Direction.CW);
|
||||
return mPath;
|
||||
}
|
||||
|
||||
private void loadBitmap(final ImagePipeline imagePipeline, final ImageRequest request) {
|
||||
mLoading.set(true);
|
||||
final DataSource<CloseableReference<CloseableImage>> dataSource
|
||||
= imagePipeline.fetchDecodedImage(request, mContext);
|
||||
BaseBitmapDataSubscriber subscriber = new BaseBitmapDataSubscriber() {
|
||||
@Override
|
||||
public void onNewResultImpl(Bitmap bitmap) {
|
||||
mLoading.set(false);
|
||||
SvgView view = getSvgView();
|
||||
if (view != null) {
|
||||
view.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailureImpl(DataSource dataSource) {
|
||||
// No cleanup required here.
|
||||
// TODO: more details about this failure
|
||||
mLoading.set(false);
|
||||
FLog.w(ReactConstants.TAG, dataSource.getFailureCause(), "RNSVG: fetchDecodedImage failed!");
|
||||
}
|
||||
@Override
|
||||
public void onFailureImpl(DataSource dataSource) {
|
||||
// No cleanup required here.
|
||||
// TODO: more details about this failure
|
||||
mLoading.set(false);
|
||||
FLog.w(
|
||||
ReactConstants.TAG,
|
||||
dataSource.getFailureCause(),
|
||||
"RNSVG: fetchDecodedImage failed!");
|
||||
}
|
||||
};
|
||||
dataSource.subscribe(subscriber, UiThreadImmediateExecutorService.getInstance());
|
||||
dataSource.subscribe(subscriber, UiThreadImmediateExecutorService.getInstance());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private RectF getRect() {
|
||||
double x = relativeOnWidth(mX);
|
||||
double y = relativeOnHeight(mY);
|
||||
double w = relativeOnWidth(mW);
|
||||
double h = relativeOnHeight(mH);
|
||||
if (w == 0) {
|
||||
w = mImageWidth * mScale;
|
||||
}
|
||||
if (h == 0) {
|
||||
h = mImageHeight * mScale;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private RectF getRect() {
|
||||
double x = relativeOnWidth(mX);
|
||||
double y = relativeOnHeight(mY);
|
||||
double w = relativeOnWidth(mW);
|
||||
double h = relativeOnHeight(mH);
|
||||
if (w == 0) {
|
||||
w = mImageWidth * mScale;
|
||||
}
|
||||
if (h == 0) {
|
||||
h = mImageHeight * mScale;
|
||||
}
|
||||
return new RectF((float) x, (float) y, (float) (x + w), (float) (y + h));
|
||||
}
|
||||
|
||||
return new RectF((float) x, (float) y, (float) (x + w), (float) (y + h));
|
||||
private void doRender(Canvas canvas, Paint paint, Bitmap bitmap, float opacity) {
|
||||
if (mImageWidth == 0 || mImageHeight == 0) {
|
||||
mImageWidth = bitmap.getWidth();
|
||||
mImageHeight = bitmap.getHeight();
|
||||
}
|
||||
|
||||
private void doRender(Canvas canvas, Paint paint, Bitmap bitmap, float opacity) {
|
||||
if (mImageWidth == 0 || mImageHeight == 0) {
|
||||
mImageWidth = bitmap.getWidth();
|
||||
mImageHeight = bitmap.getHeight();
|
||||
}
|
||||
RectF renderRect = getRect();
|
||||
RectF vbRect = new RectF(0, 0, mImageWidth, mImageHeight);
|
||||
Matrix transform = ViewBox.getTransform(vbRect, renderRect, mAlign, mMeetOrSlice);
|
||||
transform.mapRect(vbRect);
|
||||
|
||||
RectF renderRect = getRect();
|
||||
RectF vbRect = new RectF(0, 0, mImageWidth, mImageHeight);
|
||||
Matrix transform = ViewBox.getTransform(vbRect, renderRect, mAlign, mMeetOrSlice);
|
||||
transform.mapRect(vbRect);
|
||||
canvas.clipPath(getPath(canvas, paint));
|
||||
|
||||
canvas.clipPath(getPath(canvas, paint));
|
||||
|
||||
Path clipPath = getClipPath(canvas, paint);
|
||||
if (clipPath != null) {
|
||||
canvas.clipPath(clipPath);
|
||||
}
|
||||
|
||||
Paint alphaPaint = new Paint();
|
||||
alphaPaint.setAlpha((int) (opacity * 255));
|
||||
canvas.drawBitmap(bitmap, null, vbRect, alphaPaint);
|
||||
mCTM.mapRect(vbRect);
|
||||
this.setClientRect(vbRect);
|
||||
Path clipPath = getClipPath(canvas, paint);
|
||||
if (clipPath != null) {
|
||||
canvas.clipPath(clipPath);
|
||||
}
|
||||
|
||||
private void tryRenderFromBitmapCache(ImagePipeline imagePipeline, ImageRequest request, Canvas canvas, Paint paint, float opacity) {
|
||||
final DataSource<CloseableReference<CloseableImage>> dataSource
|
||||
= imagePipeline.fetchImageFromBitmapCache(request, mContext);
|
||||
Paint alphaPaint = new Paint();
|
||||
alphaPaint.setAlpha((int) (opacity * 255));
|
||||
canvas.drawBitmap(bitmap, null, vbRect, alphaPaint);
|
||||
mCTM.mapRect(vbRect);
|
||||
this.setClientRect(vbRect);
|
||||
}
|
||||
|
||||
try {
|
||||
final CloseableReference<CloseableImage> imageReference = dataSource.getResult();
|
||||
if (imageReference == null) {
|
||||
return;
|
||||
}
|
||||
private void tryRenderFromBitmapCache(
|
||||
ImagePipeline imagePipeline,
|
||||
ImageRequest request,
|
||||
Canvas canvas,
|
||||
Paint paint,
|
||||
float opacity) {
|
||||
final DataSource<CloseableReference<CloseableImage>> dataSource =
|
||||
imagePipeline.fetchImageFromBitmapCache(request, mContext);
|
||||
|
||||
try {
|
||||
CloseableImage closeableImage = imageReference.get();
|
||||
if (!(closeableImage instanceof CloseableBitmap)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final CloseableReference<CloseableImage> imageReference = dataSource.getResult();
|
||||
if (imageReference == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
CloseableBitmap closeableBitmap = (CloseableBitmap) closeableImage;
|
||||
final Bitmap bitmap = closeableBitmap.getUnderlyingBitmap();
|
||||
|
||||
if (bitmap == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
doRender(canvas, paint, bitmap, opacity);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
} finally {
|
||||
CloseableReference.closeSafely(imageReference);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
} finally {
|
||||
dataSource.close();
|
||||
try {
|
||||
CloseableImage closeableImage = imageReference.get();
|
||||
if (!(closeableImage instanceof CloseableBitmap)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CloseableBitmap closeableBitmap = (CloseableBitmap) closeableImage;
|
||||
final Bitmap bitmap = closeableBitmap.getUnderlyingBitmap();
|
||||
|
||||
if (bitmap == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
doRender(canvas, paint, bitmap, opacity);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
} finally {
|
||||
CloseableReference.closeSafely(imageReference);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
} finally {
|
||||
dataSource.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,83 +6,81 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class LineView extends RenderableView {
|
||||
private SVGLength mX1;
|
||||
private SVGLength mY1;
|
||||
private SVGLength mX2;
|
||||
private SVGLength mY2;
|
||||
private SVGLength mX1;
|
||||
private SVGLength mY1;
|
||||
private SVGLength mX2;
|
||||
private SVGLength mY2;
|
||||
|
||||
public LineView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
public LineView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@ReactProp(name = "x1")
|
||||
public void setX1(Dynamic x1) {
|
||||
mX1 = SVGLength.from(x1);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "x1")
|
||||
public void setX1(Dynamic x1) {
|
||||
mX1 = SVGLength.from(x1);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setX1(String x1) {
|
||||
mX1 = SVGLength.from(x1);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "y1")
|
||||
public void setY1(Dynamic y1) {
|
||||
mY1 = SVGLength.from(y1);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "y1")
|
||||
public void setY1(Dynamic y1) {
|
||||
mY1 = SVGLength.from(y1);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setY1(String y1) {
|
||||
mY1 = SVGLength.from(y1);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "x2")
|
||||
public void setX2(Dynamic x2) {
|
||||
mX2 = SVGLength.from(x2);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "x2")
|
||||
public void setX2(Dynamic x2) {
|
||||
mX2 = SVGLength.from(x2);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setX2(String x2) {
|
||||
mX2 = SVGLength.from(x2);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "y2")
|
||||
public void setY2(Dynamic y2) {
|
||||
mY2 = SVGLength.from(y2);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "y2")
|
||||
public void setY2(Dynamic y2) {
|
||||
mY2 = SVGLength.from(y2);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setY2(String y2) {
|
||||
mY2 = SVGLength.from(y2);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
Path getPath(Canvas canvas, Paint paint) {
|
||||
Path path = new Path();
|
||||
double x1 = relativeOnWidth(mX1);
|
||||
double y1 = relativeOnHeight(mY1);
|
||||
double x2 = relativeOnWidth(mX2);
|
||||
double y2 = relativeOnHeight(mY2);
|
||||
@Override
|
||||
Path getPath(Canvas canvas, Paint paint) {
|
||||
Path path = new Path();
|
||||
double x1 = relativeOnWidth(mX1);
|
||||
double y1 = relativeOnHeight(mY1);
|
||||
double x2 = relativeOnWidth(mX2);
|
||||
double y2 = relativeOnHeight(mY2);
|
||||
|
||||
path.moveTo((float) x1, (float) y1);
|
||||
path.lineTo((float) x2, (float) y2);
|
||||
return path;
|
||||
}
|
||||
path.moveTo((float) x1, (float) y1);
|
||||
path.lineTo((float) x2, (float) y2);
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,140 +6,138 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Matrix;
|
||||
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class LinearGradientView extends DefinitionView {
|
||||
|
||||
private SVGLength mX1;
|
||||
private SVGLength mY1;
|
||||
private SVGLength mX2;
|
||||
private SVGLength mY2;
|
||||
private ReadableArray mGradient;
|
||||
private Brush.BrushUnits mGradientUnits;
|
||||
private SVGLength mX1;
|
||||
private SVGLength mY1;
|
||||
private SVGLength mX2;
|
||||
private SVGLength mY2;
|
||||
private ReadableArray mGradient;
|
||||
private Brush.BrushUnits mGradientUnits;
|
||||
|
||||
private static final float[] sRawMatrix = new float[]{
|
||||
private static final float[] sRawMatrix =
|
||||
new float[] {
|
||||
1, 0, 0,
|
||||
0, 1, 0,
|
||||
0, 0, 1
|
||||
};
|
||||
private Matrix mMatrix = null;
|
||||
};
|
||||
private Matrix mMatrix = null;
|
||||
|
||||
public LinearGradientView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
public LinearGradientView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@ReactProp(name = "x1")
|
||||
public void setX1(Dynamic x1) {
|
||||
mX1 = SVGLength.from(x1);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "x1")
|
||||
public void setX1(Dynamic x1) {
|
||||
mX1 = SVGLength.from(x1);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setX1(String x1) {
|
||||
mX1 = SVGLength.from(x1);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "y1")
|
||||
public void setY1(Dynamic y1) {
|
||||
mY1 = SVGLength.from(y1);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "y1")
|
||||
public void setY1(Dynamic y1) {
|
||||
mY1 = SVGLength.from(y1);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setY1(String y1) {
|
||||
mY1 = SVGLength.from(y1);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "x2")
|
||||
public void setX2(Dynamic x2) {
|
||||
mX2 = SVGLength.from(x2);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "x2")
|
||||
public void setX2(Dynamic x2) {
|
||||
mX2 = SVGLength.from(x2);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setX2(String x2) {
|
||||
mX2 = SVGLength.from(x2);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "y2")
|
||||
public void setY2(Dynamic y2) {
|
||||
mY2 = SVGLength.from(y2);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "y2")
|
||||
public void setY2(Dynamic y2) {
|
||||
mY2 = SVGLength.from(y2);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setY2(String y2) {
|
||||
mY2 = SVGLength.from(y2);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "gradient")
|
||||
public void setGradient(ReadableArray gradient) {
|
||||
mGradient = gradient;
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "gradient")
|
||||
public void setGradient(ReadableArray gradient) {
|
||||
mGradient = gradient;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "gradientUnits")
|
||||
public void setGradientUnits(int gradientUnits) {
|
||||
switch (gradientUnits) {
|
||||
case 0:
|
||||
mGradientUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
|
||||
break;
|
||||
case 1:
|
||||
mGradientUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
|
||||
break;
|
||||
@ReactProp(name = "gradientUnits")
|
||||
public void setGradientUnits(int gradientUnits) {
|
||||
switch (gradientUnits) {
|
||||
case 0:
|
||||
mGradientUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
|
||||
break;
|
||||
case 1:
|
||||
mGradientUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
|
||||
break;
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "gradientTransform")
|
||||
public void setGradientTransform(@Nullable ReadableArray matrixArray) {
|
||||
if (matrixArray != null) {
|
||||
int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
|
||||
if (matrixSize == 6) {
|
||||
if (mMatrix == null) {
|
||||
mMatrix = new Matrix();
|
||||
}
|
||||
invalidate();
|
||||
mMatrix.setValues(sRawMatrix);
|
||||
} else if (matrixSize != -1) {
|
||||
FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
|
||||
}
|
||||
} else {
|
||||
mMatrix = null;
|
||||
}
|
||||
|
||||
@ReactProp(name = "gradientTransform")
|
||||
public void setGradientTransform(@Nullable ReadableArray matrixArray) {
|
||||
if (matrixArray != null) {
|
||||
int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
|
||||
if (matrixSize == 6) {
|
||||
if (mMatrix == null) {
|
||||
mMatrix = new Matrix();
|
||||
}
|
||||
mMatrix.setValues(sRawMatrix);
|
||||
} else if (matrixSize != -1) {
|
||||
FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
|
||||
}
|
||||
} else {
|
||||
mMatrix = null;
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
void saveDefinition() {
|
||||
if (mName != null) {
|
||||
SVGLength[] points = new SVGLength[]{mX1, mY1, mX2, mY2};
|
||||
Brush brush = new Brush(Brush.BrushType.LINEAR_GRADIENT, points, mGradientUnits);
|
||||
brush.setGradientColors(mGradient);
|
||||
if (mMatrix != null) {
|
||||
brush.setGradientTransform(mMatrix);
|
||||
}
|
||||
|
||||
SvgView svg = getSvgView();
|
||||
if (mGradientUnits == Brush.BrushUnits.USER_SPACE_ON_USE) {
|
||||
brush.setUserSpaceBoundingBox(svg.getCanvasBounds());
|
||||
}
|
||||
|
||||
svg.defineBrush(brush, mName);
|
||||
}
|
||||
@Override
|
||||
void saveDefinition() {
|
||||
if (mName != null) {
|
||||
SVGLength[] points = new SVGLength[] {mX1, mY1, mX2, mY2};
|
||||
Brush brush = new Brush(Brush.BrushType.LINEAR_GRADIENT, points, mGradientUnits);
|
||||
brush.setGradientColors(mGradient);
|
||||
if (mMatrix != null) {
|
||||
brush.setGradientTransform(mMatrix);
|
||||
}
|
||||
|
||||
SvgView svg = getSvgView();
|
||||
if (mGradientUnits == Brush.BrushUnits.USER_SPACE_ON_USE) {
|
||||
brush.setUserSpaceBoundingBox(svg.getCanvasBounds());
|
||||
}
|
||||
|
||||
svg.defineBrush(brush, mName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
@@ -15,7 +14,6 @@ import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.view.View;
|
||||
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
@@ -23,167 +21,173 @@ import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
@SuppressLint("ViewConstructor")
|
||||
class MarkerView extends GroupView {
|
||||
|
||||
private SVGLength mRefX;
|
||||
private SVGLength mRefY;
|
||||
private SVGLength mMarkerWidth;
|
||||
private SVGLength mMarkerHeight;
|
||||
private String mMarkerUnits;
|
||||
private String mOrient;
|
||||
private SVGLength mRefX;
|
||||
private SVGLength mRefY;
|
||||
private SVGLength mMarkerWidth;
|
||||
private SVGLength mMarkerHeight;
|
||||
private String mMarkerUnits;
|
||||
private String mOrient;
|
||||
|
||||
private float mMinX;
|
||||
private float mMinY;
|
||||
private float mVbWidth;
|
||||
private float mVbHeight;
|
||||
String mAlign;
|
||||
int mMeetOrSlice;
|
||||
private float mMinX;
|
||||
private float mMinY;
|
||||
private float mVbWidth;
|
||||
private float mVbHeight;
|
||||
String mAlign;
|
||||
int mMeetOrSlice;
|
||||
|
||||
Matrix markerTransform = new Matrix();
|
||||
Matrix markerTransform = new Matrix();
|
||||
|
||||
public MarkerView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
public MarkerView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@ReactProp(name = "refX")
|
||||
public void setRefX(Dynamic refX) {
|
||||
mRefX = SVGLength.from(refX);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "refX")
|
||||
public void setRefX(Dynamic refX) {
|
||||
mRefX = SVGLength.from(refX);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setRefX(String refX) {
|
||||
mRefX = SVGLength.from(refX);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "refY")
|
||||
public void setRefY(Dynamic refY) {
|
||||
mRefY = SVGLength.from(refY);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "refY")
|
||||
public void setRefY(Dynamic refY) {
|
||||
mRefY = SVGLength.from(refY);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setRefY(String refY) {
|
||||
mRefY = SVGLength.from(refY);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "markerWidth")
|
||||
public void setMarkerWidth(Dynamic markerWidth) {
|
||||
mMarkerWidth = SVGLength.from(markerWidth);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "markerWidth")
|
||||
public void setMarkerWidth(Dynamic markerWidth) {
|
||||
mMarkerWidth = SVGLength.from(markerWidth);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setMarkerWidth(String markerWidth) {
|
||||
mMarkerWidth = SVGLength.from(markerWidth);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "markerHeight")
|
||||
public void setMarkerHeight(Dynamic markerHeight) {
|
||||
mMarkerHeight = SVGLength.from(markerHeight);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "markerHeight")
|
||||
public void setMarkerHeight(Dynamic markerHeight) {
|
||||
mMarkerHeight = SVGLength.from(markerHeight);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setMarkerHeight(String markerHeight) {
|
||||
mMarkerHeight = SVGLength.from(markerHeight);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "markerUnits")
|
||||
public void setMarkerUnits(String markerUnits) {
|
||||
mMarkerUnits = markerUnits;
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "markerUnits")
|
||||
public void setMarkerUnits(String markerUnits) {
|
||||
mMarkerUnits = markerUnits;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "orient")
|
||||
public void setOrient(String orient) {
|
||||
mOrient = orient;
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "orient")
|
||||
public void setOrient(String orient) {
|
||||
mOrient = orient;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "minX")
|
||||
public void setMinX(float minX) {
|
||||
mMinX = minX;
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "minX")
|
||||
public void setMinX(float minX) {
|
||||
mMinX = minX;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "minY")
|
||||
public void setMinY(float minY) {
|
||||
mMinY = minY;
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "minY")
|
||||
public void setMinY(float minY) {
|
||||
mMinY = minY;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "vbWidth")
|
||||
public void setVbWidth(float vbWidth) {
|
||||
mVbWidth = vbWidth;
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "vbWidth")
|
||||
public void setVbWidth(float vbWidth) {
|
||||
mVbWidth = vbWidth;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "vbHeight")
|
||||
public void setVbHeight(float vbHeight) {
|
||||
mVbHeight = vbHeight;
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "vbHeight")
|
||||
public void setVbHeight(float vbHeight) {
|
||||
mVbHeight = vbHeight;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "align")
|
||||
public void setAlign(String align) {
|
||||
mAlign = align;
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "align")
|
||||
public void setAlign(String align) {
|
||||
mAlign = align;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "meetOrSlice")
|
||||
public void setMeetOrSlice(int meetOrSlice) {
|
||||
mMeetOrSlice = meetOrSlice;
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "meetOrSlice")
|
||||
public void setMeetOrSlice(int meetOrSlice) {
|
||||
mMeetOrSlice = meetOrSlice;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
void saveDefinition() {
|
||||
if (mName != null) {
|
||||
SvgView svg = getSvgView();
|
||||
svg.defineMarker(this, mName);
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View node = getChildAt(i);
|
||||
if (node instanceof VirtualView) {
|
||||
((VirtualView)node).saveDefinition();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
void saveDefinition() {
|
||||
if (mName != null) {
|
||||
SvgView svg = getSvgView();
|
||||
svg.defineMarker(this, mName);
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View node = getChildAt(i);
|
||||
if (node instanceof VirtualView) {
|
||||
((VirtualView) node).saveDefinition();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void renderMarker(
|
||||
Canvas canvas, Paint paint, float opacity, RNSVGMarkerPosition position, float strokeWidth) {
|
||||
int count = saveAndSetupCanvas(canvas, mCTM);
|
||||
|
||||
markerTransform.reset();
|
||||
Point origin = position.origin;
|
||||
markerTransform.setTranslate((float) origin.x * mScale, (float) origin.y * mScale);
|
||||
|
||||
double markerAngle = "auto".equals(mOrient) ? -1 : Double.parseDouble(mOrient);
|
||||
float degrees = 180 + (float) (markerAngle == -1 ? position.angle : markerAngle);
|
||||
markerTransform.preRotate(degrees);
|
||||
|
||||
boolean useStrokeWidth = "strokeWidth".equals(mMarkerUnits);
|
||||
if (useStrokeWidth) {
|
||||
markerTransform.preScale(strokeWidth, strokeWidth);
|
||||
}
|
||||
|
||||
void renderMarker(Canvas canvas, Paint paint, float opacity, RNSVGMarkerPosition position, float strokeWidth) {
|
||||
int count = saveAndSetupCanvas(canvas, mCTM);
|
||||
|
||||
markerTransform.reset();
|
||||
Point origin = position.origin;
|
||||
markerTransform.setTranslate((float)origin.x * mScale, (float)origin.y * mScale);
|
||||
|
||||
double markerAngle = "auto".equals(mOrient) ? -1 : Double.parseDouble(mOrient);
|
||||
float degrees = 180 + (float) (markerAngle == -1 ? position.angle : markerAngle);
|
||||
markerTransform.preRotate(degrees);
|
||||
|
||||
boolean useStrokeWidth = "strokeWidth".equals(mMarkerUnits);
|
||||
if (useStrokeWidth) {
|
||||
markerTransform.preScale(strokeWidth, strokeWidth);
|
||||
}
|
||||
|
||||
double width = relativeOnWidth(mMarkerWidth) / mScale;
|
||||
double height = relativeOnHeight(mMarkerHeight) / mScale;
|
||||
RectF eRect = new RectF(0, 0, (float)width, (float)height);
|
||||
if (mAlign != null) {
|
||||
RectF vbRect = new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale);
|
||||
Matrix viewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice);
|
||||
float[] values = new float[9];
|
||||
viewBoxMatrix.getValues(values);
|
||||
markerTransform.preScale(values[Matrix.MSCALE_X], values[Matrix.MSCALE_Y]);
|
||||
}
|
||||
|
||||
double x = relativeOnWidth(mRefX);
|
||||
double y = relativeOnHeight(mRefY);
|
||||
markerTransform.preTranslate((float)-x, (float)-y);
|
||||
|
||||
canvas.concat(markerTransform);
|
||||
|
||||
drawGroup(canvas, paint, opacity);
|
||||
|
||||
restoreCanvas(canvas, count);
|
||||
double width = relativeOnWidth(mMarkerWidth) / mScale;
|
||||
double height = relativeOnHeight(mMarkerHeight) / mScale;
|
||||
RectF eRect = new RectF(0, 0, (float) width, (float) height);
|
||||
if (mAlign != null) {
|
||||
RectF vbRect =
|
||||
new RectF(
|
||||
mMinX * mScale,
|
||||
mMinY * mScale,
|
||||
(mMinX + mVbWidth) * mScale,
|
||||
(mMinY + mVbHeight) * mScale);
|
||||
Matrix viewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice);
|
||||
float[] values = new float[9];
|
||||
viewBoxMatrix.getValues(values);
|
||||
markerTransform.preScale(values[Matrix.MSCALE_X], values[Matrix.MSCALE_Y]);
|
||||
}
|
||||
|
||||
double x = relativeOnWidth(mRefX);
|
||||
double y = relativeOnHeight(mRefY);
|
||||
markerTransform.preTranslate((float) -x, (float) -y);
|
||||
|
||||
canvas.concat(markerTransform);
|
||||
|
||||
drawGroup(canvas, paint, opacity);
|
||||
|
||||
restoreCanvas(canvas, count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,140 +6,139 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Matrix;
|
||||
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class MaskView extends GroupView {
|
||||
|
||||
SVGLength mX;
|
||||
SVGLength mY;
|
||||
SVGLength mW;
|
||||
SVGLength mH;
|
||||
SVGLength mX;
|
||||
SVGLength mY;
|
||||
SVGLength mW;
|
||||
SVGLength mH;
|
||||
|
||||
// TODO implement proper support for units
|
||||
@SuppressWarnings({"FieldCanBeLocal", "unused"})
|
||||
private Brush.BrushUnits mMaskUnits;
|
||||
@SuppressWarnings({"FieldCanBeLocal", "unused"})
|
||||
private Brush.BrushUnits mMaskContentUnits;
|
||||
// TODO implement proper support for units
|
||||
@SuppressWarnings({"FieldCanBeLocal", "unused"})
|
||||
private Brush.BrushUnits mMaskUnits;
|
||||
|
||||
private static final float[] sRawMatrix = new float[]{
|
||||
@SuppressWarnings({"FieldCanBeLocal", "unused"})
|
||||
private Brush.BrushUnits mMaskContentUnits;
|
||||
|
||||
private static final float[] sRawMatrix =
|
||||
new float[] {
|
||||
1, 0, 0,
|
||||
0, 1, 0,
|
||||
0, 0, 1
|
||||
};
|
||||
private Matrix mMatrix = null;
|
||||
};
|
||||
private Matrix mMatrix = null;
|
||||
|
||||
public MaskView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
public MaskView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@ReactProp(name = "x")
|
||||
public void setX(Dynamic x) {
|
||||
mX = SVGLength.from(x);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "x")
|
||||
public void setX(Dynamic x) {
|
||||
mX = SVGLength.from(x);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setX(String x) {
|
||||
mX = SVGLength.from(x);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "y")
|
||||
public void setY(Dynamic y) {
|
||||
mY = SVGLength.from(y);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "y")
|
||||
public void setY(Dynamic y) {
|
||||
mY = SVGLength.from(y);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setY(String y) {
|
||||
mY = SVGLength.from(y);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "width")
|
||||
public void setWidth(Dynamic width) {
|
||||
mW = SVGLength.from(width);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "width")
|
||||
public void setWidth(Dynamic width) {
|
||||
mW = SVGLength.from(width);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setWidth(String width) {
|
||||
mW = SVGLength.from(width);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "height")
|
||||
public void setHeight(Dynamic height) {
|
||||
mH = SVGLength.from(height);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "height")
|
||||
public void setHeight(Dynamic height) {
|
||||
mH = SVGLength.from(height);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setHeight(String height) {
|
||||
mH = SVGLength.from(height);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "maskUnits")
|
||||
public void setMaskUnits(int maskUnits) {
|
||||
switch (maskUnits) {
|
||||
case 0:
|
||||
mMaskUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
|
||||
break;
|
||||
case 1:
|
||||
mMaskUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
|
||||
break;
|
||||
@ReactProp(name = "maskUnits")
|
||||
public void setMaskUnits(int maskUnits) {
|
||||
switch (maskUnits) {
|
||||
case 0:
|
||||
mMaskUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
|
||||
break;
|
||||
case 1:
|
||||
mMaskUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
|
||||
break;
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "maskContentUnits")
|
||||
public void setMaskContentUnits(int maskContentUnits) {
|
||||
switch (maskContentUnits) {
|
||||
case 0:
|
||||
mMaskContentUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
|
||||
break;
|
||||
case 1:
|
||||
mMaskContentUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
|
||||
break;
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "maskTransform")
|
||||
public void setMaskTransform(@Nullable ReadableArray matrixArray) {
|
||||
if (matrixArray != null) {
|
||||
int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
|
||||
if (matrixSize == 6) {
|
||||
if (mMatrix == null) {
|
||||
mMatrix = new Matrix();
|
||||
}
|
||||
invalidate();
|
||||
mMatrix.setValues(sRawMatrix);
|
||||
} else if (matrixSize != -1) {
|
||||
FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
|
||||
}
|
||||
} else {
|
||||
mMatrix = null;
|
||||
}
|
||||
|
||||
@ReactProp(name = "maskContentUnits")
|
||||
public void setMaskContentUnits(int maskContentUnits) {
|
||||
switch (maskContentUnits) {
|
||||
case 0:
|
||||
mMaskContentUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
|
||||
break;
|
||||
case 1:
|
||||
mMaskContentUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
|
||||
break;
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "maskTransform")
|
||||
public void setMaskTransform(@Nullable ReadableArray matrixArray) {
|
||||
if (matrixArray != null) {
|
||||
int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
|
||||
if (matrixSize == 6) {
|
||||
if (mMatrix == null) {
|
||||
mMatrix = new Matrix();
|
||||
}
|
||||
mMatrix.setValues(sRawMatrix);
|
||||
} else if (matrixSize != -1) {
|
||||
FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
|
||||
}
|
||||
} else {
|
||||
mMatrix = null;
|
||||
}
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
void saveDefinition() {
|
||||
if (mName != null) {
|
||||
SvgView svg = getSvgView();
|
||||
svg.defineMask(this, mName);
|
||||
}
|
||||
@Override
|
||||
void saveDefinition() {
|
||||
if (mName != null) {
|
||||
SvgView svg = getSvgView();
|
||||
svg.defineMask(this, mName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,37 +6,34 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class PathView extends RenderableView {
|
||||
private Path mPath;
|
||||
private Path mPath;
|
||||
|
||||
public PathView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
PathParser.mScale = mScale;
|
||||
mPath = new Path();
|
||||
}
|
||||
public PathView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
PathParser.mScale = mScale;
|
||||
mPath = new Path();
|
||||
}
|
||||
|
||||
@ReactProp(name = "d")
|
||||
public void setD(String d) {
|
||||
mPath = PathParser.parse(d);
|
||||
elements = PathParser.elements;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
Path getPath(Canvas canvas, Paint paint) {
|
||||
return mPath;
|
||||
}
|
||||
@ReactProp(name = "d")
|
||||
public void setD(String d) {
|
||||
mPath = PathParser.parse(d);
|
||||
elements = PathParser.elements;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
Path getPath(Canvas canvas, Paint paint) {
|
||||
return mPath;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,197 +6,197 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.RectF;
|
||||
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class PatternView extends GroupView {
|
||||
|
||||
private SVGLength mX;
|
||||
private SVGLength mY;
|
||||
private SVGLength mW;
|
||||
private SVGLength mH;
|
||||
private Brush.BrushUnits mPatternUnits;
|
||||
private Brush.BrushUnits mPatternContentUnits;
|
||||
private SVGLength mX;
|
||||
private SVGLength mY;
|
||||
private SVGLength mW;
|
||||
private SVGLength mH;
|
||||
private Brush.BrushUnits mPatternUnits;
|
||||
private Brush.BrushUnits mPatternContentUnits;
|
||||
|
||||
private float mMinX;
|
||||
private float mMinY;
|
||||
private float mVbWidth;
|
||||
private float mVbHeight;
|
||||
String mAlign;
|
||||
int mMeetOrSlice;
|
||||
private float mMinX;
|
||||
private float mMinY;
|
||||
private float mVbWidth;
|
||||
private float mVbHeight;
|
||||
String mAlign;
|
||||
int mMeetOrSlice;
|
||||
|
||||
private static final float[] sRawMatrix = new float[]{
|
||||
private static final float[] sRawMatrix =
|
||||
new float[] {
|
||||
1, 0, 0,
|
||||
0, 1, 0,
|
||||
0, 0, 1
|
||||
};
|
||||
private Matrix mMatrix = null;
|
||||
};
|
||||
private Matrix mMatrix = null;
|
||||
|
||||
public PatternView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
public PatternView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@ReactProp(name = "x")
|
||||
public void setX(Dynamic x) {
|
||||
mX = SVGLength.from(x);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "x")
|
||||
public void setX(Dynamic x) {
|
||||
mX = SVGLength.from(x);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setX(String x) {
|
||||
mX = SVGLength.from(x);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "y")
|
||||
public void setY(Dynamic y) {
|
||||
mY = SVGLength.from(y);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "y")
|
||||
public void setY(Dynamic y) {
|
||||
mY = SVGLength.from(y);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setY(String y) {
|
||||
mY = SVGLength.from(y);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "width")
|
||||
public void setWidth(Dynamic width) {
|
||||
mW = SVGLength.from(width);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "width")
|
||||
public void setWidth(Dynamic width) {
|
||||
mW = SVGLength.from(width);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setWidth(String width) {
|
||||
mW = SVGLength.from(width);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "height")
|
||||
public void setHeight(Dynamic height) {
|
||||
mH = SVGLength.from(height);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "height")
|
||||
public void setHeight(Dynamic height) {
|
||||
mH = SVGLength.from(height);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setHeight(String height) {
|
||||
mH = SVGLength.from(height);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "patternUnits")
|
||||
public void setPatternUnits(int patternUnits) {
|
||||
switch (patternUnits) {
|
||||
case 0:
|
||||
mPatternUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
|
||||
break;
|
||||
case 1:
|
||||
mPatternUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
|
||||
break;
|
||||
@ReactProp(name = "patternUnits")
|
||||
public void setPatternUnits(int patternUnits) {
|
||||
switch (patternUnits) {
|
||||
case 0:
|
||||
mPatternUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
|
||||
break;
|
||||
case 1:
|
||||
mPatternUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
|
||||
break;
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "patternContentUnits")
|
||||
public void setPatternContentUnits(int patternContentUnits) {
|
||||
switch (patternContentUnits) {
|
||||
case 0:
|
||||
mPatternContentUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
|
||||
break;
|
||||
case 1:
|
||||
mPatternContentUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
|
||||
break;
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "patternTransform")
|
||||
public void setPatternTransform(@Nullable ReadableArray matrixArray) {
|
||||
if (matrixArray != null) {
|
||||
int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
|
||||
if (matrixSize == 6) {
|
||||
if (mMatrix == null) {
|
||||
mMatrix = new Matrix();
|
||||
}
|
||||
invalidate();
|
||||
mMatrix.setValues(sRawMatrix);
|
||||
} else if (matrixSize != -1) {
|
||||
FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
|
||||
}
|
||||
} else {
|
||||
mMatrix = null;
|
||||
}
|
||||
|
||||
@ReactProp(name = "patternContentUnits")
|
||||
public void setPatternContentUnits(int patternContentUnits) {
|
||||
switch (patternContentUnits) {
|
||||
case 0:
|
||||
mPatternContentUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
|
||||
break;
|
||||
case 1:
|
||||
mPatternContentUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
|
||||
break;
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "patternTransform")
|
||||
public void setPatternTransform(@Nullable ReadableArray matrixArray) {
|
||||
if (matrixArray != null) {
|
||||
int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
|
||||
if (matrixSize == 6) {
|
||||
if (mMatrix == null) {
|
||||
mMatrix = new Matrix();
|
||||
}
|
||||
mMatrix.setValues(sRawMatrix);
|
||||
} else if (matrixSize != -1) {
|
||||
FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
|
||||
}
|
||||
} else {
|
||||
mMatrix = null;
|
||||
}
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "minX")
|
||||
public void setMinX(float minX) {
|
||||
mMinX = minX;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "minY")
|
||||
public void setMinY(float minY) {
|
||||
mMinY = minY;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "vbWidth")
|
||||
public void setVbWidth(float vbWidth) {
|
||||
mVbWidth = vbWidth;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "vbHeight")
|
||||
public void setVbHeight(float vbHeight) {
|
||||
mVbHeight = vbHeight;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "align")
|
||||
public void setAlign(String align) {
|
||||
mAlign = align;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "meetOrSlice")
|
||||
public void setMeetOrSlice(int meetOrSlice) {
|
||||
mMeetOrSlice = meetOrSlice;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
RectF getViewBox() {
|
||||
return new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale);
|
||||
}
|
||||
|
||||
@Override
|
||||
void saveDefinition() {
|
||||
if (mName != null) {
|
||||
SVGLength[] points = new SVGLength[]{mX,mY,mW,mH};
|
||||
Brush brush = new Brush(Brush.BrushType.PATTERN, points, mPatternUnits);
|
||||
brush.setContentUnits(mPatternContentUnits);
|
||||
brush.setPattern(this);
|
||||
|
||||
if (mMatrix != null) {
|
||||
brush.setGradientTransform(mMatrix);
|
||||
}
|
||||
|
||||
SvgView svg = getSvgView();
|
||||
if (mPatternUnits == Brush.BrushUnits.USER_SPACE_ON_USE || mPatternContentUnits == Brush.BrushUnits.USER_SPACE_ON_USE) {
|
||||
brush.setUserSpaceBoundingBox(svg.getCanvasBounds());
|
||||
}
|
||||
|
||||
svg.defineBrush(brush, mName);
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "minX")
|
||||
public void setMinX(float minX) {
|
||||
mMinX = minX;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "minY")
|
||||
public void setMinY(float minY) {
|
||||
mMinY = minY;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "vbWidth")
|
||||
public void setVbWidth(float vbWidth) {
|
||||
mVbWidth = vbWidth;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "vbHeight")
|
||||
public void setVbHeight(float vbHeight) {
|
||||
mVbHeight = vbHeight;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "align")
|
||||
public void setAlign(String align) {
|
||||
mAlign = align;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "meetOrSlice")
|
||||
public void setMeetOrSlice(int meetOrSlice) {
|
||||
mMeetOrSlice = meetOrSlice;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
RectF getViewBox() {
|
||||
return new RectF(
|
||||
mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale);
|
||||
}
|
||||
|
||||
@Override
|
||||
void saveDefinition() {
|
||||
if (mName != null) {
|
||||
SVGLength[] points = new SVGLength[] {mX, mY, mW, mH};
|
||||
Brush brush = new Brush(Brush.BrushType.PATTERN, points, mPatternUnits);
|
||||
brush.setContentUnits(mPatternContentUnits);
|
||||
brush.setPattern(this);
|
||||
|
||||
if (mMatrix != null) {
|
||||
brush.setGradientTransform(mMatrix);
|
||||
}
|
||||
|
||||
SvgView svg = getSvgView();
|
||||
if (mPatternUnits == Brush.BrushUnits.USER_SPACE_ON_USE
|
||||
|| mPatternContentUnits == Brush.BrushUnits.USER_SPACE_ON_USE) {
|
||||
brush.setUserSpaceBoundingBox(svg.getCanvasBounds());
|
||||
}
|
||||
|
||||
svg.defineBrush(brush, mName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,215 +6,211 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
|
||||
/**
|
||||
* Contains static helper methods for accessing props.
|
||||
*/
|
||||
/** Contains static helper methods for accessing props. */
|
||||
class PropHelper {
|
||||
|
||||
private static final int inputMatrixDataSize = 6;
|
||||
private static final int inputMatrixDataSize = 6;
|
||||
|
||||
/**
|
||||
* Converts given {@link ReadableArray} to a matrix data array, {@code float[6]}.
|
||||
* Writes result to the array passed in {@param into}.
|
||||
* This method will write exactly six items to the output array from the input array.
|
||||
*
|
||||
* If the input array has a different size, then only the size is returned;
|
||||
* Does not check output array size. Ensure space for at least six elements.
|
||||
*
|
||||
* @param value input array
|
||||
* @param sRawMatrix output matrix
|
||||
* @param mScale current resolution scaling
|
||||
* @return size of input array
|
||||
*/
|
||||
static int toMatrixData(ReadableArray value, float[] sRawMatrix, float mScale) {
|
||||
int fromSize = value.size();
|
||||
if (fromSize != inputMatrixDataSize) {
|
||||
return fromSize;
|
||||
}
|
||||
|
||||
sRawMatrix[0] = (float) value.getDouble(0);
|
||||
sRawMatrix[1] = (float) value.getDouble(2);
|
||||
sRawMatrix[2] = (float) value.getDouble(4) * mScale;
|
||||
sRawMatrix[3] = (float) value.getDouble(1);
|
||||
sRawMatrix[4] = (float) value.getDouble(3);
|
||||
sRawMatrix[5] = (float) value.getDouble(5) * mScale;
|
||||
|
||||
return inputMatrixDataSize;
|
||||
/**
|
||||
* Converts given {@link ReadableArray} to a matrix data array, {@code float[6]}. Writes result to
|
||||
* the array passed in {@param into}. This method will write exactly six items to the output array
|
||||
* from the input array.
|
||||
*
|
||||
* <p>If the input array has a different size, then only the size is returned; Does not check
|
||||
* output array size. Ensure space for at least six elements.
|
||||
*
|
||||
* @param value input array
|
||||
* @param sRawMatrix output matrix
|
||||
* @param mScale current resolution scaling
|
||||
* @return size of input array
|
||||
*/
|
||||
static int toMatrixData(ReadableArray value, float[] sRawMatrix, float mScale) {
|
||||
int fromSize = value.size();
|
||||
if (fromSize != inputMatrixDataSize) {
|
||||
return fromSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts length string into px / user units
|
||||
* in the current user coordinate system
|
||||
*
|
||||
* @param length length string
|
||||
* @param relative relative size for percentages
|
||||
* @param scale scaling parameter
|
||||
* @param fontSize current font size
|
||||
* @return value in the current user coordinate system
|
||||
*/
|
||||
static double fromRelative(String length, double relative, double scale, double fontSize) {
|
||||
/*
|
||||
TODO list
|
||||
sRawMatrix[0] = (float) value.getDouble(0);
|
||||
sRawMatrix[1] = (float) value.getDouble(2);
|
||||
sRawMatrix[2] = (float) value.getDouble(4) * mScale;
|
||||
sRawMatrix[3] = (float) value.getDouble(1);
|
||||
sRawMatrix[4] = (float) value.getDouble(3);
|
||||
sRawMatrix[5] = (float) value.getDouble(5) * mScale;
|
||||
|
||||
unit relative to
|
||||
em font size of the element
|
||||
ex x-height of the element’s font
|
||||
ch width of the "0" (ZERO, U+0030) glyph in the element’s font
|
||||
rem font size of the root element
|
||||
vw 1% of viewport’s width
|
||||
vh 1% of viewport’s height
|
||||
vmin 1% of viewport’s smaller dimension
|
||||
vmax 1% of viewport’s larger dimension
|
||||
return inputMatrixDataSize;
|
||||
}
|
||||
|
||||
relative-size [ larger | smaller ]
|
||||
absolute-size: [ xx-small | x-small | small | medium | large | x-large | xx-large ]
|
||||
/**
|
||||
* Converts length string into px / user units in the current user coordinate system
|
||||
*
|
||||
* @param length length string
|
||||
* @param relative relative size for percentages
|
||||
* @param scale scaling parameter
|
||||
* @param fontSize current font size
|
||||
* @return value in the current user coordinate system
|
||||
*/
|
||||
static double fromRelative(String length, double relative, double scale, double fontSize) {
|
||||
/*
|
||||
TODO list
|
||||
|
||||
https://www.w3.org/TR/css3-values/#relative-lengths
|
||||
https://www.w3.org/TR/css3-values/#absolute-lengths
|
||||
https://drafts.csswg.org/css-cascade-4/#computed-value
|
||||
https://drafts.csswg.org/css-fonts-3/#propdef-font-size
|
||||
https://drafts.csswg.org/css2/fonts.html#propdef-font-size
|
||||
*/
|
||||
length = length.trim();
|
||||
int stringLength = length.length();
|
||||
int percentIndex = stringLength - 1;
|
||||
if (stringLength == 0 || length.equals("normal")) {
|
||||
return 0d;
|
||||
} else if (length.codePointAt(percentIndex) == '%') {
|
||||
return Double.valueOf(length.substring(0, percentIndex)) / 100 * relative;
|
||||
} else {
|
||||
int twoLetterUnitIndex = stringLength - 2;
|
||||
if (twoLetterUnitIndex > 0) {
|
||||
String lastTwo = length.substring(twoLetterUnitIndex);
|
||||
int end = twoLetterUnitIndex;
|
||||
double unit = 1;
|
||||
unit relative to
|
||||
em font size of the element
|
||||
ex x-height of the element’s font
|
||||
ch width of the "0" (ZERO, U+0030) glyph in the element’s font
|
||||
rem font size of the root element
|
||||
vw 1% of viewport’s width
|
||||
vh 1% of viewport’s height
|
||||
vmin 1% of viewport’s smaller dimension
|
||||
vmax 1% of viewport’s larger dimension
|
||||
|
||||
switch (lastTwo) {
|
||||
case "px":
|
||||
break;
|
||||
relative-size [ larger | smaller ]
|
||||
absolute-size: [ xx-small | x-small | small | medium | large | x-large | xx-large ]
|
||||
|
||||
case "em":
|
||||
unit = fontSize;
|
||||
break;
|
||||
|
||||
/*
|
||||
"1pt" equals "1.25px" (and therefore 1.25 user units)
|
||||
"1pc" equals "15px" (and therefore 15 user units)
|
||||
"1mm" would be "3.543307px" (3.543307 user units)
|
||||
"1cm" equals "35.43307px" (and therefore 35.43307 user units)
|
||||
"1in" equals "90px" (and therefore 90 user units)
|
||||
*/
|
||||
|
||||
case "pt":
|
||||
unit = 1.25d;
|
||||
break;
|
||||
|
||||
case "pc":
|
||||
unit = 15;
|
||||
break;
|
||||
|
||||
case "mm":
|
||||
unit = 3.543307d;
|
||||
break;
|
||||
|
||||
case "cm":
|
||||
unit = 35.43307d;
|
||||
break;
|
||||
|
||||
case "in":
|
||||
unit = 90;
|
||||
break;
|
||||
|
||||
default:
|
||||
end = stringLength;
|
||||
}
|
||||
|
||||
return Double.valueOf(length.substring(0, end)) * unit * scale;
|
||||
} else {
|
||||
return Double.valueOf(length) * scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Converts SVGLength into px / user units
|
||||
* in the current user coordinate system
|
||||
*
|
||||
* @param length length string
|
||||
* @param relative relative size for percentages
|
||||
* @param offset offset for all units
|
||||
* @param scale scaling parameter
|
||||
* @param fontSize current font size
|
||||
* @return value in the current user coordinate system
|
||||
*/
|
||||
static double fromRelative(SVGLength length, double relative, double offset, double scale, double fontSize) {
|
||||
/*
|
||||
TODO list
|
||||
|
||||
unit relative to
|
||||
em font size of the element
|
||||
ex x-height of the element’s font
|
||||
ch width of the "0" (ZERO, U+0030) glyph in the element’s font
|
||||
rem font size of the root element
|
||||
vw 1% of viewport’s width
|
||||
vh 1% of viewport’s height
|
||||
vmin 1% of viewport’s smaller dimension
|
||||
vmax 1% of viewport’s larger dimension
|
||||
|
||||
relative-size [ larger | smaller ]
|
||||
absolute-size: [ xx-small | x-small | small | medium | large | x-large | xx-large ]
|
||||
|
||||
https://www.w3.org/TR/css3-values/#relative-lengths
|
||||
https://www.w3.org/TR/css3-values/#absolute-lengths
|
||||
https://drafts.csswg.org/css-cascade-4/#computed-value
|
||||
https://drafts.csswg.org/css-fonts-3/#propdef-font-size
|
||||
https://drafts.csswg.org/css2/fonts.html#propdef-font-size
|
||||
*/
|
||||
if (length == null) {
|
||||
return offset;
|
||||
}
|
||||
SVGLength.UnitType unitType = length.unit;
|
||||
double value = length.value;
|
||||
https://www.w3.org/TR/css3-values/#relative-lengths
|
||||
https://www.w3.org/TR/css3-values/#absolute-lengths
|
||||
https://drafts.csswg.org/css-cascade-4/#computed-value
|
||||
https://drafts.csswg.org/css-fonts-3/#propdef-font-size
|
||||
https://drafts.csswg.org/css2/fonts.html#propdef-font-size
|
||||
*/
|
||||
length = length.trim();
|
||||
int stringLength = length.length();
|
||||
int percentIndex = stringLength - 1;
|
||||
if (stringLength == 0 || length.equals("normal")) {
|
||||
return 0d;
|
||||
} else if (length.codePointAt(percentIndex) == '%') {
|
||||
return Double.valueOf(length.substring(0, percentIndex)) / 100 * relative;
|
||||
} else {
|
||||
int twoLetterUnitIndex = stringLength - 2;
|
||||
if (twoLetterUnitIndex > 0) {
|
||||
String lastTwo = length.substring(twoLetterUnitIndex);
|
||||
int end = twoLetterUnitIndex;
|
||||
double unit = 1;
|
||||
switch (unitType) {
|
||||
case NUMBER:
|
||||
case PX:
|
||||
break;
|
||||
|
||||
case PERCENTAGE:
|
||||
return value / 100 * relative + offset;
|
||||
switch (lastTwo) {
|
||||
case "px":
|
||||
break;
|
||||
|
||||
case EMS:
|
||||
unit = fontSize;
|
||||
break;
|
||||
case EXS:
|
||||
unit = fontSize / 2;
|
||||
break;
|
||||
case "em":
|
||||
unit = fontSize;
|
||||
break;
|
||||
|
||||
case CM:
|
||||
unit = 35.43307;
|
||||
break;
|
||||
case MM:
|
||||
unit = 3.543307;
|
||||
break;
|
||||
case IN:
|
||||
unit = 90;
|
||||
break;
|
||||
case PT:
|
||||
unit = 1.25;
|
||||
break;
|
||||
case PC:
|
||||
unit = 15;
|
||||
break;
|
||||
/*
|
||||
"1pt" equals "1.25px" (and therefore 1.25 user units)
|
||||
"1pc" equals "15px" (and therefore 15 user units)
|
||||
"1mm" would be "3.543307px" (3.543307 user units)
|
||||
"1cm" equals "35.43307px" (and therefore 35.43307 user units)
|
||||
"1in" equals "90px" (and therefore 90 user units)
|
||||
*/
|
||||
|
||||
default:
|
||||
case UNKNOWN:
|
||||
return value * scale + offset;
|
||||
case "pt":
|
||||
unit = 1.25d;
|
||||
break;
|
||||
|
||||
case "pc":
|
||||
unit = 15;
|
||||
break;
|
||||
|
||||
case "mm":
|
||||
unit = 3.543307d;
|
||||
break;
|
||||
|
||||
case "cm":
|
||||
unit = 35.43307d;
|
||||
break;
|
||||
|
||||
case "in":
|
||||
unit = 90;
|
||||
break;
|
||||
|
||||
default:
|
||||
end = stringLength;
|
||||
}
|
||||
return value * unit * scale + offset;
|
||||
|
||||
return Double.valueOf(length.substring(0, end)) * unit * scale;
|
||||
} else {
|
||||
return Double.valueOf(length) * scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Converts SVGLength into px / user units in the current user coordinate system
|
||||
*
|
||||
* @param length length string
|
||||
* @param relative relative size for percentages
|
||||
* @param offset offset for all units
|
||||
* @param scale scaling parameter
|
||||
* @param fontSize current font size
|
||||
* @return value in the current user coordinate system
|
||||
*/
|
||||
static double fromRelative(
|
||||
SVGLength length, double relative, double offset, double scale, double fontSize) {
|
||||
/*
|
||||
TODO list
|
||||
|
||||
unit relative to
|
||||
em font size of the element
|
||||
ex x-height of the element’s font
|
||||
ch width of the "0" (ZERO, U+0030) glyph in the element’s font
|
||||
rem font size of the root element
|
||||
vw 1% of viewport’s width
|
||||
vh 1% of viewport’s height
|
||||
vmin 1% of viewport’s smaller dimension
|
||||
vmax 1% of viewport’s 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,178 +3,170 @@ package com.horcrux.svg;
|
||||
import java.util.ArrayList;
|
||||
|
||||
enum RNSVGMarkerType {
|
||||
kStartMarker,
|
||||
kMidMarker,
|
||||
kEndMarker
|
||||
kStartMarker,
|
||||
kMidMarker,
|
||||
kEndMarker
|
||||
}
|
||||
|
||||
enum ElementType {
|
||||
kCGPathElementAddCurveToPoint,
|
||||
kCGPathElementAddQuadCurveToPoint,
|
||||
kCGPathElementMoveToPoint,
|
||||
kCGPathElementAddLineToPoint,
|
||||
kCGPathElementCloseSubpath
|
||||
kCGPathElementAddCurveToPoint,
|
||||
kCGPathElementAddQuadCurveToPoint,
|
||||
kCGPathElementMoveToPoint,
|
||||
kCGPathElementAddLineToPoint,
|
||||
kCGPathElementCloseSubpath
|
||||
}
|
||||
|
||||
class Point {
|
||||
double x;
|
||||
double y;
|
||||
Point(double x, double y){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
double x;
|
||||
double y;
|
||||
|
||||
Point(double x, double y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
class SegmentData {
|
||||
Point start_tangent; // Tangent in the start point of the segment.
|
||||
Point end_tangent; // Tangent in the end point of the segment.
|
||||
Point position; // The end point of the segment.
|
||||
Point start_tangent; // Tangent in the start point of the segment.
|
||||
Point end_tangent; // Tangent in the end point of the segment.
|
||||
Point position; // The end point of the segment.
|
||||
}
|
||||
|
||||
class RNSVGMarkerPosition {
|
||||
|
||||
static private ArrayList<RNSVGMarkerPosition> positions_;
|
||||
static private int element_index_;
|
||||
static private Point origin_;
|
||||
static private Point subpath_start_;
|
||||
static private Point in_slope_;
|
||||
static private Point out_slope_;
|
||||
@SuppressWarnings("unused")
|
||||
static private boolean auto_start_reverse_; // TODO
|
||||
private static ArrayList<RNSVGMarkerPosition> positions_;
|
||||
private static int element_index_;
|
||||
private static Point origin_;
|
||||
private static Point subpath_start_;
|
||||
private static Point in_slope_;
|
||||
private static Point out_slope_;
|
||||
|
||||
RNSVGMarkerType type;
|
||||
Point origin;
|
||||
double angle;
|
||||
@SuppressWarnings("unused")
|
||||
private static boolean auto_start_reverse_; // TODO
|
||||
|
||||
private RNSVGMarkerPosition(RNSVGMarkerType type, Point origin, double angle) {
|
||||
this.type = type;
|
||||
this.origin = origin;
|
||||
this.angle = angle;
|
||||
RNSVGMarkerType type;
|
||||
Point origin;
|
||||
double angle;
|
||||
|
||||
private RNSVGMarkerPosition(RNSVGMarkerType type, Point origin, double angle) {
|
||||
this.type = type;
|
||||
this.origin = origin;
|
||||
this.angle = angle;
|
||||
}
|
||||
|
||||
static ArrayList<RNSVGMarkerPosition> fromPath(ArrayList<PathElement> elements) {
|
||||
positions_ = new ArrayList<>();
|
||||
element_index_ = 0;
|
||||
origin_ = new Point(0, 0);
|
||||
subpath_start_ = new Point(0, 0);
|
||||
for (PathElement e : elements) {
|
||||
UpdateFromPathElement(e);
|
||||
}
|
||||
PathIsDone();
|
||||
return positions_;
|
||||
}
|
||||
|
||||
static ArrayList<RNSVGMarkerPosition> fromPath(ArrayList<PathElement> elements) {
|
||||
positions_ = new ArrayList<>();
|
||||
element_index_ = 0;
|
||||
origin_ = new Point(0, 0);
|
||||
subpath_start_ = new Point(0, 0);
|
||||
for (PathElement e : elements) {
|
||||
UpdateFromPathElement(e);
|
||||
}
|
||||
PathIsDone();
|
||||
return positions_;
|
||||
}
|
||||
private static void PathIsDone() {
|
||||
double angle = CurrentAngle(RNSVGMarkerType.kEndMarker);
|
||||
positions_.add(new RNSVGMarkerPosition(RNSVGMarkerType.kEndMarker, origin_, angle));
|
||||
}
|
||||
|
||||
private static void PathIsDone() {
|
||||
double angle = CurrentAngle(RNSVGMarkerType.kEndMarker);
|
||||
positions_.add(new RNSVGMarkerPosition(RNSVGMarkerType.kEndMarker, origin_, angle));
|
||||
}
|
||||
private static double BisectingAngle(double in_angle, double out_angle) {
|
||||
// WK193015: Prevent bugs due to angles being non-continuous.
|
||||
if (Math.abs(in_angle - out_angle) > 180) in_angle += 360;
|
||||
return (in_angle + out_angle) / 2;
|
||||
}
|
||||
|
||||
private static double BisectingAngle(double in_angle, double out_angle) {
|
||||
// WK193015: Prevent bugs due to angles being non-continuous.
|
||||
if (Math.abs(in_angle - out_angle) > 180)
|
||||
in_angle += 360;
|
||||
return (in_angle + out_angle) / 2;
|
||||
}
|
||||
private static double rad2deg(double rad) {
|
||||
double RNSVG_radToDeg = 180 / Math.PI;
|
||||
return rad * RNSVG_radToDeg;
|
||||
}
|
||||
|
||||
private static double rad2deg(double rad) {
|
||||
double RNSVG_radToDeg = 180 / Math.PI;
|
||||
return rad * RNSVG_radToDeg;
|
||||
}
|
||||
private static double SlopeAngleRadians(Point p) {
|
||||
return Math.atan2(p.y, p.x);
|
||||
}
|
||||
|
||||
private static double SlopeAngleRadians(Point p) {
|
||||
return Math.atan2(p.y, p.x);
|
||||
private static double CurrentAngle(RNSVGMarkerType type) {
|
||||
// For details of this calculation, see:
|
||||
// http://www.w3.org/TR/SVG/single-page.html#painting-MarkerElement
|
||||
double in_angle = rad2deg(SlopeAngleRadians(in_slope_));
|
||||
double out_angle = rad2deg(SlopeAngleRadians(out_slope_));
|
||||
switch (type) {
|
||||
case kStartMarker:
|
||||
if (auto_start_reverse_) out_angle += 180;
|
||||
return out_angle;
|
||||
case kMidMarker:
|
||||
return BisectingAngle(in_angle, out_angle);
|
||||
case kEndMarker:
|
||||
return in_angle;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static double CurrentAngle(RNSVGMarkerType type) {
|
||||
// For details of this calculation, see:
|
||||
// http://www.w3.org/TR/SVG/single-page.html#painting-MarkerElement
|
||||
double in_angle = rad2deg(SlopeAngleRadians(in_slope_));
|
||||
double out_angle = rad2deg(SlopeAngleRadians(out_slope_));
|
||||
switch (type) {
|
||||
case kStartMarker:
|
||||
if (auto_start_reverse_)
|
||||
out_angle += 180;
|
||||
return out_angle;
|
||||
case kMidMarker:
|
||||
return BisectingAngle(in_angle, out_angle);
|
||||
case kEndMarker:
|
||||
return in_angle;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
private static Point subtract(Point p1, Point p2) {
|
||||
return new Point(p2.x - p1.x, p2.y - p1.y);
|
||||
}
|
||||
|
||||
private static Point subtract(Point p1, Point p2) {
|
||||
return new Point(p2.x - p1.x, p2.y - p1.y);
|
||||
}
|
||||
private static boolean isZero(Point p) {
|
||||
return p.x == 0 && p.y == 0;
|
||||
}
|
||||
|
||||
private static boolean isZero(Point p) {
|
||||
return p.x == 0 && p.y == 0;
|
||||
}
|
||||
private static void ComputeQuadTangents(SegmentData data, Point start, Point control, Point end) {
|
||||
data.start_tangent = subtract(control, start);
|
||||
data.end_tangent = subtract(end, control);
|
||||
if (isZero(data.start_tangent)) data.start_tangent = data.end_tangent;
|
||||
else if (isZero(data.end_tangent)) data.end_tangent = data.start_tangent;
|
||||
}
|
||||
|
||||
private static void ComputeQuadTangents(SegmentData data,
|
||||
Point start,
|
||||
Point control,
|
||||
Point end) {
|
||||
data.start_tangent = subtract(control, start);
|
||||
data.end_tangent = subtract(end, control);
|
||||
if (isZero(data.start_tangent))
|
||||
data.start_tangent = data.end_tangent;
|
||||
else if (isZero(data.end_tangent))
|
||||
data.end_tangent = data.start_tangent;
|
||||
private static SegmentData ExtractPathElementFeatures(PathElement element) {
|
||||
SegmentData data = new SegmentData();
|
||||
Point[] points = element.points;
|
||||
switch (element.type) {
|
||||
case kCGPathElementAddCurveToPoint:
|
||||
data.position = points[2];
|
||||
data.start_tangent = subtract(points[0], origin_);
|
||||
data.end_tangent = subtract(points[2], points[1]);
|
||||
if (isZero(data.start_tangent)) ComputeQuadTangents(data, points[0], points[1], points[2]);
|
||||
else if (isZero(data.end_tangent)) ComputeQuadTangents(data, origin_, points[0], points[1]);
|
||||
break;
|
||||
case kCGPathElementAddQuadCurveToPoint:
|
||||
data.position = points[1];
|
||||
ComputeQuadTangents(data, origin_, points[0], points[1]);
|
||||
break;
|
||||
case kCGPathElementMoveToPoint:
|
||||
case kCGPathElementAddLineToPoint:
|
||||
data.position = points[0];
|
||||
data.start_tangent = subtract(data.position, origin_);
|
||||
data.end_tangent = subtract(data.position, origin_);
|
||||
break;
|
||||
case kCGPathElementCloseSubpath:
|
||||
data.position = subpath_start_;
|
||||
data.start_tangent = subtract(data.position, origin_);
|
||||
data.end_tangent = subtract(data.position, origin_);
|
||||
break;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private static SegmentData ExtractPathElementFeatures(PathElement element) {
|
||||
SegmentData data = new SegmentData();
|
||||
Point[] points = element.points;
|
||||
switch (element.type) {
|
||||
case kCGPathElementAddCurveToPoint:
|
||||
data.position = points[2];
|
||||
data.start_tangent = subtract(points[0], origin_);
|
||||
data.end_tangent = subtract(points[2], points[1]);
|
||||
if (isZero(data.start_tangent))
|
||||
ComputeQuadTangents(data, points[0], points[1], points[2]);
|
||||
else if (isZero(data.end_tangent))
|
||||
ComputeQuadTangents(data, origin_, points[0], points[1]);
|
||||
break;
|
||||
case kCGPathElementAddQuadCurveToPoint:
|
||||
data.position = points[1];
|
||||
ComputeQuadTangents(data, origin_, points[0], points[1]);
|
||||
break;
|
||||
case kCGPathElementMoveToPoint:
|
||||
case kCGPathElementAddLineToPoint:
|
||||
data.position = points[0];
|
||||
data.start_tangent = subtract(data.position, origin_);
|
||||
data.end_tangent = subtract(data.position, origin_);
|
||||
break;
|
||||
case kCGPathElementCloseSubpath:
|
||||
data.position = subpath_start_;
|
||||
data.start_tangent = subtract(data.position, origin_);
|
||||
data.end_tangent = subtract(data.position, origin_);
|
||||
break;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private static void UpdateFromPathElement(PathElement element) {
|
||||
SegmentData segment_data = ExtractPathElementFeatures(element);
|
||||
// First update the outgoing slope for the previous element.
|
||||
out_slope_ = segment_data.start_tangent;
|
||||
// Record the marker for the previous element.
|
||||
if (element_index_ > 0) {
|
||||
RNSVGMarkerType marker_type =
|
||||
element_index_ == 1 ? RNSVGMarkerType.kStartMarker : RNSVGMarkerType.kMidMarker;
|
||||
double angle = CurrentAngle(marker_type);
|
||||
positions_.add(new RNSVGMarkerPosition(marker_type, origin_, angle));
|
||||
}
|
||||
// Update the incoming slope for this marker position.
|
||||
in_slope_ = segment_data.end_tangent;
|
||||
// Update marker position.
|
||||
origin_ = segment_data.position;
|
||||
// If this is a 'move to' segment, save the point for use with 'close'.
|
||||
if (element.type == ElementType.kCGPathElementMoveToPoint)
|
||||
subpath_start_ = element.points[0];
|
||||
else if (element.type == ElementType.kCGPathElementCloseSubpath)
|
||||
subpath_start_ = new Point(0, 0);
|
||||
++element_index_;
|
||||
private static void UpdateFromPathElement(PathElement element) {
|
||||
SegmentData segment_data = ExtractPathElementFeatures(element);
|
||||
// First update the outgoing slope for the previous element.
|
||||
out_slope_ = segment_data.start_tangent;
|
||||
// Record the marker for the previous element.
|
||||
if (element_index_ > 0) {
|
||||
RNSVGMarkerType marker_type =
|
||||
element_index_ == 1 ? RNSVGMarkerType.kStartMarker : RNSVGMarkerType.kMidMarker;
|
||||
double angle = CurrentAngle(marker_type);
|
||||
positions_.add(new RNSVGMarkerPosition(marker_type, origin_, angle));
|
||||
}
|
||||
// Update the incoming slope for this marker position.
|
||||
in_slope_ = segment_data.end_tangent;
|
||||
// Update marker position.
|
||||
origin_ = segment_data.position;
|
||||
// If this is a 'move to' segment, save the point for use with 'close'.
|
||||
if (element.type == ElementType.kCGPathElementMoveToPoint) subpath_start_ = element.points[0];
|
||||
else if (element.type == ElementType.kCGPathElementCloseSubpath)
|
||||
subpath_start_ = new Point(0, 0);
|
||||
++element_index_;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,16 +6,16 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import static com.facebook.react.common.StandardCharsets.UTF_8;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PathMeasure;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Region;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
@@ -23,251 +23,247 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static com.facebook.react.common.StandardCharsets.UTF_8;
|
||||
|
||||
class RNSVGRenderableManager extends ReactContextBaseJavaModule {
|
||||
RNSVGRenderableManager(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
RNSVGRenderableManager(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName() {
|
||||
return "RNSVGRenderableManager";
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public boolean isPointInFill(int tag, ReadableMap options) {
|
||||
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
|
||||
if (svg == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName() {
|
||||
return "RNSVGRenderableManager";
|
||||
float scale = svg.mScale;
|
||||
float x = (float) options.getDouble("x") * scale;
|
||||
float y = (float) options.getDouble("y") * scale;
|
||||
|
||||
int i = svg.hitTest(new float[] {x, y});
|
||||
return i != -1;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public boolean isPointInStroke(int tag, ReadableMap options) {
|
||||
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
|
||||
if (svg == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public boolean isPointInFill(int tag, ReadableMap options) {
|
||||
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
|
||||
if (svg == null) {
|
||||
return false;
|
||||
try {
|
||||
svg.getPath(null, null);
|
||||
} catch (NullPointerException e) {
|
||||
svg.invalidate();
|
||||
return false;
|
||||
}
|
||||
|
||||
svg.initBounds();
|
||||
|
||||
float scale = svg.mScale;
|
||||
int x = (int) (options.getDouble("x") * scale);
|
||||
int y = (int) (options.getDouble("y") * scale);
|
||||
|
||||
Region strokeRegion = svg.mStrokeRegion;
|
||||
return strokeRegion != null && strokeRegion.contains(x, y);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public float getTotalLength(int tag) {
|
||||
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
|
||||
if (svg == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Path path;
|
||||
|
||||
try {
|
||||
path = svg.getPath(null, null);
|
||||
} catch (NullPointerException e) {
|
||||
svg.invalidate();
|
||||
return -1;
|
||||
}
|
||||
|
||||
PathMeasure pm = new PathMeasure(path, false);
|
||||
return pm.getLength() / svg.mScale;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public WritableMap getPointAtLength(int tag, ReadableMap options) {
|
||||
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
|
||||
if (svg == null) {
|
||||
return Arguments.createMap();
|
||||
}
|
||||
|
||||
Path path;
|
||||
|
||||
try {
|
||||
path = svg.getPath(null, null);
|
||||
} catch (NullPointerException e) {
|
||||
svg.invalidate();
|
||||
return Arguments.createMap();
|
||||
}
|
||||
|
||||
PathMeasure pm = new PathMeasure(path, false);
|
||||
float length = (float) options.getDouble("length");
|
||||
float scale = svg.mScale;
|
||||
|
||||
float[] pos = new float[2];
|
||||
float[] tan = new float[2];
|
||||
float distance = Math.max(0, Math.min(length, pm.getLength()));
|
||||
pm.getPosTan(distance, pos, tan);
|
||||
|
||||
double angle = Math.atan2(tan[1], tan[0]);
|
||||
WritableMap result = Arguments.createMap();
|
||||
result.putDouble("x", pos[0] / scale);
|
||||
result.putDouble("y", pos[1] / scale);
|
||||
result.putDouble("angle", angle);
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public WritableMap getBBox(int tag, ReadableMap options) {
|
||||
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
|
||||
if (svg == null) {
|
||||
return Arguments.createMap();
|
||||
}
|
||||
|
||||
boolean fill = options.getBoolean("fill");
|
||||
boolean stroke = options.getBoolean("stroke");
|
||||
boolean markers = options.getBoolean("markers");
|
||||
boolean clipped = options.getBoolean("clipped");
|
||||
|
||||
try {
|
||||
svg.getPath(null, null);
|
||||
} catch (NullPointerException e) {
|
||||
svg.invalidate();
|
||||
return Arguments.createMap();
|
||||
}
|
||||
|
||||
float scale = svg.mScale;
|
||||
svg.initBounds();
|
||||
|
||||
RectF bounds = new RectF();
|
||||
RectF fillBounds = svg.mFillBounds;
|
||||
RectF strokeBounds = svg.mStrokeBounds;
|
||||
RectF markerBounds = svg.mMarkerBounds;
|
||||
RectF clipBounds = svg.mClipBounds;
|
||||
|
||||
if (fill && fillBounds != null) {
|
||||
bounds.union(fillBounds);
|
||||
}
|
||||
if (stroke && strokeBounds != null) {
|
||||
bounds.union(strokeBounds);
|
||||
}
|
||||
if (markers && markerBounds != null) {
|
||||
bounds.union(markerBounds);
|
||||
}
|
||||
if (clipped && clipBounds != null) {
|
||||
bounds.intersect(clipBounds);
|
||||
}
|
||||
|
||||
WritableMap result = Arguments.createMap();
|
||||
result.putDouble("x", bounds.left / scale);
|
||||
result.putDouble("y", bounds.top / scale);
|
||||
result.putDouble("width", bounds.width() / scale);
|
||||
result.putDouble("height", bounds.height() / scale);
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public WritableMap getCTM(int tag) {
|
||||
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
|
||||
if (svg == null) {
|
||||
return Arguments.createMap();
|
||||
}
|
||||
|
||||
float scale = svg.mScale;
|
||||
Matrix ctm = new Matrix(svg.mCTM);
|
||||
Matrix invViewBoxMatrix = svg.getSvgView().mInvViewBoxMatrix;
|
||||
ctm.preConcat(invViewBoxMatrix);
|
||||
|
||||
float[] values = new float[9];
|
||||
ctm.getValues(values);
|
||||
|
||||
WritableMap result = Arguments.createMap();
|
||||
result.putDouble("a", values[Matrix.MSCALE_X]);
|
||||
result.putDouble("b", values[Matrix.MSKEW_Y]);
|
||||
result.putDouble("c", values[Matrix.MSKEW_X]);
|
||||
result.putDouble("d", values[Matrix.MSCALE_Y]);
|
||||
result.putDouble("e", values[Matrix.MTRANS_X] / scale);
|
||||
result.putDouble("f", values[Matrix.MTRANS_Y] / scale);
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public WritableMap getScreenCTM(int tag) {
|
||||
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
|
||||
if (svg == null) {
|
||||
return Arguments.createMap();
|
||||
}
|
||||
|
||||
float[] values = new float[9];
|
||||
svg.mCTM.getValues(values);
|
||||
float scale = svg.mScale;
|
||||
|
||||
WritableMap result = Arguments.createMap();
|
||||
result.putDouble("a", values[Matrix.MSCALE_X]);
|
||||
result.putDouble("b", values[Matrix.MSKEW_Y]);
|
||||
result.putDouble("c", values[Matrix.MSKEW_X]);
|
||||
result.putDouble("d", values[Matrix.MSCALE_Y]);
|
||||
result.putDouble("e", values[Matrix.MTRANS_X] / scale);
|
||||
result.putDouble("f", values[Matrix.MTRANS_Y] / scale);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getRawResource(String name, Promise promise) {
|
||||
try {
|
||||
ReactApplicationContext context = getReactApplicationContext();
|
||||
Resources resources = context.getResources();
|
||||
String packageName = context.getPackageName();
|
||||
int id = resources.getIdentifier(name, "raw", packageName);
|
||||
InputStream stream = resources.openRawResource(id);
|
||||
try {
|
||||
InputStreamReader reader = new InputStreamReader(stream, UTF_8);
|
||||
char[] buffer = new char[DEFAULT_BUFFER_SIZE];
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int n;
|
||||
while ((n = reader.read(buffer, 0, DEFAULT_BUFFER_SIZE)) != EOF) {
|
||||
builder.append(buffer, 0, n);
|
||||
}
|
||||
|
||||
float scale = svg.mScale;
|
||||
float x = (float) options.getDouble("x") * scale;
|
||||
float y = (float) options.getDouble("y") * scale;
|
||||
|
||||
int i = svg.hitTest(new float[]{x, y});
|
||||
return i != -1;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public boolean isPointInStroke(int tag, ReadableMap options) {
|
||||
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
|
||||
if (svg == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String result = builder.toString();
|
||||
promise.resolve(result);
|
||||
} finally {
|
||||
try {
|
||||
svg.getPath(null, null);
|
||||
} catch (NullPointerException e) {
|
||||
svg.invalidate();
|
||||
return false;
|
||||
stream.close();
|
||||
} catch (IOException ioe) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
svg.initBounds();
|
||||
|
||||
float scale = svg.mScale;
|
||||
int x = (int) (options.getDouble("x") * scale);
|
||||
int y = (int) (options.getDouble("y") * scale);
|
||||
|
||||
Region strokeRegion = svg.mStrokeRegion;
|
||||
return strokeRegion != null && strokeRegion.contains(x, y);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
promise.reject(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public float getTotalLength(int tag) {
|
||||
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
|
||||
if (svg == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Path path;
|
||||
|
||||
try {
|
||||
path = svg.getPath(null, null);
|
||||
} catch (NullPointerException e) {
|
||||
svg.invalidate();
|
||||
return -1;
|
||||
}
|
||||
|
||||
PathMeasure pm = new PathMeasure(path, false);
|
||||
return pm.getLength() / svg.mScale;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public WritableMap getPointAtLength(int tag, ReadableMap options) {
|
||||
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
|
||||
if (svg == null) {
|
||||
return Arguments.createMap();
|
||||
}
|
||||
|
||||
Path path;
|
||||
|
||||
try {
|
||||
path = svg.getPath(null, null);
|
||||
} catch (NullPointerException e) {
|
||||
svg.invalidate();
|
||||
return Arguments.createMap();
|
||||
}
|
||||
|
||||
PathMeasure pm = new PathMeasure(path, false);
|
||||
float length = (float) options.getDouble("length");
|
||||
float scale = svg.mScale;
|
||||
|
||||
float[] pos = new float[2];
|
||||
float[] tan = new float[2];
|
||||
float distance = Math.max(0, Math.min(length, pm.getLength()));
|
||||
pm.getPosTan(distance, pos, tan);
|
||||
|
||||
double angle = Math.atan2(tan[1], tan[0]);
|
||||
WritableMap result = Arguments.createMap();
|
||||
result.putDouble("x", pos[0] / scale);
|
||||
result.putDouble("y", pos[1] / scale);
|
||||
result.putDouble("angle", angle);
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public WritableMap getBBox(int tag, ReadableMap options) {
|
||||
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
|
||||
if (svg == null) {
|
||||
return Arguments.createMap();
|
||||
}
|
||||
|
||||
boolean fill = options.getBoolean("fill");
|
||||
boolean stroke = options.getBoolean("stroke");
|
||||
boolean markers = options.getBoolean("markers");
|
||||
boolean clipped = options.getBoolean("clipped");
|
||||
|
||||
try {
|
||||
svg.getPath(null, null);
|
||||
} catch (NullPointerException e) {
|
||||
svg.invalidate();
|
||||
return Arguments.createMap();
|
||||
}
|
||||
|
||||
float scale = svg.mScale;
|
||||
svg.initBounds();
|
||||
|
||||
RectF bounds = new RectF();
|
||||
RectF fillBounds = svg.mFillBounds;
|
||||
RectF strokeBounds = svg.mStrokeBounds;
|
||||
RectF markerBounds = svg.mMarkerBounds;
|
||||
RectF clipBounds = svg.mClipBounds;
|
||||
|
||||
if (fill && fillBounds != null) {
|
||||
bounds.union(fillBounds);
|
||||
}
|
||||
if (stroke && strokeBounds != null) {
|
||||
bounds.union(strokeBounds);
|
||||
}
|
||||
if (markers && markerBounds != null) {
|
||||
bounds.union(markerBounds);
|
||||
}
|
||||
if (clipped && clipBounds != null) {
|
||||
bounds.intersect(clipBounds);
|
||||
}
|
||||
|
||||
WritableMap result = Arguments.createMap();
|
||||
result.putDouble("x", bounds.left / scale);
|
||||
result.putDouble("y", bounds.top / scale);
|
||||
result.putDouble("width", bounds.width() / scale);
|
||||
result.putDouble("height", bounds.height() / scale);
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public WritableMap getCTM(int tag) {
|
||||
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
|
||||
if (svg == null) {
|
||||
return Arguments.createMap();
|
||||
}
|
||||
|
||||
float scale = svg.mScale;
|
||||
Matrix ctm = new Matrix(svg.mCTM);
|
||||
Matrix invViewBoxMatrix = svg.getSvgView().mInvViewBoxMatrix;
|
||||
ctm.preConcat(invViewBoxMatrix);
|
||||
|
||||
float[] values = new float[9];
|
||||
ctm.getValues(values);
|
||||
|
||||
WritableMap result = Arguments.createMap();
|
||||
result.putDouble("a", values[Matrix.MSCALE_X]);
|
||||
result.putDouble("b", values[Matrix.MSKEW_Y]);
|
||||
result.putDouble("c", values[Matrix.MSKEW_X]);
|
||||
result.putDouble("d", values[Matrix.MSCALE_Y]);
|
||||
result.putDouble("e", values[Matrix.MTRANS_X] / scale);
|
||||
result.putDouble("f", values[Matrix.MTRANS_Y] / scale);
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public WritableMap getScreenCTM(int tag) {
|
||||
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
|
||||
if (svg == null) {
|
||||
return Arguments.createMap();
|
||||
}
|
||||
|
||||
float[] values = new float[9];
|
||||
svg.mCTM.getValues(values);
|
||||
float scale = svg.mScale;
|
||||
|
||||
WritableMap result = Arguments.createMap();
|
||||
result.putDouble("a", values[Matrix.MSCALE_X]);
|
||||
result.putDouble("b", values[Matrix.MSKEW_Y]);
|
||||
result.putDouble("c", values[Matrix.MSKEW_X]);
|
||||
result.putDouble("d", values[Matrix.MSCALE_Y]);
|
||||
result.putDouble("e", values[Matrix.MTRANS_X] / scale);
|
||||
result.putDouble("f", values[Matrix.MTRANS_Y] / scale);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getRawResource(String name, Promise promise) {
|
||||
try {
|
||||
ReactApplicationContext context = getReactApplicationContext();
|
||||
Resources resources = context.getResources();
|
||||
String packageName = context.getPackageName();
|
||||
int id = resources.getIdentifier(name, "raw", packageName);
|
||||
InputStream stream = resources.openRawResource(id);
|
||||
try {
|
||||
InputStreamReader reader = new InputStreamReader(stream, UTF_8);
|
||||
char[] buffer = new char[DEFAULT_BUFFER_SIZE];
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int n;
|
||||
while ((n = reader.read(buffer, 0, DEFAULT_BUFFER_SIZE)) != EOF) {
|
||||
builder.append(buffer, 0, n);
|
||||
}
|
||||
String result = builder.toString();
|
||||
promise.resolve(result);
|
||||
} finally {
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException ioe) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
promise.reject(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static final int EOF = -1;
|
||||
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
|
||||
private static final int EOF = -1;
|
||||
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
|
||||
}
|
||||
|
||||
@@ -6,163 +6,161 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Matrix;
|
||||
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class RadialGradientView extends DefinitionView {
|
||||
private SVGLength mFx;
|
||||
private SVGLength mFy;
|
||||
private SVGLength mRx;
|
||||
private SVGLength mRy;
|
||||
private SVGLength mCx;
|
||||
private SVGLength mCy;
|
||||
private ReadableArray mGradient;
|
||||
private Brush.BrushUnits mGradientUnits;
|
||||
private SVGLength mFx;
|
||||
private SVGLength mFy;
|
||||
private SVGLength mRx;
|
||||
private SVGLength mRy;
|
||||
private SVGLength mCx;
|
||||
private SVGLength mCy;
|
||||
private ReadableArray mGradient;
|
||||
private Brush.BrushUnits mGradientUnits;
|
||||
|
||||
private static final float[] sRawMatrix = new float[]{
|
||||
private static final float[] sRawMatrix =
|
||||
new float[] {
|
||||
1, 0, 0,
|
||||
0, 1, 0,
|
||||
0, 0, 1
|
||||
};
|
||||
private Matrix mMatrix = null;
|
||||
};
|
||||
private Matrix mMatrix = null;
|
||||
|
||||
public RadialGradientView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
public RadialGradientView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@ReactProp(name = "fx")
|
||||
public void setFx(Dynamic fx) {
|
||||
mFx = SVGLength.from(fx);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "fx")
|
||||
public void setFx(Dynamic fx) {
|
||||
mFx = SVGLength.from(fx);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setFx(String fx) {
|
||||
mFx = SVGLength.from(fx);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "fy")
|
||||
public void setFy(Dynamic fy) {
|
||||
mFy = SVGLength.from(fy);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "fy")
|
||||
public void setFy(Dynamic fy) {
|
||||
mFy = SVGLength.from(fy);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setFy(String fy) {
|
||||
mFy = SVGLength.from(fy);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "rx")
|
||||
public void setRx(Dynamic rx) {
|
||||
mRx = SVGLength.from(rx);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "rx")
|
||||
public void setRx(Dynamic rx) {
|
||||
mRx = SVGLength.from(rx);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setRx(String rx) {
|
||||
mRx = SVGLength.from(rx);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "ry")
|
||||
public void setRy(Dynamic ry) {
|
||||
mRy = SVGLength.from(ry);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "ry")
|
||||
public void setRy(Dynamic ry) {
|
||||
mRy = SVGLength.from(ry);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setRy(String ry) {
|
||||
mRy = SVGLength.from(ry);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "cx")
|
||||
public void setCx(Dynamic cx) {
|
||||
mCx = SVGLength.from(cx);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "cx")
|
||||
public void setCx(Dynamic cx) {
|
||||
mCx = SVGLength.from(cx);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setCx(String cx) {
|
||||
mCx = SVGLength.from(cx);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "cy")
|
||||
public void setCy(Dynamic cy) {
|
||||
mCy = SVGLength.from(cy);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "cy")
|
||||
public void setCy(Dynamic cy) {
|
||||
mCy = SVGLength.from(cy);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setCy(String cy) {
|
||||
mCy = SVGLength.from(cy);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "gradient")
|
||||
public void setGradient(ReadableArray gradient) {
|
||||
mGradient = gradient;
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "gradient")
|
||||
public void setGradient(ReadableArray gradient) {
|
||||
mGradient = gradient;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "gradientUnits")
|
||||
public void setGradientUnits(int gradientUnits) {
|
||||
switch (gradientUnits) {
|
||||
case 0:
|
||||
mGradientUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
|
||||
break;
|
||||
case 1:
|
||||
mGradientUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
|
||||
break;
|
||||
@ReactProp(name = "gradientUnits")
|
||||
public void setGradientUnits(int gradientUnits) {
|
||||
switch (gradientUnits) {
|
||||
case 0:
|
||||
mGradientUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
|
||||
break;
|
||||
case 1:
|
||||
mGradientUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
|
||||
break;
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "gradientTransform")
|
||||
public void setGradientTransform(@Nullable ReadableArray matrixArray) {
|
||||
if (matrixArray != null) {
|
||||
int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
|
||||
if (matrixSize == 6) {
|
||||
if (mMatrix == null) {
|
||||
mMatrix = new Matrix();
|
||||
}
|
||||
invalidate();
|
||||
mMatrix.setValues(sRawMatrix);
|
||||
} else if (matrixSize != -1) {
|
||||
FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
|
||||
}
|
||||
} else {
|
||||
mMatrix = null;
|
||||
}
|
||||
|
||||
@ReactProp(name = "gradientTransform")
|
||||
public void setGradientTransform(@Nullable ReadableArray matrixArray) {
|
||||
if (matrixArray != null) {
|
||||
int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
|
||||
if (matrixSize == 6) {
|
||||
if (mMatrix == null) {
|
||||
mMatrix = new Matrix();
|
||||
}
|
||||
mMatrix.setValues(sRawMatrix);
|
||||
} else if (matrixSize != -1) {
|
||||
FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
|
||||
}
|
||||
} else {
|
||||
mMatrix = null;
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
void saveDefinition() {
|
||||
if (mName != null) {
|
||||
SVGLength[] points = new SVGLength[]{mFx,mFy,mRx,mRy,mCx,mCy};
|
||||
Brush brush = new Brush(Brush.BrushType.RADIAL_GRADIENT, points, mGradientUnits);
|
||||
brush.setGradientColors(mGradient);
|
||||
if (mMatrix != null) {
|
||||
brush.setGradientTransform(mMatrix);
|
||||
}
|
||||
|
||||
SvgView svg = getSvgView();
|
||||
if (mGradientUnits == Brush.BrushUnits.USER_SPACE_ON_USE) {
|
||||
brush.setUserSpaceBoundingBox(svg.getCanvasBounds());
|
||||
}
|
||||
|
||||
svg.defineBrush(brush, mName);
|
||||
}
|
||||
@Override
|
||||
void saveDefinition() {
|
||||
if (mName != null) {
|
||||
SVGLength[] points = new SVGLength[] {mFx, mFy, mRx, mRy, mCx, mCy};
|
||||
Brush brush = new Brush(Brush.BrushType.RADIAL_GRADIENT, points, mGradientUnits);
|
||||
brush.setGradientColors(mGradient);
|
||||
if (mMatrix != null) {
|
||||
brush.setGradientTransform(mMatrix);
|
||||
}
|
||||
|
||||
SvgView svg = getSvgView();
|
||||
if (mGradientUnits == Brush.BrushUnits.USER_SPACE_ON_USE) {
|
||||
brush.setUserSpaceBoundingBox(svg.getCanvasBounds());
|
||||
}
|
||||
|
||||
svg.defineBrush(brush, mName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
@@ -15,128 +14,139 @@ import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Build;
|
||||
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class RectView extends RenderableView {
|
||||
private SVGLength mX;
|
||||
private SVGLength mY;
|
||||
private SVGLength mW;
|
||||
private SVGLength mH;
|
||||
private SVGLength mRx;
|
||||
private SVGLength mRy;
|
||||
private SVGLength mX;
|
||||
private SVGLength mY;
|
||||
private SVGLength mW;
|
||||
private SVGLength mH;
|
||||
private SVGLength mRx;
|
||||
private SVGLength mRy;
|
||||
|
||||
public RectView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
public RectView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@ReactProp(name = "x")
|
||||
public void setX(Dynamic x) {
|
||||
mX = SVGLength.from(x);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "x")
|
||||
public void setX(Dynamic x) {
|
||||
mX = SVGLength.from(x);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setX(String x) {
|
||||
mX = SVGLength.from(x);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "y")
|
||||
public void setY(Dynamic y) {
|
||||
mY = SVGLength.from(y);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "y")
|
||||
public void setY(Dynamic y) {
|
||||
mY = SVGLength.from(y);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setY(String y) {
|
||||
mY = SVGLength.from(y);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "width")
|
||||
public void setWidth(Dynamic width) {
|
||||
mW = SVGLength.from(width);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "width")
|
||||
public void setWidth(Dynamic width) {
|
||||
mW = SVGLength.from(width);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setWidth(String width) {
|
||||
mW = SVGLength.from(width);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "height")
|
||||
public void setHeight(Dynamic height) {
|
||||
mH = SVGLength.from(height);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "height")
|
||||
public void setHeight(Dynamic height) {
|
||||
mH = SVGLength.from(height);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setHeight(String height) {
|
||||
mH = SVGLength.from(height);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "rx")
|
||||
public void setRx(Dynamic rx) {
|
||||
mRx = SVGLength.from(rx);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "rx")
|
||||
public void setRx(Dynamic rx) {
|
||||
mRx = SVGLength.from(rx);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setRx(String rx) {
|
||||
mRx = SVGLength.from(rx);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "ry")
|
||||
public void setRy(Dynamic ry) {
|
||||
mRy = SVGLength.from(ry);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "ry")
|
||||
public void setRy(Dynamic ry) {
|
||||
mRy = SVGLength.from(ry);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setRy(String ry) {
|
||||
mRy = SVGLength.from(ry);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
Path getPath(Canvas canvas, Paint paint) {
|
||||
Path path = new Path();
|
||||
double x = relativeOnWidth(mX);
|
||||
double y = relativeOnHeight(mY);
|
||||
double w = relativeOnWidth(mW);
|
||||
double h = relativeOnHeight(mH);
|
||||
@Override
|
||||
Path getPath(Canvas canvas, Paint paint) {
|
||||
Path path = new Path();
|
||||
double x = relativeOnWidth(mX);
|
||||
double y = relativeOnHeight(mY);
|
||||
double w = relativeOnWidth(mW);
|
||||
double h = relativeOnHeight(mH);
|
||||
|
||||
if (mRx != null || mRy != null) {
|
||||
double rx = 0d;
|
||||
double ry = 0d;
|
||||
if (mRx == null) {
|
||||
ry = relativeOnHeight(mRy);
|
||||
rx = ry;
|
||||
} else if (mRy == null) {
|
||||
rx = relativeOnWidth(mRx);
|
||||
ry = rx;
|
||||
} else {
|
||||
rx = relativeOnWidth(mRx);
|
||||
ry = relativeOnHeight(mRy);
|
||||
}
|
||||
if (mRx != null || mRy != null) {
|
||||
double rx = 0d;
|
||||
double ry = 0d;
|
||||
if (mRx == null) {
|
||||
ry = relativeOnHeight(mRy);
|
||||
rx = ry;
|
||||
} else if (mRy == null) {
|
||||
rx = relativeOnWidth(mRx);
|
||||
ry = rx;
|
||||
} else {
|
||||
rx = relativeOnWidth(mRx);
|
||||
ry = relativeOnHeight(mRy);
|
||||
}
|
||||
|
||||
if (rx > w / 2) {
|
||||
rx = w / 2;
|
||||
}
|
||||
if (rx > w / 2) {
|
||||
rx = w / 2;
|
||||
}
|
||||
|
||||
if (ry > h / 2) {
|
||||
ry = h / 2;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
path.addRoundRect((float) x, (float) y, (float) (x + w), (float) (y + h), (float) rx, (float) ry, Path.Direction.CW);
|
||||
} else {
|
||||
path.addRoundRect(new RectF((float) x, (float) y, (float) (x + w), (float) (y + h)), (float) rx, (float) ry, Path.Direction.CW);
|
||||
}
|
||||
} else {
|
||||
path.addRect((float) x, (float) y, (float) (x + w), (float) (y + h), Path.Direction.CW);
|
||||
path.close(); // Ensure isSimplePath = false such that rect doesn't become represented using integers
|
||||
}
|
||||
return path;
|
||||
if (ry > h / 2) {
|
||||
ry = h / 2;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
path.addRoundRect(
|
||||
(float) x,
|
||||
(float) y,
|
||||
(float) (x + w),
|
||||
(float) (y + h),
|
||||
(float) rx,
|
||||
(float) ry,
|
||||
Path.Direction.CW);
|
||||
} else {
|
||||
path.addRoundRect(
|
||||
new RectF((float) x, (float) y, (float) (x + w), (float) (y + h)),
|
||||
(float) rx,
|
||||
(float) ry,
|
||||
Path.Direction.CW);
|
||||
}
|
||||
} else {
|
||||
path.addRect((float) x, (float) y, (float) (x + w), (float) (y + h), Path.Direction.CW);
|
||||
path.close(); // Ensure isSimplePath = false such that rect doesn't become represented using
|
||||
// integers
|
||||
}
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,6 @@ package com.horcrux.svg;
|
||||
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
class SVGLength {
|
||||
@@ -121,26 +120,29 @@ class SVGLength {
|
||||
|
||||
static ArrayList<SVGLength> arrayFrom(Dynamic dynamic) {
|
||||
switch (dynamic.getType()) {
|
||||
case Number: {
|
||||
ArrayList<SVGLength> list = new ArrayList<>(1);
|
||||
list.add(new SVGLength(dynamic.asDouble()));
|
||||
return list;
|
||||
}
|
||||
case Array: {
|
||||
ReadableArray arr = dynamic.asArray();
|
||||
int size = arr.size();
|
||||
ArrayList<SVGLength> list = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
Dynamic val = arr.getDynamic(i);
|
||||
list.add(from(val));
|
||||
case Number:
|
||||
{
|
||||
ArrayList<SVGLength> list = new ArrayList<>(1);
|
||||
list.add(new SVGLength(dynamic.asDouble()));
|
||||
return list;
|
||||
}
|
||||
case Array:
|
||||
{
|
||||
ReadableArray arr = dynamic.asArray();
|
||||
int size = arr.size();
|
||||
ArrayList<SVGLength> list = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
Dynamic val = arr.getDynamic(i);
|
||||
list.add(from(val));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
case String:
|
||||
{
|
||||
ArrayList<SVGLength> list = new ArrayList<>(1);
|
||||
list.add(new SVGLength(dynamic.asString()));
|
||||
return list;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
case String: {
|
||||
ArrayList<SVGLength> list = new ArrayList<>(1);
|
||||
list.add(new SVGLength(dynamic.asString()));
|
||||
return list;
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -6,71 +6,66 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import static com.horcrux.svg.RenderableViewManager.*;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.bridge.JavaScriptModule;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static com.horcrux.svg.RenderableViewManager.*;
|
||||
|
||||
public class SvgPackage implements ReactPackage {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(@Nonnull ReactApplicationContext reactContext) {
|
||||
return Arrays.<ViewManager>asList(
|
||||
new GroupViewManager(),
|
||||
new PathViewManager(),
|
||||
new CircleViewManager(),
|
||||
new EllipseViewManager(),
|
||||
new LineViewManager(),
|
||||
new RectViewManager(),
|
||||
new TextViewManager(),
|
||||
new TSpanViewManager(),
|
||||
new TextPathViewManager(),
|
||||
new ImageViewManager(),
|
||||
new ClipPathViewManager(),
|
||||
new DefsViewManager(),
|
||||
new UseViewManager(),
|
||||
new SymbolManager(),
|
||||
new LinearGradientManager(),
|
||||
new RadialGradientManager(),
|
||||
new PatternManager(),
|
||||
new MaskManager(),
|
||||
new ForeignObjectManager(),
|
||||
new MarkerManager(),
|
||||
new SvgViewManager());
|
||||
}
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(@Nonnull ReactApplicationContext reactContext) {
|
||||
return Arrays.<ViewManager>asList(
|
||||
new GroupViewManager(),
|
||||
new PathViewManager(),
|
||||
new CircleViewManager(),
|
||||
new EllipseViewManager(),
|
||||
new LineViewManager(),
|
||||
new RectViewManager(),
|
||||
new TextViewManager(),
|
||||
new TSpanViewManager(),
|
||||
new TextPathViewManager(),
|
||||
new ImageViewManager(),
|
||||
new ClipPathViewManager(),
|
||||
new DefsViewManager(),
|
||||
new UseViewManager(),
|
||||
new SymbolManager(),
|
||||
new LinearGradientManager(),
|
||||
new RadialGradientManager(),
|
||||
new PatternManager(),
|
||||
new MaskManager(),
|
||||
new ForeignObjectManager(),
|
||||
new MarkerManager(),
|
||||
new SvgViewManager());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(@Nonnull ReactApplicationContext reactContext) {
|
||||
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||
// For Fabric, we load c++ native library here, this triggers svg's Fabric
|
||||
// component registration which is necessary in order to avoid asking users
|
||||
// to manually add init calls in their application code.
|
||||
// This should no longer be needed if RN's autolink mechanism has Fabric support
|
||||
SoLoader.loadLibrary("rnsvg_modules");
|
||||
}
|
||||
return Arrays.<NativeModule>asList(
|
||||
new SvgViewModule(reactContext),
|
||||
new RNSVGRenderableManager(reactContext)
|
||||
);
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(@Nonnull ReactApplicationContext reactContext) {
|
||||
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||
// For Fabric, we load c++ native library here, this triggers svg's Fabric
|
||||
// component registration which is necessary in order to avoid asking users
|
||||
// to manually add init calls in their application code.
|
||||
// This should no longer be needed if RN's autolink mechanism has Fabric support
|
||||
SoLoader.loadLibrary("rnsvg_modules");
|
||||
}
|
||||
return Arrays.<NativeModule>asList(
|
||||
new SvgViewModule(reactContext), new RNSVGRenderableManager(reactContext));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public List<Class<? extends JavaScriptModule>> createJSModules() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@SuppressWarnings("unused")
|
||||
public List<Class<? extends JavaScriptModule>> createJSModules() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
@@ -20,9 +19,6 @@ import android.graphics.Typeface;
|
||||
import android.util.Base64;
|
||||
import android.view.View;
|
||||
import android.view.ViewParent;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.facebook.react.bridge.ColorPropConverter;
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
@@ -30,207 +26,203 @@ import com.facebook.react.uimanager.DisplayMetricsHolder;
|
||||
import com.facebook.react.uimanager.ReactCompoundView;
|
||||
import com.facebook.react.uimanager.ReactCompoundViewGroup;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
import com.facebook.react.views.view.ReactViewGroup;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Custom {@link View} implementation that draws an RNSVGSvg React view and its children.
|
||||
*/
|
||||
/** Custom {@link View} implementation that draws an RNSVGSvg React view and its children. */
|
||||
@SuppressLint("ViewConstructor")
|
||||
public class SvgView extends FabricEnabledViewGroup implements ReactCompoundView, ReactCompoundViewGroup {
|
||||
public class SvgView extends FabricEnabledViewGroup
|
||||
implements ReactCompoundView, ReactCompoundViewGroup {
|
||||
|
||||
@Override
|
||||
public boolean interceptsTouchEvent(float touchX, float touchY) {
|
||||
return true;
|
||||
@Override
|
||||
public boolean interceptsTouchEvent(float touchX, float touchY) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public enum Events {
|
||||
EVENT_DATA_URL("onDataURL");
|
||||
|
||||
private final String mName;
|
||||
|
||||
Events(final String name) {
|
||||
mName = name;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public enum Events {
|
||||
EVENT_DATA_URL("onDataURL");
|
||||
|
||||
private final String mName;
|
||||
|
||||
Events(final String name) {
|
||||
mName = name;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public String toString() {
|
||||
return mName;
|
||||
}
|
||||
@Nonnull
|
||||
public String toString() {
|
||||
return mName;
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable Bitmap mBitmap;
|
||||
private @Nullable Bitmap mBitmap;
|
||||
|
||||
public SvgView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
mScale = DisplayMetricsHolder.getScreenDisplayMetrics().density;
|
||||
// for some reason on Fabric the `onDraw` won't be called without it
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
public SvgView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
mScale = DisplayMetricsHolder.getScreenDisplayMetrics().density;
|
||||
// for some reason on Fabric the `onDraw` won't be called without it
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setId(int id) {
|
||||
super.setId(id);
|
||||
SvgViewManager.setSvgView(id, this);
|
||||
}
|
||||
@Override
|
||||
public void setId(int id) {
|
||||
super.setId(id);
|
||||
SvgViewManager.setSvgView(id, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
super.invalidate();
|
||||
ViewParent parent = getParent();
|
||||
if (parent instanceof VirtualView) {
|
||||
if (!mRendered) {
|
||||
return;
|
||||
}
|
||||
mRendered = false;
|
||||
((VirtualView) parent).getSvgView().invalidate();
|
||||
return;
|
||||
}
|
||||
if (mBitmap != null) {
|
||||
mBitmap.recycle();
|
||||
}
|
||||
mBitmap = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
if (getParent() instanceof VirtualView) {
|
||||
return;
|
||||
}
|
||||
super.onDraw(canvas);
|
||||
if (mBitmap == null) {
|
||||
mBitmap = drawOutput();
|
||||
}
|
||||
if (mBitmap != null) {
|
||||
canvas.drawBitmap(mBitmap, 0, 0, null);
|
||||
if (toDataUrlTask != null) {
|
||||
toDataUrlTask.run();
|
||||
toDataUrlTask = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Runnable toDataUrlTask = null;
|
||||
|
||||
void setToDataUrlTask(Runnable task) {
|
||||
toDataUrlTask = task;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int reactTagForTouch(float touchX, float touchY) {
|
||||
return hitTest(touchX, touchY);
|
||||
}
|
||||
|
||||
private boolean mResponsible = false;
|
||||
|
||||
private final Map<String, VirtualView> mDefinedClipPaths = new HashMap<>();
|
||||
private final Map<String, VirtualView> mDefinedTemplates = new HashMap<>();
|
||||
private final Map<String, VirtualView> mDefinedMarkers = new HashMap<>();
|
||||
private final Map<String, VirtualView> mDefinedMasks = new HashMap<>();
|
||||
private final Map<String, Brush> mDefinedBrushes = new HashMap<>();
|
||||
private Canvas mCanvas;
|
||||
private final float mScale;
|
||||
|
||||
private float mMinX;
|
||||
private float mMinY;
|
||||
private float mVbWidth;
|
||||
private float mVbHeight;
|
||||
private SVGLength mbbWidth;
|
||||
private SVGLength mbbHeight;
|
||||
private String mAlign;
|
||||
private int mMeetOrSlice;
|
||||
final Matrix mInvViewBoxMatrix = new Matrix();
|
||||
private boolean mInvertible = true;
|
||||
private boolean mRendered = false;
|
||||
int mTintColor = 0;
|
||||
|
||||
boolean notRendered() {
|
||||
return !mRendered;
|
||||
}
|
||||
|
||||
private void clearChildCache() {
|
||||
if (!mRendered) {
|
||||
return;
|
||||
}
|
||||
mRendered = false;
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View node = getChildAt(i);
|
||||
if (node instanceof VirtualView) {
|
||||
VirtualView n = ((VirtualView)node);
|
||||
n.clearChildCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactProp(name = "tintColor")
|
||||
public void setTintColor(@Nullable Dynamic tintColor) {
|
||||
switch (tintColor.getType()) {
|
||||
case Map:
|
||||
mTintColor = ColorPropConverter.getColor(tintColor.asMap(), getContext());
|
||||
break;
|
||||
case Number:
|
||||
mTintColor = tintColor.asInt();
|
||||
break;
|
||||
default:
|
||||
mTintColor = 0;
|
||||
@Override
|
||||
public void invalidate() {
|
||||
super.invalidate();
|
||||
ViewParent parent = getParent();
|
||||
if (parent instanceof VirtualView) {
|
||||
if (!mRendered) {
|
||||
return;
|
||||
}
|
||||
invalidate();
|
||||
clearChildCache();
|
||||
mRendered = false;
|
||||
((VirtualView) parent).getSvgView().invalidate();
|
||||
return;
|
||||
}
|
||||
if (mBitmap != null) {
|
||||
mBitmap.recycle();
|
||||
}
|
||||
mBitmap = null;
|
||||
}
|
||||
|
||||
public void setTintColor(@Nullable Integer tintColor) {
|
||||
mTintColor = tintColor;
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
if (getParent() instanceof VirtualView) {
|
||||
return;
|
||||
}
|
||||
super.onDraw(canvas);
|
||||
if (mBitmap == null) {
|
||||
mBitmap = drawOutput();
|
||||
}
|
||||
if (mBitmap != null) {
|
||||
canvas.drawBitmap(mBitmap, 0, 0, null);
|
||||
if (toDataUrlTask != null) {
|
||||
toDataUrlTask.run();
|
||||
toDataUrlTask = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Runnable toDataUrlTask = null;
|
||||
|
||||
void setToDataUrlTask(Runnable task) {
|
||||
toDataUrlTask = task;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int reactTagForTouch(float touchX, float touchY) {
|
||||
return hitTest(touchX, touchY);
|
||||
}
|
||||
|
||||
private boolean mResponsible = false;
|
||||
|
||||
private final Map<String, VirtualView> mDefinedClipPaths = new HashMap<>();
|
||||
private final Map<String, VirtualView> mDefinedTemplates = new HashMap<>();
|
||||
private final Map<String, VirtualView> mDefinedMarkers = new HashMap<>();
|
||||
private final Map<String, VirtualView> mDefinedMasks = new HashMap<>();
|
||||
private final Map<String, Brush> mDefinedBrushes = new HashMap<>();
|
||||
private Canvas mCanvas;
|
||||
private final float mScale;
|
||||
|
||||
private float mMinX;
|
||||
private float mMinY;
|
||||
private float mVbWidth;
|
||||
private float mVbHeight;
|
||||
private SVGLength mbbWidth;
|
||||
private SVGLength mbbHeight;
|
||||
private String mAlign;
|
||||
private int mMeetOrSlice;
|
||||
final Matrix mInvViewBoxMatrix = new Matrix();
|
||||
private boolean mInvertible = true;
|
||||
private boolean mRendered = false;
|
||||
int mTintColor = 0;
|
||||
|
||||
boolean notRendered() {
|
||||
return !mRendered;
|
||||
}
|
||||
|
||||
private void clearChildCache() {
|
||||
if (!mRendered) {
|
||||
return;
|
||||
}
|
||||
mRendered = false;
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View node = getChildAt(i);
|
||||
if (node instanceof VirtualView) {
|
||||
VirtualView n = ((VirtualView) node);
|
||||
n.clearChildCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactProp(name = "tintColor")
|
||||
public void setTintColor(@Nullable Dynamic tintColor) {
|
||||
switch (tintColor.getType()) {
|
||||
case Map:
|
||||
mTintColor = ColorPropConverter.getColor(tintColor.asMap(), getContext());
|
||||
break;
|
||||
case Number:
|
||||
mTintColor = tintColor.asInt();
|
||||
break;
|
||||
default:
|
||||
mTintColor = 0;
|
||||
}
|
||||
invalidate();
|
||||
clearChildCache();
|
||||
}
|
||||
|
||||
@ReactProp(name = "minX")
|
||||
public void setMinX(float minX) {
|
||||
mMinX = minX;
|
||||
invalidate();
|
||||
clearChildCache();
|
||||
}
|
||||
public void setTintColor(@Nullable Integer tintColor) {
|
||||
mTintColor = tintColor;
|
||||
invalidate();
|
||||
clearChildCache();
|
||||
}
|
||||
|
||||
@ReactProp(name = "minY")
|
||||
public void setMinY(float minY) {
|
||||
mMinY = minY;
|
||||
invalidate();
|
||||
clearChildCache();
|
||||
}
|
||||
@ReactProp(name = "minX")
|
||||
public void setMinX(float minX) {
|
||||
mMinX = minX;
|
||||
invalidate();
|
||||
clearChildCache();
|
||||
}
|
||||
|
||||
@ReactProp(name = "vbWidth")
|
||||
public void setVbWidth(float vbWidth) {
|
||||
mVbWidth = vbWidth;
|
||||
invalidate();
|
||||
clearChildCache();
|
||||
}
|
||||
@ReactProp(name = "minY")
|
||||
public void setMinY(float minY) {
|
||||
mMinY = minY;
|
||||
invalidate();
|
||||
clearChildCache();
|
||||
}
|
||||
|
||||
@ReactProp(name = "vbHeight")
|
||||
public void setVbHeight(float vbHeight) {
|
||||
mVbHeight = vbHeight;
|
||||
invalidate();
|
||||
clearChildCache();
|
||||
}
|
||||
@ReactProp(name = "vbWidth")
|
||||
public void setVbWidth(float vbWidth) {
|
||||
mVbWidth = vbWidth;
|
||||
invalidate();
|
||||
clearChildCache();
|
||||
}
|
||||
|
||||
@ReactProp(name = "bbWidth")
|
||||
public void setBbWidth(Dynamic bbWidth) {
|
||||
mbbWidth = SVGLength.from(bbWidth);
|
||||
invalidate();
|
||||
clearChildCache();
|
||||
}
|
||||
@ReactProp(name = "vbHeight")
|
||||
public void setVbHeight(float vbHeight) {
|
||||
mVbHeight = vbHeight;
|
||||
invalidate();
|
||||
clearChildCache();
|
||||
}
|
||||
|
||||
@ReactProp(name = "bbWidth")
|
||||
public void setBbWidth(Dynamic bbWidth) {
|
||||
mbbWidth = SVGLength.from(bbWidth);
|
||||
invalidate();
|
||||
clearChildCache();
|
||||
}
|
||||
|
||||
public void setBbWidth(String bbWidth) {
|
||||
mbbWidth = SVGLength.from(bbWidth);
|
||||
@@ -238,12 +230,12 @@ public class SvgView extends FabricEnabledViewGroup implements ReactCompoundView
|
||||
clearChildCache();
|
||||
}
|
||||
|
||||
@ReactProp(name = "bbHeight")
|
||||
public void setBbHeight(Dynamic bbHeight) {
|
||||
mbbHeight = SVGLength.from(bbHeight);
|
||||
invalidate();
|
||||
clearChildCache();
|
||||
}
|
||||
@ReactProp(name = "bbHeight")
|
||||
public void setBbHeight(Dynamic bbHeight) {
|
||||
mbbHeight = SVGLength.from(bbHeight);
|
||||
invalidate();
|
||||
clearChildCache();
|
||||
}
|
||||
|
||||
public void setBbHeight(String bbHeight) {
|
||||
mbbHeight = SVGLength.from(bbHeight);
|
||||
@@ -251,202 +243,198 @@ public class SvgView extends FabricEnabledViewGroup implements ReactCompoundView
|
||||
clearChildCache();
|
||||
}
|
||||
|
||||
@ReactProp(name = "align")
|
||||
public void setAlign(String align) {
|
||||
mAlign = align;
|
||||
invalidate();
|
||||
clearChildCache();
|
||||
@ReactProp(name = "align")
|
||||
public void setAlign(String align) {
|
||||
mAlign = align;
|
||||
invalidate();
|
||||
clearChildCache();
|
||||
}
|
||||
|
||||
@ReactProp(name = "meetOrSlice")
|
||||
public void setMeetOrSlice(int meetOrSlice) {
|
||||
mMeetOrSlice = meetOrSlice;
|
||||
invalidate();
|
||||
clearChildCache();
|
||||
}
|
||||
|
||||
private Bitmap drawOutput() {
|
||||
mRendered = true;
|
||||
float width = getWidth();
|
||||
float height = getHeight();
|
||||
boolean invalid =
|
||||
Float.isNaN(width)
|
||||
|| Float.isNaN(height)
|
||||
|| width < 1
|
||||
|| height < 1
|
||||
|| (Math.log10(width) + Math.log10(height) > 42);
|
||||
if (invalid) {
|
||||
return null;
|
||||
}
|
||||
Bitmap bitmap = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888);
|
||||
|
||||
drawChildren(new Canvas(bitmap));
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
Rect getCanvasBounds() {
|
||||
return mCanvas.getClipBounds();
|
||||
}
|
||||
|
||||
synchronized void drawChildren(final Canvas canvas) {
|
||||
mRendered = true;
|
||||
mCanvas = canvas;
|
||||
Matrix mViewBoxMatrix = new Matrix();
|
||||
if (mAlign != null) {
|
||||
RectF vbRect = getViewBox();
|
||||
float width = canvas.getWidth();
|
||||
float height = canvas.getHeight();
|
||||
boolean nested = getParent() instanceof VirtualView;
|
||||
if (nested) {
|
||||
width = (float) PropHelper.fromRelative(mbbWidth, width, 0f, mScale, 12);
|
||||
height = (float) PropHelper.fromRelative(mbbHeight, height, 0f, mScale, 12);
|
||||
}
|
||||
RectF eRect = new RectF(0, 0, width, height);
|
||||
if (nested) {
|
||||
canvas.clipRect(eRect);
|
||||
}
|
||||
mViewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice);
|
||||
mInvertible = mViewBoxMatrix.invert(mInvViewBoxMatrix);
|
||||
canvas.concat(mViewBoxMatrix);
|
||||
}
|
||||
|
||||
@ReactProp(name = "meetOrSlice")
|
||||
public void setMeetOrSlice(int meetOrSlice) {
|
||||
mMeetOrSlice = meetOrSlice;
|
||||
invalidate();
|
||||
clearChildCache();
|
||||
final Paint paint = new Paint();
|
||||
|
||||
paint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG | Paint.SUBPIXEL_TEXT_FLAG);
|
||||
|
||||
paint.setTypeface(Typeface.DEFAULT);
|
||||
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View node = getChildAt(i);
|
||||
if (node instanceof VirtualView) {
|
||||
((VirtualView) node).saveDefinition();
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap drawOutput() {
|
||||
mRendered = true;
|
||||
float width = getWidth();
|
||||
float height = getHeight();
|
||||
boolean invalid = Float.isNaN(width) || Float.isNaN(height) || width < 1 || height < 1 || (Math.log10(width) + Math.log10(height) > 42);
|
||||
if (invalid) {
|
||||
return null;
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View lNode = getChildAt(i);
|
||||
if (lNode instanceof VirtualView) {
|
||||
VirtualView node = (VirtualView) lNode;
|
||||
int count = node.saveAndSetupCanvas(canvas, mViewBoxMatrix);
|
||||
node.render(canvas, paint, 1f);
|
||||
node.restoreCanvas(canvas, count);
|
||||
|
||||
if (node.isResponsible() && !mResponsible) {
|
||||
mResponsible = true;
|
||||
}
|
||||
Bitmap bitmap = Bitmap.createBitmap(
|
||||
(int) width,
|
||||
(int) height,
|
||||
Bitmap.Config.ARGB_8888);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawChildren(new Canvas(bitmap));
|
||||
return bitmap;
|
||||
private RectF getViewBox() {
|
||||
return new RectF(
|
||||
mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale);
|
||||
}
|
||||
|
||||
String toDataURL() {
|
||||
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
|
||||
|
||||
clearChildCache();
|
||||
drawChildren(new Canvas(bitmap));
|
||||
clearChildCache();
|
||||
this.invalidate();
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
|
||||
bitmap.recycle();
|
||||
byte[] bitmapBytes = stream.toByteArray();
|
||||
return Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
|
||||
}
|
||||
|
||||
String toDataURL(int width, int height) {
|
||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
|
||||
clearChildCache();
|
||||
drawChildren(new Canvas(bitmap));
|
||||
clearChildCache();
|
||||
this.invalidate();
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
|
||||
bitmap.recycle();
|
||||
byte[] bitmapBytes = stream.toByteArray();
|
||||
return Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
|
||||
}
|
||||
|
||||
void enableTouchEvents() {
|
||||
if (!mResponsible) {
|
||||
mResponsible = true;
|
||||
}
|
||||
}
|
||||
|
||||
boolean isResponsible() {
|
||||
return mResponsible;
|
||||
}
|
||||
|
||||
private int hitTest(float touchX, float touchY) {
|
||||
if (!mResponsible || !mInvertible) {
|
||||
return getId();
|
||||
}
|
||||
|
||||
Rect getCanvasBounds() {
|
||||
return mCanvas.getClipBounds();
|
||||
float[] transformed = {touchX, touchY};
|
||||
mInvViewBoxMatrix.mapPoints(transformed);
|
||||
|
||||
int count = getChildCount();
|
||||
int viewTag = -1;
|
||||
for (int i = count - 1; i >= 0; i--) {
|
||||
View child = getChildAt(i);
|
||||
if (child instanceof VirtualView) {
|
||||
viewTag = ((VirtualView) child).hitTest(transformed);
|
||||
} else if (child instanceof SvgView) {
|
||||
viewTag = ((SvgView) child).hitTest(touchX, touchY);
|
||||
}
|
||||
if (viewTag != -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void drawChildren(final Canvas canvas) {
|
||||
mRendered = true;
|
||||
mCanvas = canvas;
|
||||
Matrix mViewBoxMatrix = new Matrix();
|
||||
if (mAlign != null) {
|
||||
RectF vbRect = getViewBox();
|
||||
float width = canvas.getWidth();
|
||||
float height = canvas.getHeight();
|
||||
boolean nested = getParent() instanceof VirtualView;
|
||||
if (nested) {
|
||||
width = (float) PropHelper.fromRelative(mbbWidth, width, 0f, mScale, 12);
|
||||
height = (float) PropHelper.fromRelative(mbbHeight, height, 0f, mScale, 12);
|
||||
}
|
||||
RectF eRect = new RectF(0,0, width, height);
|
||||
if (nested) {
|
||||
canvas.clipRect(eRect);
|
||||
}
|
||||
mViewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice);
|
||||
mInvertible = mViewBoxMatrix.invert(mInvViewBoxMatrix);
|
||||
canvas.concat(mViewBoxMatrix);
|
||||
}
|
||||
return viewTag == -1 ? getId() : viewTag;
|
||||
}
|
||||
|
||||
final Paint paint = new Paint();
|
||||
void defineClipPath(VirtualView clipPath, String clipPathRef) {
|
||||
mDefinedClipPaths.put(clipPathRef, clipPath);
|
||||
}
|
||||
|
||||
paint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG | Paint.SUBPIXEL_TEXT_FLAG);
|
||||
VirtualView getDefinedClipPath(String clipPathRef) {
|
||||
return mDefinedClipPaths.get(clipPathRef);
|
||||
}
|
||||
|
||||
paint.setTypeface(Typeface.DEFAULT);
|
||||
void defineTemplate(VirtualView template, String templateRef) {
|
||||
mDefinedTemplates.put(templateRef, template);
|
||||
}
|
||||
|
||||
VirtualView getDefinedTemplate(String templateRef) {
|
||||
return mDefinedTemplates.get(templateRef);
|
||||
}
|
||||
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View node = getChildAt(i);
|
||||
if (node instanceof VirtualView) {
|
||||
((VirtualView)node).saveDefinition();
|
||||
}
|
||||
}
|
||||
void defineBrush(Brush brush, String brushRef) {
|
||||
mDefinedBrushes.put(brushRef, brush);
|
||||
}
|
||||
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View lNode = getChildAt(i);
|
||||
if (lNode instanceof VirtualView) {
|
||||
VirtualView node = (VirtualView)lNode;
|
||||
int count = node.saveAndSetupCanvas(canvas, mViewBoxMatrix);
|
||||
node.render(canvas, paint, 1f);
|
||||
node.restoreCanvas(canvas, count);
|
||||
Brush getDefinedBrush(String brushRef) {
|
||||
return mDefinedBrushes.get(brushRef);
|
||||
}
|
||||
|
||||
if (node.isResponsible() && !mResponsible) {
|
||||
mResponsible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void defineMask(VirtualView mask, String maskRef) {
|
||||
mDefinedMasks.put(maskRef, mask);
|
||||
}
|
||||
|
||||
private RectF getViewBox() {
|
||||
return new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale);
|
||||
}
|
||||
VirtualView getDefinedMask(String maskRef) {
|
||||
return mDefinedMasks.get(maskRef);
|
||||
}
|
||||
|
||||
String toDataURL() {
|
||||
Bitmap bitmap = Bitmap.createBitmap(
|
||||
getWidth(),
|
||||
getHeight(),
|
||||
Bitmap.Config.ARGB_8888);
|
||||
void defineMarker(VirtualView marker, String markerRef) {
|
||||
mDefinedMarkers.put(markerRef, marker);
|
||||
}
|
||||
|
||||
clearChildCache();
|
||||
drawChildren(new Canvas(bitmap));
|
||||
clearChildCache();
|
||||
this.invalidate();
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
|
||||
bitmap.recycle();
|
||||
byte[] bitmapBytes = stream.toByteArray();
|
||||
return Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
|
||||
}
|
||||
|
||||
String toDataURL(int width, int height) {
|
||||
Bitmap bitmap = Bitmap.createBitmap(
|
||||
width,
|
||||
height,
|
||||
Bitmap.Config.ARGB_8888);
|
||||
|
||||
clearChildCache();
|
||||
drawChildren(new Canvas(bitmap));
|
||||
clearChildCache();
|
||||
this.invalidate();
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
|
||||
bitmap.recycle();
|
||||
byte[] bitmapBytes = stream.toByteArray();
|
||||
return Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
|
||||
}
|
||||
|
||||
void enableTouchEvents() {
|
||||
if (!mResponsible) {
|
||||
mResponsible = true;
|
||||
}
|
||||
}
|
||||
|
||||
boolean isResponsible() {
|
||||
return mResponsible;
|
||||
}
|
||||
|
||||
private int hitTest(float touchX, float touchY) {
|
||||
if (!mResponsible || !mInvertible) {
|
||||
return getId();
|
||||
}
|
||||
|
||||
float[] transformed = { touchX, touchY };
|
||||
mInvViewBoxMatrix.mapPoints(transformed);
|
||||
|
||||
int count = getChildCount();
|
||||
int viewTag = -1;
|
||||
for (int i = count - 1; i >= 0; i--) {
|
||||
View child = getChildAt(i);
|
||||
if (child instanceof VirtualView) {
|
||||
viewTag = ((VirtualView) child).hitTest(transformed);
|
||||
} else if (child instanceof SvgView) {
|
||||
viewTag = ((SvgView) child).hitTest(touchX, touchY);
|
||||
}
|
||||
if (viewTag != -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return viewTag == -1 ? getId() : viewTag;
|
||||
}
|
||||
|
||||
void defineClipPath(VirtualView clipPath, String clipPathRef) {
|
||||
mDefinedClipPaths.put(clipPathRef, clipPath);
|
||||
}
|
||||
|
||||
VirtualView getDefinedClipPath(String clipPathRef) {
|
||||
return mDefinedClipPaths.get(clipPathRef);
|
||||
}
|
||||
|
||||
void defineTemplate(VirtualView template, String templateRef) {
|
||||
mDefinedTemplates.put(templateRef, template);
|
||||
}
|
||||
|
||||
VirtualView getDefinedTemplate(String templateRef) {
|
||||
return mDefinedTemplates.get(templateRef);
|
||||
}
|
||||
|
||||
void defineBrush(Brush brush, String brushRef) {
|
||||
mDefinedBrushes.put(brushRef, brush);
|
||||
}
|
||||
|
||||
Brush getDefinedBrush(String brushRef) {
|
||||
return mDefinedBrushes.get(brushRef);
|
||||
}
|
||||
|
||||
void defineMask(VirtualView mask, String maskRef) {
|
||||
mDefinedMasks.put(maskRef, mask);
|
||||
}
|
||||
|
||||
VirtualView getDefinedMask(String maskRef) {
|
||||
return mDefinedMasks.get(maskRef);
|
||||
}
|
||||
|
||||
void defineMarker(VirtualView marker, String markerRef) {
|
||||
mDefinedMarkers.put(markerRef, marker);
|
||||
}
|
||||
|
||||
VirtualView getDefinedMarker(String markerRef) {
|
||||
return mDefinedMarkers.get(markerRef);
|
||||
}
|
||||
VirtualView getDefinedMarker(String markerRef) {
|
||||
return mDefinedMarkers.get(markerRef);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,145 +6,142 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.uimanager.ThemedReactContext;
|
||||
import com.facebook.react.uimanager.ViewManagerDelegate;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
import com.facebook.react.views.view.ReactViewGroup;
|
||||
import com.facebook.react.views.view.ReactViewManager;
|
||||
import com.facebook.react.viewmanagers.RNSVGSvgViewManagerDelegate;
|
||||
import com.facebook.react.viewmanagers.RNSVGSvgViewManagerInterface;
|
||||
|
||||
import com.facebook.react.views.view.ReactViewGroup;
|
||||
import com.facebook.react.views.view.ReactViewManager;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* ViewManager for RNSVGSvgView React views. Renders as a {@link SvgView} and handles
|
||||
* invalidating the native view on view updates happening in the underlying tree.
|
||||
* ViewManager for RNSVGSvgView React views. Renders as a {@link SvgView} and handles invalidating
|
||||
* the native view on view updates happening in the underlying tree.
|
||||
*/
|
||||
class SvgViewManager extends ReactViewManager implements RNSVGSvgViewManagerInterface<SvgView> {
|
||||
|
||||
private static final String REACT_CLASS = "RNSVGSvgView";
|
||||
private static final String REACT_CLASS = "RNSVGSvgView";
|
||||
|
||||
private static final SparseArray<SvgView> mTagToSvgView = new SparseArray<>();
|
||||
private static final SparseArray<Runnable> mTagToRunnable = new SparseArray<>();
|
||||
private static final SparseArray<SvgView> mTagToSvgView = new SparseArray<>();
|
||||
private static final SparseArray<Runnable> mTagToRunnable = new SparseArray<>();
|
||||
|
||||
private final ViewManagerDelegate<SvgView> mDelegate;
|
||||
private final ViewManagerDelegate<SvgView> mDelegate;
|
||||
|
||||
protected ViewManagerDelegate getDelegate(){
|
||||
return mDelegate;
|
||||
protected ViewManagerDelegate getDelegate() {
|
||||
return mDelegate;
|
||||
}
|
||||
|
||||
public SvgViewManager() {
|
||||
mDelegate = new RNSVGSvgViewManagerDelegate(this);
|
||||
}
|
||||
|
||||
static void setSvgView(int tag, SvgView svg) {
|
||||
mTagToSvgView.put(tag, svg);
|
||||
Runnable task = mTagToRunnable.get(tag);
|
||||
if (task != null) {
|
||||
task.run();
|
||||
mTagToRunnable.delete(tag);
|
||||
}
|
||||
}
|
||||
|
||||
public SvgViewManager() {
|
||||
mDelegate = new RNSVGSvgViewManagerDelegate(this);
|
||||
}
|
||||
static void runWhenViewIsAvailable(int tag, Runnable task) {
|
||||
mTagToRunnable.put(tag, task);
|
||||
}
|
||||
|
||||
static void setSvgView(int tag, SvgView svg) {
|
||||
mTagToSvgView.put(tag, svg);
|
||||
Runnable task = mTagToRunnable.get(tag);
|
||||
if (task != null) {
|
||||
task.run();
|
||||
mTagToRunnable.delete(tag);
|
||||
}
|
||||
}
|
||||
static @Nullable SvgView getSvgViewByTag(int tag) {
|
||||
return mTagToSvgView.get(tag);
|
||||
}
|
||||
|
||||
static void runWhenViewIsAvailable(int tag, Runnable task) {
|
||||
mTagToRunnable.put(tag, task);
|
||||
}
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName() {
|
||||
return REACT_CLASS;
|
||||
}
|
||||
|
||||
static @Nullable SvgView getSvgViewByTag(int tag) {
|
||||
return mTagToSvgView.get(tag);
|
||||
}
|
||||
@Nonnull
|
||||
@Override
|
||||
public ReactViewGroup createViewInstance(ThemedReactContext reactContext) {
|
||||
return new SvgView(reactContext);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName() {
|
||||
return REACT_CLASS;
|
||||
}
|
||||
@Override
|
||||
public void updateExtraData(ReactViewGroup root, Object extraData) {
|
||||
super.updateExtraData(root, extraData);
|
||||
root.invalidate();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ReactViewGroup createViewInstance(ThemedReactContext reactContext) {
|
||||
return new SvgView(reactContext);
|
||||
}
|
||||
@Override
|
||||
public void onDropViewInstance(@Nonnull ReactViewGroup view) {
|
||||
super.onDropViewInstance(view);
|
||||
mTagToSvgView.remove(view.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateExtraData(ReactViewGroup root, Object extraData) {
|
||||
super.updateExtraData(root, extraData);
|
||||
root.invalidate();
|
||||
}
|
||||
@Override
|
||||
public boolean needsCustomLayoutForChildren() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDropViewInstance(@Nonnull ReactViewGroup view) {
|
||||
super.onDropViewInstance(view);
|
||||
mTagToSvgView.remove(view.getId());
|
||||
}
|
||||
@ReactProp(name = "tintColor")
|
||||
public void setTintColor(SvgView node, @Nullable Dynamic tintColor) {
|
||||
node.setTintColor(tintColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsCustomLayoutForChildren() {
|
||||
return true;
|
||||
}
|
||||
@ReactProp(name = "color")
|
||||
public void setColor(SvgView node, @Nullable Dynamic color) {
|
||||
node.setTintColor(color);
|
||||
}
|
||||
|
||||
@ReactProp(name = "tintColor")
|
||||
public void setTintColor(SvgView node, @Nullable Dynamic tintColor) {
|
||||
node.setTintColor(tintColor);
|
||||
}
|
||||
@ReactProp(name = "minX")
|
||||
@Override
|
||||
public void setMinX(SvgView node, float minX) {
|
||||
node.setMinX(minX);
|
||||
}
|
||||
|
||||
@ReactProp(name = "color")
|
||||
public void setColor(SvgView node, @Nullable Dynamic color) {
|
||||
node.setTintColor(color);
|
||||
}
|
||||
@ReactProp(name = "minY")
|
||||
@Override
|
||||
public void setMinY(SvgView node, float minY) {
|
||||
node.setMinY(minY);
|
||||
}
|
||||
|
||||
@ReactProp(name = "minX")
|
||||
@Override
|
||||
public void setMinX(SvgView node, float minX) {
|
||||
node.setMinX(minX);
|
||||
}
|
||||
@ReactProp(name = "vbWidth")
|
||||
@Override
|
||||
public void setVbWidth(SvgView node, float vbWidth) {
|
||||
node.setVbWidth(vbWidth);
|
||||
}
|
||||
|
||||
@ReactProp(name = "minY")
|
||||
@Override
|
||||
public void setMinY(SvgView node, float minY) {
|
||||
node.setMinY(minY);
|
||||
}
|
||||
@ReactProp(name = "vbHeight")
|
||||
@Override
|
||||
public void setVbHeight(SvgView node, float vbHeight) {
|
||||
node.setVbHeight(vbHeight);
|
||||
}
|
||||
|
||||
@ReactProp(name = "vbWidth")
|
||||
@Override
|
||||
public void setVbWidth(SvgView node, float vbWidth) {
|
||||
node.setVbWidth(vbWidth);
|
||||
}
|
||||
@ReactProp(name = "bbWidth")
|
||||
public void setBbWidth(SvgView node, Dynamic bbWidth) {
|
||||
node.setBbWidth(bbWidth);
|
||||
}
|
||||
|
||||
@ReactProp(name = "vbHeight")
|
||||
@Override
|
||||
public void setVbHeight(SvgView node, float vbHeight) {
|
||||
node.setVbHeight(vbHeight);
|
||||
}
|
||||
@ReactProp(name = "bbHeight")
|
||||
public void setBbHeight(SvgView node, Dynamic bbHeight) {
|
||||
node.setBbHeight(bbHeight);
|
||||
}
|
||||
|
||||
@ReactProp(name = "bbWidth")
|
||||
public void setBbWidth(SvgView node, Dynamic bbWidth) {
|
||||
node.setBbWidth(bbWidth);
|
||||
}
|
||||
@ReactProp(name = "align")
|
||||
@Override
|
||||
public void setAlign(SvgView node, String align) {
|
||||
node.setAlign(align);
|
||||
}
|
||||
|
||||
@ReactProp(name = "bbHeight")
|
||||
public void setBbHeight(SvgView node, Dynamic bbHeight) {
|
||||
node.setBbHeight(bbHeight);
|
||||
}
|
||||
|
||||
@ReactProp(name = "align")
|
||||
@Override
|
||||
public void setAlign(SvgView node, String align) {
|
||||
node.setAlign(align);
|
||||
}
|
||||
|
||||
@ReactProp(name = "meetOrSlice")
|
||||
@Override
|
||||
public void setMeetOrSlice(SvgView node, int meetOrSlice) {
|
||||
node.setMeetOrSlice(meetOrSlice);
|
||||
}
|
||||
@ReactProp(name = "meetOrSlice")
|
||||
@Override
|
||||
public void setMeetOrSlice(SvgView node, int meetOrSlice) {
|
||||
node.setMeetOrSlice(meetOrSlice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTintColor(SvgView view, @Nullable Integer value) {
|
||||
@@ -165,5 +162,4 @@ class SvgViewManager extends ReactViewManager implements RNSVGSvgViewManagerInte
|
||||
public void setBbHeight(SvgView view, @Nullable String value) {
|
||||
view.setBbHeight(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import com.facebook.react.bridge.Callback;
|
||||
@@ -15,70 +14,69 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
class SvgViewModule extends ReactContextBaseJavaModule {
|
||||
SvgViewModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
SvgViewModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName() {
|
||||
return "RNSVGSvgViewManager";
|
||||
}
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName() {
|
||||
return "RNSVGSvgViewManager";
|
||||
}
|
||||
|
||||
private static void toDataURL(final int tag, final ReadableMap options, final Callback successCallback, final int attempt) {
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
private static void toDataURL(
|
||||
final int tag, final ReadableMap options, final Callback successCallback, final int attempt) {
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
SvgView svg = SvgViewManager.getSvgViewByTag(tag);
|
||||
|
||||
if (svg == null) {
|
||||
SvgViewManager.runWhenViewIsAvailable(
|
||||
tag,
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
SvgView svg = SvgViewManager.getSvgViewByTag(tag);
|
||||
|
||||
if (svg == null) {
|
||||
SvgViewManager.runWhenViewIsAvailable(tag, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
SvgView svg = SvgViewManager.getSvgViewByTag(tag);
|
||||
if (svg == null) { // Should never happen
|
||||
return;
|
||||
}
|
||||
svg.setToDataUrlTask(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
toDataURL(tag, options, successCallback, attempt + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (svg.notRendered()) {
|
||||
svg.setToDataUrlTask(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
toDataURL(tag, options, successCallback, attempt + 1);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (options != null) {
|
||||
successCallback.invoke(
|
||||
svg.toDataURL(
|
||||
options.getInt("width"),
|
||||
options.getInt("height")
|
||||
)
|
||||
);
|
||||
} else {
|
||||
successCallback.invoke(svg.toDataURL());
|
||||
SvgView svg = SvgViewManager.getSvgViewByTag(tag);
|
||||
if (svg == null) { // Should never happen
|
||||
return;
|
||||
}
|
||||
svg.setToDataUrlTask(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
toDataURL(tag, options, successCallback, attempt + 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
} else if (svg.notRendered()) {
|
||||
svg.setToDataUrlTask(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
toDataURL(tag, options, successCallback, attempt + 1);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (options != null) {
|
||||
successCallback.invoke(
|
||||
svg.toDataURL(options.getInt("width"), options.getInt("height")));
|
||||
} else {
|
||||
successCallback.invoke(svg.toDataURL());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ReactMethod
|
||||
public void toDataURL(int tag, ReadableMap options, Callback successCallback) {
|
||||
toDataURL(tag, options, successCallback, 0);
|
||||
}
|
||||
@SuppressWarnings("unused")
|
||||
@ReactMethod
|
||||
public void toDataURL(int tag, ReadableMap options, Callback successCallback) {
|
||||
toDataURL(tag, options, successCallback, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
@@ -14,72 +13,76 @@ import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class SymbolView extends GroupView {
|
||||
|
||||
private float mMinX;
|
||||
private float mMinY;
|
||||
private float mVbWidth;
|
||||
private float mVbHeight;
|
||||
private String mAlign;
|
||||
private int mMeetOrSlice;
|
||||
private float mMinX;
|
||||
private float mMinY;
|
||||
private float mVbWidth;
|
||||
private float mVbHeight;
|
||||
private String mAlign;
|
||||
private int mMeetOrSlice;
|
||||
|
||||
public SymbolView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
public SymbolView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@ReactProp(name = "minX")
|
||||
public void setMinX(float minX) {
|
||||
mMinX = minX;
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "minX")
|
||||
public void setMinX(float minX) {
|
||||
mMinX = minX;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "minY")
|
||||
public void setMinY(float minY) {
|
||||
mMinY = minY;
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "minY")
|
||||
public void setMinY(float minY) {
|
||||
mMinY = minY;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "vbWidth")
|
||||
public void setVbWidth(float vbWidth) {
|
||||
mVbWidth = vbWidth;
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "vbWidth")
|
||||
public void setVbWidth(float vbWidth) {
|
||||
mVbWidth = vbWidth;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "vbHeight")
|
||||
public void setVbHeight(float vbHeight) {
|
||||
mVbHeight = vbHeight;
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "vbHeight")
|
||||
public void setVbHeight(float vbHeight) {
|
||||
mVbHeight = vbHeight;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "align")
|
||||
public void setAlign(String align) {
|
||||
mAlign = align;
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "align")
|
||||
public void setAlign(String align) {
|
||||
mAlign = align;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "meetOrSlice")
|
||||
public void setMeetOrSlice(int meetOrSlice) {
|
||||
mMeetOrSlice = meetOrSlice;
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "meetOrSlice")
|
||||
public void setMeetOrSlice(int meetOrSlice) {
|
||||
mMeetOrSlice = meetOrSlice;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
void draw(Canvas canvas, Paint paint, float opacity) {
|
||||
saveDefinition();
|
||||
}
|
||||
@Override
|
||||
void draw(Canvas canvas, Paint paint, float opacity) {
|
||||
saveDefinition();
|
||||
}
|
||||
|
||||
void drawSymbol(Canvas canvas, Paint paint, float opacity, float width, float height) {
|
||||
if (mAlign != null) {
|
||||
RectF vbRect = new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale);
|
||||
RectF eRect = new RectF(0, 0, width, height);
|
||||
Matrix viewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice);
|
||||
canvas.concat(viewBoxMatrix);
|
||||
super.draw(canvas, paint, opacity);
|
||||
}
|
||||
void drawSymbol(Canvas canvas, Paint paint, float opacity, float width, float height) {
|
||||
if (mAlign != null) {
|
||||
RectF vbRect =
|
||||
new RectF(
|
||||
mMinX * mScale,
|
||||
mMinY * mScale,
|
||||
(mMinX + mVbWidth) * mScale,
|
||||
(mMinY + mVbHeight) * mScale);
|
||||
RectF eRect = new RectF(0, 0, width, height);
|
||||
Matrix viewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice);
|
||||
canvas.concat(viewBoxMatrix);
|
||||
super.draw(canvas, paint, opacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -6,129 +6,126 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import static com.horcrux.svg.TextProperties.*;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static com.horcrux.svg.TextProperties.*;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class TextPathView extends TextView {
|
||||
|
||||
private String mHref;
|
||||
private TextPathSide mSide;
|
||||
private TextPathMidLine mMidLine;
|
||||
private @Nullable SVGLength mStartOffset;
|
||||
private TextPathMethod mMethod = TextPathMethod.align;
|
||||
private TextPathSpacing mSpacing = TextPathSpacing.exact;
|
||||
private String mHref;
|
||||
private TextPathSide mSide;
|
||||
private TextPathMidLine mMidLine;
|
||||
private @Nullable SVGLength mStartOffset;
|
||||
private TextPathMethod mMethod = TextPathMethod.align;
|
||||
private TextPathSpacing mSpacing = TextPathSpacing.exact;
|
||||
|
||||
public TextPathView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
public TextPathView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@ReactProp(name = "href")
|
||||
public void setHref(String href) {
|
||||
mHref = href;
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "href")
|
||||
public void setHref(String href) {
|
||||
mHref = href;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "startOffset")
|
||||
public void setStartOffset(Dynamic startOffset) {
|
||||
mStartOffset = SVGLength.from(startOffset);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "startOffset")
|
||||
public void setStartOffset(Dynamic startOffset) {
|
||||
mStartOffset = SVGLength.from(startOffset);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setStartOffset(String startOffset) {
|
||||
mStartOffset = SVGLength.from(startOffset);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "method")
|
||||
public void setMethod(@Nullable String method) {
|
||||
mMethod = TextPathMethod.valueOf(method);
|
||||
invalidate();
|
||||
@ReactProp(name = "method")
|
||||
public void setMethod(@Nullable String method) {
|
||||
mMethod = TextPathMethod.valueOf(method);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "spacing")
|
||||
public void setSpacing(@Nullable String spacing) {
|
||||
mSpacing = TextPathSpacing.valueOf(spacing);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "side")
|
||||
public void setSide(@Nullable String side) {
|
||||
mSide = TextPathSide.valueOf(side);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "midLine")
|
||||
public void setSharp(@Nullable String midLine) {
|
||||
mMidLine = TextPathMidLine.valueOf(midLine);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
TextPathMethod getMethod() {
|
||||
return mMethod;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
TextPathSpacing getSpacing() {
|
||||
return mSpacing;
|
||||
}
|
||||
|
||||
TextPathSide getSide() {
|
||||
return mSide;
|
||||
}
|
||||
|
||||
TextPathMidLine getMidLine() {
|
||||
return mMidLine;
|
||||
}
|
||||
|
||||
SVGLength getStartOffset() {
|
||||
return mStartOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
void draw(Canvas canvas, Paint paint, float opacity) {
|
||||
drawGroup(canvas, paint, opacity);
|
||||
}
|
||||
|
||||
Path getTextPath(Canvas canvas, Paint paint) {
|
||||
SvgView svg = getSvgView();
|
||||
VirtualView template = svg.getDefinedTemplate(mHref);
|
||||
|
||||
if (!(template instanceof RenderableView)) {
|
||||
// warning about this.
|
||||
return null;
|
||||
}
|
||||
|
||||
@ReactProp(name = "spacing")
|
||||
public void setSpacing(@Nullable String spacing) {
|
||||
mSpacing = TextPathSpacing.valueOf(spacing);
|
||||
invalidate();
|
||||
}
|
||||
RenderableView view = (RenderableView) template;
|
||||
return view.getPath(canvas, paint);
|
||||
}
|
||||
|
||||
@ReactProp(name = "side")
|
||||
public void setSide(@Nullable String side) {
|
||||
mSide = TextPathSide.valueOf(side);
|
||||
invalidate();
|
||||
}
|
||||
@Override
|
||||
Path getPath(Canvas canvas, Paint paint) {
|
||||
return getGroupPath(canvas, paint);
|
||||
}
|
||||
|
||||
@ReactProp(name = "midLine")
|
||||
public void setSharp(@Nullable String midLine) {
|
||||
mMidLine = TextPathMidLine.valueOf(midLine);
|
||||
invalidate();
|
||||
}
|
||||
@Override
|
||||
void pushGlyphContext() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
TextPathMethod getMethod() {
|
||||
return mMethod;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
TextPathSpacing getSpacing() {
|
||||
return mSpacing;
|
||||
}
|
||||
|
||||
TextPathSide getSide() {
|
||||
return mSide;
|
||||
}
|
||||
|
||||
TextPathMidLine getMidLine() {
|
||||
return mMidLine;
|
||||
}
|
||||
|
||||
SVGLength getStartOffset() {
|
||||
return mStartOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
void draw(Canvas canvas, Paint paint, float opacity) {
|
||||
drawGroup(canvas, paint, opacity);
|
||||
}
|
||||
|
||||
Path getTextPath(Canvas canvas, Paint paint) {
|
||||
SvgView svg = getSvgView();
|
||||
VirtualView template = svg.getDefinedTemplate(mHref);
|
||||
|
||||
if (!(template instanceof RenderableView)) {
|
||||
// warning about this.
|
||||
return null;
|
||||
}
|
||||
|
||||
RenderableView view = (RenderableView)template;
|
||||
return view.getPath(canvas, paint);
|
||||
}
|
||||
|
||||
@Override
|
||||
Path getPath(Canvas canvas, Paint paint) {
|
||||
return getGroupPath(canvas, paint);
|
||||
}
|
||||
|
||||
@Override
|
||||
void pushGlyphContext() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
void popGlyphContext() {
|
||||
// do nothing
|
||||
}
|
||||
@Override
|
||||
void popGlyphContext() {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,212 +2,218 @@ package com.horcrux.svg;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
class TextProperties {
|
||||
|
||||
/*
|
||||
https://drafts.csswg.org/css-inline/#propdef-alignment-baseline
|
||||
2.2.1. Alignment Point: alignment-baseline longhand
|
||||
|
||||
Name: alignment-baseline
|
||||
Value: baseline | text-bottom | alphabetic | ideographic | middle | central | mathematical | text-top | bottom | center | top
|
||||
Initial: baseline
|
||||
Applies to: inline-level boxes, flex items, grid items, table cells
|
||||
Inherited: no
|
||||
Percentages: N/A
|
||||
Media: visual
|
||||
Computed value: as specified
|
||||
Canonical order: per grammar
|
||||
Animation type: discrete
|
||||
*/
|
||||
enum AlignmentBaseline {
|
||||
baseline("baseline"),
|
||||
textBottom("text-bottom"),
|
||||
alphabetic("alphabetic"),
|
||||
ideographic("ideographic"),
|
||||
middle("middle"),
|
||||
central("central"),
|
||||
mathematical("mathematical"),
|
||||
textTop("text-top"),
|
||||
bottom("bottom"),
|
||||
center("center"),
|
||||
top("top"),
|
||||
/*
|
||||
https://drafts.csswg.org/css-inline/#propdef-alignment-baseline
|
||||
2.2.1. Alignment Point: alignment-baseline longhand
|
||||
SVG implementations may support the following aliases in order to support legacy content:
|
||||
|
||||
Name: alignment-baseline
|
||||
Value: baseline | text-bottom | alphabetic | ideographic | middle | central | mathematical | text-top | bottom | center | top
|
||||
Initial: baseline
|
||||
Applies to: inline-level boxes, flex items, grid items, table cells
|
||||
Inherited: no
|
||||
Percentages: N/A
|
||||
Media: visual
|
||||
Computed value: as specified
|
||||
Canonical order: per grammar
|
||||
Animation type: discrete
|
||||
*/
|
||||
enum AlignmentBaseline {
|
||||
baseline("baseline"),
|
||||
textBottom("text-bottom"),
|
||||
alphabetic("alphabetic"),
|
||||
ideographic("ideographic"),
|
||||
middle("middle"),
|
||||
central("central"),
|
||||
mathematical("mathematical"),
|
||||
textTop("text-top"),
|
||||
bottom("bottom"),
|
||||
center("center"),
|
||||
top("top"),
|
||||
/*
|
||||
SVG implementations may support the following aliases in order to support legacy content:
|
||||
text-before-edge = text-top
|
||||
text-after-edge = text-bottom
|
||||
*/
|
||||
textBeforeEdge("text-before-edge"),
|
||||
textAfterEdge("text-after-edge"),
|
||||
// SVG 1.1
|
||||
beforeEdge("before-edge"),
|
||||
afterEdge("after-edge"),
|
||||
hanging("hanging"),
|
||||
;
|
||||
|
||||
text-before-edge = text-top
|
||||
text-after-edge = text-bottom
|
||||
*/
|
||||
textBeforeEdge("text-before-edge"),
|
||||
textAfterEdge("text-after-edge"),
|
||||
// SVG 1.1
|
||||
beforeEdge("before-edge"),
|
||||
afterEdge("after-edge"),
|
||||
hanging("hanging"),
|
||||
;
|
||||
private final String alignment;
|
||||
|
||||
private final String alignment;
|
||||
|
||||
AlignmentBaseline(String alignment) {
|
||||
this.alignment = alignment;
|
||||
}
|
||||
|
||||
static AlignmentBaseline getEnum(String strVal) {
|
||||
if (!alignmentToEnum.containsKey(strVal)) {
|
||||
throw new IllegalArgumentException("Unknown String Value: " + strVal);
|
||||
}
|
||||
return alignmentToEnum.get(strVal);
|
||||
}
|
||||
|
||||
private static final Map<String, AlignmentBaseline> alignmentToEnum = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (final AlignmentBaseline en : AlignmentBaseline.values()) {
|
||||
alignmentToEnum.put(en.alignment, en);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return alignment;
|
||||
}
|
||||
AlignmentBaseline(String alignment) {
|
||||
this.alignment = alignment;
|
||||
}
|
||||
|
||||
// TODO implement rtl
|
||||
static AlignmentBaseline getEnum(String strVal) {
|
||||
if (!alignmentToEnum.containsKey(strVal)) {
|
||||
throw new IllegalArgumentException("Unknown String Value: " + strVal);
|
||||
}
|
||||
return alignmentToEnum.get(strVal);
|
||||
}
|
||||
|
||||
private static final Map<String, AlignmentBaseline> alignmentToEnum = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (final AlignmentBaseline en : AlignmentBaseline.values()) {
|
||||
alignmentToEnum.put(en.alignment, en);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return alignment;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO implement rtl
|
||||
@SuppressWarnings("unused")
|
||||
enum Direction {
|
||||
ltr,
|
||||
rtl
|
||||
}
|
||||
|
||||
enum FontVariantLigatures {
|
||||
normal,
|
||||
@SuppressWarnings("unused")
|
||||
enum Direction {
|
||||
ltr,
|
||||
rtl
|
||||
none
|
||||
}
|
||||
|
||||
enum FontStyle {
|
||||
normal,
|
||||
italic,
|
||||
@SuppressWarnings("unused")
|
||||
oblique
|
||||
}
|
||||
|
||||
enum FontWeight {
|
||||
// Absolute
|
||||
Normal("normal"),
|
||||
Bold("bold"),
|
||||
w100("100"),
|
||||
w200("200"),
|
||||
w300("300"),
|
||||
w400("400"),
|
||||
w500("500"),
|
||||
w600("600"),
|
||||
w700("700"),
|
||||
w800("800"),
|
||||
w900("900"),
|
||||
// Relative
|
||||
Bolder("bolder"),
|
||||
Lighter("lighter");
|
||||
|
||||
private final String weight;
|
||||
|
||||
FontWeight(String weight) {
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
enum FontVariantLigatures {
|
||||
normal,
|
||||
@SuppressWarnings("unused")none
|
||||
static boolean hasEnum(String strVal) {
|
||||
return weightToEnum.containsKey(strVal);
|
||||
}
|
||||
|
||||
enum FontStyle {
|
||||
normal,
|
||||
italic,
|
||||
@SuppressWarnings("unused")oblique
|
||||
static FontWeight get(String strVal) {
|
||||
return weightToEnum.get(strVal);
|
||||
}
|
||||
|
||||
enum FontWeight {
|
||||
// Absolute
|
||||
Normal ("normal"),
|
||||
Bold ("bold"),
|
||||
w100 ("100"),
|
||||
w200 ("200"),
|
||||
w300 ("300"),
|
||||
w400 ("400"),
|
||||
w500 ("500"),
|
||||
w600 ("600"),
|
||||
w700 ("700"),
|
||||
w800 ("800"),
|
||||
w900 ("900"),
|
||||
// Relative
|
||||
Bolder ("bolder"),
|
||||
Lighter ("lighter");
|
||||
private static final Map<String, FontWeight> weightToEnum = new HashMap<>();
|
||||
|
||||
private final String weight;
|
||||
FontWeight(String weight) {
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
static boolean hasEnum(String strVal) {
|
||||
return weightToEnum.containsKey(strVal);
|
||||
}
|
||||
|
||||
static FontWeight get(String strVal) {
|
||||
return weightToEnum.get(strVal);
|
||||
}
|
||||
|
||||
private static final Map<String, FontWeight> weightToEnum = new HashMap<>();
|
||||
static {
|
||||
for (final FontWeight en : FontWeight.values()) {
|
||||
weightToEnum.put(en.weight, en);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return weight;
|
||||
}
|
||||
static {
|
||||
for (final FontWeight en : FontWeight.values()) {
|
||||
weightToEnum.put(en.weight, en);
|
||||
}
|
||||
}
|
||||
|
||||
enum TextAnchor
|
||||
{
|
||||
start,
|
||||
middle,
|
||||
end
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return weight;
|
||||
}
|
||||
}
|
||||
|
||||
enum TextAnchor {
|
||||
start,
|
||||
middle,
|
||||
end
|
||||
}
|
||||
|
||||
enum TextDecoration {
|
||||
None("none"),
|
||||
Underline("underline"),
|
||||
Overline("overline"),
|
||||
LineThrough("line-through"),
|
||||
Blink("blink");
|
||||
|
||||
private final String decoration;
|
||||
|
||||
TextDecoration(String decoration) {
|
||||
this.decoration = decoration;
|
||||
}
|
||||
|
||||
enum TextDecoration
|
||||
{
|
||||
None("none"),
|
||||
Underline("underline"),
|
||||
Overline("overline"),
|
||||
LineThrough("line-through"),
|
||||
Blink("blink");
|
||||
|
||||
private final String decoration;
|
||||
TextDecoration(String decoration) {
|
||||
this.decoration = decoration;
|
||||
}
|
||||
|
||||
static TextDecoration getEnum(String strVal) {
|
||||
if(!decorationToEnum.containsKey(strVal)) {
|
||||
throw new IllegalArgumentException("Unknown String Value: " + strVal);
|
||||
}
|
||||
return decorationToEnum.get(strVal);
|
||||
}
|
||||
|
||||
private static final Map<String, TextDecoration> decorationToEnum = new HashMap<>();
|
||||
static {
|
||||
for (final TextDecoration en : TextDecoration.values()) {
|
||||
decorationToEnum.put(en.decoration, en);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return decoration;
|
||||
}
|
||||
static TextDecoration getEnum(String strVal) {
|
||||
if (!decorationToEnum.containsKey(strVal)) {
|
||||
throw new IllegalArgumentException("Unknown String Value: " + strVal);
|
||||
}
|
||||
return decorationToEnum.get(strVal);
|
||||
}
|
||||
|
||||
enum TextLengthAdjust
|
||||
{
|
||||
spacing,
|
||||
spacingAndGlyphs
|
||||
private static final Map<String, TextDecoration> decorationToEnum = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (final TextDecoration en : TextDecoration.values()) {
|
||||
decorationToEnum.put(en.decoration, en);
|
||||
}
|
||||
}
|
||||
|
||||
enum TextPathMethod {
|
||||
align,
|
||||
@SuppressWarnings("unused")stretch
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return decoration;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
TODO suggest adding a compatibility mid-line rendering attribute to textPath,
|
||||
for a chrome/firefox/opera/safari compatible sharp text path rendering,
|
||||
which doesn't bend text smoothly along a right angle curve, (like Edge does)
|
||||
but keeps the mid-line orthogonal to the mid-point tangent at all times instead.
|
||||
*/
|
||||
enum TextPathMidLine {
|
||||
sharp,
|
||||
@SuppressWarnings("unused")smooth
|
||||
}
|
||||
enum TextLengthAdjust {
|
||||
spacing,
|
||||
spacingAndGlyphs
|
||||
}
|
||||
|
||||
enum TextPathSide {
|
||||
@SuppressWarnings("unused")left,
|
||||
right
|
||||
}
|
||||
enum TextPathMethod {
|
||||
align,
|
||||
@SuppressWarnings("unused")
|
||||
stretch
|
||||
}
|
||||
|
||||
enum TextPathSpacing {
|
||||
@SuppressWarnings("unused")auto,
|
||||
exact
|
||||
}
|
||||
/*
|
||||
TODO suggest adding a compatibility mid-line rendering attribute to textPath,
|
||||
for a chrome/firefox/opera/safari compatible sharp text path rendering,
|
||||
which doesn't bend text smoothly along a right angle curve, (like Edge does)
|
||||
but keeps the mid-line orthogonal to the mid-point tangent at all times instead.
|
||||
*/
|
||||
enum TextPathMidLine {
|
||||
sharp,
|
||||
@SuppressWarnings("unused")
|
||||
smooth
|
||||
}
|
||||
|
||||
enum TextPathSide {
|
||||
@SuppressWarnings("unused")
|
||||
left,
|
||||
right
|
||||
}
|
||||
|
||||
enum TextPathSpacing {
|
||||
@SuppressWarnings("unused")
|
||||
auto,
|
||||
exact
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,11 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import static com.horcrux.svg.TextProperties.AlignmentBaseline;
|
||||
import static com.horcrux.svg.TextProperties.TextLengthAdjust;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
@@ -16,292 +18,288 @@ import android.graphics.Path;
|
||||
import android.graphics.Region;
|
||||
import android.view.View;
|
||||
import android.view.ViewParent;
|
||||
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static com.horcrux.svg.TextProperties.AlignmentBaseline;
|
||||
import static com.horcrux.svg.TextProperties.TextLengthAdjust;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class TextView extends GroupView {
|
||||
SVGLength mInlineSize = null;
|
||||
SVGLength mTextLength = null;
|
||||
private String mBaselineShift = null;
|
||||
TextLengthAdjust mLengthAdjust = TextLengthAdjust.spacing;
|
||||
private AlignmentBaseline mAlignmentBaseline;
|
||||
@Nullable private ArrayList<SVGLength> mPositionX;
|
||||
@Nullable private ArrayList<SVGLength> mPositionY;
|
||||
@Nullable private ArrayList<SVGLength> mRotate;
|
||||
@Nullable private ArrayList<SVGLength> mDeltaX;
|
||||
@Nullable private ArrayList<SVGLength> mDeltaY;
|
||||
double cachedAdvance = Double.NaN;
|
||||
SVGLength mInlineSize = null;
|
||||
SVGLength mTextLength = null;
|
||||
private String mBaselineShift = null;
|
||||
TextLengthAdjust mLengthAdjust = TextLengthAdjust.spacing;
|
||||
private AlignmentBaseline mAlignmentBaseline;
|
||||
@Nullable private ArrayList<SVGLength> mPositionX;
|
||||
@Nullable private ArrayList<SVGLength> mPositionY;
|
||||
@Nullable private ArrayList<SVGLength> mRotate;
|
||||
@Nullable private ArrayList<SVGLength> mDeltaX;
|
||||
@Nullable private ArrayList<SVGLength> mDeltaY;
|
||||
double cachedAdvance = Double.NaN;
|
||||
|
||||
public TextView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
public TextView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
if (mPath == null) {
|
||||
return;
|
||||
}
|
||||
super.invalidate();
|
||||
getTextContainer().clearChildCache();
|
||||
@Override
|
||||
public void invalidate() {
|
||||
if (mPath == null) {
|
||||
return;
|
||||
}
|
||||
super.invalidate();
|
||||
getTextContainer().clearChildCache();
|
||||
}
|
||||
|
||||
void clearCache() {
|
||||
cachedAdvance = Double.NaN;
|
||||
super.clearCache();
|
||||
}
|
||||
void clearCache() {
|
||||
cachedAdvance = Double.NaN;
|
||||
super.clearCache();
|
||||
}
|
||||
|
||||
@ReactProp(name = "inlineSize")
|
||||
public void setInlineSize(Dynamic inlineSize) {
|
||||
mInlineSize = SVGLength.from(inlineSize);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "inlineSize")
|
||||
public void setInlineSize(Dynamic inlineSize) {
|
||||
mInlineSize = SVGLength.from(inlineSize);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setInlineSize(String inlineSize) {
|
||||
mInlineSize = SVGLength.from(inlineSize);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "textLength")
|
||||
public void setTextLength(Dynamic length) {
|
||||
mTextLength = SVGLength.from(length);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "textLength")
|
||||
public void setTextLength(Dynamic length) {
|
||||
mTextLength = SVGLength.from(length);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setTextLength(String length) {
|
||||
mTextLength = SVGLength.from(length);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "lengthAdjust")
|
||||
public void setLengthAdjust(@Nullable String adjustment) {
|
||||
mLengthAdjust = TextLengthAdjust.valueOf(adjustment);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "lengthAdjust")
|
||||
public void setLengthAdjust(@Nullable String adjustment) {
|
||||
mLengthAdjust = TextLengthAdjust.valueOf(adjustment);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "alignmentBaseline")
|
||||
public void setMethod(@Nullable String alignment) {
|
||||
mAlignmentBaseline = AlignmentBaseline.getEnum(alignment);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "alignmentBaseline")
|
||||
public void setMethod(@Nullable String alignment) {
|
||||
mAlignmentBaseline = AlignmentBaseline.getEnum(alignment);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "baselineShift")
|
||||
public void setBaselineShift(Dynamic baselineShift) {
|
||||
mBaselineShift = SVGLength.toString(baselineShift);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "baselineShift")
|
||||
public void setBaselineShift(Dynamic baselineShift) {
|
||||
mBaselineShift = SVGLength.toString(baselineShift);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setBaselineShift(String baselineShift) {
|
||||
mBaselineShift = baselineShift;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "verticalAlign")
|
||||
public void setVerticalAlign(@Nullable String verticalAlign) {
|
||||
if (verticalAlign != null) {
|
||||
verticalAlign = verticalAlign.trim();
|
||||
int i = verticalAlign.lastIndexOf(' ');
|
||||
try {
|
||||
mAlignmentBaseline = AlignmentBaseline.getEnum(verticalAlign.substring(i));
|
||||
} catch (IllegalArgumentException e) {
|
||||
mAlignmentBaseline = AlignmentBaseline.baseline;
|
||||
}
|
||||
try {
|
||||
mBaselineShift = verticalAlign.substring(0, i);
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
mBaselineShift = null;
|
||||
}
|
||||
} else {
|
||||
mAlignmentBaseline = AlignmentBaseline.baseline;
|
||||
mBaselineShift = null;
|
||||
}
|
||||
invalidate();
|
||||
@ReactProp(name = "verticalAlign")
|
||||
public void setVerticalAlign(@Nullable String verticalAlign) {
|
||||
if (verticalAlign != null) {
|
||||
verticalAlign = verticalAlign.trim();
|
||||
int i = verticalAlign.lastIndexOf(' ');
|
||||
try {
|
||||
mAlignmentBaseline = AlignmentBaseline.getEnum(verticalAlign.substring(i));
|
||||
} catch (IllegalArgumentException e) {
|
||||
mAlignmentBaseline = AlignmentBaseline.baseline;
|
||||
}
|
||||
try {
|
||||
mBaselineShift = verticalAlign.substring(0, i);
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
mBaselineShift = null;
|
||||
}
|
||||
} else {
|
||||
mAlignmentBaseline = AlignmentBaseline.baseline;
|
||||
mBaselineShift = null;
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "rotate")
|
||||
public void setRotate(Dynamic rotate) {
|
||||
mRotate = SVGLength.arrayFrom(rotate);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "rotate")
|
||||
public void setRotate(Dynamic rotate) {
|
||||
mRotate = SVGLength.arrayFrom(rotate);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setRotate(ReadableArray rotate) {
|
||||
mRotate = SVGLength.arrayFrom(rotate);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "dx")
|
||||
public void setDeltaX(Dynamic deltaX) {
|
||||
mDeltaX = SVGLength.arrayFrom(deltaX);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "dx")
|
||||
public void setDeltaX(Dynamic deltaX) {
|
||||
mDeltaX = SVGLength.arrayFrom(deltaX);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setDeltaX(ReadableArray deltaX) {
|
||||
mDeltaX = SVGLength.arrayFrom(deltaX);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "dy")
|
||||
public void setDeltaY(Dynamic deltaY) {
|
||||
mDeltaY = SVGLength.arrayFrom(deltaY);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "dy")
|
||||
public void setDeltaY(Dynamic deltaY) {
|
||||
mDeltaY = SVGLength.arrayFrom(deltaY);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setDeltaY(ReadableArray deltaY) {
|
||||
mDeltaY = SVGLength.arrayFrom(deltaY);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "x")
|
||||
public void setPositionX(Dynamic positionX) {
|
||||
mPositionX = SVGLength.arrayFrom(positionX);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "x")
|
||||
public void setPositionX(Dynamic positionX) {
|
||||
mPositionX = SVGLength.arrayFrom(positionX);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setPositionX(ReadableArray positionX) {
|
||||
mPositionX = SVGLength.arrayFrom(positionX);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
@ReactProp(name = "y")
|
||||
public void setPositionY(Dynamic positionY) {
|
||||
mPositionY = SVGLength.arrayFrom(positionY);
|
||||
invalidate();
|
||||
}
|
||||
public void setPositionY(Dynamic positionY) {
|
||||
mPositionY = SVGLength.arrayFrom(positionY);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setPositionY(ReadableArray positionY) {
|
||||
mPositionY = SVGLength.arrayFrom(positionY);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
void draw(Canvas canvas, Paint paint, float opacity) {
|
||||
setupGlyphContext(canvas);
|
||||
clip(canvas, paint);
|
||||
getGroupPath(canvas, paint);
|
||||
pushGlyphContext();
|
||||
drawGroup(canvas, paint, opacity);
|
||||
popGlyphContext();
|
||||
}
|
||||
@Override
|
||||
void draw(Canvas canvas, Paint paint, float opacity) {
|
||||
setupGlyphContext(canvas);
|
||||
clip(canvas, paint);
|
||||
getGroupPath(canvas, paint);
|
||||
pushGlyphContext();
|
||||
drawGroup(canvas, paint, opacity);
|
||||
popGlyphContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
Path getPath(Canvas canvas, Paint paint) {
|
||||
if (mPath != null) {
|
||||
return mPath;
|
||||
@Override
|
||||
Path getPath(Canvas canvas, Paint paint) {
|
||||
if (mPath != null) {
|
||||
return mPath;
|
||||
}
|
||||
setupGlyphContext(canvas);
|
||||
return getGroupPath(canvas, paint);
|
||||
}
|
||||
|
||||
@Override
|
||||
Path getPath(Canvas canvas, Paint paint, Region.Op op) {
|
||||
return getPath(canvas, paint);
|
||||
}
|
||||
|
||||
AlignmentBaseline getAlignmentBaseline() {
|
||||
if (mAlignmentBaseline == null) {
|
||||
ViewParent parent = this.getParent();
|
||||
while (parent != null) {
|
||||
if (parent instanceof TextView) {
|
||||
TextView node = (TextView) parent;
|
||||
final AlignmentBaseline baseline = node.mAlignmentBaseline;
|
||||
if (baseline != null) {
|
||||
mAlignmentBaseline = baseline;
|
||||
return baseline;
|
||||
}
|
||||
}
|
||||
setupGlyphContext(canvas);
|
||||
return getGroupPath(canvas, paint);
|
||||
parent = parent.getParent();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
Path getPath(Canvas canvas, Paint paint, Region.Op op) {
|
||||
return getPath(canvas, paint);
|
||||
if (mAlignmentBaseline == null) {
|
||||
mAlignmentBaseline = AlignmentBaseline.baseline;
|
||||
}
|
||||
return mAlignmentBaseline;
|
||||
}
|
||||
|
||||
AlignmentBaseline getAlignmentBaseline() {
|
||||
if (mAlignmentBaseline == null) {
|
||||
ViewParent parent = this.getParent();
|
||||
while (parent != null) {
|
||||
if (parent instanceof TextView) {
|
||||
TextView node = (TextView)parent;
|
||||
final AlignmentBaseline baseline = node.mAlignmentBaseline;
|
||||
if (baseline != null) {
|
||||
mAlignmentBaseline = baseline;
|
||||
return baseline;
|
||||
}
|
||||
}
|
||||
parent = parent.getParent();
|
||||
}
|
||||
String getBaselineShift() {
|
||||
if (mBaselineShift == null) {
|
||||
ViewParent parent = this.getParent();
|
||||
while (parent != null) {
|
||||
if (parent instanceof TextView) {
|
||||
TextView node = (TextView) parent;
|
||||
final String baselineShift = node.mBaselineShift;
|
||||
if (baselineShift != null) {
|
||||
mBaselineShift = baselineShift;
|
||||
return baselineShift;
|
||||
}
|
||||
}
|
||||
if (mAlignmentBaseline == null) {
|
||||
mAlignmentBaseline = AlignmentBaseline.baseline;
|
||||
}
|
||||
return mAlignmentBaseline;
|
||||
parent = parent.getParent();
|
||||
}
|
||||
}
|
||||
return mBaselineShift;
|
||||
}
|
||||
|
||||
String getBaselineShift() {
|
||||
if (mBaselineShift == null) {
|
||||
ViewParent parent = this.getParent();
|
||||
while (parent != null) {
|
||||
if (parent instanceof TextView) {
|
||||
TextView node = (TextView)parent;
|
||||
final String baselineShift = node.mBaselineShift;
|
||||
if (baselineShift != null) {
|
||||
mBaselineShift = baselineShift;
|
||||
return baselineShift;
|
||||
}
|
||||
}
|
||||
parent = parent.getParent();
|
||||
}
|
||||
}
|
||||
return mBaselineShift;
|
||||
Path getGroupPath(Canvas canvas, Paint paint) {
|
||||
if (mPath != null) {
|
||||
return mPath;
|
||||
}
|
||||
pushGlyphContext();
|
||||
mPath = super.getPath(canvas, paint);
|
||||
popGlyphContext();
|
||||
|
||||
Path getGroupPath(Canvas canvas, Paint paint) {
|
||||
if (mPath != null) {
|
||||
return mPath;
|
||||
}
|
||||
pushGlyphContext();
|
||||
mPath = super.getPath(canvas, paint);
|
||||
popGlyphContext();
|
||||
return mPath;
|
||||
}
|
||||
|
||||
return mPath;
|
||||
}
|
||||
@Override
|
||||
void pushGlyphContext() {
|
||||
boolean isTextNode = !(this instanceof TextPathView) && !(this instanceof TSpanView);
|
||||
getTextRootGlyphContext()
|
||||
.pushContext(isTextNode, this, mFont, mPositionX, mPositionY, mDeltaX, mDeltaY, mRotate);
|
||||
}
|
||||
|
||||
@Override
|
||||
void pushGlyphContext() {
|
||||
boolean isTextNode = !(this instanceof TextPathView) && !(this instanceof TSpanView);
|
||||
getTextRootGlyphContext().pushContext(isTextNode, this, mFont, mPositionX, mPositionY, mDeltaX, mDeltaY, mRotate);
|
||||
}
|
||||
|
||||
TextView getTextAnchorRoot() {
|
||||
GlyphContext gc = getTextRootGlyphContext();
|
||||
ArrayList<FontData> font = gc.mFontContext;
|
||||
TextView node = this;
|
||||
ViewParent parent = this.getParent();
|
||||
for (int i = font.size() - 1; i >= 0; i--) {
|
||||
if (!(parent instanceof TextView) || font.get(i).textAnchor == TextProperties.TextAnchor.start || node.mPositionX != null) {
|
||||
return node;
|
||||
}
|
||||
node = (TextView) parent;
|
||||
parent = node.getParent();
|
||||
}
|
||||
TextView getTextAnchorRoot() {
|
||||
GlyphContext gc = getTextRootGlyphContext();
|
||||
ArrayList<FontData> font = gc.mFontContext;
|
||||
TextView node = this;
|
||||
ViewParent parent = this.getParent();
|
||||
for (int i = font.size() - 1; i >= 0; i--) {
|
||||
if (!(parent instanceof TextView)
|
||||
|| font.get(i).textAnchor == TextProperties.TextAnchor.start
|
||||
|| node.mPositionX != null) {
|
||||
return node;
|
||||
}
|
||||
node = (TextView) parent;
|
||||
parent = node.getParent();
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
double getSubtreeTextChunksTotalAdvance(Paint paint) {
|
||||
if (!Double.isNaN(cachedAdvance)) {
|
||||
return cachedAdvance;
|
||||
}
|
||||
double advance = 0;
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View child = getChildAt(i);
|
||||
if (child instanceof TextView) {
|
||||
TextView text = (TextView) child;
|
||||
advance += text.getSubtreeTextChunksTotalAdvance(paint);
|
||||
}
|
||||
}
|
||||
cachedAdvance = advance;
|
||||
return advance;
|
||||
double getSubtreeTextChunksTotalAdvance(Paint paint) {
|
||||
if (!Double.isNaN(cachedAdvance)) {
|
||||
return cachedAdvance;
|
||||
}
|
||||
double advance = 0;
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View child = getChildAt(i);
|
||||
if (child instanceof TextView) {
|
||||
TextView text = (TextView) child;
|
||||
advance += text.getSubtreeTextChunksTotalAdvance(paint);
|
||||
}
|
||||
}
|
||||
cachedAdvance = advance;
|
||||
return advance;
|
||||
}
|
||||
|
||||
TextView getTextContainer() {
|
||||
TextView node = this;
|
||||
ViewParent parent = this.getParent();
|
||||
while (parent instanceof TextView) {
|
||||
node = (TextView) parent;
|
||||
parent = node.getParent();
|
||||
}
|
||||
return node;
|
||||
TextView getTextContainer() {
|
||||
TextView node = this;
|
||||
ViewParent parent = this.getParent();
|
||||
while (parent instanceof TextView) {
|
||||
node = (TextView) parent;
|
||||
parent = node.getParent();
|
||||
}
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
@@ -14,7 +13,6 @@ import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
@@ -23,138 +21,151 @@ import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
class UseView extends RenderableView {
|
||||
private String mHref;
|
||||
private SVGLength mX;
|
||||
private SVGLength mY;
|
||||
private SVGLength mW;
|
||||
private SVGLength mH;
|
||||
private String mHref;
|
||||
private SVGLength mX;
|
||||
private SVGLength mY;
|
||||
private SVGLength mW;
|
||||
private SVGLength mH;
|
||||
|
||||
public UseView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
public UseView(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@ReactProp(name = "href")
|
||||
public void setHref(String href) {
|
||||
mHref = href;
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "href")
|
||||
public void setHref(String href) {
|
||||
mHref = href;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "x")
|
||||
public void setX(Dynamic x) {
|
||||
mX = SVGLength.from(x);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "x")
|
||||
public void setX(Dynamic x) {
|
||||
mX = SVGLength.from(x);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setX(String x) {
|
||||
mX = SVGLength.from(x);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "y")
|
||||
public void setY(Dynamic y) {
|
||||
mY = SVGLength.from(y);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "y")
|
||||
public void setY(Dynamic y) {
|
||||
mY = SVGLength.from(y);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setY(String y) {
|
||||
mY = SVGLength.from(y);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "width")
|
||||
public void setWidth(Dynamic width) {
|
||||
mW = SVGLength.from(width);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "width")
|
||||
public void setWidth(Dynamic width) {
|
||||
mW = SVGLength.from(width);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setWidth(String width) {
|
||||
mW = SVGLength.from(width);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "height")
|
||||
public void setHeight(Dynamic height) {
|
||||
mH = SVGLength.from(height);
|
||||
invalidate();
|
||||
}
|
||||
@ReactProp(name = "height")
|
||||
public void setHeight(Dynamic height) {
|
||||
mH = SVGLength.from(height);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setHeight(String height) {
|
||||
mH = SVGLength.from(height);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
void draw(Canvas canvas, Paint paint, float opacity) {
|
||||
VirtualView template = getSvgView().getDefinedTemplate(mHref);
|
||||
@Override
|
||||
void draw(Canvas canvas, Paint paint, float opacity) {
|
||||
VirtualView template = getSvgView().getDefinedTemplate(mHref);
|
||||
|
||||
if (template == null) {
|
||||
FLog.w(ReactConstants.TAG, "`Use` element expected a pre-defined svg template as `href` prop, " +
|
||||
"template named: " + mHref + " is not defined.");
|
||||
return;
|
||||
}
|
||||
|
||||
template.clearCache();
|
||||
canvas.translate((float) relativeOnWidth(mX), (float) relativeOnHeight(mY));
|
||||
if (template instanceof RenderableView) {
|
||||
((RenderableView)template).mergeProperties(this);
|
||||
}
|
||||
|
||||
int count = template.saveAndSetupCanvas(canvas, mCTM);
|
||||
clip(canvas, paint);
|
||||
|
||||
if (template instanceof SymbolView) {
|
||||
SymbolView symbol = (SymbolView)template;
|
||||
symbol.drawSymbol(canvas, paint, opacity, (float) relativeOnWidth(mW), (float) relativeOnHeight(mH));
|
||||
} else {
|
||||
template.draw(canvas, paint, opacity * mOpacity);
|
||||
}
|
||||
|
||||
this.setClientRect(template.getClientRect());
|
||||
|
||||
template.restoreCanvas(canvas, count);
|
||||
if (template instanceof RenderableView) {
|
||||
((RenderableView)template).resetProperties();
|
||||
}
|
||||
if (template == null) {
|
||||
FLog.w(
|
||||
ReactConstants.TAG,
|
||||
"`Use` element expected a pre-defined svg template as `href` prop, "
|
||||
+ "template named: "
|
||||
+ mHref
|
||||
+ " is not defined.");
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
int hitTest(float[] src) {
|
||||
if (!mInvertible || !mTransformInvertible) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
float[] dst = new float[2];
|
||||
mInvMatrix.mapPoints(dst, src);
|
||||
mInvTransform.mapPoints(dst);
|
||||
|
||||
VirtualView template = getSvgView().getDefinedTemplate(mHref);
|
||||
if (template == null) {
|
||||
FLog.w(ReactConstants.TAG, "`Use` element expected a pre-defined svg template as `href` prop, " +
|
||||
"template named: " + mHref + " is not defined.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int hitChild = template.hitTest(dst);
|
||||
if (hitChild != -1) {
|
||||
return (template.isResponsible() || hitChild != template.getId()) ? hitChild : getId();
|
||||
}
|
||||
|
||||
return -1;
|
||||
template.clearCache();
|
||||
canvas.translate((float) relativeOnWidth(mX), (float) relativeOnHeight(mY));
|
||||
if (template instanceof RenderableView) {
|
||||
((RenderableView) template).mergeProperties(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
Path getPath(Canvas canvas, Paint paint) {
|
||||
VirtualView template = getSvgView().getDefinedTemplate(mHref);
|
||||
if (template == null) {
|
||||
FLog.w(ReactConstants.TAG, "`Use` element expected a pre-defined svg template as `href` prop, " +
|
||||
"template named: " + mHref + " is not defined.");
|
||||
return null;
|
||||
}
|
||||
Path path = template.getPath(canvas, paint);
|
||||
Path use = new Path();
|
||||
Matrix m = new Matrix();
|
||||
m.setTranslate((float) relativeOnWidth(mX), (float) relativeOnHeight(mY));
|
||||
path.transform(m, use);
|
||||
return use;
|
||||
int count = template.saveAndSetupCanvas(canvas, mCTM);
|
||||
clip(canvas, paint);
|
||||
|
||||
if (template instanceof SymbolView) {
|
||||
SymbolView symbol = (SymbolView) template;
|
||||
symbol.drawSymbol(
|
||||
canvas, paint, opacity, (float) relativeOnWidth(mW), (float) relativeOnHeight(mH));
|
||||
} else {
|
||||
template.draw(canvas, paint, opacity * mOpacity);
|
||||
}
|
||||
|
||||
this.setClientRect(template.getClientRect());
|
||||
|
||||
template.restoreCanvas(canvas, count);
|
||||
if (template instanceof RenderableView) {
|
||||
((RenderableView) template).resetProperties();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
int hitTest(float[] src) {
|
||||
if (!mInvertible || !mTransformInvertible) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
float[] dst = new float[2];
|
||||
mInvMatrix.mapPoints(dst, src);
|
||||
mInvTransform.mapPoints(dst);
|
||||
|
||||
VirtualView template = getSvgView().getDefinedTemplate(mHref);
|
||||
if (template == null) {
|
||||
FLog.w(
|
||||
ReactConstants.TAG,
|
||||
"`Use` element expected a pre-defined svg template as `href` prop, "
|
||||
+ "template named: "
|
||||
+ mHref
|
||||
+ " is not defined.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int hitChild = template.hitTest(dst);
|
||||
if (hitChild != -1) {
|
||||
return (template.isResponsible() || hitChild != template.getId()) ? hitChild : getId();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
Path getPath(Canvas canvas, Paint paint) {
|
||||
VirtualView template = getSvgView().getDefinedTemplate(mHref);
|
||||
if (template == null) {
|
||||
FLog.w(
|
||||
ReactConstants.TAG,
|
||||
"`Use` element expected a pre-defined svg template as `href` prop, "
|
||||
+ "template named: "
|
||||
+ mHref
|
||||
+ " is not defined.");
|
||||
return null;
|
||||
}
|
||||
Path path = template.getPath(canvas, paint);
|
||||
Path use = new Path();
|
||||
Matrix m = new Matrix();
|
||||
m.setTranslate((float) relativeOnWidth(mX), (float) relativeOnHeight(mY));
|
||||
path.transform(m, use);
|
||||
return use;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.graphics.Matrix;
|
||||
@@ -14,90 +13,91 @@ import android.graphics.RectF;
|
||||
|
||||
class ViewBox {
|
||||
|
||||
private static final int MOS_MEET = 0;
|
||||
private static final int MOS_SLICE = 1;
|
||||
private static final int MOS_NONE = 2;
|
||||
private static final int MOS_MEET = 0;
|
||||
private static final int MOS_SLICE = 1;
|
||||
private static final int MOS_NONE = 2;
|
||||
|
||||
static Matrix getTransform(RectF vbRect, RectF eRect, String align, int meetOrSlice) {
|
||||
// based on https://svgwg.org/svg2-draft/coords.html#ComputingAViewportsTransform
|
||||
static Matrix getTransform(RectF vbRect, RectF eRect, String align, int meetOrSlice) {
|
||||
// based on https://svgwg.org/svg2-draft/coords.html#ComputingAViewportsTransform
|
||||
|
||||
// Let vb-x, vb-y, vb-width, vb-height be the min-x, min-y, width and height values of the viewBox attribute respectively.
|
||||
double vbX = vbRect.left;
|
||||
double vbY = vbRect.top;
|
||||
double vbWidth = vbRect.width();
|
||||
double vbHeight = vbRect.height();
|
||||
// Let vb-x, vb-y, vb-width, vb-height be the min-x, min-y, width and height values of the
|
||||
// viewBox attribute respectively.
|
||||
double vbX = vbRect.left;
|
||||
double vbY = vbRect.top;
|
||||
double vbWidth = vbRect.width();
|
||||
double vbHeight = vbRect.height();
|
||||
|
||||
// Let e-x, e-y, e-width, e-height be the position and size of the element respectively.
|
||||
double eX = eRect.left;
|
||||
double eY = eRect.top;
|
||||
double eWidth = eRect.width();
|
||||
double eHeight = eRect.height();
|
||||
// Let e-x, e-y, e-width, e-height be the position and size of the element respectively.
|
||||
double eX = eRect.left;
|
||||
double eY = eRect.top;
|
||||
double eWidth = eRect.width();
|
||||
double eHeight = eRect.height();
|
||||
|
||||
// Initialize scale-x to e-width/vb-width.
|
||||
double scaleX = eWidth / vbWidth;
|
||||
|
||||
// Initialize scale-x to e-width/vb-width.
|
||||
double scaleX = eWidth / vbWidth;
|
||||
// Initialize scale-y to e-height/vb-height.
|
||||
double scaleY = eHeight / vbHeight;
|
||||
|
||||
// Initialize scale-y to e-height/vb-height.
|
||||
double scaleY = eHeight / vbHeight;
|
||||
// Initialize translate-x to e-x - (vb-x * scale-x).
|
||||
// Initialize translate-y to e-y - (vb-y * scale-y).
|
||||
double translateX = eX - (vbX * scaleX);
|
||||
double translateY = eY - (vbY * scaleY);
|
||||
|
||||
// Initialize translate-x to e-x - (vb-x * scale-x).
|
||||
// Initialize translate-y to e-y - (vb-y * scale-y).
|
||||
double translateX = eX - (vbX * scaleX);
|
||||
double translateY = eY - (vbY * scaleY);
|
||||
// If align is 'none'
|
||||
if (meetOrSlice == MOS_NONE) {
|
||||
// Let scale be set the smaller value of scale-x and scale-y.
|
||||
// Assign scale-x and scale-y to scale.
|
||||
double scale = scaleX = scaleY = Math.min(scaleX, scaleY);
|
||||
|
||||
// If align is 'none'
|
||||
if (meetOrSlice == MOS_NONE) {
|
||||
// Let scale be set the smaller value of scale-x and scale-y.
|
||||
// Assign scale-x and scale-y to scale.
|
||||
double scale = scaleX = scaleY = Math.min(scaleX, scaleY);
|
||||
// If scale is greater than 1
|
||||
if (scale > 1) {
|
||||
// Minus translateX by (eWidth / scale - vbWidth) / 2
|
||||
// Minus translateY by (eHeight / scale - vbHeight) / 2
|
||||
translateX -= (eWidth / scale - vbWidth) / 2;
|
||||
translateY -= (eHeight / scale - vbHeight) / 2;
|
||||
} else {
|
||||
translateX -= (eWidth - vbWidth * scale) / 2;
|
||||
translateY -= (eHeight - vbHeight * scale) / 2;
|
||||
}
|
||||
} else {
|
||||
// If align is not 'none' and meetOrSlice is 'meet', set the larger of scale-x and scale-y to
|
||||
// the smaller.
|
||||
// Otherwise, if align is not 'none' and meetOrSlice is 'slice', set the smaller of scale-x
|
||||
// and scale-y to the larger.
|
||||
|
||||
// If scale is greater than 1
|
||||
if (scale > 1) {
|
||||
// Minus translateX by (eWidth / scale - vbWidth) / 2
|
||||
// Minus translateY by (eHeight / scale - vbHeight) / 2
|
||||
translateX -= (eWidth / scale - vbWidth) / 2;
|
||||
translateY -= (eHeight / scale - vbHeight) / 2;
|
||||
} else {
|
||||
translateX -= (eWidth - vbWidth * scale) / 2;
|
||||
translateY -= (eHeight - vbHeight * scale) / 2;
|
||||
}
|
||||
} else {
|
||||
// If align is not 'none' and meetOrSlice is 'meet', set the larger of scale-x and scale-y to the smaller.
|
||||
// Otherwise, if align is not 'none' and meetOrSlice is 'slice', set the smaller of scale-x and scale-y to the larger.
|
||||
if (!align.equals("none") && meetOrSlice == MOS_MEET) {
|
||||
scaleX = scaleY = Math.min(scaleX, scaleY);
|
||||
} else if (!align.equals("none") && meetOrSlice == MOS_SLICE) {
|
||||
scaleX = scaleY = Math.max(scaleX, scaleY);
|
||||
}
|
||||
|
||||
if (!align.equals("none") && meetOrSlice == MOS_MEET) {
|
||||
scaleX = scaleY = Math.min(scaleX, scaleY);
|
||||
} else if (!align.equals("none") && meetOrSlice == MOS_SLICE) {
|
||||
scaleX = scaleY = Math.max(scaleX, scaleY);
|
||||
}
|
||||
// If align contains 'xMid', add (e-width - vb-width * scale-x) / 2 to translate-x.
|
||||
if (align.contains("xMid")) {
|
||||
translateX += (eWidth - vbWidth * scaleX) / 2.0d;
|
||||
}
|
||||
|
||||
// If align contains 'xMid', add (e-width - vb-width * scale-x) / 2 to translate-x.
|
||||
if (align.contains("xMid")) {
|
||||
translateX += (eWidth - vbWidth * scaleX) / 2.0d;
|
||||
}
|
||||
// If align contains 'xMax', add (e-width - vb-width * scale-x) to translate-x.
|
||||
if (align.contains("xMax")) {
|
||||
translateX += (eWidth - vbWidth * scaleX);
|
||||
}
|
||||
|
||||
// If align contains 'xMax', add (e-width - vb-width * scale-x) to translate-x.
|
||||
if (align.contains("xMax")) {
|
||||
translateX += (eWidth - vbWidth * scaleX);
|
||||
}
|
||||
// If align contains 'yMid', add (e-height - vb-height * scale-y) / 2 to translate-y.
|
||||
if (align.contains("YMid")) {
|
||||
translateY += (eHeight - vbHeight * scaleY) / 2.0d;
|
||||
}
|
||||
|
||||
// If align contains 'yMid', add (e-height - vb-height * scale-y) / 2 to translate-y.
|
||||
if (align.contains("YMid")) {
|
||||
translateY += (eHeight - vbHeight * scaleY) / 2.0d;
|
||||
}
|
||||
|
||||
// If align contains 'yMax', add (e-height - vb-height * scale-y) to translate-y.
|
||||
if (align.contains("YMax")) {
|
||||
translateY += (eHeight - vbHeight * scaleY);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// The transform applied to content contained by the element is given by
|
||||
// translate(translate-x, translate-y) scale(scale-x, scale-y).
|
||||
Matrix transform = new Matrix();
|
||||
transform.postTranslate((float) translateX, (float) translateY);
|
||||
transform.preScale((float) scaleX, (float) scaleY);
|
||||
return transform;
|
||||
// If align contains 'yMax', add (e-height - vb-height * scale-y) to translate-y.
|
||||
if (align.contains("YMax")) {
|
||||
translateY += (eHeight - vbHeight * scaleY);
|
||||
}
|
||||
}
|
||||
|
||||
// The transform applied to content contained by the element is given by
|
||||
// translate(translate-x, translate-y) scale(scale-x, scale-y).
|
||||
Matrix transform = new Matrix();
|
||||
transform.postTranslate((float) translateX, (float) translateY);
|
||||
transform.preScale((float) scaleX, (float) scaleY);
|
||||
return transform;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,7 @@
|
||||
package com.horcrux.svg;
|
||||
|
||||
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.views.view.ReactViewGroup;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class FabricEnabledViewGroup extends ReactViewGroup {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
@interface RNSVGBrush : NSObject
|
||||
|
||||
@property (nonatomic, strong) NSString* brushRef;
|
||||
@property (nonatomic, strong) NSString *brushRef;
|
||||
|
||||
/* @abstract */
|
||||
- (instancetype)initWithArray:(NSArray *)data;
|
||||
|
||||
@@ -13,31 +13,30 @@
|
||||
|
||||
- (instancetype)initWithArray:(NSArray *)data
|
||||
{
|
||||
return [super init];
|
||||
return [super init];
|
||||
}
|
||||
|
||||
- (void)paint:(CGContextRef)context opacity:(CGFloat)opacity painter:(RNSVGPainter *)painter bounds:(CGRect)bounds
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (BOOL)applyFillColor:(CGContextRef)context opacity:(CGFloat)opacity
|
||||
{
|
||||
return NO;
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)applyStrokeColor:(CGContextRef)context opacity:(CGFloat)opacity
|
||||
{
|
||||
return NO;
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (CGColorRef)getColorWithOpacity:(CGFloat)opacity
|
||||
{
|
||||
return nil;
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)paint:(CGContextRef)context opacity:(CGFloat)opacity painter:(RNSVGPainter *)painter
|
||||
{
|
||||
// abstract
|
||||
// abstract
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
kRNSVGUndefinedType,
|
||||
kRNSVGLinearGradient,
|
||||
kRNSVGRadialGradient,
|
||||
kRNSVGPattern
|
||||
kRNSVGUndefinedType,
|
||||
kRNSVGLinearGradient,
|
||||
kRNSVGRadialGradient,
|
||||
kRNSVGPattern
|
||||
} RNSVGBrushType;
|
||||
|
||||
@@ -7,31 +7,30 @@
|
||||
*/
|
||||
|
||||
#import "RNSVGContextBrush.h"
|
||||
#import "RNSVGRenderable.h"
|
||||
#import "RNSVGNode.h"
|
||||
#import "RNSVGRenderable.h"
|
||||
|
||||
#import "RCTConvert+RNSVG.h"
|
||||
#import <React/RCTLog.h>
|
||||
#import "RCTConvert+RNSVG.h"
|
||||
|
||||
@implementation RNSVGContextBrush
|
||||
{
|
||||
BOOL _isStroke;
|
||||
@implementation RNSVGContextBrush {
|
||||
BOOL _isStroke;
|
||||
}
|
||||
|
||||
- (instancetype)initFill
|
||||
{
|
||||
if ((self = [super initWithArray:nil])) {
|
||||
_isStroke = NO;
|
||||
}
|
||||
return self;
|
||||
if ((self = [super initWithArray:nil])) {
|
||||
_isStroke = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initStroke
|
||||
{
|
||||
if ((self = [super initWithArray:nil])) {
|
||||
_isStroke = YES;
|
||||
}
|
||||
return self;
|
||||
if ((self = [super initWithArray:nil])) {
|
||||
_isStroke = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
@@ -40,46 +39,44 @@
|
||||
|
||||
- (BOOL)applyFillColor:(CGContextRef)context opacity:(CGFloat)opacity
|
||||
{
|
||||
RNSVGRenderable *element = RNSVGRenderable.contextElement;
|
||||
if (!element) {
|
||||
return NO;
|
||||
}
|
||||
RNSVGRenderable *element = RNSVGRenderable.contextElement;
|
||||
if (!element) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
RNSVGBrush *brush = _isStroke ? element.stroke : element.fill;
|
||||
RNSVGBrush *brush = _isStroke ? element.stroke : element.fill;
|
||||
|
||||
BOOL fillColor;
|
||||
BOOL fillColor;
|
||||
|
||||
if (brush.class == RNSVGBrush.class) {
|
||||
CGContextSetFillColorWithColor(context, [element.tintColor CGColor]);
|
||||
fillColor = YES;
|
||||
} else {
|
||||
fillColor = [brush applyFillColor:context opacity:opacity];
|
||||
}
|
||||
if (brush.class == RNSVGBrush.class) {
|
||||
CGContextSetFillColorWithColor(context, [element.tintColor CGColor]);
|
||||
fillColor = YES;
|
||||
} else {
|
||||
fillColor = [brush applyFillColor:context opacity:opacity];
|
||||
}
|
||||
|
||||
return fillColor;
|
||||
return fillColor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (BOOL)applyStrokeColor:(CGContextRef)context opacity:(CGFloat)opacity
|
||||
{
|
||||
RNSVGRenderable *element = RNSVGRenderable.contextElement;
|
||||
if (!element) {
|
||||
return NO;
|
||||
}
|
||||
RNSVGRenderable *element = RNSVGRenderable.contextElement;
|
||||
if (!element) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
RNSVGBrush *brush = _isStroke ? element.stroke : element.fill;
|
||||
RNSVGBrush *brush = _isStroke ? element.stroke : element.fill;
|
||||
|
||||
BOOL strokeColor;
|
||||
BOOL strokeColor;
|
||||
|
||||
if (brush.class == RNSVGBrush.class) {
|
||||
CGContextSetStrokeColorWithColor(context, [element.tintColor CGColor]);
|
||||
strokeColor = YES;
|
||||
} else {
|
||||
strokeColor = [brush applyStrokeColor:context opacity:opacity];
|
||||
}
|
||||
if (brush.class == RNSVGBrush.class) {
|
||||
CGContextSetStrokeColorWithColor(context, [element.tintColor CGColor]);
|
||||
strokeColor = YES;
|
||||
} else {
|
||||
strokeColor = [brush applyStrokeColor:context opacity:opacity];
|
||||
}
|
||||
|
||||
return YES;
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
|
||||
#import "RCTConvert+RNSVG.h"
|
||||
#import "RNSVGBrushType.h"
|
||||
#import "RNSVGUnits.h"
|
||||
#import "RNSVGLength.h"
|
||||
#import "RNSVGUnits.h"
|
||||
|
||||
@class RNSVGPattern;
|
||||
|
||||
@interface RNSVGPainter : NSObject
|
||||
|
||||
@property (nonatomic, assign) RNSVGPattern* pattern;
|
||||
@property (nonatomic, assign) RNSVGPattern *pattern;
|
||||
@property (nonatomic, assign) CGRect paintBounds;
|
||||
@property (nonatomic, assign) bool useObjectBoundingBoxForContentUnits;
|
||||
@property (nonatomic, assign) CGRect bounds;
|
||||
|
||||
@@ -10,246 +10,242 @@
|
||||
#import "RNSVGPattern.h"
|
||||
#import "RNSVGViewBox.h"
|
||||
|
||||
@implementation RNSVGPainter
|
||||
{
|
||||
NSArray<RNSVGLength *> *_points;
|
||||
NSArray<NSNumber *> *_colors;
|
||||
RNSVGBrushType _type;
|
||||
BOOL _useObjectBoundingBox;
|
||||
BOOL _useContentObjectBoundingBox;
|
||||
CGAffineTransform _transform;
|
||||
CGRect _userSpaceBoundingBox;
|
||||
@implementation RNSVGPainter {
|
||||
NSArray<RNSVGLength *> *_points;
|
||||
NSArray<NSNumber *> *_colors;
|
||||
RNSVGBrushType _type;
|
||||
BOOL _useObjectBoundingBox;
|
||||
BOOL _useContentObjectBoundingBox;
|
||||
CGAffineTransform _transform;
|
||||
CGRect _userSpaceBoundingBox;
|
||||
}
|
||||
|
||||
- (instancetype)initWithPointsArray:(NSArray<RNSVGLength *> *)pointsArray
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_points = pointsArray;
|
||||
}
|
||||
return self;
|
||||
if ((self = [super init])) {
|
||||
_points = pointsArray;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
||||
RCT_NOT_IMPLEMENTED(-(instancetype)init)
|
||||
|
||||
- (void)setUnits:(RNSVGUnits)unit
|
||||
{
|
||||
_useObjectBoundingBox = unit == kRNSVGUnitsObjectBoundingBox;
|
||||
_useObjectBoundingBox = unit == kRNSVGUnitsObjectBoundingBox;
|
||||
}
|
||||
|
||||
- (void)setContentUnits:(RNSVGUnits)unit
|
||||
{
|
||||
_useContentObjectBoundingBox = unit == kRNSVGUnitsObjectBoundingBox;
|
||||
_useContentObjectBoundingBox = unit == kRNSVGUnitsObjectBoundingBox;
|
||||
}
|
||||
|
||||
- (void)setUserSpaceBoundingBox:(CGRect)userSpaceBoundingBox
|
||||
{
|
||||
_userSpaceBoundingBox = userSpaceBoundingBox;
|
||||
_userSpaceBoundingBox = userSpaceBoundingBox;
|
||||
}
|
||||
|
||||
- (void)setTransform:(CGAffineTransform)transform
|
||||
{
|
||||
_transform = transform;
|
||||
_transform = transform;
|
||||
}
|
||||
|
||||
- (void)setPattern:(RNSVGPattern *)pattern
|
||||
{
|
||||
if (_type != kRNSVGUndefinedType) {
|
||||
// todo: throw error
|
||||
return;
|
||||
}
|
||||
if (_type != kRNSVGUndefinedType) {
|
||||
// todo: throw error
|
||||
return;
|
||||
}
|
||||
|
||||
_type = kRNSVGPattern;
|
||||
_pattern = pattern;
|
||||
_type = kRNSVGPattern;
|
||||
_pattern = pattern;
|
||||
}
|
||||
|
||||
- (void)setLinearGradientColors:(NSArray<NSNumber *> *)colors
|
||||
{
|
||||
if (_type != kRNSVGUndefinedType) {
|
||||
// todo: throw error
|
||||
return;
|
||||
}
|
||||
if (_type != kRNSVGUndefinedType) {
|
||||
// todo: throw error
|
||||
return;
|
||||
}
|
||||
|
||||
_type = kRNSVGLinearGradient;
|
||||
_colors = colors;
|
||||
_type = kRNSVGLinearGradient;
|
||||
_colors = colors;
|
||||
}
|
||||
|
||||
- (void)setRadialGradientColors:(NSArray<NSNumber *> *)colors
|
||||
{
|
||||
if (_type != kRNSVGUndefinedType) {
|
||||
// todo: throw error
|
||||
return;
|
||||
}
|
||||
if (_type != kRNSVGUndefinedType) {
|
||||
// todo: throw error
|
||||
return;
|
||||
}
|
||||
|
||||
_type = kRNSVGRadialGradient;
|
||||
_colors = colors;
|
||||
_type = kRNSVGRadialGradient;
|
||||
_colors = colors;
|
||||
}
|
||||
|
||||
- (void)paint:(CGContextRef)context bounds:(CGRect)bounds
|
||||
{
|
||||
if (_type == kRNSVGLinearGradient) {
|
||||
[self paintLinearGradient:context bounds:bounds];
|
||||
} else if (_type == kRNSVGRadialGradient) {
|
||||
[self paintRadialGradient:context bounds:bounds];
|
||||
} else if (_type == kRNSVGPattern) {
|
||||
[self paintPattern:context bounds:bounds];
|
||||
}
|
||||
if (_type == kRNSVGLinearGradient) {
|
||||
[self paintLinearGradient:context bounds:bounds];
|
||||
} else if (_type == kRNSVGRadialGradient) {
|
||||
[self paintRadialGradient:context bounds:bounds];
|
||||
} else if (_type == kRNSVGPattern) {
|
||||
[self paintPattern:context bounds:bounds];
|
||||
}
|
||||
}
|
||||
|
||||
- (CGRect)getPaintRect:(CGContextRef)context bounds:(CGRect)bounds
|
||||
{
|
||||
CGRect rect = _useObjectBoundingBox ? bounds : _userSpaceBoundingBox;
|
||||
CGFloat height = CGRectGetHeight(rect);
|
||||
CGFloat width = CGRectGetWidth(rect);
|
||||
CGFloat x = 0.0;
|
||||
CGFloat y = 0.0;
|
||||
CGRect rect = _useObjectBoundingBox ? bounds : _userSpaceBoundingBox;
|
||||
CGFloat height = CGRectGetHeight(rect);
|
||||
CGFloat width = CGRectGetWidth(rect);
|
||||
CGFloat x = 0.0;
|
||||
CGFloat y = 0.0;
|
||||
|
||||
if (_useObjectBoundingBox) {
|
||||
x = CGRectGetMinX(rect);
|
||||
y = CGRectGetMinY(rect);
|
||||
}
|
||||
if (_useObjectBoundingBox) {
|
||||
x = CGRectGetMinX(rect);
|
||||
y = CGRectGetMinY(rect);
|
||||
}
|
||||
|
||||
return CGRectMake(x, y, width, height);
|
||||
return CGRectMake(x, y, width, height);
|
||||
}
|
||||
|
||||
void PatternFunction(void* info, CGContextRef context)
|
||||
void PatternFunction(void *info, CGContextRef context)
|
||||
{
|
||||
RNSVGPainter *_painter = (__bridge RNSVGPainter *)info;
|
||||
RNSVGPattern *_pattern = [_painter pattern];
|
||||
CGRect rect = _painter.paintBounds;
|
||||
CGFloat minX = _pattern.minX;
|
||||
CGFloat minY = _pattern.minY;
|
||||
CGFloat vbWidth = _pattern.vbWidth;
|
||||
CGFloat vbHeight = _pattern.vbHeight;
|
||||
if (vbWidth > 0 && vbHeight > 0) {
|
||||
CGRect vbRect = CGRectMake(minX, minY, vbWidth, vbHeight);
|
||||
CGAffineTransform _viewBoxTransform = [RNSVGViewBox
|
||||
getTransform:vbRect
|
||||
eRect:rect
|
||||
align:_pattern.align
|
||||
meetOrSlice:_pattern.meetOrSlice];
|
||||
CGContextConcatCTM(context, _viewBoxTransform);
|
||||
}
|
||||
RNSVGPainter *_painter = (__bridge RNSVGPainter *)info;
|
||||
RNSVGPattern *_pattern = [_painter pattern];
|
||||
CGRect rect = _painter.paintBounds;
|
||||
CGFloat minX = _pattern.minX;
|
||||
CGFloat minY = _pattern.minY;
|
||||
CGFloat vbWidth = _pattern.vbWidth;
|
||||
CGFloat vbHeight = _pattern.vbHeight;
|
||||
if (vbWidth > 0 && vbHeight > 0) {
|
||||
CGRect vbRect = CGRectMake(minX, minY, vbWidth, vbHeight);
|
||||
CGAffineTransform _viewBoxTransform = [RNSVGViewBox getTransform:vbRect
|
||||
eRect:rect
|
||||
align:_pattern.align
|
||||
meetOrSlice:_pattern.meetOrSlice];
|
||||
CGContextConcatCTM(context, _viewBoxTransform);
|
||||
}
|
||||
|
||||
if (_painter.useObjectBoundingBoxForContentUnits) {
|
||||
CGRect bounds = _painter.bounds;
|
||||
CGContextConcatCTM(context, CGAffineTransformMakeScale(bounds.size.width, bounds.size.height));
|
||||
}
|
||||
if (_painter.useObjectBoundingBoxForContentUnits) {
|
||||
CGRect bounds = _painter.bounds;
|
||||
CGContextConcatCTM(context, CGAffineTransformMakeScale(bounds.size.width, bounds.size.height));
|
||||
}
|
||||
|
||||
[_pattern renderTo:context rect:rect];
|
||||
[_pattern renderTo:context rect:rect];
|
||||
}
|
||||
|
||||
- (CGFloat)getVal:(RNSVGLength*)length relative:(CGFloat)relative
|
||||
- (CGFloat)getVal:(RNSVGLength *)length relative:(CGFloat)relative
|
||||
{
|
||||
RNSVGLengthUnitType unit = [length unit];
|
||||
CGFloat val = [RNSVGPropHelper fromRelative:length
|
||||
relative:relative];
|
||||
return _useObjectBoundingBox &&
|
||||
unit == SVG_LENGTHTYPE_NUMBER ? val * relative : val;
|
||||
RNSVGLengthUnitType unit = [length unit];
|
||||
CGFloat val = [RNSVGPropHelper fromRelative:length relative:relative];
|
||||
return _useObjectBoundingBox && unit == SVG_LENGTHTYPE_NUMBER ? val * relative : val;
|
||||
}
|
||||
|
||||
- (void)paintPattern:(CGContextRef)context bounds:(CGRect)bounds
|
||||
{
|
||||
CGRect rect = [self getPaintRect:context bounds:bounds];
|
||||
CGFloat height = CGRectGetHeight(rect);
|
||||
CGFloat width = CGRectGetWidth(rect);
|
||||
CGRect rect = [self getPaintRect:context bounds:bounds];
|
||||
CGFloat height = CGRectGetHeight(rect);
|
||||
CGFloat width = CGRectGetWidth(rect);
|
||||
|
||||
CGFloat x = [self getVal:[_points objectAtIndex:0] relative:width];
|
||||
CGFloat y = [self getVal:[_points objectAtIndex:1] relative:height];
|
||||
CGFloat w = [self getVal:[_points objectAtIndex:2] relative:width];
|
||||
CGFloat h = [self getVal:[_points objectAtIndex:3] relative:height];
|
||||
CGFloat x = [self getVal:[_points objectAtIndex:0] relative:width];
|
||||
CGFloat y = [self getVal:[_points objectAtIndex:1] relative:height];
|
||||
CGFloat w = [self getVal:[_points objectAtIndex:2] relative:width];
|
||||
CGFloat h = [self getVal:[_points objectAtIndex:3] relative:height];
|
||||
|
||||
CGAffineTransform viewbox = [self.pattern.svgView getViewBoxTransform];
|
||||
CGAffineTransform viewbox = [self.pattern.svgView getViewBoxTransform];
|
||||
#if TARGET_OS_OSX
|
||||
// This is needed because macOS and iOS have different conventions for where the origin is.
|
||||
// For macOS, it's in the bottom-left corner. For iOS, it's in the top-left corner.
|
||||
viewbox = CGAffineTransformScale(viewbox, 1, -1);
|
||||
// This is needed because macOS and iOS have different conventions for where the origin is.
|
||||
// For macOS, it's in the bottom-left corner. For iOS, it's in the top-left corner.
|
||||
viewbox = CGAffineTransformScale(viewbox, 1, -1);
|
||||
#endif // TARGET_OS_OSX
|
||||
CGRect newBounds = CGRectMake(x, y, w, h);
|
||||
CGSize size = newBounds.size;
|
||||
self.useObjectBoundingBoxForContentUnits = _useContentObjectBoundingBox;
|
||||
self.paintBounds = newBounds;
|
||||
self.bounds = rect;
|
||||
CGRect newBounds = CGRectMake(x, y, w, h);
|
||||
CGSize size = newBounds.size;
|
||||
self.useObjectBoundingBoxForContentUnits = _useContentObjectBoundingBox;
|
||||
self.paintBounds = newBounds;
|
||||
self.bounds = rect;
|
||||
|
||||
const CGPatternCallbacks callbacks = { 0, &PatternFunction, NULL };
|
||||
CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL);
|
||||
CGContextSetFillColorSpace(context, patternSpace);
|
||||
CGColorSpaceRelease(patternSpace);
|
||||
const CGPatternCallbacks callbacks = {0, &PatternFunction, NULL};
|
||||
CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL);
|
||||
CGContextSetFillColorSpace(context, patternSpace);
|
||||
CGColorSpaceRelease(patternSpace);
|
||||
|
||||
CGPatternRef pattern = CGPatternCreate((__bridge void * _Nullable)(self),
|
||||
newBounds,
|
||||
viewbox,
|
||||
size.width,
|
||||
size.height,
|
||||
kCGPatternTilingConstantSpacing,
|
||||
true,
|
||||
&callbacks);
|
||||
CGFloat alpha = 1.0;
|
||||
CGContextSetFillPattern(context, pattern, &alpha);
|
||||
CGPatternRelease(pattern);
|
||||
CGPatternRef pattern = CGPatternCreate(
|
||||
(__bridge void *_Nullable)(self),
|
||||
newBounds,
|
||||
viewbox,
|
||||
size.width,
|
||||
size.height,
|
||||
kCGPatternTilingConstantSpacing,
|
||||
true,
|
||||
&callbacks);
|
||||
CGFloat alpha = 1.0;
|
||||
CGContextSetFillPattern(context, pattern, &alpha);
|
||||
CGPatternRelease(pattern);
|
||||
|
||||
CGContextFillRect(context, bounds);
|
||||
CGContextFillRect(context, bounds);
|
||||
}
|
||||
|
||||
- (void)paintLinearGradient:(CGContextRef)context bounds:(CGRect)bounds
|
||||
{
|
||||
if ([_colors count] == 0) {
|
||||
RCTLogWarn(@"No stops in gradient");
|
||||
return;
|
||||
}
|
||||
CGGradientRef gradient = CGGradientRetain([RCTConvert RNSVGCGGradient:_colors]);
|
||||
CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
|
||||
if ([_colors count] == 0) {
|
||||
RCTLogWarn(@"No stops in gradient");
|
||||
return;
|
||||
}
|
||||
CGGradientRef gradient = CGGradientRetain([RCTConvert RNSVGCGGradient:_colors]);
|
||||
CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
|
||||
|
||||
CGRect rect = [self getPaintRect:context bounds:bounds];
|
||||
CGFloat height = CGRectGetHeight(rect);
|
||||
CGFloat width = CGRectGetWidth(rect);
|
||||
CGFloat offsetX = CGRectGetMinX(rect);
|
||||
CGFloat offsetY = CGRectGetMinY(rect);
|
||||
CGRect rect = [self getPaintRect:context bounds:bounds];
|
||||
CGFloat height = CGRectGetHeight(rect);
|
||||
CGFloat width = CGRectGetWidth(rect);
|
||||
CGFloat offsetX = CGRectGetMinX(rect);
|
||||
CGFloat offsetY = CGRectGetMinY(rect);
|
||||
|
||||
CGFloat x1 = [self getVal:[_points objectAtIndex:0] relative:width] + offsetX;
|
||||
CGFloat y1 = [self getVal:[_points objectAtIndex:1] relative:height] + offsetY;
|
||||
CGFloat x2 = [self getVal:[_points objectAtIndex:2] relative:width] + offsetX;
|
||||
CGFloat y2 = [self getVal:[_points objectAtIndex:3] relative:height] + offsetY;
|
||||
CGFloat x1 = [self getVal:[_points objectAtIndex:0] relative:width] + offsetX;
|
||||
CGFloat y1 = [self getVal:[_points objectAtIndex:1] relative:height] + offsetY;
|
||||
CGFloat x2 = [self getVal:[_points objectAtIndex:2] relative:width] + offsetX;
|
||||
CGFloat y2 = [self getVal:[_points objectAtIndex:3] relative:height] + offsetY;
|
||||
|
||||
CGContextConcatCTM(context, _transform);
|
||||
CGContextDrawLinearGradient(context, gradient, CGPointMake(x1, y1), CGPointMake(x2, y2), extendOptions);
|
||||
CGGradientRelease(gradient);
|
||||
CGContextConcatCTM(context, _transform);
|
||||
CGContextDrawLinearGradient(context, gradient, CGPointMake(x1, y1), CGPointMake(x2, y2), extendOptions);
|
||||
CGGradientRelease(gradient);
|
||||
}
|
||||
|
||||
- (void)paintRadialGradient:(CGContextRef)context bounds:(CGRect)bounds
|
||||
{
|
||||
if ([_colors count] == 0) {
|
||||
RCTLogWarn(@"No stops in gradient");
|
||||
return;
|
||||
}
|
||||
CGGradientRef gradient = CGGradientRetain([RCTConvert RNSVGCGGradient:_colors]);
|
||||
CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
|
||||
if ([_colors count] == 0) {
|
||||
RCTLogWarn(@"No stops in gradient");
|
||||
return;
|
||||
}
|
||||
CGGradientRef gradient = CGGradientRetain([RCTConvert RNSVGCGGradient:_colors]);
|
||||
CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
|
||||
|
||||
CGRect rect = [self getPaintRect:context bounds:bounds];
|
||||
CGRect rect = [self getPaintRect:context bounds:bounds];
|
||||
|
||||
CGFloat width = CGRectGetWidth(rect);
|
||||
CGFloat height = CGRectGetHeight(rect);
|
||||
CGFloat width = CGRectGetWidth(rect);
|
||||
CGFloat height = CGRectGetHeight(rect);
|
||||
|
||||
CGFloat offsetX = CGRectGetMinX(rect);
|
||||
CGFloat offsetY = CGRectGetMinY(rect);
|
||||
CGFloat offsetX = CGRectGetMinX(rect);
|
||||
CGFloat offsetY = CGRectGetMinY(rect);
|
||||
|
||||
CGFloat rx = [self getVal:[_points objectAtIndex:2] relative:width];
|
||||
CGFloat ry = [self getVal:[_points objectAtIndex:3] relative:height];
|
||||
CGFloat rx = [self getVal:[_points objectAtIndex:2] relative:width];
|
||||
CGFloat ry = [self getVal:[_points objectAtIndex:3] relative:height];
|
||||
|
||||
double ratio = ry / rx;
|
||||
double ratio = ry / rx;
|
||||
|
||||
CGFloat fx = [self getVal:[_points objectAtIndex:0] relative:width] + offsetX;
|
||||
CGFloat fy = ([self getVal:[_points objectAtIndex:1] relative:height] + offsetY) / ratio;
|
||||
CGFloat fx = [self getVal:[_points objectAtIndex:0] relative:width] + offsetX;
|
||||
CGFloat fy = ([self getVal:[_points objectAtIndex:1] relative:height] + offsetY) / ratio;
|
||||
|
||||
CGFloat cx = [self getVal:[_points objectAtIndex:4] relative:width] + offsetX;
|
||||
CGFloat cy = ([self getVal:[_points objectAtIndex:5] relative:height] + offsetY) / ratio;
|
||||
CGFloat cx = [self getVal:[_points objectAtIndex:4] relative:width] + offsetX;
|
||||
CGFloat cy = ([self getVal:[_points objectAtIndex:5] relative:height] + offsetY) / ratio;
|
||||
|
||||
CGAffineTransform transform = CGAffineTransformMakeScale(1, ratio);
|
||||
CGContextConcatCTM(context, transform);
|
||||
CGAffineTransform transform = CGAffineTransformMakeScale(1, ratio);
|
||||
CGContextConcatCTM(context, transform);
|
||||
|
||||
CGContextConcatCTM(context, _transform);
|
||||
CGContextDrawRadialGradient(context, gradient, CGPointMake(fx, fy), 0, CGPointMake(cx, cy), rx, extendOptions);
|
||||
CGGradientRelease(gradient);
|
||||
CGContextConcatCTM(context, _transform);
|
||||
CGContextDrawRadialGradient(context, gradient, CGPointMake(fx, fy), 0, CGPointMake(cx, cy), rx, extendOptions);
|
||||
CGGradientRelease(gradient);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -7,39 +7,38 @@
|
||||
*/
|
||||
|
||||
#import "RNSVGPainterBrush.h"
|
||||
#import "RNSVGPainter.h"
|
||||
#import "RCTConvert+RNSVG.h"
|
||||
#import <React/RCTLog.h>
|
||||
#import "RCTConvert+RNSVG.h"
|
||||
#import "RNSVGPainter.h"
|
||||
|
||||
@implementation RNSVGPainterBrush
|
||||
|
||||
- (instancetype)initWithArray:(NSArray *)array
|
||||
{
|
||||
if ((self = [super initWithArray:array])) {
|
||||
if (array.count != 2) {
|
||||
RCTLogError(@"-[%@ %@] expects 2 elements, received %@",
|
||||
self.class, NSStringFromSelector(_cmd), array);
|
||||
return nil;
|
||||
}
|
||||
|
||||
self.brushRef = [array objectAtIndex:1];
|
||||
if ((self = [super initWithArray:array])) {
|
||||
if (array.count != 2) {
|
||||
RCTLogError(@"-[%@ %@] expects 2 elements, received %@", self.class, NSStringFromSelector(_cmd), array);
|
||||
return nil;
|
||||
}
|
||||
return self;
|
||||
|
||||
self.brushRef = [array objectAtIndex:1];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)paint:(CGContextRef)context opacity:(CGFloat)opacity painter:(RNSVGPainter *)painter bounds:(CGRect)bounds
|
||||
{
|
||||
BOOL transparency = opacity < 1;
|
||||
if (transparency) {
|
||||
CGContextSetAlpha(context, opacity);
|
||||
CGContextBeginTransparencyLayer(context, NULL);
|
||||
}
|
||||
BOOL transparency = opacity < 1;
|
||||
if (transparency) {
|
||||
CGContextSetAlpha(context, opacity);
|
||||
CGContextBeginTransparencyLayer(context, NULL);
|
||||
}
|
||||
|
||||
[painter paint:context bounds:bounds];
|
||||
[painter paint:context bounds:bounds];
|
||||
|
||||
if (transparency) {
|
||||
CGContextEndTransparencyLayer(context);
|
||||
}
|
||||
if (transparency) {
|
||||
CGContextEndTransparencyLayer(context);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -9,65 +9,63 @@
|
||||
#import "RNSVGSolidColorBrush.h"
|
||||
#import "RNSVGUIKit.h"
|
||||
|
||||
#import "RCTConvert+RNSVG.h"
|
||||
#import <React/RCTLog.h>
|
||||
#import "RCTConvert+RNSVG.h"
|
||||
|
||||
@implementation RNSVGSolidColorBrush
|
||||
{
|
||||
RNSVGColor *_color;
|
||||
@implementation RNSVGSolidColorBrush {
|
||||
RNSVGColor *_color;
|
||||
}
|
||||
|
||||
- (instancetype)initWithArray:(NSArray<RNSVGLength *> *)array
|
||||
{
|
||||
if ((self = [super initWithArray:array])) {
|
||||
_color = [RCTConvert RNSVGColor:array offset:1];
|
||||
}
|
||||
return self;
|
||||
if ((self = [super initWithArray:array])) {
|
||||
_color = [RCTConvert RNSVGColor:array offset:1];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithNumber:(NSNumber *)number
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_color = [RCTConvert RNSVGColor:number];
|
||||
}
|
||||
return self;
|
||||
if ((self = [super init])) {
|
||||
_color = [RCTConvert RNSVGColor:number];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithColor:(RNSVGColor *)color
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_color = color;
|
||||
}
|
||||
return self;
|
||||
if ((self = [super init])) {
|
||||
_color = color;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
_color = nil;
|
||||
_color = nil;
|
||||
}
|
||||
|
||||
- (CGColorRef)getColorWithOpacity:(CGFloat)opacity
|
||||
{
|
||||
CGColorRef baseColor = _color.CGColor;
|
||||
CGColorRef color = CGColorCreateCopyWithAlpha(baseColor, opacity * CGColorGetAlpha(baseColor));
|
||||
return color;
|
||||
CGColorRef baseColor = _color.CGColor;
|
||||
CGColorRef color = CGColorCreateCopyWithAlpha(baseColor, opacity * CGColorGetAlpha(baseColor));
|
||||
return color;
|
||||
}
|
||||
|
||||
- (BOOL)applyFillColor:(CGContextRef)context opacity:(CGFloat)opacity
|
||||
{
|
||||
CGColorRef color = [self getColorWithOpacity:opacity];
|
||||
CGContextSetFillColorWithColor(context, color);
|
||||
CGColorRelease(color);
|
||||
return YES;
|
||||
CGColorRef color = [self getColorWithOpacity:opacity];
|
||||
CGContextSetFillColorWithColor(context, color);
|
||||
CGColorRelease(color);
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)applyStrokeColor:(CGContextRef)context opacity:(CGFloat)opacity
|
||||
{
|
||||
CGColorRef color = [self getColorWithOpacity:opacity];
|
||||
CGContextSetStrokeColorWithColor(context, color);
|
||||
CGColorRelease(color);
|
||||
return YES;
|
||||
CGColorRef color = [self getColorWithOpacity:opacity];
|
||||
CGContextSetStrokeColorWithColor(context, color);
|
||||
CGColorRelease(color);
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RCTConversions.h"
|
||||
#import <react/renderer/components/view/conversions.h>
|
||||
#import "RCTConversions.h"
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RNSVGFabricConversions.h"
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
@@ -39,33 +39,32 @@ using namespace facebook::react;
|
||||
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGClipPathProps>(props);
|
||||
setCommonNodeProps(newProps, self);
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGClipPathProps>(props);
|
||||
setCommonNodeProps(newProps, self);
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
[super prepareForRecycle];
|
||||
}
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
- (void)parseReference
|
||||
{
|
||||
self.dirty = false;
|
||||
[self.svgView defineClipPath:self clipPathName:self.name];
|
||||
self.dirty = false;
|
||||
[self.svgView defineClipPath:self clipPathName:self.name];
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)isSimpleClipPath
|
||||
{
|
||||
NSArray<RNSVGView*> *children = self.subviews;
|
||||
if (children.count == 1) {
|
||||
RNSVGView* child = children[0];
|
||||
if ([child class] != [RNSVGGroup class]) {
|
||||
return true;
|
||||
}
|
||||
NSArray<RNSVGView *> *children = self.subviews;
|
||||
if (children.count == 1) {
|
||||
RNSVGView *child = children[0];
|
||||
if ([child class] != [RNSVGGroup class]) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RCTConversions.h"
|
||||
#import <react/renderer/components/view/conversions.h>
|
||||
#import "RCTConversions.h"
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RNSVGFabricConversions.h"
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
@@ -39,23 +39,23 @@ using namespace facebook::react;
|
||||
|
||||
- (void)renderTo:(CGContextRef)context
|
||||
{
|
||||
// Defs do not render
|
||||
// Defs do not render
|
||||
}
|
||||
|
||||
- (void)parseReference
|
||||
{
|
||||
self.dirty = false;
|
||||
[self traverseSubviews:^(RNSVGNode *node) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
[node parseReference];
|
||||
}
|
||||
return YES;
|
||||
}];
|
||||
self.dirty = false;
|
||||
[self traverseSubviews:^(RNSVGNode *node) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
[node parseReference];
|
||||
}
|
||||
return YES;
|
||||
}];
|
||||
}
|
||||
|
||||
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
return nil;
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RCTConversions.h"
|
||||
#import <react/renderer/components/view/conversions.h>
|
||||
#import "RCTConversions.h"
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RNSVGFabricConversions.h"
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
@@ -41,177 +41,177 @@ using namespace facebook::react;
|
||||
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGForeignObjectProps>(props);
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGForeignObjectProps>(props);
|
||||
|
||||
self.x = RCTNSStringFromStringNilIfEmpty(newProps.x) ? [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)] : nil;
|
||||
self.y = RCTNSStringFromStringNilIfEmpty(newProps.y) ? [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)] : nil;
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.foreignObjectheight)) {
|
||||
self.foreignObjectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.foreignObjectheight)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.foreignObjectwidth)) {
|
||||
self.foreignObjectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.foreignObjectwidth)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
|
||||
self.foreignObjectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
|
||||
self.foreignObjectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
|
||||
}
|
||||
self.x = RCTNSStringFromStringNilIfEmpty(newProps.x)
|
||||
? [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)]
|
||||
: nil;
|
||||
self.y = RCTNSStringFromStringNilIfEmpty(newProps.y)
|
||||
? [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)]
|
||||
: nil;
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.foreignObjectheight)) {
|
||||
self.foreignObjectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.foreignObjectheight)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.foreignObjectwidth)) {
|
||||
self.foreignObjectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.foreignObjectwidth)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
|
||||
self.foreignObjectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
|
||||
self.foreignObjectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
|
||||
}
|
||||
|
||||
setCommonGroupProps(newProps, self);
|
||||
setCommonGroupProps(newProps, self);
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
_x = nil;
|
||||
_y = nil;
|
||||
_foreignObjectheight = nil;
|
||||
_foreignObjectwidth = nil;
|
||||
[super prepareForRecycle];
|
||||
_x = nil;
|
||||
_y = nil;
|
||||
_foreignObjectheight = nil;
|
||||
_foreignObjectwidth = nil;
|
||||
}
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
return nil;
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)parseReference
|
||||
{
|
||||
self.dirty = false;
|
||||
self.dirty = false;
|
||||
}
|
||||
|
||||
- (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect
|
||||
{
|
||||
[self clip:context];
|
||||
CGContextTranslateCTM(context, [self relativeOnWidth:self.x], [self relativeOnHeight:self.y]);
|
||||
CGRect clip = CGRectMake(
|
||||
0,
|
||||
0,
|
||||
[self relativeOnWidth:self.foreignObjectwidth],
|
||||
[self relativeOnHeight:self.foreignObjectheight]
|
||||
);
|
||||
CGContextClipToRect(context, clip);
|
||||
[super renderLayerTo:context rect:rect];
|
||||
[self clip:context];
|
||||
CGContextTranslateCTM(context, [self relativeOnWidth:self.x], [self relativeOnHeight:self.y]);
|
||||
CGRect clip = CGRectMake(
|
||||
0, 0, [self relativeOnWidth:self.foreignObjectwidth], [self relativeOnHeight:self.foreignObjectheight]);
|
||||
CGContextClipToRect(context, clip);
|
||||
[super renderLayerTo:context rect:rect];
|
||||
}
|
||||
|
||||
- (void)renderGroupTo:(CGContextRef)context rect:(CGRect)rect
|
||||
{
|
||||
[self pushGlyphContext];
|
||||
[self pushGlyphContext];
|
||||
|
||||
__block CGRect bounds = CGRectNull;
|
||||
|
||||
[self traverseSubviews:^(RNSVGView *node) {
|
||||
if ([node isKindOfClass:[RNSVGMask class]] || [node isKindOfClass:[RNSVGClipPath class]]) {
|
||||
// no-op
|
||||
} else if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
RNSVGNode* svgNode = (RNSVGNode*)node;
|
||||
if (svgNode.display && [@"none" isEqualToString:svgNode.display]) {
|
||||
return YES;
|
||||
}
|
||||
if (svgNode.responsible && !self.svgView.responsible) {
|
||||
self.svgView.responsible = YES;
|
||||
}
|
||||
|
||||
if ([node isKindOfClass:[RNSVGRenderable class]]) {
|
||||
[(RNSVGRenderable*)node mergeProperties:self];
|
||||
}
|
||||
|
||||
[svgNode renderTo:context rect:rect];
|
||||
|
||||
CGRect nodeRect = svgNode.clientRect;
|
||||
if (!CGRectIsEmpty(nodeRect)) {
|
||||
bounds = CGRectUnion(bounds, nodeRect);
|
||||
}
|
||||
|
||||
if ([node isKindOfClass:[RNSVGRenderable class]]) {
|
||||
[(RNSVGRenderable*)node resetProperties];
|
||||
}
|
||||
} else if ([node isKindOfClass:[RNSVGSvgView class]]) {
|
||||
RNSVGSvgView* svgView = (RNSVGSvgView*)node;
|
||||
CGFloat width = [self relativeOnWidth:svgView.bbWidth];
|
||||
CGFloat height = [self relativeOnHeight:svgView.bbHeight];
|
||||
CGRect rect = CGRectMake(0, 0, width, height);
|
||||
CGContextClipToRect(context, rect);
|
||||
[svgView drawToContext:context withRect:rect];
|
||||
} else {
|
||||
node.hidden = false;
|
||||
[node.layer renderInContext:context];
|
||||
node.hidden = true;
|
||||
}
|
||||
__block CGRect bounds = CGRectNull;
|
||||
|
||||
[self traverseSubviews:^(RNSVGView *node) {
|
||||
if ([node isKindOfClass:[RNSVGMask class]] || [node isKindOfClass:[RNSVGClipPath class]]) {
|
||||
// no-op
|
||||
} else if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
RNSVGNode *svgNode = (RNSVGNode *)node;
|
||||
if (svgNode.display && [@"none" isEqualToString:svgNode.display]) {
|
||||
return YES;
|
||||
}];
|
||||
CGPathRef path = [self getPath:context];
|
||||
[self setHitArea:path];
|
||||
if (!CGRectEqualToRect(bounds, CGRectNull)) {
|
||||
self.clientRect = bounds;
|
||||
self.fillBounds = CGPathGetBoundingBox(path);
|
||||
self.strokeBounds = CGPathGetBoundingBox(self.strokePath);
|
||||
self.pathBounds = CGRectUnion(self.fillBounds, self.strokeBounds);
|
||||
}
|
||||
if (svgNode.responsible && !self.svgView.responsible) {
|
||||
self.svgView.responsible = YES;
|
||||
}
|
||||
|
||||
CGAffineTransform current = CGContextGetCTM(context);
|
||||
CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM);
|
||||
if ([node isKindOfClass:[RNSVGRenderable class]]) {
|
||||
[(RNSVGRenderable *)node mergeProperties:self];
|
||||
}
|
||||
|
||||
self.ctm = svgToClientTransform;
|
||||
self.screenCTM = current;
|
||||
[svgNode renderTo:context rect:rect];
|
||||
|
||||
CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms);
|
||||
CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
|
||||
CGPoint center = CGPointApplyAffineTransform(mid, transform);
|
||||
CGRect nodeRect = svgNode.clientRect;
|
||||
if (!CGRectIsEmpty(nodeRect)) {
|
||||
bounds = CGRectUnion(bounds, nodeRect);
|
||||
}
|
||||
|
||||
self.bounds = bounds;
|
||||
if (!isnan(center.x) && !isnan(center.y)) {
|
||||
self.center = center;
|
||||
}
|
||||
self.frame = bounds;
|
||||
if ([node isKindOfClass:[RNSVGRenderable class]]) {
|
||||
[(RNSVGRenderable *)node resetProperties];
|
||||
}
|
||||
} else if ([node isKindOfClass:[RNSVGSvgView class]]) {
|
||||
RNSVGSvgView *svgView = (RNSVGSvgView *)node;
|
||||
CGFloat width = [self relativeOnWidth:svgView.bbWidth];
|
||||
CGFloat height = [self relativeOnHeight:svgView.bbHeight];
|
||||
CGRect rect = CGRectMake(0, 0, width, height);
|
||||
CGContextClipToRect(context, rect);
|
||||
[svgView drawToContext:context withRect:rect];
|
||||
} else {
|
||||
node.hidden = false;
|
||||
[node.layer renderInContext:context];
|
||||
node.hidden = true;
|
||||
}
|
||||
|
||||
[self popGlyphContext];
|
||||
return YES;
|
||||
}];
|
||||
CGPathRef path = [self getPath:context];
|
||||
[self setHitArea:path];
|
||||
if (!CGRectEqualToRect(bounds, CGRectNull)) {
|
||||
self.clientRect = bounds;
|
||||
self.fillBounds = CGPathGetBoundingBox(path);
|
||||
self.strokeBounds = CGPathGetBoundingBox(self.strokePath);
|
||||
self.pathBounds = CGRectUnion(self.fillBounds, self.strokeBounds);
|
||||
|
||||
CGAffineTransform current = CGContextGetCTM(context);
|
||||
CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM);
|
||||
|
||||
self.ctm = svgToClientTransform;
|
||||
self.screenCTM = current;
|
||||
|
||||
CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms);
|
||||
CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
|
||||
CGPoint center = CGPointApplyAffineTransform(mid, transform);
|
||||
|
||||
self.bounds = bounds;
|
||||
if (!isnan(center.x) && !isnan(center.y)) {
|
||||
self.center = center;
|
||||
}
|
||||
self.frame = bounds;
|
||||
}
|
||||
|
||||
[self popGlyphContext];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)rect
|
||||
{
|
||||
[self invalidate];
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setX:(RNSVGLength *)x
|
||||
{
|
||||
if ([x isEqualTo:_x]) {
|
||||
return;
|
||||
}
|
||||
if ([x isEqualTo:_x]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_x = x;
|
||||
[self invalidate];
|
||||
_x = x;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setY:(RNSVGLength *)y
|
||||
{
|
||||
if ([y isEqualTo:_y]) {
|
||||
return;
|
||||
}
|
||||
if ([y isEqualTo:_y]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_y = y;
|
||||
[self invalidate];
|
||||
_y = y;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setForeignObjectwidth:(RNSVGLength *)foreignObjectwidth
|
||||
{
|
||||
if ([foreignObjectwidth isEqualTo:_foreignObjectwidth]) {
|
||||
return;
|
||||
}
|
||||
if ([foreignObjectwidth isEqualTo:_foreignObjectwidth]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_foreignObjectwidth = foreignObjectwidth;
|
||||
[self invalidate];
|
||||
_foreignObjectwidth = foreignObjectwidth;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setForeignObjectheight:(RNSVGLength *)foreignObjectheight
|
||||
{
|
||||
if ([foreignObjectheight isEqualTo:_foreignObjectheight]) {
|
||||
return;
|
||||
}
|
||||
if ([foreignObjectheight isEqualTo:_foreignObjectheight]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_foreignObjectheight = foreignObjectheight;
|
||||
[self invalidate];
|
||||
_foreignObjectheight = foreignObjectheight;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
|
||||
#import "RNSVGUIKit.h"
|
||||
|
||||
#import "RNSVGContainer.h"
|
||||
#import "RNSVGCGFCRule.h"
|
||||
#import "RNSVGSvgView.h"
|
||||
#import "RNSVGPath.h"
|
||||
#import "RNSVGContainer.h"
|
||||
#import "RNSVGGlyphContext.h"
|
||||
#import "RNSVGPath.h"
|
||||
#import "RNSVGSvgView.h"
|
||||
|
||||
@interface RNSVGGroup : RNSVGPath <RNSVGContainer>
|
||||
|
||||
|
||||
@@ -10,18 +10,16 @@
|
||||
#import "RNSVGClipPath.h"
|
||||
#import "RNSVGMask.h"
|
||||
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RCTConversions.h"
|
||||
#import <react/renderer/components/view/conversions.h>
|
||||
#import "RCTConversions.h"
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RNSVGFabricConversions.h"
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
@implementation RNSVGGroup
|
||||
{
|
||||
RNSVGGlyphContext *_glyphContext;
|
||||
@implementation RNSVGGroup {
|
||||
RNSVGGlyphContext *_glyphContext;
|
||||
}
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
@@ -45,251 +43,251 @@ using namespace facebook::react;
|
||||
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGGroupProps>(props);
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGGroupProps>(props);
|
||||
|
||||
setCommonGroupProps(newProps, self);
|
||||
setCommonGroupProps(newProps, self);
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
_font = nil;
|
||||
_glyphContext = nil;
|
||||
[super prepareForRecycle];
|
||||
_font = nil;
|
||||
_glyphContext = nil;
|
||||
}
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
- (void)setFont:(NSDictionary*)font
|
||||
- (void)setFont:(NSDictionary *)font
|
||||
{
|
||||
if (font == _font) {
|
||||
return;
|
||||
}
|
||||
if (font == _font) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_font = font;
|
||||
[self invalidate];
|
||||
_font = font;
|
||||
}
|
||||
|
||||
- (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect
|
||||
{
|
||||
[self clip:context];
|
||||
[self setupGlyphContext:context];
|
||||
[self renderGroupTo:context rect:rect];
|
||||
[self clip:context];
|
||||
[self setupGlyphContext:context];
|
||||
[self renderGroupTo:context rect:rect];
|
||||
}
|
||||
|
||||
- (void)renderGroupTo:(CGContextRef)context rect:(CGRect)rect
|
||||
{
|
||||
[self pushGlyphContext];
|
||||
[self pushGlyphContext];
|
||||
|
||||
__block CGRect bounds = CGRectNull;
|
||||
|
||||
[self traverseSubviews:^(RNSVGView *node) {
|
||||
if ([node isKindOfClass:[RNSVGMask class]] || [node isKindOfClass:[RNSVGClipPath class]]) {
|
||||
// no-op
|
||||
} else if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
RNSVGNode* svgNode = (RNSVGNode*)node;
|
||||
if (svgNode.display && [@"none" isEqualToString:svgNode.display]) {
|
||||
return YES;
|
||||
}
|
||||
if (svgNode.responsible && !self.svgView.responsible) {
|
||||
self.svgView.responsible = YES;
|
||||
}
|
||||
|
||||
if ([node isKindOfClass:[RNSVGRenderable class]]) {
|
||||
[(RNSVGRenderable*)node mergeProperties:self];
|
||||
}
|
||||
|
||||
[svgNode renderTo:context rect:rect];
|
||||
|
||||
CGRect nodeRect = svgNode.clientRect;
|
||||
if (!CGRectIsEmpty(nodeRect)) {
|
||||
bounds = CGRectUnion(bounds, nodeRect);
|
||||
}
|
||||
|
||||
if ([node isKindOfClass:[RNSVGRenderable class]]) {
|
||||
[(RNSVGRenderable*)node resetProperties];
|
||||
}
|
||||
} else if ([node isKindOfClass:[RNSVGSvgView class]]) {
|
||||
RNSVGSvgView* svgView = (RNSVGSvgView*)node;
|
||||
CGFloat width = [self relativeOnWidth:svgView.bbWidth];
|
||||
CGFloat height = [self relativeOnHeight:svgView.bbHeight];
|
||||
CGRect rect = CGRectMake(0, 0, width, height);
|
||||
CGContextClipToRect(context, rect);
|
||||
[svgView drawToContext:context withRect:rect];
|
||||
} else {
|
||||
[node drawRect:rect];
|
||||
}
|
||||
__block CGRect bounds = CGRectNull;
|
||||
|
||||
[self traverseSubviews:^(RNSVGView *node) {
|
||||
if ([node isKindOfClass:[RNSVGMask class]] || [node isKindOfClass:[RNSVGClipPath class]]) {
|
||||
// no-op
|
||||
} else if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
RNSVGNode *svgNode = (RNSVGNode *)node;
|
||||
if (svgNode.display && [@"none" isEqualToString:svgNode.display]) {
|
||||
return YES;
|
||||
}];
|
||||
CGPathRef path = [self getPath:context];
|
||||
[self setHitArea:path];
|
||||
if (!CGRectEqualToRect(bounds, CGRectNull)) {
|
||||
self.clientRect = bounds;
|
||||
self.fillBounds = CGPathGetBoundingBox(path);
|
||||
self.strokeBounds = CGPathGetBoundingBox(self.strokePath);
|
||||
self.pathBounds = CGRectUnion(self.fillBounds, self.strokeBounds);
|
||||
}
|
||||
if (svgNode.responsible && !self.svgView.responsible) {
|
||||
self.svgView.responsible = YES;
|
||||
}
|
||||
|
||||
CGAffineTransform current = CGContextGetCTM(context);
|
||||
CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM);
|
||||
if ([node isKindOfClass:[RNSVGRenderable class]]) {
|
||||
[(RNSVGRenderable *)node mergeProperties:self];
|
||||
}
|
||||
|
||||
self.ctm = svgToClientTransform;
|
||||
self.screenCTM = current;
|
||||
[svgNode renderTo:context rect:rect];
|
||||
|
||||
CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms);
|
||||
CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
|
||||
CGPoint center = CGPointApplyAffineTransform(mid, transform);
|
||||
CGRect nodeRect = svgNode.clientRect;
|
||||
if (!CGRectIsEmpty(nodeRect)) {
|
||||
bounds = CGRectUnion(bounds, nodeRect);
|
||||
}
|
||||
|
||||
self.bounds = bounds;
|
||||
if (!isnan(center.x) && !isnan(center.y)) {
|
||||
self.center = center;
|
||||
}
|
||||
self.frame = bounds;
|
||||
if ([node isKindOfClass:[RNSVGRenderable class]]) {
|
||||
[(RNSVGRenderable *)node resetProperties];
|
||||
}
|
||||
} else if ([node isKindOfClass:[RNSVGSvgView class]]) {
|
||||
RNSVGSvgView *svgView = (RNSVGSvgView *)node;
|
||||
CGFloat width = [self relativeOnWidth:svgView.bbWidth];
|
||||
CGFloat height = [self relativeOnHeight:svgView.bbHeight];
|
||||
CGRect rect = CGRectMake(0, 0, width, height);
|
||||
CGContextClipToRect(context, rect);
|
||||
[svgView drawToContext:context withRect:rect];
|
||||
} else {
|
||||
[node drawRect:rect];
|
||||
}
|
||||
|
||||
[self popGlyphContext];
|
||||
return YES;
|
||||
}];
|
||||
CGPathRef path = [self getPath:context];
|
||||
[self setHitArea:path];
|
||||
if (!CGRectEqualToRect(bounds, CGRectNull)) {
|
||||
self.clientRect = bounds;
|
||||
self.fillBounds = CGPathGetBoundingBox(path);
|
||||
self.strokeBounds = CGPathGetBoundingBox(self.strokePath);
|
||||
self.pathBounds = CGRectUnion(self.fillBounds, self.strokeBounds);
|
||||
|
||||
CGAffineTransform current = CGContextGetCTM(context);
|
||||
CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM);
|
||||
|
||||
self.ctm = svgToClientTransform;
|
||||
self.screenCTM = current;
|
||||
|
||||
CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms);
|
||||
CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
|
||||
CGPoint center = CGPointApplyAffineTransform(mid, transform);
|
||||
|
||||
self.bounds = bounds;
|
||||
if (!isnan(center.x) && !isnan(center.y)) {
|
||||
self.center = center;
|
||||
}
|
||||
self.frame = bounds;
|
||||
}
|
||||
|
||||
[self popGlyphContext];
|
||||
}
|
||||
|
||||
- (void)setupGlyphContext:(CGContextRef)context
|
||||
{
|
||||
CGRect clipBounds = CGContextGetClipBoundingBox(context);
|
||||
clipBounds = CGRectApplyAffineTransform(clipBounds, self.matrix);
|
||||
clipBounds = CGRectApplyAffineTransform(clipBounds, self.transforms);
|
||||
CGFloat width = CGRectGetWidth(clipBounds);
|
||||
CGFloat height = CGRectGetHeight(clipBounds);
|
||||
CGRect clipBounds = CGContextGetClipBoundingBox(context);
|
||||
clipBounds = CGRectApplyAffineTransform(clipBounds, self.matrix);
|
||||
clipBounds = CGRectApplyAffineTransform(clipBounds, self.transforms);
|
||||
CGFloat width = CGRectGetWidth(clipBounds);
|
||||
CGFloat height = CGRectGetHeight(clipBounds);
|
||||
|
||||
_glyphContext = [[RNSVGGlyphContext alloc] initWithWidth:width
|
||||
height:height];
|
||||
_glyphContext = [[RNSVGGlyphContext alloc] initWithWidth:width height:height];
|
||||
}
|
||||
|
||||
- (RNSVGGlyphContext *)getGlyphContext
|
||||
{
|
||||
return _glyphContext;
|
||||
return _glyphContext;
|
||||
}
|
||||
|
||||
- (void)pushGlyphContext
|
||||
{
|
||||
__typeof__(self) __weak weakSelf = self;
|
||||
[[self.textRoot getGlyphContext] pushContext:weakSelf font:self.font];
|
||||
__typeof__(self) __weak weakSelf = self;
|
||||
[[self.textRoot getGlyphContext] pushContext:weakSelf font:self.font];
|
||||
}
|
||||
|
||||
- (void)popGlyphContext
|
||||
{
|
||||
[[self.textRoot getGlyphContext] popContext];
|
||||
[[self.textRoot getGlyphContext] popContext];
|
||||
}
|
||||
|
||||
- (void)renderPathTo:(CGContextRef)context rect:(CGRect)rect
|
||||
{
|
||||
[super renderLayerTo:context rect:rect];
|
||||
[super renderLayerTo:context rect:rect];
|
||||
}
|
||||
|
||||
- (CGPathRef)getPath:(CGContextRef)context
|
||||
{
|
||||
CGPathRef cached = self.path;
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
CGMutablePathRef __block path = CGPathCreateMutable();
|
||||
[self traverseSubviews:^(RNSVGNode *node) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]] && ![node isKindOfClass:[RNSVGMask class]]) {
|
||||
CGAffineTransform transform = CGAffineTransformConcat(node.matrix, node.transforms);
|
||||
CGPathAddPath(path, &transform, [node getPath:context]);
|
||||
CGPathAddPath(path, &transform, [node markerPath]);
|
||||
node.dirty = false;
|
||||
}
|
||||
return YES;
|
||||
}];
|
||||
|
||||
cached = CGPathRetain((CGPathRef)CFAutorelease(path));
|
||||
self.path = cached;
|
||||
CGPathRef cached = self.path;
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
CGMutablePathRef __block path = CGPathCreateMutable();
|
||||
[self traverseSubviews:^(RNSVGNode *node) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]] && ![node isKindOfClass:[RNSVGMask class]]) {
|
||||
CGAffineTransform transform = CGAffineTransformConcat(node.matrix, node.transforms);
|
||||
CGPathAddPath(path, &transform, [node getPath:context]);
|
||||
CGPathAddPath(path, &transform, [node markerPath]);
|
||||
node.dirty = false;
|
||||
}
|
||||
return YES;
|
||||
}];
|
||||
|
||||
cached = CGPathRetain((CGPathRef)CFAutorelease(path));
|
||||
self.path = cached;
|
||||
return cached;
|
||||
}
|
||||
|
||||
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix);
|
||||
transformed = CGPointApplyAffineTransform(transformed, self.invTransform);
|
||||
|
||||
if (!CGRectContainsPoint(self.pathBounds, transformed)) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (self.clipPath) {
|
||||
RNSVGClipPath *clipNode = (RNSVGClipPath*)[self.svgView getDefinedClipPath:self.clipPath];
|
||||
if ([clipNode isSimpleClipPath]) {
|
||||
CGPathRef clipPath = [self getClipPath];
|
||||
if (clipPath && !CGPathContainsPoint(clipPath, nil, transformed, clipNode.clipRule == kRNSVGCGFCRuleEvenodd)) {
|
||||
return nil;
|
||||
}
|
||||
} else {
|
||||
RNSVGRenderable *clipGroup = (RNSVGRenderable*)clipNode;
|
||||
if (![clipGroup hitTest:transformed withEvent:event]) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!event) {
|
||||
NSPredicate *const anyActive = [NSPredicate predicateWithFormat:@"self isKindOfClass: %@ AND active == TRUE", [RNSVGNode class]];
|
||||
NSArray *const filtered = [self.subviews filteredArrayUsingPredicate:anyActive];
|
||||
if ([filtered count] != 0) {
|
||||
return [filtered.lastObject hitTest:transformed withEvent:event];
|
||||
}
|
||||
}
|
||||
|
||||
for (RNSVGView *node in [self.subviews reverseObjectEnumerator]) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
if ([node isKindOfClass:[RNSVGMask class]]) {
|
||||
continue;
|
||||
}
|
||||
RNSVGNode* svgNode = (RNSVGNode*)node;
|
||||
if (event) {
|
||||
svgNode.active = NO;
|
||||
}
|
||||
RNSVGPlatformView *hitChild = [svgNode hitTest:transformed withEvent:event];
|
||||
if (hitChild) {
|
||||
svgNode.active = YES;
|
||||
return (svgNode.responsible || (svgNode != hitChild)) ? hitChild : self;
|
||||
}
|
||||
} else if ([node isKindOfClass:[RNSVGSvgView class]]) {
|
||||
RNSVGSvgView* svgView = (RNSVGSvgView*)node;
|
||||
RNSVGPlatformView *hitChild = [svgView hitTest:transformed withEvent:event];
|
||||
if (hitChild) {
|
||||
return hitChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RNSVGPlatformView *hitSelf = [super hitTest:transformed withEvent:event];
|
||||
if (hitSelf) {
|
||||
return hitSelf;
|
||||
}
|
||||
CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix);
|
||||
transformed = CGPointApplyAffineTransform(transformed, self.invTransform);
|
||||
|
||||
if (!CGRectContainsPoint(self.pathBounds, transformed)) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (self.clipPath) {
|
||||
RNSVGClipPath *clipNode = (RNSVGClipPath *)[self.svgView getDefinedClipPath:self.clipPath];
|
||||
if ([clipNode isSimpleClipPath]) {
|
||||
CGPathRef clipPath = [self getClipPath];
|
||||
if (clipPath && !CGPathContainsPoint(clipPath, nil, transformed, clipNode.clipRule == kRNSVGCGFCRuleEvenodd)) {
|
||||
return nil;
|
||||
}
|
||||
} else {
|
||||
RNSVGRenderable *clipGroup = (RNSVGRenderable *)clipNode;
|
||||
if (![clipGroup hitTest:transformed withEvent:event]) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!event) {
|
||||
NSPredicate *const anyActive =
|
||||
[NSPredicate predicateWithFormat:@"self isKindOfClass: %@ AND active == TRUE", [RNSVGNode class]];
|
||||
NSArray *const filtered = [self.subviews filteredArrayUsingPredicate:anyActive];
|
||||
if ([filtered count] != 0) {
|
||||
return [filtered.lastObject hitTest:transformed withEvent:event];
|
||||
}
|
||||
}
|
||||
|
||||
for (RNSVGView *node in [self.subviews reverseObjectEnumerator]) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
if ([node isKindOfClass:[RNSVGMask class]]) {
|
||||
continue;
|
||||
}
|
||||
RNSVGNode *svgNode = (RNSVGNode *)node;
|
||||
if (event) {
|
||||
svgNode.active = NO;
|
||||
}
|
||||
RNSVGPlatformView *hitChild = [svgNode hitTest:transformed withEvent:event];
|
||||
if (hitChild) {
|
||||
svgNode.active = YES;
|
||||
return (svgNode.responsible || (svgNode != hitChild)) ? hitChild : self;
|
||||
}
|
||||
} else if ([node isKindOfClass:[RNSVGSvgView class]]) {
|
||||
RNSVGSvgView *svgView = (RNSVGSvgView *)node;
|
||||
RNSVGPlatformView *hitChild = [svgView hitTest:transformed withEvent:event];
|
||||
if (hitChild) {
|
||||
return hitChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RNSVGPlatformView *hitSelf = [super hitTest:transformed withEvent:event];
|
||||
if (hitSelf) {
|
||||
return hitSelf;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)parseReference
|
||||
{
|
||||
self.dirty = false;
|
||||
if (self.name) {
|
||||
__typeof__(self) __weak weakSelf = self;
|
||||
[self.svgView defineTemplate:weakSelf templateName:self.name];
|
||||
}
|
||||
self.dirty = false;
|
||||
if (self.name) {
|
||||
__typeof__(self) __weak weakSelf = self;
|
||||
[self.svgView defineTemplate:weakSelf templateName:self.name];
|
||||
}
|
||||
|
||||
[self traverseSubviews:^(RNSVGNode *node) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
[node parseReference];
|
||||
}
|
||||
return YES;
|
||||
}];
|
||||
[self traverseSubviews:^(RNSVGNode *node) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
[node parseReference];
|
||||
}
|
||||
return YES;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)resetProperties
|
||||
{
|
||||
[self traverseSubviews:^(__kindof RNSVGNode *node) {
|
||||
if ([node isKindOfClass:[RNSVGRenderable class]]) {
|
||||
[(RNSVGRenderable*)node resetProperties];
|
||||
}
|
||||
return YES;
|
||||
}];
|
||||
[self traverseSubviews:^(__kindof RNSVGNode *node) {
|
||||
if ([node isKindOfClass:[RNSVGRenderable class]]) {
|
||||
[(RNSVGRenderable *)node resetProperties];
|
||||
}
|
||||
return YES;
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <React/RCTBridge.h>
|
||||
#import "RNSVGLength.h"
|
||||
#import "RNSVGRenderable.h"
|
||||
#import "RNSVGVBMOS.h"
|
||||
#import "RNSVGLength.h"
|
||||
|
||||
#import <React/RCTImageSource.h>
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
|
||||
@property (nonatomic, weak) RCTBridge *bridge;
|
||||
@property (nonatomic, assign) id src;
|
||||
@property (nonatomic, strong) RNSVGLength* x;
|
||||
@property (nonatomic, strong) RNSVGLength* y;
|
||||
@property (nonatomic, strong) RNSVGLength* imagewidth;
|
||||
@property (nonatomic, strong) RNSVGLength* imageheight;
|
||||
@property (nonatomic, strong) RNSVGLength *x;
|
||||
@property (nonatomic, strong) RNSVGLength *y;
|
||||
@property (nonatomic, strong) RNSVGLength *imagewidth;
|
||||
@property (nonatomic, strong) RNSVGLength *imageheight;
|
||||
@property (nonatomic, strong) NSString *align;
|
||||
@property (nonatomic, assign) RNSVGVBMOS meetOrSlice;
|
||||
|
||||
|
||||
@@ -15,25 +15,25 @@
|
||||
|
||||
#else
|
||||
|
||||
#import <React/RCTImageURLLoader.h>
|
||||
#import <React/RCTImageShadowView.h>
|
||||
#import <React/RCTImageView.h>
|
||||
#import <React/RCTImageLoaderProtocol.h>
|
||||
#import <React/RCTImageShadowView.h>
|
||||
#import <React/RCTImageURLLoader.h>
|
||||
#import <React/RCTImageView.h>
|
||||
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
#import <React/RCTLog.h>
|
||||
#import "RNSVGViewBox.h"
|
||||
#import "RCTBridge.h"
|
||||
#import "RNSVGViewBox.h"
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RCTConversions.h"
|
||||
#import <react/renderer/components/view/conversions.h>
|
||||
#import "RNSVGFabricConversions.h"
|
||||
#import "RCTConversions.h"
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RCTImagePrimitivesConversions.h"
|
||||
#import "RCTImageSource.h"
|
||||
#import "RNSVGFabricConversions.h"
|
||||
|
||||
// Some RN private method hacking below similar to how it is done in RNScreens:
|
||||
// https://github.com/software-mansion/react-native-screens/blob/90e548739f35b5ded2524a9d6410033fc233f586/ios/RNSScreenStackHeaderConfig.mm#L30
|
||||
@@ -43,11 +43,10 @@
|
||||
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
@implementation RNSVGImage
|
||||
{
|
||||
CGImageRef _image;
|
||||
CGSize _imageSize;
|
||||
RCTImageLoaderCancellationBlock _reloadImageCancellationBlock;
|
||||
@implementation RNSVGImage {
|
||||
CGImageRef _image;
|
||||
CGSize _imageSize;
|
||||
RCTImageLoaderCancellationBlock _reloadImageCancellationBlock;
|
||||
}
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
using namespace facebook::react;
|
||||
@@ -71,104 +70,106 @@ using namespace facebook::react;
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGImageProps>(props);
|
||||
const auto &oldImageProps = *std::static_pointer_cast<const RNSVGImageProps>(oldProps);
|
||||
const auto &oldImageProps = *std::static_pointer_cast<const RNSVGImageProps>(oldProps);
|
||||
|
||||
self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)];
|
||||
self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)];
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.imageheight)) {
|
||||
self.imageheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.imageheight)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.imagewidth)) {
|
||||
self.imagewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.imagewidth)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
|
||||
self.imageheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
|
||||
self.imagewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
|
||||
}
|
||||
|
||||
if (oldProps == nullptr || oldImageProps.src != newProps.src) {
|
||||
// TODO: make it the same as in e.g. slider
|
||||
NSURLRequest *request = NSURLRequestFromImageSource(newProps.src);
|
||||
CGSize size = RCTCGSizeFromSize(newProps.src.size);
|
||||
CGFloat scale = newProps.src.scale;
|
||||
RCTImageSource *imageSource = [[RCTImageSource alloc] initWithURLRequest:request size:size scale:scale];
|
||||
[self setImageSrc:imageSource request:request];
|
||||
}
|
||||
self.align = RCTNSStringFromStringNilIfEmpty(newProps.align);
|
||||
self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice);
|
||||
self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)];
|
||||
self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)];
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.imageheight)) {
|
||||
self.imageheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.imageheight)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.imagewidth)) {
|
||||
self.imagewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.imagewidth)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
|
||||
self.imageheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
|
||||
self.imagewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
|
||||
}
|
||||
|
||||
setCommonRenderableProps(newProps, self);
|
||||
if (oldProps == nullptr || oldImageProps.src != newProps.src) {
|
||||
// TODO: make it the same as in e.g. slider
|
||||
NSURLRequest *request = NSURLRequestFromImageSource(newProps.src);
|
||||
CGSize size = RCTCGSizeFromSize(newProps.src.size);
|
||||
CGFloat scale = newProps.src.scale;
|
||||
RCTImageSource *imageSource = [[RCTImageSource alloc] initWithURLRequest:request size:size scale:scale];
|
||||
[self setImageSrc:imageSource request:request];
|
||||
}
|
||||
self.align = RCTNSStringFromStringNilIfEmpty(newProps.align);
|
||||
self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice);
|
||||
|
||||
setCommonRenderableProps(newProps, self);
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
_x = nil;
|
||||
_y = nil;
|
||||
_imageheight = nil;
|
||||
_imagewidth = nil;
|
||||
_src = nil;
|
||||
_align = nil;
|
||||
_meetOrSlice = kRNSVGVBMOSMeet;
|
||||
|
||||
if (_image) {
|
||||
CGImageRelease(_image);
|
||||
}
|
||||
_image = nil;
|
||||
_imageSize = CGSizeZero;
|
||||
_reloadImageCancellationBlock = nil;
|
||||
[super prepareForRecycle];
|
||||
_x = nil;
|
||||
_y = nil;
|
||||
_imageheight = nil;
|
||||
_imagewidth = nil;
|
||||
_src = nil;
|
||||
_align = nil;
|
||||
_meetOrSlice = kRNSVGVBMOSMeet;
|
||||
|
||||
if (_image) {
|
||||
CGImageRelease(_image);
|
||||
}
|
||||
_image = nil;
|
||||
_imageSize = CGSizeZero;
|
||||
_reloadImageCancellationBlock = nil;
|
||||
}
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
- (void)setSrc:(id)src
|
||||
{
|
||||
if (src == _src) {
|
||||
return;
|
||||
}
|
||||
_src = src;
|
||||
CGImageRelease(_image);
|
||||
_image = nil;
|
||||
RCTImageSource *source = [RCTConvert RCTImageSource:src];
|
||||
if (source.size.width != 0 && source.size.height != 0) {
|
||||
_imageSize = source.size;
|
||||
} else {
|
||||
_imageSize = CGSizeMake(0, 0);
|
||||
}
|
||||
if (src == _src) {
|
||||
return;
|
||||
}
|
||||
_src = src;
|
||||
CGImageRelease(_image);
|
||||
_image = nil;
|
||||
RCTImageSource *source = [RCTConvert RCTImageSource:src];
|
||||
if (source.size.width != 0 && source.size.height != 0) {
|
||||
_imageSize = source.size;
|
||||
} else {
|
||||
_imageSize = CGSizeMake(0, 0);
|
||||
}
|
||||
|
||||
RCTImageLoaderCancellationBlock previousCancellationBlock = _reloadImageCancellationBlock;
|
||||
if (previousCancellationBlock) {
|
||||
previousCancellationBlock();
|
||||
_reloadImageCancellationBlock = nil;
|
||||
}
|
||||
RCTImageLoaderCancellationBlock previousCancellationBlock = _reloadImageCancellationBlock;
|
||||
if (previousCancellationBlock) {
|
||||
previousCancellationBlock();
|
||||
_reloadImageCancellationBlock = nil;
|
||||
}
|
||||
|
||||
_reloadImageCancellationBlock = [[self.bridge moduleForName:@"ImageLoader"] loadImageWithURLRequest:[RCTConvert NSURLRequest:src] callback:^(NSError *error, UIImage *image) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self->_image = CGImageRetain(image.CGImage);
|
||||
self->_imageSize = CGSizeMake(CGImageGetWidth(self->_image), CGImageGetHeight(self->_image));
|
||||
[self invalidate];
|
||||
});
|
||||
}];
|
||||
_reloadImageCancellationBlock = [[self.bridge moduleForName:@"ImageLoader"]
|
||||
loadImageWithURLRequest:[RCTConvert NSURLRequest:src]
|
||||
callback:^(NSError *error, UIImage *image) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self->_image = CGImageRetain(image.CGImage);
|
||||
self->_imageSize = CGSizeMake(CGImageGetWidth(self->_image), CGImageGetHeight(self->_image));
|
||||
[self invalidate];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setImageSrc:(RCTImageSource *)source request:(NSURLRequest *)request
|
||||
{
|
||||
CGImageRelease(_image);
|
||||
_image = nil;
|
||||
if (source.size.width != 0 && source.size.height != 0) {
|
||||
_imageSize = source.size;
|
||||
} else {
|
||||
_imageSize = CGSizeMake(0, 0);
|
||||
}
|
||||
CGImageRelease(_image);
|
||||
_image = nil;
|
||||
if (source.size.width != 0 && source.size.height != 0) {
|
||||
_imageSize = source.size;
|
||||
} else {
|
||||
_imageSize = CGSizeMake(0, 0);
|
||||
}
|
||||
|
||||
RCTImageLoaderCancellationBlock previousCancellationBlock = _reloadImageCancellationBlock;
|
||||
if (previousCancellationBlock) {
|
||||
previousCancellationBlock();
|
||||
_reloadImageCancellationBlock = nil;
|
||||
}
|
||||
RCTImageLoaderCancellationBlock previousCancellationBlock = _reloadImageCancellationBlock;
|
||||
if (previousCancellationBlock) {
|
||||
previousCancellationBlock();
|
||||
_reloadImageCancellationBlock = nil;
|
||||
}
|
||||
|
||||
_reloadImageCancellationBlock = [[
|
||||
_reloadImageCancellationBlock = [[
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
[RCTBridge currentBridge]
|
||||
#else
|
||||
@@ -185,130 +186,133 @@ using namespace facebook::react;
|
||||
|
||||
- (void)setX:(RNSVGLength *)x
|
||||
{
|
||||
if ([x isEqualTo:_x]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_x = x;
|
||||
if ([x isEqualTo:_x]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_x = x;
|
||||
}
|
||||
|
||||
- (void)setY:(RNSVGLength *)y
|
||||
{
|
||||
if ([y isEqualTo:_y]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_y = y;
|
||||
if ([y isEqualTo:_y]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_y = y;
|
||||
}
|
||||
|
||||
- (void)setImagewidth:(RNSVGLength *)width
|
||||
{
|
||||
if ([width isEqualTo:_imagewidth]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_imagewidth = width;
|
||||
if ([width isEqualTo:_imagewidth]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_imagewidth = width;
|
||||
}
|
||||
|
||||
- (void)setImageheight:(RNSVGLength *)height
|
||||
{
|
||||
if ([height isEqualTo:_imageheight]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_imageheight = height;
|
||||
if ([height isEqualTo:_imageheight]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_imageheight = height;
|
||||
}
|
||||
|
||||
- (void)setAlign:(NSString *)align
|
||||
{
|
||||
if ([align isEqualToString:_align]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_align = align;
|
||||
if ([align isEqualToString:_align]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_align = align;
|
||||
}
|
||||
|
||||
- (void)setMeetOrSlice:(RNSVGVBMOS)meetOrSlice
|
||||
{
|
||||
if (meetOrSlice == _meetOrSlice) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_meetOrSlice = meetOrSlice;
|
||||
if (meetOrSlice == _meetOrSlice) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_meetOrSlice = meetOrSlice;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
CGImageRelease(_image);
|
||||
CGImageRelease(_image);
|
||||
}
|
||||
|
||||
- (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect
|
||||
{
|
||||
if (CGSizeEqualToSize(CGSizeZero, _imageSize)) {
|
||||
return;
|
||||
}
|
||||
CGContextSaveGState(context);
|
||||
if (CGSizeEqualToSize(CGSizeZero, _imageSize)) {
|
||||
return;
|
||||
}
|
||||
CGContextSaveGState(context);
|
||||
|
||||
// add hit area
|
||||
CGRect hitArea = [self getHitArea];
|
||||
CGPathRef hitAreaPath = CGPathCreateWithRect(hitArea, nil);
|
||||
[self setHitArea:hitAreaPath];
|
||||
CGPathRelease(hitAreaPath);
|
||||
self.pathBounds = hitArea;
|
||||
self.fillBounds = hitArea;
|
||||
self.strokeBounds = hitArea;
|
||||
// add hit area
|
||||
CGRect hitArea = [self getHitArea];
|
||||
CGPathRef hitAreaPath = CGPathCreateWithRect(hitArea, nil);
|
||||
[self setHitArea:hitAreaPath];
|
||||
CGPathRelease(hitAreaPath);
|
||||
self.pathBounds = hitArea;
|
||||
self.fillBounds = hitArea;
|
||||
self.strokeBounds = hitArea;
|
||||
|
||||
// apply viewBox transform on Image render.
|
||||
CGRect imageBounds = CGRectMake(0, 0, _imageSize.width, _imageSize.height);
|
||||
CGAffineTransform viewbox = [RNSVGViewBox getTransform:imageBounds eRect:hitArea align:self.align meetOrSlice:self.meetOrSlice];
|
||||
// apply viewBox transform on Image render.
|
||||
CGRect imageBounds = CGRectMake(0, 0, _imageSize.width, _imageSize.height);
|
||||
CGAffineTransform viewbox = [RNSVGViewBox getTransform:imageBounds
|
||||
eRect:hitArea
|
||||
align:self.align
|
||||
meetOrSlice:self.meetOrSlice];
|
||||
|
||||
[self clip:context];
|
||||
CGContextClipToRect(context, hitArea);
|
||||
CGContextConcatCTM(context, viewbox);
|
||||
CGContextTranslateCTM(context, 0, imageBounds.size.height);
|
||||
CGContextScaleCTM(context, 1, -1);
|
||||
CGContextDrawImage(context, imageBounds, _image);
|
||||
CGContextRestoreGState(context);
|
||||
[self clip:context];
|
||||
CGContextClipToRect(context, hitArea);
|
||||
CGContextConcatCTM(context, viewbox);
|
||||
CGContextTranslateCTM(context, 0, imageBounds.size.height);
|
||||
CGContextScaleCTM(context, 1, -1);
|
||||
CGContextDrawImage(context, imageBounds, _image);
|
||||
CGContextRestoreGState(context);
|
||||
|
||||
CGRect bounds = hitArea;
|
||||
self.clientRect = bounds;
|
||||
CGRect bounds = hitArea;
|
||||
self.clientRect = bounds;
|
||||
|
||||
CGAffineTransform current = CGContextGetCTM(context);
|
||||
CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM);
|
||||
CGAffineTransform current = CGContextGetCTM(context);
|
||||
CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM);
|
||||
|
||||
self.ctm = svgToClientTransform;
|
||||
self.screenCTM = current;
|
||||
self.ctm = svgToClientTransform;
|
||||
self.screenCTM = current;
|
||||
|
||||
CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms);
|
||||
CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
|
||||
CGPoint center = CGPointApplyAffineTransform(mid, transform);
|
||||
CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms);
|
||||
CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
|
||||
CGPoint center = CGPointApplyAffineTransform(mid, transform);
|
||||
|
||||
self.bounds = bounds;
|
||||
if (!isnan(center.x) && !isnan(center.y)) {
|
||||
self.center = center;
|
||||
}
|
||||
self.frame = bounds;
|
||||
self.bounds = bounds;
|
||||
if (!isnan(center.x) && !isnan(center.y)) {
|
||||
self.center = center;
|
||||
}
|
||||
self.frame = bounds;
|
||||
}
|
||||
|
||||
- (CGRect)getHitArea
|
||||
{
|
||||
CGFloat x = [self relativeOnWidth:self.x];
|
||||
CGFloat y = [self relativeOnHeight:self.y];
|
||||
CGFloat width = [self relativeOnWidth:self.imagewidth];
|
||||
CGFloat height = [self relativeOnHeight:self.imageheight];
|
||||
if (width == 0) {
|
||||
width = _imageSize.width;
|
||||
}
|
||||
if (height == 0) {
|
||||
height = _imageSize.height;
|
||||
}
|
||||
CGFloat x = [self relativeOnWidth:self.x];
|
||||
CGFloat y = [self relativeOnHeight:self.y];
|
||||
CGFloat width = [self relativeOnWidth:self.imagewidth];
|
||||
CGFloat height = [self relativeOnHeight:self.imageheight];
|
||||
if (width == 0) {
|
||||
width = _imageSize.width;
|
||||
}
|
||||
if (height == 0) {
|
||||
height = _imageSize.height;
|
||||
}
|
||||
|
||||
return CGRectMake(x, y, width, height);
|
||||
return CGRectMake(x, y, width, height);
|
||||
}
|
||||
|
||||
- (CGPathRef)getPath:(CGContextRef)context
|
||||
{
|
||||
return (CGPathRef)CFAutorelease(CGPathCreateWithRect([self getHitArea], nil));
|
||||
return (CGPathRef)CFAutorelease(CGPathCreateWithRect([self getHitArea], nil));
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "RNSVGNode.h"
|
||||
#import "RNSVGLength.h"
|
||||
#import "RNSVGNode.h"
|
||||
|
||||
@interface RNSVGLinearGradient : RNSVGNode
|
||||
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
#import "RNSVGLinearGradient.h"
|
||||
#import "RNSVGPainter.h"
|
||||
#import "RNSVGBrushType.h"
|
||||
#import "RNSVGPainter.h"
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RCTConversions.h"
|
||||
#import <react/renderer/components/view/conversions.h>
|
||||
#import "RCTConversions.h"
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RNSVGFabricConversions.h"
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
@@ -40,133 +40,139 @@ using namespace facebook::react;
|
||||
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGLinearGradientProps>(props);
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGLinearGradientProps>(props);
|
||||
|
||||
self.x1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x1)];
|
||||
self.y1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y1)];
|
||||
self.x2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x2)];
|
||||
self.y2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y2)];
|
||||
if (newProps.gradient.size() > 0) {
|
||||
NSMutableArray<NSNumber *> *gradientArray = [NSMutableArray new];
|
||||
for (auto number : newProps.gradient) {
|
||||
[gradientArray addObject:[NSNumber numberWithDouble:number]];
|
||||
}
|
||||
self.gradient = gradientArray;
|
||||
self.x1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x1)];
|
||||
self.y1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y1)];
|
||||
self.x2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x2)];
|
||||
self.y2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y2)];
|
||||
if (newProps.gradient.size() > 0) {
|
||||
NSMutableArray<NSNumber *> *gradientArray = [NSMutableArray new];
|
||||
for (auto number : newProps.gradient) {
|
||||
[gradientArray addObject:[NSNumber numberWithDouble:number]];
|
||||
}
|
||||
self.gradientUnits = newProps.gradientUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
|
||||
if (newProps.gradientTransform.size() == 6) {
|
||||
self.gradientTransform = CGAffineTransformMake(newProps.gradientTransform.at(0), newProps.gradientTransform.at(1), newProps.gradientTransform.at(2), newProps.gradientTransform.at(3), newProps.gradientTransform.at(4), newProps.gradientTransform.at(5));
|
||||
}
|
||||
|
||||
setCommonNodeProps(newProps, self);
|
||||
self.gradient = gradientArray;
|
||||
}
|
||||
self.gradientUnits = newProps.gradientUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
|
||||
if (newProps.gradientTransform.size() == 6) {
|
||||
self.gradientTransform = CGAffineTransformMake(
|
||||
newProps.gradientTransform.at(0),
|
||||
newProps.gradientTransform.at(1),
|
||||
newProps.gradientTransform.at(2),
|
||||
newProps.gradientTransform.at(3),
|
||||
newProps.gradientTransform.at(4),
|
||||
newProps.gradientTransform.at(5));
|
||||
}
|
||||
|
||||
setCommonNodeProps(newProps, self);
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
_x1 = nil;
|
||||
_y1 = nil;
|
||||
_x2 = nil;
|
||||
_y2 = nil;
|
||||
_gradient = nil;
|
||||
_gradientUnits = kRNSVGUnitsObjectBoundingBox;
|
||||
_gradientTransform = CGAffineTransformIdentity;
|
||||
[super prepareForRecycle];
|
||||
_x1 = nil;
|
||||
_y1 = nil;
|
||||
_x2 = nil;
|
||||
_y2 = nil;
|
||||
_gradient = nil;
|
||||
_gradientUnits = kRNSVGUnitsObjectBoundingBox;
|
||||
_gradientTransform = CGAffineTransformIdentity;
|
||||
}
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_gradientTransform = CGAffineTransformIdentity;
|
||||
}
|
||||
return self;
|
||||
if (self = [super init]) {
|
||||
_gradientTransform = CGAffineTransformIdentity;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setX1:(RNSVGLength *)x1
|
||||
{
|
||||
if ([x1 isEqualTo:_x1]) {
|
||||
return;
|
||||
}
|
||||
if ([x1 isEqualTo:_x1]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_x1 = x1;
|
||||
[self invalidate];
|
||||
_x1 = x1;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setY1:(RNSVGLength *)y1
|
||||
{
|
||||
if ([y1 isEqualTo:_y1]) {
|
||||
return;
|
||||
}
|
||||
if ([y1 isEqualTo:_y1]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_y1 = y1;
|
||||
[self invalidate];
|
||||
_y1 = y1;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setX2:(RNSVGLength *)x2
|
||||
{
|
||||
if ([x2 isEqualTo:_x2]) {
|
||||
return;
|
||||
}
|
||||
if ([x2 isEqualTo:_x2]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_x2 = x2;
|
||||
[self invalidate];
|
||||
_x2 = x2;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setY2:(RNSVGLength *)y2
|
||||
{
|
||||
if ([y2 isEqualTo:_y2]) {
|
||||
return;
|
||||
}
|
||||
if ([y2 isEqualTo:_y2]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_y2 = y2;
|
||||
[self invalidate];
|
||||
_y2 = y2;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setGradient:(NSArray<NSNumber *> *)gradient
|
||||
{
|
||||
if (gradient == _gradient) {
|
||||
return;
|
||||
}
|
||||
if (gradient == _gradient) {
|
||||
return;
|
||||
}
|
||||
|
||||
_gradient = gradient;
|
||||
[self invalidate];
|
||||
_gradient = gradient;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setGradientUnits:(RNSVGUnits)gradientUnits
|
||||
{
|
||||
if (gradientUnits == _gradientUnits) {
|
||||
return;
|
||||
}
|
||||
if (gradientUnits == _gradientUnits) {
|
||||
return;
|
||||
}
|
||||
|
||||
_gradientUnits = gradientUnits;
|
||||
[self invalidate];
|
||||
_gradientUnits = gradientUnits;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setGradientTransform:(CGAffineTransform)gradientTransform
|
||||
{
|
||||
_gradientTransform = gradientTransform;
|
||||
[self invalidate];
|
||||
_gradientTransform = gradientTransform;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
return nil;
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)parseReference
|
||||
{
|
||||
self.dirty = false;
|
||||
NSArray<RNSVGLength *> *points = @[self.x1, self.y1, self.x2, self.y2];
|
||||
RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points];
|
||||
[painter setUnits:self.gradientUnits];
|
||||
[painter setTransform:self.gradientTransform];
|
||||
[painter setLinearGradientColors:self.gradient];
|
||||
self.dirty = false;
|
||||
NSArray<RNSVGLength *> *points = @[ self.x1, self.y1, self.x2, self.y2 ];
|
||||
RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points];
|
||||
[painter setUnits:self.gradientUnits];
|
||||
[painter setTransform:self.gradientTransform];
|
||||
[painter setLinearGradientColors:self.gradient];
|
||||
|
||||
if (self.gradientUnits == kRNSVGUnitsUserSpaceOnUse) {
|
||||
[painter setUserSpaceBoundingBox:[self.svgView getContextBounds]];
|
||||
}
|
||||
if (self.gradientUnits == kRNSVGUnitsUserSpaceOnUse) {
|
||||
[painter setUserSpaceBoundingBox:[self.svgView getContextBounds]];
|
||||
}
|
||||
|
||||
[self.svgView definePainter:painter painterName:self.name];
|
||||
[self.svgView definePainter:painter painterName:self.name];
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
@property (nonatomic, strong) NSString *align;
|
||||
@property (nonatomic, assign) RNSVGVBMOS meetOrSlice;
|
||||
|
||||
- (void)renderMarker:(CGContextRef)context rect:(CGRect)rect position:(RNSVGMarkerPosition*)position strokeWidth:(CGFloat)strokeWidth;
|
||||
- (void)renderMarker:(CGContextRef)context
|
||||
rect:(CGRect)rect
|
||||
position:(RNSVGMarkerPosition *)position
|
||||
strokeWidth:(CGFloat)strokeWidth;
|
||||
|
||||
@end
|
||||
|
||||
@@ -6,16 +6,16 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
#import "RNSVGMarker.h"
|
||||
#import "RNSVGPainter.h"
|
||||
#import "RNSVGBrushType.h"
|
||||
#import "RNSVGNode.h"
|
||||
#import "RNSVGPainter.h"
|
||||
#import "RNSVGViewBox.h"
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RCTConversions.h"
|
||||
#import <react/renderer/components/view/conversions.h>
|
||||
#import "RCTConversions.h"
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RNSVGFabricConversions.h"
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
@@ -42,230 +42,234 @@ using namespace facebook::react;
|
||||
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGMarkerProps>(props);
|
||||
|
||||
self.refX = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.refX)];
|
||||
self.refY = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.refY)];
|
||||
self.markerHeight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.markerHeight)];
|
||||
self.markerWidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.markerWidth)];
|
||||
self.markerUnits = RCTNSStringFromStringNilIfEmpty(newProps.markerUnits);
|
||||
self.orient = RCTNSStringFromStringNilIfEmpty(newProps.orient);
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGMarkerProps>(props);
|
||||
|
||||
self.minX = newProps.minX;
|
||||
self.minY = newProps.minY;
|
||||
self.vbWidth = newProps.vbWidth;
|
||||
self.vbHeight = newProps.vbHeight;
|
||||
self.align = RCTNSStringFromStringNilIfEmpty(newProps.align);
|
||||
self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice);
|
||||
self.refX = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.refX)];
|
||||
self.refY = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.refY)];
|
||||
self.markerHeight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.markerHeight)];
|
||||
self.markerWidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.markerWidth)];
|
||||
self.markerUnits = RCTNSStringFromStringNilIfEmpty(newProps.markerUnits);
|
||||
self.orient = RCTNSStringFromStringNilIfEmpty(newProps.orient);
|
||||
|
||||
setCommonGroupProps(newProps, self);
|
||||
self.minX = newProps.minX;
|
||||
self.minY = newProps.minY;
|
||||
self.vbWidth = newProps.vbWidth;
|
||||
self.vbHeight = newProps.vbHeight;
|
||||
self.align = RCTNSStringFromStringNilIfEmpty(newProps.align);
|
||||
self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice);
|
||||
|
||||
setCommonGroupProps(newProps, self);
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
_refX = nil;
|
||||
_refY = nil;
|
||||
_markerHeight = nil;
|
||||
_markerWidth = nil;
|
||||
_markerUnits = nil;
|
||||
_orient = nil;
|
||||
|
||||
_minX = 0;
|
||||
_minY = 0;
|
||||
_vbWidth = 0;
|
||||
_vbHeight = 0;
|
||||
_align = nil;
|
||||
_meetOrSlice = kRNSVGVBMOSMeet;
|
||||
[super prepareForRecycle];
|
||||
_refX = nil;
|
||||
_refY = nil;
|
||||
_markerHeight = nil;
|
||||
_markerWidth = nil;
|
||||
_markerUnits = nil;
|
||||
_orient = nil;
|
||||
|
||||
_minX = 0;
|
||||
_minY = 0;
|
||||
_vbWidth = 0;
|
||||
_vbHeight = 0;
|
||||
_align = nil;
|
||||
_meetOrSlice = kRNSVGVBMOSMeet;
|
||||
}
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
return nil;
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)parseReference
|
||||
{
|
||||
self.dirty = false;
|
||||
[self.svgView defineMarker:self markerName:self.name];
|
||||
[self traverseSubviews:^(RNSVGNode *node) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
[node parseReference];
|
||||
}
|
||||
return YES;
|
||||
}];
|
||||
self.dirty = false;
|
||||
[self.svgView defineMarker:self markerName:self.name];
|
||||
[self traverseSubviews:^(RNSVGNode *node) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
[node parseReference];
|
||||
}
|
||||
return YES;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setX:(RNSVGLength *)refX
|
||||
{
|
||||
if ([refX isEqualTo:_refX]) {
|
||||
return;
|
||||
}
|
||||
if ([refX isEqualTo:_refX]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_refX = refX;
|
||||
[self invalidate];
|
||||
_refX = refX;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setY:(RNSVGLength *)refY
|
||||
{
|
||||
if ([refY isEqualTo:_refY]) {
|
||||
return;
|
||||
}
|
||||
if ([refY isEqualTo:_refY]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_refY = refY;
|
||||
[self invalidate];
|
||||
_refY = refY;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setMarkerWidth:(RNSVGLength *)markerWidth
|
||||
{
|
||||
if ([markerWidth isEqualTo:_markerWidth]) {
|
||||
return;
|
||||
}
|
||||
if ([markerWidth isEqualTo:_markerWidth]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_markerWidth = markerWidth;
|
||||
[self invalidate];
|
||||
_markerWidth = markerWidth;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setMarkerHeight:(RNSVGLength *)markerHeight
|
||||
{
|
||||
if ([markerHeight isEqualTo:_markerHeight]) {
|
||||
return;
|
||||
}
|
||||
if ([markerHeight isEqualTo:_markerHeight]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_markerHeight = markerHeight;
|
||||
[self invalidate];
|
||||
_markerHeight = markerHeight;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setMarkerUnits:(NSString *)markerUnits
|
||||
{
|
||||
if ([_markerUnits isEqualToString:markerUnits]) {
|
||||
return;
|
||||
}
|
||||
if ([_markerUnits isEqualToString:markerUnits]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_markerUnits = markerUnits;
|
||||
[self invalidate];
|
||||
_markerUnits = markerUnits;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setOrient:(NSString *)orient
|
||||
{
|
||||
if ([orient isEqualToString:_orient]) {
|
||||
return;
|
||||
}
|
||||
if ([orient isEqualToString:_orient]) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_orient = orient;
|
||||
[self invalidate];
|
||||
_orient = orient;
|
||||
}
|
||||
|
||||
- (void)setMinX:(CGFloat)minX
|
||||
{
|
||||
if (minX == _minX) {
|
||||
return;
|
||||
}
|
||||
if (minX == _minX) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_minX = minX;
|
||||
[self invalidate];
|
||||
_minX = minX;
|
||||
}
|
||||
|
||||
- (void)setMinY:(CGFloat)minY
|
||||
{
|
||||
if (minY == _minY) {
|
||||
return;
|
||||
}
|
||||
if (minY == _minY) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_minY = minY;
|
||||
[self invalidate];
|
||||
_minY = minY;
|
||||
}
|
||||
|
||||
- (void)setVbWidth:(CGFloat)vbWidth
|
||||
{
|
||||
if (vbWidth == _vbWidth) {
|
||||
return;
|
||||
}
|
||||
if (vbWidth == _vbWidth) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_vbWidth = vbWidth;
|
||||
[self invalidate];
|
||||
_vbWidth = vbWidth;
|
||||
}
|
||||
|
||||
- (void)setVbHeight:(CGFloat)vbHeight
|
||||
{
|
||||
if (_vbHeight == vbHeight) {
|
||||
return;
|
||||
}
|
||||
if (_vbHeight == vbHeight) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_vbHeight = vbHeight;
|
||||
[self invalidate];
|
||||
_vbHeight = vbHeight;
|
||||
}
|
||||
|
||||
- (void)setAlign:(NSString *)align
|
||||
{
|
||||
if ([align isEqualToString:_align]) {
|
||||
return;
|
||||
}
|
||||
if ([align isEqualToString:_align]) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_align = align;
|
||||
[self invalidate];
|
||||
_align = align;
|
||||
}
|
||||
|
||||
- (void)setMeetOrSlice:(RNSVGVBMOS)meetOrSlice
|
||||
{
|
||||
if (meetOrSlice == _meetOrSlice) {
|
||||
return;
|
||||
}
|
||||
if (meetOrSlice == _meetOrSlice) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_meetOrSlice = meetOrSlice;
|
||||
[self invalidate];
|
||||
_meetOrSlice = meetOrSlice;
|
||||
}
|
||||
|
||||
static CGFloat RNSVG_degToRad = (CGFloat)M_PI / 180;
|
||||
|
||||
double deg2rad(CGFloat deg) {
|
||||
return deg * RNSVG_degToRad;
|
||||
double deg2rad(CGFloat deg)
|
||||
{
|
||||
return deg * RNSVG_degToRad;
|
||||
}
|
||||
|
||||
- (void)renderMarker:(CGContextRef)context rect:(CGRect)rect position:(RNSVGMarkerPosition*)position strokeWidth:(CGFloat)strokeWidth
|
||||
- (void)renderMarker:(CGContextRef)context
|
||||
rect:(CGRect)rect
|
||||
position:(RNSVGMarkerPosition *)position
|
||||
strokeWidth:(CGFloat)strokeWidth
|
||||
{
|
||||
CGContextSaveGState(context);
|
||||
CGContextSaveGState(context);
|
||||
|
||||
CGPoint origin = [position origin];
|
||||
CGAffineTransform transform = CGAffineTransformMakeTranslation(origin.x, origin.y);
|
||||
CGPoint origin = [position origin];
|
||||
CGAffineTransform transform = CGAffineTransformMakeTranslation(origin.x, origin.y);
|
||||
|
||||
float markerAngle = [@"auto" isEqualToString:_orient] ? -1 : [_orient doubleValue];
|
||||
float angle = 180 + (markerAngle == -1 ? [position angle] : markerAngle);
|
||||
float rad = deg2rad(angle);
|
||||
transform = CGAffineTransformRotate(transform, rad);
|
||||
float markerAngle = [@"auto" isEqualToString:_orient] ? -1 : [_orient doubleValue];
|
||||
float angle = 180 + (markerAngle == -1 ? [position angle] : markerAngle);
|
||||
float rad = deg2rad(angle);
|
||||
transform = CGAffineTransformRotate(transform, rad);
|
||||
|
||||
bool useStrokeWidth = [@"strokeWidth" isEqualToString:_markerUnits];
|
||||
if (useStrokeWidth) {
|
||||
transform = CGAffineTransformScale(transform, strokeWidth, strokeWidth);
|
||||
}
|
||||
bool useStrokeWidth = [@"strokeWidth" isEqualToString:_markerUnits];
|
||||
if (useStrokeWidth) {
|
||||
transform = CGAffineTransformScale(transform, strokeWidth, strokeWidth);
|
||||
}
|
||||
|
||||
CGFloat width = [self relativeOnWidth:self.markerWidth];
|
||||
CGFloat height = [self relativeOnHeight:self.markerHeight];
|
||||
CGRect eRect = CGRectMake(0, 0, width, height);
|
||||
if (self.align) {
|
||||
CGAffineTransform viewBoxTransform = [RNSVGViewBox getTransform:CGRectMake(self.minX, self.minY, self.vbWidth, self.vbHeight)
|
||||
eRect:eRect
|
||||
align:self.align
|
||||
meetOrSlice:self.meetOrSlice];
|
||||
transform = CGAffineTransformScale(transform, viewBoxTransform.a, viewBoxTransform.d);
|
||||
}
|
||||
CGFloat width = [self relativeOnWidth:self.markerWidth];
|
||||
CGFloat height = [self relativeOnHeight:self.markerHeight];
|
||||
CGRect eRect = CGRectMake(0, 0, width, height);
|
||||
if (self.align) {
|
||||
CGAffineTransform viewBoxTransform =
|
||||
[RNSVGViewBox getTransform:CGRectMake(self.minX, self.minY, self.vbWidth, self.vbHeight)
|
||||
eRect:eRect
|
||||
align:self.align
|
||||
meetOrSlice:self.meetOrSlice];
|
||||
transform = CGAffineTransformScale(transform, viewBoxTransform.a, viewBoxTransform.d);
|
||||
}
|
||||
|
||||
CGFloat x = [self relativeOnWidth:self.refX];
|
||||
CGFloat y = [self relativeOnHeight:self.refY];
|
||||
transform = CGAffineTransformTranslate(transform, -x, -y);
|
||||
CGFloat x = [self relativeOnWidth:self.refX];
|
||||
CGFloat y = [self relativeOnHeight:self.refY];
|
||||
transform = CGAffineTransformTranslate(transform, -x, -y);
|
||||
|
||||
self.transform = transform;
|
||||
CGContextConcatCTM(context, transform);
|
||||
self.transform = transform;
|
||||
CGContextConcatCTM(context, transform);
|
||||
|
||||
[self renderGroupTo:context rect:eRect];
|
||||
[self renderGroupTo:context rect:eRect];
|
||||
|
||||
CGContextRestoreGState(context);
|
||||
CGContextRestoreGState(context);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
Class<RCTComponentViewProtocol> RNSVGMarkerCls(void)
|
||||
{
|
||||
|
||||
@@ -6,16 +6,15 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
#import "RNSVGMask.h"
|
||||
#import "RNSVGPainter.h"
|
||||
#import "RNSVGBrushType.h"
|
||||
#import "RNSVGNode.h"
|
||||
|
||||
#import "RNSVGPainter.h"
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RCTConversions.h"
|
||||
#import <react/renderer/components/view/conversions.h>
|
||||
#import "RCTConversions.h"
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RNSVGFabricConversions.h"
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
@@ -42,119 +41,125 @@ using namespace facebook::react;
|
||||
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGMaskProps>(props);
|
||||
|
||||
self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)];
|
||||
self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)];
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.maskheight)) {
|
||||
self.maskheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.maskheight)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.maskwidth)) {
|
||||
self.maskwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.maskwidth)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
|
||||
self.maskheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
|
||||
self.maskwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
|
||||
}
|
||||
self.maskUnits = newProps.maskUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
|
||||
self.maskContentUnits = newProps.maskUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
|
||||
if (newProps.maskTransform.size() == 6) {
|
||||
self.maskTransform = CGAffineTransformMake(newProps.maskTransform.at(0), newProps.maskTransform.at(1), newProps.maskTransform.at(2), newProps.maskTransform.at(3), newProps.maskTransform.at(4), newProps.maskTransform.at(5));
|
||||
}
|
||||
|
||||
setCommonGroupProps(newProps, self);
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGMaskProps>(props);
|
||||
|
||||
self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)];
|
||||
self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)];
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.maskheight)) {
|
||||
self.maskheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.maskheight)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.maskwidth)) {
|
||||
self.maskwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.maskwidth)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
|
||||
self.maskheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
|
||||
self.maskwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
|
||||
}
|
||||
self.maskUnits = newProps.maskUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
|
||||
self.maskContentUnits = newProps.maskUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
|
||||
if (newProps.maskTransform.size() == 6) {
|
||||
self.maskTransform = CGAffineTransformMake(
|
||||
newProps.maskTransform.at(0),
|
||||
newProps.maskTransform.at(1),
|
||||
newProps.maskTransform.at(2),
|
||||
newProps.maskTransform.at(3),
|
||||
newProps.maskTransform.at(4),
|
||||
newProps.maskTransform.at(5));
|
||||
}
|
||||
|
||||
setCommonGroupProps(newProps, self);
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
_x = nil;
|
||||
_y = nil;
|
||||
_maskheight = nil;
|
||||
_maskwidth = nil;
|
||||
_maskUnits = kRNSVGUnitsObjectBoundingBox;
|
||||
_maskContentUnits = kRNSVGUnitsObjectBoundingBox;
|
||||
_maskTransform = CGAffineTransformIdentity;
|
||||
[super prepareForRecycle];
|
||||
_x = nil;
|
||||
_y = nil;
|
||||
_maskheight = nil;
|
||||
_maskwidth = nil;
|
||||
_maskUnits = kRNSVGUnitsObjectBoundingBox;
|
||||
_maskContentUnits = kRNSVGUnitsObjectBoundingBox;
|
||||
_maskTransform = CGAffineTransformIdentity;
|
||||
}
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
return nil;
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)parseReference
|
||||
{
|
||||
self.dirty = false;
|
||||
[self.svgView defineMask:self maskName:self.name];
|
||||
self.dirty = false;
|
||||
[self.svgView defineMask:self maskName:self.name];
|
||||
}
|
||||
|
||||
- (void)setX:(RNSVGLength *)x
|
||||
{
|
||||
if ([x isEqualTo:_x]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_x = x;
|
||||
[self invalidate];
|
||||
if ([x isEqualTo:_x]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_x = x;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setY:(RNSVGLength *)y
|
||||
{
|
||||
if ([y isEqualTo:_y]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_y = y;
|
||||
[self invalidate];
|
||||
if ([y isEqualTo:_y]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_y = y;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setMaskwidth:(RNSVGLength *)maskwidth
|
||||
{
|
||||
if ([maskwidth isEqualTo:_maskwidth]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_maskwidth = maskwidth;
|
||||
[self invalidate];
|
||||
if ([maskwidth isEqualTo:_maskwidth]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_maskwidth = maskwidth;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setMaskheight:(RNSVGLength *)maskheight
|
||||
{
|
||||
if ([maskheight isEqualTo:_maskheight]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_maskheight = maskheight;
|
||||
[self invalidate];
|
||||
if ([maskheight isEqualTo:_maskheight]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_maskheight = maskheight;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setMaskUnits:(RNSVGUnits)maskUnits
|
||||
{
|
||||
if (maskUnits == _maskUnits) {
|
||||
return;
|
||||
}
|
||||
|
||||
_maskUnits = maskUnits;
|
||||
[self invalidate];
|
||||
if (maskUnits == _maskUnits) {
|
||||
return;
|
||||
}
|
||||
|
||||
_maskUnits = maskUnits;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setMaskContentUnits:(RNSVGUnits)maskContentUnits
|
||||
{
|
||||
if (maskContentUnits == _maskContentUnits) {
|
||||
return;
|
||||
}
|
||||
|
||||
_maskContentUnits = maskContentUnits;
|
||||
[self invalidate];
|
||||
if (maskContentUnits == _maskContentUnits) {
|
||||
return;
|
||||
}
|
||||
|
||||
_maskContentUnits = maskContentUnits;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setMaskTransform:(CGAffineTransform)maskTransform
|
||||
{
|
||||
_maskTransform = maskTransform;
|
||||
[self invalidate];
|
||||
_maskTransform = maskTransform;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -10,15 +10,14 @@
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RCTConversions.h"
|
||||
#import <react/renderer/components/view/conversions.h>
|
||||
#import "RCTConversions.h"
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RNSVGFabricConversions.h"
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
@implementation RNSVGPath
|
||||
{
|
||||
CGPathRef _path;
|
||||
@implementation RNSVGPath {
|
||||
CGPathRef _path;
|
||||
}
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
@@ -42,43 +41,43 @@ using namespace facebook::react;
|
||||
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGPathProps>(props);
|
||||
self.d = [[RNSVGPathParser alloc] initWithPathString: RCTNSStringFromString(newProps.d)];
|
||||
|
||||
setCommonRenderableProps(newProps, self);
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGPathProps>(props);
|
||||
self.d = [[RNSVGPathParser alloc] initWithPathString:RCTNSStringFromString(newProps.d)];
|
||||
|
||||
setCommonRenderableProps(newProps, self);
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
if (_path) {
|
||||
CGPathRelease(_path);
|
||||
}
|
||||
_path = nil;
|
||||
_d = nil;
|
||||
[super prepareForRecycle];
|
||||
if (_path) {
|
||||
CGPathRelease(_path);
|
||||
}
|
||||
_path = nil;
|
||||
_d = nil;
|
||||
}
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
- (void)setD:(RNSVGPathParser *)d
|
||||
{
|
||||
if (d == _d) {
|
||||
return;
|
||||
}
|
||||
if (d == _d) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_d = d;
|
||||
CGPathRelease(_path);
|
||||
_path = CGPathRetain([d getPath]);
|
||||
[self invalidate];
|
||||
_d = d;
|
||||
CGPathRelease(_path);
|
||||
_path = CGPathRetain([d getPath]);
|
||||
}
|
||||
|
||||
- (CGPathRef)getPath:(CGContextRef)context
|
||||
{
|
||||
return _path;
|
||||
return _path;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
CGPathRelease(_path);
|
||||
CGPathRelease(_path);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
#import "RNSVGPattern.h"
|
||||
#import "RNSVGPainter.h"
|
||||
#import "RNSVGBrushType.h"
|
||||
#import "RNSVGNode.h"
|
||||
#import "RNSVGPainter.h"
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RCTConversions.h"
|
||||
#import <react/renderer/components/view/conversions.h>
|
||||
#import "RCTConversions.h"
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RNSVGFabricConversions.h"
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
@@ -41,211 +41,217 @@ using namespace facebook::react;
|
||||
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGPatternProps>(props);
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGPatternProps>(props);
|
||||
|
||||
self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)];
|
||||
self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)];
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.patternheight)) {
|
||||
self.patternheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.patternheight)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.patternwidth)) {
|
||||
self.patternwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.patternwidth)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
|
||||
self.patternheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
|
||||
self.patternwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
|
||||
}
|
||||
self.patternUnits = newProps.patternUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
|
||||
self.patternContentUnits = newProps.patternUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
|
||||
if (newProps.patternTransform.size() == 6) {
|
||||
self.patternTransform = CGAffineTransformMake(newProps.patternTransform.at(0), newProps.patternTransform.at(1), newProps.patternTransform.at(2), newProps.patternTransform.at(3), newProps.patternTransform.at(4), newProps.patternTransform.at(5));
|
||||
}
|
||||
self.minX = newProps.minX;
|
||||
self.minY = newProps.minY;
|
||||
self.vbWidth = newProps.vbWidth;
|
||||
self.vbHeight = newProps.vbHeight;
|
||||
self.align = RCTNSStringFromStringNilIfEmpty(newProps.align);
|
||||
self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice);
|
||||
|
||||
setCommonGroupProps(newProps, self);
|
||||
self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)];
|
||||
self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)];
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.patternheight)) {
|
||||
self.patternheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.patternheight)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.patternwidth)) {
|
||||
self.patternwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.patternwidth)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
|
||||
self.patternheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
|
||||
self.patternwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
|
||||
}
|
||||
self.patternUnits = newProps.patternUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
|
||||
self.patternContentUnits = newProps.patternUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
|
||||
if (newProps.patternTransform.size() == 6) {
|
||||
self.patternTransform = CGAffineTransformMake(
|
||||
newProps.patternTransform.at(0),
|
||||
newProps.patternTransform.at(1),
|
||||
newProps.patternTransform.at(2),
|
||||
newProps.patternTransform.at(3),
|
||||
newProps.patternTransform.at(4),
|
||||
newProps.patternTransform.at(5));
|
||||
}
|
||||
self.minX = newProps.minX;
|
||||
self.minY = newProps.minY;
|
||||
self.vbWidth = newProps.vbWidth;
|
||||
self.vbHeight = newProps.vbHeight;
|
||||
self.align = RCTNSStringFromStringNilIfEmpty(newProps.align);
|
||||
self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice);
|
||||
|
||||
setCommonGroupProps(newProps, self);
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
_x = nil;
|
||||
_y = nil;
|
||||
_patternheight = nil;
|
||||
_patternwidth = nil;
|
||||
_patternUnits = kRNSVGUnitsObjectBoundingBox;
|
||||
_patternContentUnits = kRNSVGUnitsObjectBoundingBox;
|
||||
_patternTransform = CGAffineTransformIdentity;
|
||||
|
||||
_minX = 0;
|
||||
_minY = 0;
|
||||
_vbWidth = 0;
|
||||
_vbHeight = 0;
|
||||
_align = nil;
|
||||
_meetOrSlice = kRNSVGVBMOSMeet;
|
||||
[super prepareForRecycle];
|
||||
_x = nil;
|
||||
_y = nil;
|
||||
_patternheight = nil;
|
||||
_patternwidth = nil;
|
||||
_patternUnits = kRNSVGUnitsObjectBoundingBox;
|
||||
_patternContentUnits = kRNSVGUnitsObjectBoundingBox;
|
||||
_patternTransform = CGAffineTransformIdentity;
|
||||
|
||||
_minX = 0;
|
||||
_minY = 0;
|
||||
_vbWidth = 0;
|
||||
_vbHeight = 0;
|
||||
_align = nil;
|
||||
_meetOrSlice = kRNSVGVBMOSMeet;
|
||||
}
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_patternTransform = CGAffineTransformIdentity;
|
||||
}
|
||||
return self;
|
||||
if (self = [super init]) {
|
||||
_patternTransform = CGAffineTransformIdentity;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
return nil;
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)parseReference
|
||||
{
|
||||
self.dirty = false;
|
||||
NSArray<RNSVGLength *> *points = @[self.x, self.y, self.patternwidth, self.patternheight];
|
||||
RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points];
|
||||
[painter setUnits:self.patternUnits];
|
||||
[painter setContentUnits:self.patternContentUnits];
|
||||
[painter setTransform:self.patternTransform];
|
||||
[painter setPattern:self];
|
||||
self.dirty = false;
|
||||
NSArray<RNSVGLength *> *points = @[ self.x, self.y, self.patternwidth, self.patternheight ];
|
||||
RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points];
|
||||
[painter setUnits:self.patternUnits];
|
||||
[painter setContentUnits:self.patternContentUnits];
|
||||
[painter setTransform:self.patternTransform];
|
||||
[painter setPattern:self];
|
||||
|
||||
if (self.patternUnits == kRNSVGUnitsUserSpaceOnUse || self.patternContentUnits == kRNSVGUnitsUserSpaceOnUse) {
|
||||
[painter setUserSpaceBoundingBox:[self.svgView getContextBounds]];
|
||||
}
|
||||
if (self.patternUnits == kRNSVGUnitsUserSpaceOnUse || self.patternContentUnits == kRNSVGUnitsUserSpaceOnUse) {
|
||||
[painter setUserSpaceBoundingBox:[self.svgView getContextBounds]];
|
||||
}
|
||||
|
||||
[self.svgView definePainter:painter painterName:self.name];
|
||||
[self.svgView definePainter:painter painterName:self.name];
|
||||
}
|
||||
|
||||
- (void)setX:(RNSVGLength *)x
|
||||
{
|
||||
if ([x isEqualTo:_x]) {
|
||||
return;
|
||||
}
|
||||
if ([x isEqualTo:_x]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_x = x;
|
||||
[self invalidate];
|
||||
_x = x;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setY:(RNSVGLength *)y
|
||||
{
|
||||
if ([y isEqualTo:_y]) {
|
||||
return;
|
||||
}
|
||||
if ([y isEqualTo:_y]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_y = y;
|
||||
[self invalidate];
|
||||
_y = y;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setPatternwidth:(RNSVGLength *)patternwidth
|
||||
{
|
||||
if ([patternwidth isEqualTo:_patternwidth]) {
|
||||
return;
|
||||
}
|
||||
if ([patternwidth isEqualTo:_patternwidth]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_patternwidth = patternwidth;
|
||||
[self invalidate];
|
||||
_patternwidth = patternwidth;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setPatternheight:(RNSVGLength *)patternheight
|
||||
{
|
||||
if ([patternheight isEqualTo:_patternheight]) {
|
||||
return;
|
||||
}
|
||||
if ([patternheight isEqualTo:_patternheight]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_patternheight = patternheight;
|
||||
[self invalidate];
|
||||
_patternheight = patternheight;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setPatternUnits:(RNSVGUnits)patternUnits
|
||||
{
|
||||
if (patternUnits == _patternUnits) {
|
||||
return;
|
||||
}
|
||||
if (patternUnits == _patternUnits) {
|
||||
return;
|
||||
}
|
||||
|
||||
_patternUnits = patternUnits;
|
||||
[self invalidate];
|
||||
_patternUnits = patternUnits;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setPatternContentUnits:(RNSVGUnits)patternContentUnits
|
||||
{
|
||||
if (patternContentUnits == _patternContentUnits) {
|
||||
return;
|
||||
}
|
||||
if (patternContentUnits == _patternContentUnits) {
|
||||
return;
|
||||
}
|
||||
|
||||
_patternContentUnits = patternContentUnits;
|
||||
[self invalidate];
|
||||
_patternContentUnits = patternContentUnits;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setPatternTransform:(CGAffineTransform)patternTransform
|
||||
{
|
||||
_patternTransform = patternTransform;
|
||||
[self invalidate];
|
||||
_patternTransform = patternTransform;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setMinX:(CGFloat)minX
|
||||
{
|
||||
if (minX == _minX) {
|
||||
return;
|
||||
}
|
||||
if (minX == _minX) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_minX = minX;
|
||||
[self invalidate];
|
||||
_minX = minX;
|
||||
}
|
||||
|
||||
- (void)setMinY:(CGFloat)minY
|
||||
{
|
||||
if (minY == _minY) {
|
||||
return;
|
||||
}
|
||||
if (minY == _minY) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_minY = minY;
|
||||
[self invalidate];
|
||||
_minY = minY;
|
||||
}
|
||||
|
||||
- (void)setVbWidth:(CGFloat)vbWidth
|
||||
{
|
||||
if (vbWidth == _vbWidth) {
|
||||
return;
|
||||
}
|
||||
if (vbWidth == _vbWidth) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_vbWidth = vbWidth;
|
||||
[self invalidate];
|
||||
_vbWidth = vbWidth;
|
||||
}
|
||||
|
||||
- (void)setVbHeight:(CGFloat)vbHeight
|
||||
{
|
||||
if (_vbHeight == vbHeight) {
|
||||
return;
|
||||
}
|
||||
if (_vbHeight == vbHeight) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_vbHeight = vbHeight;
|
||||
[self invalidate];
|
||||
_vbHeight = vbHeight;
|
||||
}
|
||||
|
||||
- (void)setAlign:(NSString *)align
|
||||
{
|
||||
if ([align isEqualToString:_align]) {
|
||||
return;
|
||||
}
|
||||
if ([align isEqualToString:_align]) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_align = align;
|
||||
[self invalidate];
|
||||
_align = align;
|
||||
}
|
||||
|
||||
- (void)setMeetOrSlice:(RNSVGVBMOS)meetOrSlice
|
||||
{
|
||||
if (meetOrSlice == _meetOrSlice) {
|
||||
return;
|
||||
}
|
||||
if (meetOrSlice == _meetOrSlice) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_meetOrSlice = meetOrSlice;
|
||||
[self invalidate];
|
||||
_meetOrSlice = meetOrSlice;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "RNSVGNode.h"
|
||||
#import "RNSVGLength.h"
|
||||
#import "RNSVGNode.h"
|
||||
|
||||
@interface RNSVGRadialGradient : RNSVGNode
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RCTConversions.h"
|
||||
#import <react/renderer/components/view/conversions.h>
|
||||
#import "RCTConversions.h"
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RNSVGFabricConversions.h"
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
@@ -38,156 +38,162 @@ using namespace facebook::react;
|
||||
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGRadialGradientProps>(props);
|
||||
|
||||
self.fx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.fx)];
|
||||
self.fy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.fy)];
|
||||
self.cx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cx)];
|
||||
self.cy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cy)];
|
||||
self.rx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rx)];
|
||||
self.ry = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.ry)];
|
||||
if (newProps.gradient.size() > 0) {
|
||||
NSMutableArray<NSNumber *> *gradientArray = [NSMutableArray new];
|
||||
for (auto number : newProps.gradient) {
|
||||
[gradientArray addObject:[NSNumber numberWithDouble:number]];
|
||||
}
|
||||
self.gradient = gradientArray;
|
||||
}
|
||||
self.gradientUnits = newProps.gradientUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
|
||||
if (newProps.gradientTransform.size() == 6) {
|
||||
self.gradientTransform = CGAffineTransformMake(newProps.gradientTransform.at(0), newProps.gradientTransform.at(1), newProps.gradientTransform.at(2), newProps.gradientTransform.at(3), newProps.gradientTransform.at(4), newProps.gradientTransform.at(5));
|
||||
}
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGRadialGradientProps>(props);
|
||||
|
||||
setCommonNodeProps(newProps, self);
|
||||
self.fx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.fx)];
|
||||
self.fy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.fy)];
|
||||
self.cx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cx)];
|
||||
self.cy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cy)];
|
||||
self.rx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rx)];
|
||||
self.ry = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.ry)];
|
||||
if (newProps.gradient.size() > 0) {
|
||||
NSMutableArray<NSNumber *> *gradientArray = [NSMutableArray new];
|
||||
for (auto number : newProps.gradient) {
|
||||
[gradientArray addObject:[NSNumber numberWithDouble:number]];
|
||||
}
|
||||
self.gradient = gradientArray;
|
||||
}
|
||||
self.gradientUnits = newProps.gradientUnits == 0 ? kRNSVGUnitsObjectBoundingBox : kRNSVGUnitsUserSpaceOnUse;
|
||||
if (newProps.gradientTransform.size() == 6) {
|
||||
self.gradientTransform = CGAffineTransformMake(
|
||||
newProps.gradientTransform.at(0),
|
||||
newProps.gradientTransform.at(1),
|
||||
newProps.gradientTransform.at(2),
|
||||
newProps.gradientTransform.at(3),
|
||||
newProps.gradientTransform.at(4),
|
||||
newProps.gradientTransform.at(5));
|
||||
}
|
||||
|
||||
setCommonNodeProps(newProps, self);
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
_fx = nil;
|
||||
_fy = nil;
|
||||
_cx = nil;
|
||||
_cy = nil;
|
||||
_rx = nil;
|
||||
_ry = nil;
|
||||
_gradient = nil;
|
||||
_gradientUnits = kRNSVGUnitsObjectBoundingBox;
|
||||
_gradientTransform = CGAffineTransformIdentity;
|
||||
[super prepareForRecycle];
|
||||
_fx = nil;
|
||||
_fy = nil;
|
||||
_cx = nil;
|
||||
_cy = nil;
|
||||
_rx = nil;
|
||||
_ry = nil;
|
||||
_gradient = nil;
|
||||
_gradientUnits = kRNSVGUnitsObjectBoundingBox;
|
||||
_gradientTransform = CGAffineTransformIdentity;
|
||||
}
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_gradientTransform = CGAffineTransformIdentity;
|
||||
}
|
||||
return self;
|
||||
if (self = [super init]) {
|
||||
_gradientTransform = CGAffineTransformIdentity;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setFx:(RNSVGLength *)fx
|
||||
{
|
||||
if ([fx isEqualTo:_fx]) {
|
||||
return;
|
||||
}
|
||||
if ([fx isEqualTo:_fx]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_fx = fx;
|
||||
[self invalidate];
|
||||
_fx = fx;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setFy:(RNSVGLength *)fy
|
||||
{
|
||||
if ([fy isEqualTo:_fy]) {
|
||||
return;
|
||||
}
|
||||
if ([fy isEqualTo:_fy]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_fy = fy;
|
||||
[self invalidate];
|
||||
_fy = fy;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setRx:(RNSVGLength *)rx
|
||||
{
|
||||
if ([rx isEqualTo:_rx]) {
|
||||
return;
|
||||
}
|
||||
if ([rx isEqualTo:_rx]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_rx = rx;
|
||||
[self invalidate];
|
||||
_rx = rx;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setRy:(RNSVGLength *)ry
|
||||
{
|
||||
if ([ry isEqualTo:_ry]) {
|
||||
return;
|
||||
}
|
||||
if ([ry isEqualTo:_ry]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_ry = ry;
|
||||
[self invalidate];
|
||||
_ry = ry;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setCx:(RNSVGLength *)cx
|
||||
{
|
||||
if ([cx isEqualTo:_cx]) {
|
||||
return;
|
||||
}
|
||||
if ([cx isEqualTo:_cx]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_cx = cx;
|
||||
[self invalidate];
|
||||
_cx = cx;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setCy:(RNSVGLength *)cy
|
||||
{
|
||||
if ([cy isEqualTo:_cy]) {
|
||||
return;
|
||||
}
|
||||
if ([cy isEqualTo:_cy]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_cy = cy;
|
||||
[self invalidate];
|
||||
_cy = cy;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setGradient:(NSArray<NSNumber *> *)gradient
|
||||
{
|
||||
if (gradient == _gradient) {
|
||||
return;
|
||||
}
|
||||
if (gradient == _gradient) {
|
||||
return;
|
||||
}
|
||||
|
||||
_gradient = gradient;
|
||||
[self invalidate];
|
||||
_gradient = gradient;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setGradientUnits:(RNSVGUnits)gradientUnits
|
||||
{
|
||||
if (gradientUnits == _gradientUnits) {
|
||||
return;
|
||||
}
|
||||
if (gradientUnits == _gradientUnits) {
|
||||
return;
|
||||
}
|
||||
|
||||
_gradientUnits = gradientUnits;
|
||||
[self invalidate];
|
||||
_gradientUnits = gradientUnits;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)setGradientTransform:(CGAffineTransform)gradientTransform
|
||||
{
|
||||
_gradientTransform = gradientTransform;
|
||||
[self invalidate];
|
||||
_gradientTransform = gradientTransform;
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
return nil;
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)parseReference
|
||||
{
|
||||
self.dirty = false;
|
||||
NSArray<RNSVGLength *> *points = @[self.fx, self.fy, self.rx, self.ry, self.cx, self.cy];
|
||||
RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points];
|
||||
[painter setUnits:self.gradientUnits];
|
||||
[painter setTransform:self.gradientTransform];
|
||||
[painter setRadialGradientColors:self.gradient];
|
||||
self.dirty = false;
|
||||
NSArray<RNSVGLength *> *points = @[ self.fx, self.fy, self.rx, self.ry, self.cx, self.cy ];
|
||||
RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points];
|
||||
[painter setUnits:self.gradientUnits];
|
||||
[painter setTransform:self.gradientTransform];
|
||||
[painter setRadialGradientColors:self.gradient];
|
||||
|
||||
if (self.gradientUnits == kRNSVGUnitsUserSpaceOnUse) {
|
||||
[painter setUserSpaceBoundingBox:[self.svgView getContextBounds]];
|
||||
}
|
||||
if (self.gradientUnits == kRNSVGUnitsUserSpaceOnUse) {
|
||||
[painter setUserSpaceBoundingBox:[self.svgView getContextBounds]];
|
||||
}
|
||||
|
||||
[self.svgView definePainter:painter painterName:self.name];
|
||||
[self.svgView definePainter:painter painterName:self.name];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
#import "RNSVGUIKit.h"
|
||||
|
||||
#import "RNSVGPainter.h"
|
||||
#import "RNSVGContainer.h"
|
||||
#import "RNSVGPainter.h"
|
||||
#import "RNSVGVBMOS.h"
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
@@ -20,9 +20,9 @@
|
||||
|
||||
@interface RNSVGSvgView :
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
RCTViewComponentView <RNSVGContainer>
|
||||
RCTViewComponentView <RNSVGContainer>
|
||||
#else
|
||||
RNSVGView <RNSVGContainer>
|
||||
RNSVGView <RNSVGContainer>
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
@property (nonatomic, strong) RNSVGLength *bbWidth;
|
||||
|
||||
@@ -7,27 +7,26 @@
|
||||
*/
|
||||
|
||||
#import "RNSVGSvgView.h"
|
||||
#import "RNSVGViewBox.h"
|
||||
#import "RNSVGNode.h"
|
||||
#import <React/RCTLog.h>
|
||||
#import "RNSVGNode.h"
|
||||
#import "RNSVGViewBox.h"
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RCTConversions.h"
|
||||
#import <react/renderer/components/view/conversions.h>
|
||||
#import "RCTConversions.h"
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RNSVGFabricConversions.h"
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
@implementation RNSVGSvgView
|
||||
{
|
||||
NSMutableDictionary<NSString *, RNSVGNode *> *_clipPaths;
|
||||
NSMutableDictionary<NSString *, RNSVGNode *> *_templates;
|
||||
NSMutableDictionary<NSString *, RNSVGPainter *> *_painters;
|
||||
NSMutableDictionary<NSString *, RNSVGNode *> *_markers;
|
||||
NSMutableDictionary<NSString *, RNSVGNode *> *_masks;
|
||||
CGAffineTransform _invviewBoxTransform;
|
||||
bool rendered;
|
||||
@implementation RNSVGSvgView {
|
||||
NSMutableDictionary<NSString *, RNSVGNode *> *_clipPaths;
|
||||
NSMutableDictionary<NSString *, RNSVGNode *> *_templates;
|
||||
NSMutableDictionary<NSString *, RNSVGPainter *> *_painters;
|
||||
NSMutableDictionary<NSString *, RNSVGNode *> *_markers;
|
||||
NSMutableDictionary<NSString *, RNSVGNode *> *_masks;
|
||||
CGAffineTransform _invviewBoxTransform;
|
||||
bool rendered;
|
||||
}
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
@@ -36,21 +35,21 @@ using namespace facebook::react;
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
#if !TARGET_OS_OSX // Not available on macOS
|
||||
// This is necessary to ensure that [self setNeedsDisplay] actually triggers
|
||||
// a redraw when our parent transitions between hidden and visible.
|
||||
self.contentMode = UIViewContentModeRedraw;
|
||||
// This is necessary to ensure that [self setNeedsDisplay] actually triggers
|
||||
// a redraw when our parent transitions between hidden and visible.
|
||||
self.contentMode = UIViewContentModeRedraw;
|
||||
#endif // TARGET_OS_OSX
|
||||
rendered = false;
|
||||
rendered = false;
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
static const auto defaultProps = std::make_shared<const RNSVGSvgViewProps>();
|
||||
_props = defaultProps;
|
||||
// TODO: think if we can do it better
|
||||
self.opaque = NO;
|
||||
static const auto defaultProps = std::make_shared<const RNSVGSvgViewProps>();
|
||||
_props = defaultProps;
|
||||
// TODO: think if we can do it better
|
||||
self.opaque = NO;
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
}
|
||||
return self;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
@@ -63,379 +62,374 @@ using namespace facebook::react;
|
||||
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGSvgViewProps>(props);
|
||||
|
||||
self.minX = newProps.minX;
|
||||
self.minY = newProps.minY;
|
||||
self.vbWidth = newProps.vbWidth;
|
||||
self.vbHeight = newProps.vbHeight;
|
||||
self.bbWidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.bbWidth)];
|
||||
self.bbHeight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.bbHeight)];
|
||||
self.align = RCTNSStringFromStringNilIfEmpty(newProps.align);
|
||||
self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice);
|
||||
if (RCTUIColorFromSharedColor(newProps.tintColor)) {
|
||||
self.tintColor = RCTUIColorFromSharedColor(newProps.tintColor);
|
||||
}
|
||||
if (RCTUIColorFromSharedColor(newProps.color)) {
|
||||
self.tintColor = RCTUIColorFromSharedColor(newProps.color);
|
||||
}
|
||||
}
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGSvgViewProps>(props);
|
||||
|
||||
self.minX = newProps.minX;
|
||||
self.minY = newProps.minY;
|
||||
self.vbWidth = newProps.vbWidth;
|
||||
self.vbHeight = newProps.vbHeight;
|
||||
self.bbWidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.bbWidth)];
|
||||
self.bbHeight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.bbHeight)];
|
||||
self.align = RCTNSStringFromStringNilIfEmpty(newProps.align);
|
||||
self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice);
|
||||
if (RCTUIColorFromSharedColor(newProps.tintColor)) {
|
||||
self.tintColor = RCTUIColorFromSharedColor(newProps.tintColor);
|
||||
}
|
||||
if (RCTUIColorFromSharedColor(newProps.color)) {
|
||||
self.tintColor = RCTUIColorFromSharedColor(newProps.color);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
_minX = 0;
|
||||
_minY = 0;
|
||||
_vbWidth = 0;
|
||||
_vbHeight = 0;
|
||||
_bbWidth = 0;
|
||||
_bbHeight = 0;
|
||||
_align = nil;
|
||||
_meetOrSlice = kRNSVGVBMOSMeet;
|
||||
|
||||
_responsible = NO;
|
||||
_active = NO;
|
||||
_boundingBox = CGRectZero;
|
||||
_initialCTM = CGAffineTransformIdentity;
|
||||
_invInitialCTM = CGAffineTransformIdentity;
|
||||
_viewBoxTransform = CGAffineTransformIdentity;
|
||||
|
||||
_clipPaths = nil;
|
||||
_templates = nil;
|
||||
_painters = nil;
|
||||
_markers = nil;
|
||||
_masks = nil;
|
||||
_invviewBoxTransform = CGAffineTransformIdentity;
|
||||
rendered = NO;
|
||||
[super prepareForRecycle];
|
||||
_minX = 0;
|
||||
_minY = 0;
|
||||
_vbWidth = 0;
|
||||
_vbHeight = 0;
|
||||
_bbWidth = 0;
|
||||
_bbHeight = 0;
|
||||
_align = nil;
|
||||
_meetOrSlice = kRNSVGVBMOSMeet;
|
||||
|
||||
_responsible = NO;
|
||||
_active = NO;
|
||||
_boundingBox = CGRectZero;
|
||||
_initialCTM = CGAffineTransformIdentity;
|
||||
_invInitialCTM = CGAffineTransformIdentity;
|
||||
_viewBoxTransform = CGAffineTransformIdentity;
|
||||
|
||||
_clipPaths = nil;
|
||||
_templates = nil;
|
||||
_painters = nil;
|
||||
_markers = nil;
|
||||
_masks = nil;
|
||||
_invviewBoxTransform = CGAffineTransformIdentity;
|
||||
rendered = NO;
|
||||
}
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
- (void)insertReactSubview:(RNSVGView *)subview atIndex:(NSInteger)atIndex
|
||||
{
|
||||
[super insertReactSubview:subview atIndex:atIndex];
|
||||
[self insertSubview:subview atIndex:atIndex];
|
||||
[self invalidate];
|
||||
[super insertReactSubview:subview atIndex:atIndex];
|
||||
[self insertSubview:subview atIndex:atIndex];
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)removeReactSubview:(RNSVGView *)subview
|
||||
{
|
||||
[super removeReactSubview:subview];
|
||||
[self invalidate];
|
||||
[super removeReactSubview:subview];
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)didUpdateReactSubviews
|
||||
{
|
||||
// Do nothing, as subviews are inserted by insertReactSubview:
|
||||
// Do nothing, as subviews are inserted by insertReactSubview:
|
||||
}
|
||||
|
||||
- (void)clearChildCache
|
||||
{
|
||||
if (!rendered) {
|
||||
return;
|
||||
}
|
||||
rendered = false;
|
||||
for (__kindof RNSVGNode *node in self.subviews) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
[node clearChildCache];
|
||||
}
|
||||
if (!rendered) {
|
||||
return;
|
||||
}
|
||||
rendered = false;
|
||||
for (__kindof RNSVGNode *node in self.subviews) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
[node clearChildCache];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
RNSVGPlatformView* parent = self.superview;
|
||||
if ([parent isKindOfClass:[RNSVGNode class]]) {
|
||||
if (!rendered) {
|
||||
return;
|
||||
}
|
||||
RNSVGNode* svgNode = (RNSVGNode*)parent;
|
||||
[svgNode invalidate];
|
||||
rendered = false;
|
||||
return;
|
||||
RNSVGPlatformView *parent = self.superview;
|
||||
if ([parent isKindOfClass:[RNSVGNode class]]) {
|
||||
if (!rendered) {
|
||||
return;
|
||||
}
|
||||
[self setNeedsDisplay];
|
||||
RNSVGNode *svgNode = (RNSVGNode *)parent;
|
||||
[svgNode invalidate];
|
||||
rendered = false;
|
||||
return;
|
||||
}
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (void)tintColorDidChange
|
||||
{
|
||||
[self invalidate];
|
||||
[self clearChildCache];
|
||||
[self invalidate];
|
||||
[self clearChildCache];
|
||||
}
|
||||
|
||||
- (void)setMinX:(CGFloat)minX
|
||||
{
|
||||
if (minX == _minX) {
|
||||
return;
|
||||
}
|
||||
if (minX == _minX) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
[self clearChildCache];
|
||||
_minX = minX;
|
||||
[self invalidate];
|
||||
[self clearChildCache];
|
||||
_minX = minX;
|
||||
}
|
||||
|
||||
- (void)setMinY:(CGFloat)minY
|
||||
{
|
||||
if (minY == _minY) {
|
||||
return;
|
||||
}
|
||||
if (minY == _minY) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
[self clearChildCache];
|
||||
_minY = minY;
|
||||
[self invalidate];
|
||||
[self clearChildCache];
|
||||
_minY = minY;
|
||||
}
|
||||
|
||||
- (void)setVbWidth:(CGFloat)vbWidth
|
||||
{
|
||||
if (vbWidth == _vbWidth) {
|
||||
return;
|
||||
}
|
||||
if (vbWidth == _vbWidth) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
[self clearChildCache];
|
||||
_vbWidth = vbWidth;
|
||||
[self invalidate];
|
||||
[self clearChildCache];
|
||||
_vbWidth = vbWidth;
|
||||
}
|
||||
|
||||
- (void)setVbHeight:(CGFloat)vbHeight
|
||||
{
|
||||
if (_vbHeight == vbHeight) {
|
||||
return;
|
||||
}
|
||||
if (_vbHeight == vbHeight) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
[self clearChildCache];
|
||||
_vbHeight = vbHeight;
|
||||
[self invalidate];
|
||||
[self clearChildCache];
|
||||
_vbHeight = vbHeight;
|
||||
}
|
||||
|
||||
- (void)setBbWidth:(RNSVGLength *)bbWidth
|
||||
{
|
||||
if ([bbWidth isEqualTo:_bbWidth]) {
|
||||
return;
|
||||
}
|
||||
if ([bbWidth isEqualTo:_bbWidth]) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
[self clearChildCache];
|
||||
_bbWidth = bbWidth;
|
||||
[self invalidate];
|
||||
[self clearChildCache];
|
||||
_bbWidth = bbWidth;
|
||||
}
|
||||
|
||||
- (void)setBbHeight:(RNSVGLength *)bbHeight
|
||||
{
|
||||
if ([bbHeight isEqualTo:_bbHeight]) {
|
||||
return;
|
||||
}
|
||||
if ([bbHeight isEqualTo:_bbHeight]) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
[self clearChildCache];
|
||||
_bbHeight = bbHeight;
|
||||
[self invalidate];
|
||||
[self clearChildCache];
|
||||
_bbHeight = bbHeight;
|
||||
}
|
||||
|
||||
- (void)setAlign:(NSString *)align
|
||||
{
|
||||
if ([align isEqualToString:_align]) {
|
||||
return;
|
||||
}
|
||||
if ([align isEqualToString:_align]) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
[self clearChildCache];
|
||||
_align = align;
|
||||
[self invalidate];
|
||||
[self clearChildCache];
|
||||
_align = align;
|
||||
}
|
||||
|
||||
- (void)setMeetOrSlice:(RNSVGVBMOS)meetOrSlice
|
||||
{
|
||||
if (meetOrSlice == _meetOrSlice) {
|
||||
return;
|
||||
}
|
||||
if (meetOrSlice == _meetOrSlice) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
[self clearChildCache];
|
||||
_meetOrSlice = meetOrSlice;
|
||||
[self invalidate];
|
||||
[self clearChildCache];
|
||||
_meetOrSlice = meetOrSlice;
|
||||
}
|
||||
|
||||
- (void)drawToContext:(CGContextRef)context withRect:(CGRect)rect {
|
||||
rendered = true;
|
||||
self.initialCTM = CGContextGetCTM(context);
|
||||
self.invInitialCTM = CGAffineTransformInvert(self.initialCTM);
|
||||
if (self.align) {
|
||||
CGRect tRect = CGRectMake(self.minX, self.minY, self.vbWidth, self.vbHeight);
|
||||
_viewBoxTransform = [RNSVGViewBox getTransform:tRect
|
||||
eRect:rect
|
||||
align:self.align
|
||||
meetOrSlice:self.meetOrSlice];
|
||||
_invviewBoxTransform = CGAffineTransformInvert(_viewBoxTransform);
|
||||
CGContextConcatCTM(context, _viewBoxTransform);
|
||||
} else {
|
||||
_viewBoxTransform = CGAffineTransformIdentity;
|
||||
_invviewBoxTransform = CGAffineTransformIdentity;
|
||||
}
|
||||
- (void)drawToContext:(CGContextRef)context withRect:(CGRect)rect
|
||||
{
|
||||
rendered = true;
|
||||
self.initialCTM = CGContextGetCTM(context);
|
||||
self.invInitialCTM = CGAffineTransformInvert(self.initialCTM);
|
||||
if (self.align) {
|
||||
CGRect tRect = CGRectMake(self.minX, self.minY, self.vbWidth, self.vbHeight);
|
||||
_viewBoxTransform = [RNSVGViewBox getTransform:tRect eRect:rect align:self.align meetOrSlice:self.meetOrSlice];
|
||||
_invviewBoxTransform = CGAffineTransformInvert(_viewBoxTransform);
|
||||
CGContextConcatCTM(context, _viewBoxTransform);
|
||||
} else {
|
||||
_viewBoxTransform = CGAffineTransformIdentity;
|
||||
_invviewBoxTransform = CGAffineTransformIdentity;
|
||||
}
|
||||
|
||||
for (RNSVGView *node in self.subviews) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
RNSVGNode *svg = (RNSVGNode *)node;
|
||||
[svg renderTo:context
|
||||
rect:rect];
|
||||
} else {
|
||||
[node drawRect:rect];
|
||||
}
|
||||
for (RNSVGView *node in self.subviews) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
RNSVGNode *svg = (RNSVGNode *)node;
|
||||
[svg renderTo:context rect:rect];
|
||||
} else {
|
||||
[node drawRect:rect];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)rect
|
||||
{
|
||||
RNSVGPlatformView* parent = self.superview;
|
||||
if ([parent isKindOfClass:[RNSVGNode class]]) {
|
||||
return;
|
||||
RNSVGPlatformView *parent = self.superview;
|
||||
if ([parent isKindOfClass:[RNSVGNode class]]) {
|
||||
return;
|
||||
}
|
||||
rendered = true;
|
||||
_clipPaths = nil;
|
||||
_templates = nil;
|
||||
_painters = nil;
|
||||
_boundingBox = rect;
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
|
||||
for (RNSVGPlatformView *node in self.subviews) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
RNSVGNode *svg = (RNSVGNode *)node;
|
||||
if (svg.responsible && !self.responsible) {
|
||||
self.responsible = YES;
|
||||
}
|
||||
|
||||
[svg parseReference];
|
||||
}
|
||||
rendered = true;
|
||||
_clipPaths = nil;
|
||||
_templates = nil;
|
||||
_painters = nil;
|
||||
_boundingBox = rect;
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
}
|
||||
|
||||
for (RNSVGPlatformView *node in self.subviews) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
RNSVGNode *svg = (RNSVGNode *)node;
|
||||
if (svg.responsible && !self.responsible) {
|
||||
self.responsible = YES;
|
||||
}
|
||||
|
||||
[svg parseReference];
|
||||
}
|
||||
}
|
||||
|
||||
[self drawToContext:context withRect:rect];
|
||||
[self drawToContext:context withRect:rect];
|
||||
}
|
||||
|
||||
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
CGPoint transformed = point;
|
||||
if (self.align) {
|
||||
transformed = CGPointApplyAffineTransform(transformed, _invviewBoxTransform);
|
||||
CGPoint transformed = point;
|
||||
if (self.align) {
|
||||
transformed = CGPointApplyAffineTransform(transformed, _invviewBoxTransform);
|
||||
}
|
||||
for (RNSVGNode *node in [self.subviews reverseObjectEnumerator]) {
|
||||
if (![node isKindOfClass:[RNSVGNode class]]) {
|
||||
continue;
|
||||
}
|
||||
for (RNSVGNode *node in [self.subviews reverseObjectEnumerator]) {
|
||||
if (![node isKindOfClass:[RNSVGNode class]]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (event) {
|
||||
node.active = NO;
|
||||
}
|
||||
|
||||
RNSVGPlatformView *hitChild = [node hitTest:transformed withEvent:event];
|
||||
|
||||
if (hitChild) {
|
||||
node.active = YES;
|
||||
return (node.responsible || (node != hitChild)) ? hitChild : self;
|
||||
}
|
||||
if (event) {
|
||||
node.active = NO;
|
||||
}
|
||||
return nil;
|
||||
|
||||
RNSVGPlatformView *hitChild = [node hitTest:transformed withEvent:event];
|
||||
|
||||
if (hitChild) {
|
||||
node.active = YES;
|
||||
return (node.responsible || (node != hitChild)) ? hitChild : self;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *)getDataURL
|
||||
{
|
||||
UIGraphicsBeginImageContextWithOptions(_boundingBox.size, NO, 0);
|
||||
[self clearChildCache];
|
||||
[self drawRect:_boundingBox];
|
||||
[self clearChildCache];
|
||||
[self invalidate];
|
||||
NSData *imageData = UIImagePNGRepresentation(UIGraphicsGetImageFromCurrentImageContext());
|
||||
NSString *base64 = [imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
|
||||
UIGraphicsEndImageContext();
|
||||
return base64;
|
||||
UIGraphicsBeginImageContextWithOptions(_boundingBox.size, NO, 0);
|
||||
[self clearChildCache];
|
||||
[self drawRect:_boundingBox];
|
||||
[self clearChildCache];
|
||||
[self invalidate];
|
||||
NSData *imageData = UIImagePNGRepresentation(UIGraphicsGetImageFromCurrentImageContext());
|
||||
NSString *base64 = [imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
|
||||
UIGraphicsEndImageContext();
|
||||
return base64;
|
||||
}
|
||||
|
||||
- (NSString *)getDataURLwithBounds:(CGRect)bounds
|
||||
{
|
||||
UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 1);
|
||||
[self clearChildCache];
|
||||
[self drawRect:bounds];
|
||||
[self clearChildCache];
|
||||
[self invalidate];
|
||||
NSData *imageData = UIImagePNGRepresentation(UIGraphicsGetImageFromCurrentImageContext());
|
||||
NSString *base64 = [imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
|
||||
UIGraphicsEndImageContext();
|
||||
return base64;
|
||||
UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 1);
|
||||
[self clearChildCache];
|
||||
[self drawRect:bounds];
|
||||
[self clearChildCache];
|
||||
[self invalidate];
|
||||
NSData *imageData = UIImagePNGRepresentation(UIGraphicsGetImageFromCurrentImageContext());
|
||||
NSString *base64 = [imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
|
||||
UIGraphicsEndImageContext();
|
||||
return base64;
|
||||
}
|
||||
|
||||
- (void)reactSetInheritedBackgroundColor:(RNSVGColor *)inheritedBackgroundColor
|
||||
{
|
||||
self.backgroundColor = inheritedBackgroundColor;
|
||||
self.backgroundColor = inheritedBackgroundColor;
|
||||
}
|
||||
|
||||
- (void)defineClipPath:(__kindof RNSVGNode *)clipPath clipPathName:(NSString *)clipPathName
|
||||
{
|
||||
if (!_clipPaths) {
|
||||
_clipPaths = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
[_clipPaths setObject:clipPath forKey:clipPathName];
|
||||
if (!_clipPaths) {
|
||||
_clipPaths = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
[_clipPaths setObject:clipPath forKey:clipPathName];
|
||||
}
|
||||
|
||||
- (RNSVGNode *)getDefinedClipPath:(NSString *)clipPathName
|
||||
{
|
||||
return _clipPaths ? [_clipPaths objectForKey:clipPathName] : nil;
|
||||
return _clipPaths ? [_clipPaths objectForKey:clipPathName] : nil;
|
||||
}
|
||||
|
||||
- (void)defineTemplate:(RNSVGNode *)definedTemplate templateName:(NSString *)templateName
|
||||
{
|
||||
if (!_templates) {
|
||||
_templates = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
[_templates setObject:definedTemplate forKey:templateName];
|
||||
if (!_templates) {
|
||||
_templates = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
[_templates setObject:definedTemplate forKey:templateName];
|
||||
}
|
||||
|
||||
- (RNSVGNode *)getDefinedTemplate:(NSString *)templateName
|
||||
{
|
||||
return _templates ? [_templates objectForKey:templateName] : nil;
|
||||
return _templates ? [_templates objectForKey:templateName] : nil;
|
||||
}
|
||||
|
||||
|
||||
- (void)definePainter:(RNSVGPainter *)painter painterName:(NSString *)painterName
|
||||
{
|
||||
if (!_painters) {
|
||||
_painters = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
[_painters setObject:painter forKey:painterName];
|
||||
if (!_painters) {
|
||||
_painters = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
[_painters setObject:painter forKey:painterName];
|
||||
}
|
||||
|
||||
- (RNSVGPainter *)getDefinedPainter:(NSString *)painterName;
|
||||
{
|
||||
return _painters ? [_painters objectForKey:painterName] : nil;
|
||||
return _painters ? [_painters objectForKey:painterName] : nil;
|
||||
}
|
||||
|
||||
- (void)defineMarker:(RNSVGNode *)marker markerName:(NSString *)markerName
|
||||
{
|
||||
if (!_markers) {
|
||||
_markers = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
[_markers setObject:marker forKey:markerName];
|
||||
if (!_markers) {
|
||||
_markers = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
[_markers setObject:marker forKey:markerName];
|
||||
}
|
||||
|
||||
- (RNSVGNode *)getDefinedMarker:(NSString *)markerName;
|
||||
{
|
||||
return _markers ? [_markers objectForKey:markerName] : nil;
|
||||
return _markers ? [_markers objectForKey:markerName] : nil;
|
||||
}
|
||||
|
||||
- (void)defineMask:(RNSVGNode *)mask maskName:(NSString *)maskName
|
||||
{
|
||||
if (!_masks) {
|
||||
_masks = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
[_masks setObject:mask forKey:maskName];
|
||||
if (!_masks) {
|
||||
_masks = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
[_masks setObject:mask forKey:maskName];
|
||||
}
|
||||
|
||||
- (RNSVGNode *)getDefinedMask:(NSString *)maskName;
|
||||
{
|
||||
return _masks ? [_masks objectForKey:maskName] : nil;
|
||||
return _masks ? [_masks objectForKey:maskName] : nil;
|
||||
}
|
||||
|
||||
- (CGRect)getContextBounds
|
||||
{
|
||||
return CGContextGetClipBoundingBox(UIGraphicsGetCurrentContext());
|
||||
return CGContextGetClipBoundingBox(UIGraphicsGetCurrentContext());
|
||||
}
|
||||
|
||||
- (CGAffineTransform)getViewBoxTransform
|
||||
{
|
||||
return _viewBoxTransform;
|
||||
return _viewBoxTransform;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RCTConversions.h"
|
||||
#import <react/renderer/components/view/conversions.h>
|
||||
#import "RCTConversions.h"
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RNSVGFabricConversions.h"
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
@@ -39,110 +39,110 @@ using namespace facebook::react;
|
||||
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGSymbolProps>(props);
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGSymbolProps>(props);
|
||||
|
||||
self.minX = newProps.minX;
|
||||
self.minY = newProps.minY;
|
||||
self.vbWidth = newProps.vbWidth;
|
||||
self.vbHeight = newProps.vbHeight;
|
||||
self.align = RCTNSStringFromStringNilIfEmpty(newProps.align);
|
||||
self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice);
|
||||
|
||||
setCommonGroupProps(newProps, self);
|
||||
self.minX = newProps.minX;
|
||||
self.minY = newProps.minY;
|
||||
self.vbWidth = newProps.vbWidth;
|
||||
self.vbHeight = newProps.vbHeight;
|
||||
self.align = RCTNSStringFromStringNilIfEmpty(newProps.align);
|
||||
self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice);
|
||||
|
||||
setCommonGroupProps(newProps, self);
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
|
||||
_minX = 0;
|
||||
_minY = 0;
|
||||
_vbWidth = 0;
|
||||
_vbHeight = 0;
|
||||
_align = nil;
|
||||
_meetOrSlice = kRNSVGVBMOSMeet;
|
||||
[super prepareForRecycle];
|
||||
|
||||
_minX = 0;
|
||||
_minY = 0;
|
||||
_vbWidth = 0;
|
||||
_vbHeight = 0;
|
||||
_align = nil;
|
||||
_meetOrSlice = kRNSVGVBMOSMeet;
|
||||
}
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
- (void)setMinX:(CGFloat)minX
|
||||
{
|
||||
if (minX == _minX) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_minX = minX;
|
||||
if (minX == _minX) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_minX = minX;
|
||||
}
|
||||
|
||||
- (void)setMinY:(CGFloat)minY
|
||||
{
|
||||
if (minY == _minY) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_minY = minY;
|
||||
if (minY == _minY) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_minY = minY;
|
||||
}
|
||||
|
||||
- (void)setVbWidth:(CGFloat)vbWidth
|
||||
{
|
||||
if (vbWidth == _vbWidth) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_vbWidth = vbWidth;
|
||||
if (vbWidth == _vbWidth) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_vbWidth = vbWidth;
|
||||
}
|
||||
|
||||
- (void)setVbHeight:(CGFloat)vbHeight
|
||||
{
|
||||
if (_vbHeight == vbHeight) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_vbHeight = vbHeight;
|
||||
if (_vbHeight == vbHeight) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_vbHeight = vbHeight;
|
||||
}
|
||||
|
||||
- (void)setAlign:(NSString *)align
|
||||
{
|
||||
if ([align isEqualToString:_align]) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_align = align;
|
||||
if ([align isEqualToString:_align]) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_align = align;
|
||||
}
|
||||
|
||||
- (void)setMeetOrSlice:(RNSVGVBMOS)meetOrSlice
|
||||
{
|
||||
if (meetOrSlice == _meetOrSlice) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_meetOrSlice = meetOrSlice;
|
||||
if (meetOrSlice == _meetOrSlice) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_meetOrSlice = meetOrSlice;
|
||||
}
|
||||
|
||||
- (void)renderTo:(CGContextRef)context rect:(CGRect)rect
|
||||
{
|
||||
self.dirty = false;
|
||||
// Do not render Symbol
|
||||
self.dirty = false;
|
||||
// Do not render Symbol
|
||||
}
|
||||
|
||||
- (void)renderSymbolTo:(CGContextRef)context width:(CGFloat)width height:(CGFloat)height
|
||||
{
|
||||
CGRect eRect = CGRectMake(0, 0, width, height);
|
||||
if (self.align) {
|
||||
|
||||
CGAffineTransform viewBoxTransform = [RNSVGViewBox getTransform:CGRectMake(self.minX, self.minY, self.vbWidth, self.vbHeight)
|
||||
eRect:eRect
|
||||
align:self.align
|
||||
meetOrSlice:self.meetOrSlice];
|
||||
|
||||
CGContextConcatCTM(context, viewBoxTransform);
|
||||
}
|
||||
[self renderGroupTo:context rect:eRect];
|
||||
CGRect eRect = CGRectMake(0, 0, width, height);
|
||||
if (self.align) {
|
||||
CGAffineTransform viewBoxTransform =
|
||||
[RNSVGViewBox getTransform:CGRectMake(self.minX, self.minY, self.vbWidth, self.vbHeight)
|
||||
eRect:eRect
|
||||
align:self.align
|
||||
meetOrSlice:self.meetOrSlice];
|
||||
|
||||
CGContextConcatCTM(context, viewBoxTransform);
|
||||
}
|
||||
[self renderGroupTo:context rect:eRect];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "RNSVGRenderable.h"
|
||||
#import "RNSVGLength.h"
|
||||
#import "RNSVGRenderable.h"
|
||||
|
||||
/**
|
||||
* RNSVG defination are implemented as abstract UIViews for all elements inside Defs.
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
#import "RNSVGUse.h"
|
||||
#import "RNSVGSymbol.h"
|
||||
#import <React/RCTLog.h>
|
||||
#import "RNSVGSymbol.h"
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RCTConversions.h"
|
||||
#import <react/renderer/components/view/conversions.h>
|
||||
#import "RCTConversions.h"
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RNSVGFabricConversions.h"
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
@@ -40,166 +40,171 @@ using namespace facebook::react;
|
||||
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGUseProps>(props);
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGUseProps>(props);
|
||||
|
||||
self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)];
|
||||
self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)];
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.useheight)) {
|
||||
self.useheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.useheight)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.usewidth)) {
|
||||
self.usewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.usewidth)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
|
||||
self.useheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
|
||||
self.usewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
|
||||
}
|
||||
self.href = RCTNSStringFromStringNilIfEmpty(newProps.href);
|
||||
self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)];
|
||||
self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)];
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.useheight)) {
|
||||
self.useheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.useheight)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.usewidth)) {
|
||||
self.usewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.usewidth)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
|
||||
self.useheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
|
||||
self.usewidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
|
||||
}
|
||||
self.href = RCTNSStringFromStringNilIfEmpty(newProps.href);
|
||||
|
||||
setCommonRenderableProps(newProps, self);
|
||||
setCommonRenderableProps(newProps, self);
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
_x = nil;
|
||||
_y = nil;
|
||||
_useheight = nil;
|
||||
_usewidth = nil;
|
||||
_href = nil;
|
||||
[super prepareForRecycle];
|
||||
_x = nil;
|
||||
_y = nil;
|
||||
_useheight = nil;
|
||||
_usewidth = nil;
|
||||
_href = nil;
|
||||
}
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
- (void)setHref:(NSString *)href
|
||||
{
|
||||
if ([href isEqualToString:_href]) {
|
||||
return;
|
||||
}
|
||||
if ([href isEqualToString:_href]) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_href = href;
|
||||
[self invalidate];
|
||||
_href = href;
|
||||
}
|
||||
|
||||
- (void)setX:(RNSVGLength *)x
|
||||
{
|
||||
if ([x isEqualTo:_x]) {
|
||||
return;
|
||||
}
|
||||
if ([x isEqualTo:_x]) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_x = x;
|
||||
[self invalidate];
|
||||
_x = x;
|
||||
}
|
||||
|
||||
- (void)setY:(RNSVGLength *)y
|
||||
{
|
||||
if ([y isEqualTo:_y]) {
|
||||
return;
|
||||
}
|
||||
if ([y isEqualTo:_y]) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_y = y;
|
||||
[self invalidate];
|
||||
_y = y;
|
||||
}
|
||||
|
||||
|
||||
- (void)setUsewidth:(RNSVGLength *)usewidth
|
||||
{
|
||||
if ([usewidth isEqualTo:_usewidth]) {
|
||||
return;
|
||||
}
|
||||
if ([usewidth isEqualTo:_usewidth]) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_usewidth = usewidth;
|
||||
[self invalidate];
|
||||
_usewidth = usewidth;
|
||||
}
|
||||
|
||||
- (void)setUseheight:(RNSVGLength *)useheight
|
||||
{
|
||||
if ([useheight isEqualTo:_useheight]) {
|
||||
return;
|
||||
}
|
||||
if ([useheight isEqualTo:_useheight]) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_useheight = useheight;
|
||||
[self invalidate];
|
||||
_useheight = useheight;
|
||||
}
|
||||
|
||||
- (void)renderLayerTo:(CGContextRef)context rect:(CGRect)rect
|
||||
{
|
||||
CGContextTranslateCTM(context, [self relativeOnWidth:self.x], [self relativeOnHeight:self.y]);
|
||||
RNSVGNode* definedTemplate = [self.svgView getDefinedTemplate:self.href];
|
||||
if (definedTemplate) {
|
||||
[self beginTransparencyLayer:context];
|
||||
[self clip:context];
|
||||
CGContextTranslateCTM(context, [self relativeOnWidth:self.x], [self relativeOnHeight:self.y]);
|
||||
RNSVGNode *definedTemplate = [self.svgView getDefinedTemplate:self.href];
|
||||
if (definedTemplate) {
|
||||
[self beginTransparencyLayer:context];
|
||||
[self clip:context];
|
||||
|
||||
if ([definedTemplate isKindOfClass:[RNSVGRenderable class]]) {
|
||||
[(RNSVGRenderable*)definedTemplate mergeProperties:self];
|
||||
}
|
||||
if ([definedTemplate isKindOfClass:[RNSVGRenderable class]]) {
|
||||
[(RNSVGRenderable *)definedTemplate mergeProperties:self];
|
||||
}
|
||||
|
||||
if ([definedTemplate class] == [RNSVGSymbol class]) {
|
||||
RNSVGSymbol *symbol = (RNSVGSymbol*)definedTemplate;
|
||||
[symbol renderSymbolTo:context width:[self relativeOnWidth:self.usewidth] height:[self relativeOnHeight:self.useheight]];
|
||||
} else {
|
||||
[definedTemplate renderTo:context rect:rect];
|
||||
}
|
||||
|
||||
if ([definedTemplate isKindOfClass:[RNSVGRenderable class]]) {
|
||||
[(RNSVGRenderable*)definedTemplate resetProperties];
|
||||
}
|
||||
|
||||
[self endTransparencyLayer:context];
|
||||
} else if (self.href) {
|
||||
// TODO: calling yellow box here
|
||||
RCTLogWarn(@"`Use` element expected a pre-defined svg template as `href` prop, template named: %@ is not defined.", self.href);
|
||||
return;
|
||||
if ([definedTemplate class] == [RNSVGSymbol class]) {
|
||||
RNSVGSymbol *symbol = (RNSVGSymbol *)definedTemplate;
|
||||
[symbol renderSymbolTo:context
|
||||
width:[self relativeOnWidth:self.usewidth]
|
||||
height:[self relativeOnHeight:self.useheight]];
|
||||
} else {
|
||||
return;
|
||||
[definedTemplate renderTo:context rect:rect];
|
||||
}
|
||||
CGRect bounds = definedTemplate.clientRect;
|
||||
self.clientRect = bounds;
|
||||
|
||||
CGAffineTransform current = CGContextGetCTM(context);
|
||||
CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM);
|
||||
|
||||
self.ctm = svgToClientTransform;
|
||||
self.screenCTM = current;
|
||||
|
||||
CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms);
|
||||
CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
|
||||
CGPoint center = CGPointApplyAffineTransform(mid, transform);
|
||||
|
||||
self.bounds = bounds;
|
||||
if (!isnan(center.x) && !isnan(center.y)) {
|
||||
self.center = center;
|
||||
if ([definedTemplate isKindOfClass:[RNSVGRenderable class]]) {
|
||||
[(RNSVGRenderable *)definedTemplate resetProperties];
|
||||
}
|
||||
self.frame = bounds;
|
||||
|
||||
[self endTransparencyLayer:context];
|
||||
} else if (self.href) {
|
||||
// TODO: calling yellow box here
|
||||
RCTLogWarn(
|
||||
@"`Use` element expected a pre-defined svg template as `href` prop, template named: %@ is not defined.",
|
||||
self.href);
|
||||
return;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
CGRect bounds = definedTemplate.clientRect;
|
||||
self.clientRect = bounds;
|
||||
|
||||
CGAffineTransform current = CGContextGetCTM(context);
|
||||
CGAffineTransform svgToClientTransform = CGAffineTransformConcat(current, self.svgView.invInitialCTM);
|
||||
|
||||
self.ctm = svgToClientTransform;
|
||||
self.screenCTM = current;
|
||||
|
||||
CGAffineTransform transform = CGAffineTransformConcat(self.matrix, self.transforms);
|
||||
CGPoint mid = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
|
||||
CGPoint center = CGPointApplyAffineTransform(mid, transform);
|
||||
|
||||
self.bounds = bounds;
|
||||
if (!isnan(center.x) && !isnan(center.y)) {
|
||||
self.center = center;
|
||||
}
|
||||
self.frame = bounds;
|
||||
}
|
||||
|
||||
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
|
||||
CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix);
|
||||
transformed = CGPointApplyAffineTransform(transformed, self.invTransform);
|
||||
RNSVGNode const* definedTemplate = [self.svgView getDefinedTemplate:self.href];
|
||||
if (event) {
|
||||
self.active = NO;
|
||||
} else if (self.active) {
|
||||
return self;
|
||||
}
|
||||
RNSVGPlatformView const* hitChild = [definedTemplate hitTest:transformed withEvent:event];
|
||||
if (hitChild) {
|
||||
self.active = YES;
|
||||
return self;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CGPathRef)getPath: (CGContextRef)context
|
||||
- (RNSVGPlatformView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
CGAffineTransform transform = CGAffineTransformMakeTranslation([self relativeOnWidth:self.x], [self relativeOnHeight:self.y]);
|
||||
RNSVGNode const* definedTemplate = [self.svgView getDefinedTemplate:self.href];
|
||||
if (!definedTemplate) {
|
||||
return nil;
|
||||
}
|
||||
CGPathRef path = [definedTemplate getPath:context];
|
||||
return CGPathCreateCopyByTransformingPath(path, &transform);
|
||||
CGPoint transformed = CGPointApplyAffineTransform(point, self.invmatrix);
|
||||
transformed = CGPointApplyAffineTransform(transformed, self.invTransform);
|
||||
RNSVGNode const *definedTemplate = [self.svgView getDefinedTemplate:self.href];
|
||||
if (event) {
|
||||
self.active = NO;
|
||||
} else if (self.active) {
|
||||
return self;
|
||||
}
|
||||
RNSVGPlatformView const *hitChild = [definedTemplate hitTest:transformed withEvent:event];
|
||||
if (hitChild) {
|
||||
self.active = YES;
|
||||
return self;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CGPathRef)getPath:(CGContextRef)context
|
||||
{
|
||||
CGAffineTransform transform =
|
||||
CGAffineTransformMakeTranslation([self relativeOnWidth:self.x], [self relativeOnHeight:self.y]);
|
||||
RNSVGNode const *definedTemplate = [self.svgView getDefinedTemplate:self.href];
|
||||
if (!definedTemplate) {
|
||||
return nil;
|
||||
}
|
||||
CGPathRef path = [definedTemplate getPath:context];
|
||||
return CGPathCreateCopyByTransformingPath(path, &transform);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "RNSVGSvgView.h"
|
||||
#import <React/UIView+React.h>
|
||||
#import <React/RCTPointerEvents.h>
|
||||
#import "RNSVGCGFCRule.h"
|
||||
#import "RNSVGSvgView.h"
|
||||
|
||||
#import <React/RCTPointerEvents.h>
|
||||
#import <React/UIView+React.h>
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
#import <React/RCTViewComponentView.h>
|
||||
@@ -24,9 +25,9 @@
|
||||
|
||||
@interface RNSVGNode :
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
RCTViewComponentView
|
||||
RCTViewComponentView
|
||||
#else
|
||||
RNSVGView
|
||||
RNSVGView
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
/*
|
||||
N[1/Sqrt[2], 36]
|
||||
@@ -73,7 +74,6 @@ extern CGFloat const RNSVG_DEFAULT_FONT_SIZE;
|
||||
@property (nonatomic, assign) CGRect markerBounds;
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onLayout;
|
||||
|
||||
|
||||
/**
|
||||
* RNSVGSvgView which ownes current RNSVGNode
|
||||
*/
|
||||
@@ -111,7 +111,7 @@ extern CGFloat const RNSVG_DEFAULT_FONT_SIZE;
|
||||
/**
|
||||
* getPath will return the path inside node as a ClipPath.
|
||||
*/
|
||||
- (CGPathRef)getPath:(CGContextRef) context;
|
||||
- (CGPathRef)getPath:(CGContextRef)context;
|
||||
|
||||
- (CGFloat)relativeOnWidthString:(NSString *)length;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,8 +12,8 @@
|
||||
|
||||
#import "RNSVGBrush.h"
|
||||
#import "RNSVGCGFCRule.h"
|
||||
#import "RNSVGNode.h"
|
||||
#import "RNSVGLength.h"
|
||||
#import "RNSVGNode.h"
|
||||
#import "RNSVGVectorEffect.h"
|
||||
|
||||
@interface RNSVGRenderable : RNSVGNode
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
// Most (if not all) of this file could probably go away once react-native-macos's version of RCTUIKit.h makes its way upstream.
|
||||
// https://github.com/microsoft/react-native-macos/issues/242
|
||||
// Most (if not all) of this file could probably go away once react-native-macos's version of RCTUIKit.h makes its way
|
||||
// upstream. https://github.com/microsoft/react-native-macos/issues/242
|
||||
|
||||
#if !TARGET_OS_OSX
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
|
||||
#else // TARGET_OS_OSX [
|
||||
|
||||
// Due to name mangling, calling c-style functions from .mm files will fail, therefore we need to wrap them with extern "C"
|
||||
// so they are handled correctly. We also need to have imports positioned in a correct way,
|
||||
// so that this extern "C" wrapper is used before the functions from RCTUIKit are used.
|
||||
// Due to name mangling, calling c-style functions from .mm files will fail, therefore we need to wrap them with extern
|
||||
// "C" so they are handled correctly. We also need to have imports positioned in a correct way, so that this extern "C"
|
||||
// wrapper is used before the functions from RCTUIKit are used.
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -37,7 +37,8 @@ extern "C" {
|
||||
@end
|
||||
|
||||
// TODO: These could probably be a part of react-native-macos
|
||||
// See https://github.com/microsoft/react-native-macos/issues/658 and https://github.com/microsoft/react-native-macos/issues/659
|
||||
// See https://github.com/microsoft/react-native-macos/issues/658 and
|
||||
// https://github.com/microsoft/react-native-macos/issues/659
|
||||
@interface NSImage (RNSVGMacOSExtensions)
|
||||
@property (readonly) CGImageRef CGImage;
|
||||
@end
|
||||
|
||||
@@ -1,86 +1,83 @@
|
||||
#import "RNSVGUIKit.h"
|
||||
|
||||
@implementation RNSVGView
|
||||
{
|
||||
NSColor *_tintColor;
|
||||
@implementation RNSVGView {
|
||||
NSColor *_tintColor;
|
||||
}
|
||||
|
||||
- (CGPoint)center
|
||||
{
|
||||
NSRect frameRect = self.frame;
|
||||
CGFloat xCenter = frameRect.origin.x + frameRect.size.width / 2;
|
||||
CGFloat yCenter = frameRect.origin.y + frameRect.size.height / 2;
|
||||
return CGPointMake(xCenter, yCenter);
|
||||
NSRect frameRect = self.frame;
|
||||
CGFloat xCenter = frameRect.origin.x + frameRect.size.width / 2;
|
||||
CGFloat yCenter = frameRect.origin.y + frameRect.size.height / 2;
|
||||
return CGPointMake(xCenter, yCenter);
|
||||
}
|
||||
|
||||
- (void)setCenter:(CGPoint)point
|
||||
{
|
||||
NSRect frameRect = self.frame;
|
||||
CGFloat xOrigin = frameRect.origin.x - frameRect.size.width / 2;
|
||||
CGFloat yOrigin = frameRect.origin.y - frameRect.size.height / 2;
|
||||
self.frame = CGRectMake(xOrigin, yOrigin, frameRect.size.width, frameRect.size.height);
|
||||
NSRect frameRect = self.frame;
|
||||
CGFloat xOrigin = frameRect.origin.x - frameRect.size.width / 2;
|
||||
CGFloat yOrigin = frameRect.origin.y - frameRect.size.height / 2;
|
||||
self.frame = CGRectMake(xOrigin, yOrigin, frameRect.size.width, frameRect.size.height);
|
||||
}
|
||||
|
||||
- (NSColor *)tintColor
|
||||
{
|
||||
if (_tintColor != nil) {
|
||||
return _tintColor;
|
||||
}
|
||||
if (_tintColor != nil) {
|
||||
return _tintColor;
|
||||
}
|
||||
|
||||
// To mimic iOS's tintColor, we crawl up the view hierarchy until either:
|
||||
// (a) we find a valid color
|
||||
// (b) we reach a view that isn't an RNSVGView
|
||||
NSView *parentView = [self superview];
|
||||
if ([parentView isKindOfClass:[RNSVGView class]]) {
|
||||
return [(RNSVGView *)parentView tintColor];
|
||||
} else {
|
||||
return [NSColor controlAccentColor];
|
||||
}
|
||||
// To mimic iOS's tintColor, we crawl up the view hierarchy until either:
|
||||
// (a) we find a valid color
|
||||
// (b) we reach a view that isn't an RNSVGView
|
||||
NSView *parentView = [self superview];
|
||||
if ([parentView isKindOfClass:[RNSVGView class]]) {
|
||||
return [(RNSVGView *)parentView tintColor];
|
||||
} else {
|
||||
return [NSColor controlAccentColor];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setTintColor:(NSColor *)tintColor
|
||||
{
|
||||
_tintColor = tintColor;
|
||||
[self setNeedsDisplay:YES];
|
||||
_tintColor = tintColor;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation NSImage (RNSVGMacOSExtensions)
|
||||
|
||||
- (CGImageRef) CGImage
|
||||
- (CGImageRef)CGImage
|
||||
{
|
||||
return [self CGImageForProposedRect:NULL context:NULL hints:NULL];
|
||||
return [self CGImageForProposedRect:NULL context:NULL hints:NULL];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation NSValue (RNSVGMacOSExtensions)
|
||||
|
||||
+ (NSValue *)valueWithCGAffineTransform:(CGAffineTransform)transform
|
||||
{
|
||||
return [NSValue valueWithBytes:&transform objCType:@encode(CGAffineTransform)];
|
||||
return [NSValue valueWithBytes:&transform objCType:@encode(CGAffineTransform)];
|
||||
}
|
||||
|
||||
+ (NSValue *)valueWithCGPoint:(CGPoint)point
|
||||
{
|
||||
return [NSValue valueWithBytes:&point objCType:@encode(CGPoint)];
|
||||
return [NSValue valueWithBytes:&point objCType:@encode(CGPoint)];
|
||||
}
|
||||
|
||||
- (CGAffineTransform)CGAffineTransformValue
|
||||
{
|
||||
CGAffineTransform value;
|
||||
[self getValue:&value];
|
||||
return value;
|
||||
CGAffineTransform value;
|
||||
[self getValue:&value];
|
||||
return value;
|
||||
}
|
||||
|
||||
- (CGPoint)CGPointValue
|
||||
{
|
||||
CGPoint value;
|
||||
[self getValue:&value];
|
||||
return value;
|
||||
CGPoint value;
|
||||
[self getValue:&value];
|
||||
return value;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -12,9 +12,8 @@
|
||||
|
||||
@interface RNSVGCircle : RNSVGRenderable
|
||||
|
||||
@property (nonatomic, strong) RNSVGLength* cx;
|
||||
@property (nonatomic, strong) RNSVGLength* cy;
|
||||
@property (nonatomic, strong) RNSVGLength* r;
|
||||
@property (nonatomic, strong) RNSVGLength *cx;
|
||||
@property (nonatomic, strong) RNSVGLength *cy;
|
||||
@property (nonatomic, strong) RNSVGLength *r;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RCTConversions.h"
|
||||
#import <react/renderer/components/view/conversions.h>
|
||||
#import "RCTConversions.h"
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RNSVGFabricConversions.h"
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
@@ -40,59 +40,59 @@ using namespace facebook::react;
|
||||
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGCircleProps>(props);
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGCircleProps>(props);
|
||||
|
||||
self.cx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cx)];
|
||||
self.cy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cy)];
|
||||
self.r = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.r)];
|
||||
|
||||
setCommonRenderableProps(newProps, self);
|
||||
self.cx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cx)];
|
||||
self.cy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cy)];
|
||||
self.r = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.r)];
|
||||
|
||||
setCommonRenderableProps(newProps, self);
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
_cx = nil;
|
||||
_cy = nil;
|
||||
_r = nil;
|
||||
[super prepareForRecycle];
|
||||
_cx = nil;
|
||||
_cy = nil;
|
||||
_r = nil;
|
||||
}
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
- (void)setCx:(RNSVGLength *)cx
|
||||
{
|
||||
if ([cx isEqualTo:_cx]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_cx = cx;
|
||||
if ([cx isEqualTo:_cx]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_cx = cx;
|
||||
}
|
||||
|
||||
- (void)setCy:(RNSVGLength *)cy
|
||||
{
|
||||
if ([cy isEqualTo:_cy]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_cy = cy;
|
||||
if ([cy isEqualTo:_cy]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_cy = cy;
|
||||
}
|
||||
|
||||
- (void)setR:(RNSVGLength *)r
|
||||
{
|
||||
if ([r isEqualTo:_r]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_r = r;
|
||||
if ([r isEqualTo:_r]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_r = r;
|
||||
}
|
||||
|
||||
- (CGPathRef)getPath:(CGContextRef)context
|
||||
{
|
||||
CGMutablePathRef path = CGPathCreateMutable();
|
||||
CGFloat cx = [self relativeOnWidth:self.cx];
|
||||
CGFloat cy = [self relativeOnHeight:self.cy];
|
||||
CGFloat r = [self relativeOnOther:self.r];
|
||||
CGPathAddArc(path, nil, cx, cy, r, 0, 2 * (CGFloat)M_PI, NO);
|
||||
return (CGPathRef)CFAutorelease(path);
|
||||
CGMutablePathRef path = CGPathCreateMutable();
|
||||
CGFloat cx = [self relativeOnWidth:self.cx];
|
||||
CGFloat cy = [self relativeOnHeight:self.cy];
|
||||
CGFloat r = [self relativeOnOther:self.r];
|
||||
CGPathAddArc(path, nil, cx, cy, r, 0, 2 * (CGFloat)M_PI, NO);
|
||||
return (CGPathRef)CFAutorelease(path);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
#import "RNSVGPath.h"
|
||||
|
||||
@interface RNSVGEllipse : RNSVGRenderable
|
||||
@property (nonatomic, strong) RNSVGLength* cx;
|
||||
@property (nonatomic, strong) RNSVGLength* cy;
|
||||
@property (nonatomic, strong) RNSVGLength* rx;
|
||||
@property (nonatomic, strong) RNSVGLength* ry;
|
||||
@property (nonatomic, strong) RNSVGLength *cx;
|
||||
@property (nonatomic, strong) RNSVGLength *cy;
|
||||
@property (nonatomic, strong) RNSVGLength *rx;
|
||||
@property (nonatomic, strong) RNSVGLength *ry;
|
||||
@end
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RCTConversions.h"
|
||||
#import <react/renderer/components/view/conversions.h>
|
||||
#import "RCTConversions.h"
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RNSVGFabricConversions.h"
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
@@ -40,71 +40,71 @@ using namespace facebook::react;
|
||||
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGEllipseProps>(props);
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGEllipseProps>(props);
|
||||
|
||||
self.cx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cx)];
|
||||
self.cy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cy)];
|
||||
self.rx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rx)];
|
||||
self.ry = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.ry)];
|
||||
self.cx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cx)];
|
||||
self.cy = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.cy)];
|
||||
self.rx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rx)];
|
||||
self.ry = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.ry)];
|
||||
|
||||
setCommonRenderableProps(newProps, self);
|
||||
setCommonRenderableProps(newProps, self);
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
_cx = nil;
|
||||
_cy = nil;
|
||||
_rx = nil;
|
||||
_ry = nil;
|
||||
[super prepareForRecycle];
|
||||
_cx = nil;
|
||||
_cy = nil;
|
||||
_rx = nil;
|
||||
_ry = nil;
|
||||
}
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
- (void)setCx:(RNSVGLength *)cx
|
||||
{
|
||||
if ([cx isEqualTo:_cx]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_cx = cx;
|
||||
if ([cx isEqualTo:_cx]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_cx = cx;
|
||||
}
|
||||
|
||||
- (void)setCy:(RNSVGLength *)cy
|
||||
{
|
||||
if ([cy isEqualTo:_cy]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_cy = cy;
|
||||
if ([cy isEqualTo:_cy]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_cy = cy;
|
||||
}
|
||||
|
||||
- (void)setRx:(RNSVGLength *)rx
|
||||
{
|
||||
if ([rx isEqualTo:_rx]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_rx = rx;
|
||||
if ([rx isEqualTo:_rx]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_rx = rx;
|
||||
}
|
||||
|
||||
- (void)setRy:(RNSVGLength *)ry
|
||||
{
|
||||
if ([ry isEqualTo:_ry]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_ry = ry;
|
||||
if ([ry isEqualTo:_ry]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_ry = ry;
|
||||
}
|
||||
|
||||
- (CGPathRef)getPath:(CGContextRef)context
|
||||
{
|
||||
CGMutablePathRef path = CGPathCreateMutable();
|
||||
CGFloat cx = [self relativeOnWidth:self.cx];
|
||||
CGFloat cy = [self relativeOnHeight:self.cy];
|
||||
CGFloat rx = [self relativeOnWidth:self.rx];
|
||||
CGFloat ry = [self relativeOnHeight:self.ry];
|
||||
CGPathAddEllipseInRect(path, nil, CGRectMake(cx - rx, cy - ry, rx * 2, ry * 2));
|
||||
return (CGPathRef)CFAutorelease(path);
|
||||
CGMutablePathRef path = CGPathCreateMutable();
|
||||
CGFloat cx = [self relativeOnWidth:self.cx];
|
||||
CGFloat cy = [self relativeOnHeight:self.cy];
|
||||
CGFloat rx = [self relativeOnWidth:self.rx];
|
||||
CGFloat ry = [self relativeOnHeight:self.ry];
|
||||
CGPathAddEllipseInRect(path, nil, CGRectMake(cx - rx, cy - ry, rx * 2, ry * 2));
|
||||
return (CGPathRef)CFAutorelease(path);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
#import "RNSVGPath.h"
|
||||
|
||||
@interface RNSVGLine : RNSVGRenderable
|
||||
@property (nonatomic, strong) RNSVGLength* x1;
|
||||
@property (nonatomic, strong) RNSVGLength* y1;
|
||||
@property (nonatomic, strong) RNSVGLength* x2;
|
||||
@property (nonatomic, strong) RNSVGLength* y2;
|
||||
@property (nonatomic, strong) RNSVGLength *x1;
|
||||
@property (nonatomic, strong) RNSVGLength *y1;
|
||||
@property (nonatomic, strong) RNSVGLength *x2;
|
||||
@property (nonatomic, strong) RNSVGLength *y2;
|
||||
@end
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RCTConversions.h"
|
||||
#import <react/renderer/components/view/conversions.h>
|
||||
#import "RCTConversions.h"
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RNSVGFabricConversions.h"
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
@@ -40,73 +40,73 @@ using namespace facebook::react;
|
||||
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGLineProps>(props);
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGLineProps>(props);
|
||||
|
||||
self.x1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x1)];
|
||||
self.y1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y1)];
|
||||
self.x2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x2)];
|
||||
self.y2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y2)];
|
||||
self.x1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x1)];
|
||||
self.y1 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y1)];
|
||||
self.x2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x2)];
|
||||
self.y2 = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y2)];
|
||||
|
||||
setCommonRenderableProps(newProps, self);
|
||||
setCommonRenderableProps(newProps, self);
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
_x1 = nil;
|
||||
_y1 = nil;
|
||||
_x2 = nil;
|
||||
_y2 = nil;
|
||||
[super prepareForRecycle];
|
||||
_x1 = nil;
|
||||
_y1 = nil;
|
||||
_x2 = nil;
|
||||
_y2 = nil;
|
||||
}
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
- (void)setX1:(RNSVGLength *)x1
|
||||
{
|
||||
if ([x1 isEqualTo:_x1]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_x1 = x1;
|
||||
if ([x1 isEqualTo:_x1]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_x1 = x1;
|
||||
}
|
||||
|
||||
- (void)setY1:(RNSVGLength *)y1
|
||||
{
|
||||
if ([y1 isEqualTo:_y1]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_y1 = y1;
|
||||
if ([y1 isEqualTo:_y1]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_y1 = y1;
|
||||
}
|
||||
|
||||
- (void)setX2:(RNSVGLength *)x2
|
||||
{
|
||||
if ([x2 isEqualTo:_x2]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_x2 = x2;
|
||||
if ([x2 isEqualTo:_x2]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_x2 = x2;
|
||||
}
|
||||
|
||||
- (void)setY2:(RNSVGLength *)y2
|
||||
{
|
||||
if ([y2 isEqualTo:_y2]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_y2 = y2;
|
||||
if ([y2 isEqualTo:_y2]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_y2 = y2;
|
||||
}
|
||||
|
||||
- (CGPathRef)getPath:(CGContextRef)context
|
||||
{
|
||||
CGMutablePathRef path = CGPathCreateMutable();
|
||||
CGFloat x1 = [self relativeOnWidth:self.x1];
|
||||
CGFloat y1 = [self relativeOnHeight:self.y1];
|
||||
CGFloat x2 = [self relativeOnWidth:self.x2];
|
||||
CGFloat y2 = [self relativeOnHeight:self.y2];
|
||||
CGPathMoveToPoint(path, nil, x1, y1);
|
||||
CGPathAddLineToPoint(path, nil, x2, y2);
|
||||
|
||||
return (CGPathRef)CFAutorelease(path);
|
||||
CGMutablePathRef path = CGPathCreateMutable();
|
||||
CGFloat x1 = [self relativeOnWidth:self.x1];
|
||||
CGFloat y1 = [self relativeOnHeight:self.y1];
|
||||
CGFloat x2 = [self relativeOnWidth:self.x2];
|
||||
CGFloat y2 = [self relativeOnHeight:self.y2];
|
||||
CGPathMoveToPoint(path, nil, x1, y1);
|
||||
CGPathAddLineToPoint(path, nil, x2, y2);
|
||||
|
||||
return (CGPathRef)CFAutorelease(path);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -12,11 +12,11 @@
|
||||
|
||||
@interface RNSVGRect : RNSVGRenderable
|
||||
|
||||
@property (nonatomic, strong) RNSVGLength* x;
|
||||
@property (nonatomic, strong) RNSVGLength* y;
|
||||
@property (nonatomic, strong) RNSVGLength* rectwidth;
|
||||
@property (nonatomic, strong) RNSVGLength* rectheight;
|
||||
@property (nonatomic, strong) RNSVGLength* rx;
|
||||
@property (nonatomic, strong) RNSVGLength* ry;
|
||||
@property (nonatomic, strong) RNSVGLength *x;
|
||||
@property (nonatomic, strong) RNSVGLength *y;
|
||||
@property (nonatomic, strong) RNSVGLength *rectwidth;
|
||||
@property (nonatomic, strong) RNSVGLength *rectheight;
|
||||
@property (nonatomic, strong) RNSVGLength *rx;
|
||||
@property (nonatomic, strong) RNSVGLength *ry;
|
||||
|
||||
@end
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
|
||||
#ifdef RN_FABRIC_ENABLED
|
||||
#import <react/renderer/components/rnsvg/ComponentDescriptors.h>
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RCTConversions.h"
|
||||
#import <react/renderer/components/view/conversions.h>
|
||||
#import "RCTConversions.h"
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
#import "RNSVGFabricConversions.h"
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
@@ -40,132 +40,132 @@ using namespace facebook::react;
|
||||
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGRectProps>(props);
|
||||
const auto &newProps = *std::static_pointer_cast<const RNSVGRectProps>(props);
|
||||
|
||||
self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)];
|
||||
self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)];
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.rectheight)) {
|
||||
self.rectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rectheight)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.rectwidth)) {
|
||||
self.rectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rectwidth)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
|
||||
self.rectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
|
||||
self.rectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
|
||||
}
|
||||
self.rx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rx)];
|
||||
self.ry = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.ry)];
|
||||
self.x = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.x)];
|
||||
self.y = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.y)];
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.rectheight)) {
|
||||
self.rectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rectheight)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.rectwidth)) {
|
||||
self.rectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rectwidth)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.height)) {
|
||||
self.rectheight = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.height)];
|
||||
}
|
||||
if (RCTNSStringFromStringNilIfEmpty(newProps.width)) {
|
||||
self.rectwidth = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.width)];
|
||||
}
|
||||
self.rx = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.rx)];
|
||||
self.ry = [RNSVGLength lengthWithString:RCTNSStringFromString(newProps.ry)];
|
||||
|
||||
setCommonRenderableProps(newProps, self);
|
||||
setCommonRenderableProps(newProps, self);
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
|
||||
_x = nil;
|
||||
_y = nil;
|
||||
_rectwidth = nil;
|
||||
_rectheight = nil;
|
||||
_rx = nil;
|
||||
_ry = nil;
|
||||
[super prepareForRecycle];
|
||||
|
||||
_x = nil;
|
||||
_y = nil;
|
||||
_rectwidth = nil;
|
||||
_rectheight = nil;
|
||||
_rx = nil;
|
||||
_ry = nil;
|
||||
}
|
||||
|
||||
#endif // RN_FABRIC_ENABLED
|
||||
|
||||
- (void)setX:(RNSVGLength *)x
|
||||
{
|
||||
if ([x isEqualTo:_x]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_x = x;
|
||||
if ([x isEqualTo:_x]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_x = x;
|
||||
}
|
||||
|
||||
- (void)setY:(RNSVGLength *)y
|
||||
{
|
||||
if ([y isEqualTo:_y]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_y = y;
|
||||
if ([y isEqualTo:_y]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_y = y;
|
||||
}
|
||||
|
||||
- (void)setRectwidth:(RNSVGLength *)rectwidth
|
||||
{
|
||||
if ([rectwidth isEqualTo:_rectwidth]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_rectwidth = rectwidth;
|
||||
if ([rectwidth isEqualTo:_rectwidth]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_rectwidth = rectwidth;
|
||||
}
|
||||
|
||||
- (void)setRectheight:(RNSVGLength *)rectheight
|
||||
{
|
||||
if ([rectheight isEqualTo:_rectheight]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_rectheight = rectheight;
|
||||
if ([rectheight isEqualTo:_rectheight]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_rectheight = rectheight;
|
||||
}
|
||||
|
||||
- (void)setRx:(RNSVGLength *)rx
|
||||
{
|
||||
if ([rx isEqualTo:_rx]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_rx = rx;
|
||||
if ([rx isEqualTo:_rx]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_rx = rx;
|
||||
}
|
||||
|
||||
- (void)setRy:(RNSVGLength *)ry
|
||||
{
|
||||
if ([ry isEqualTo:_ry]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_ry = ry;
|
||||
if ([ry isEqualTo:_ry]) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
_ry = ry;
|
||||
}
|
||||
|
||||
- (CGPathRef)getPath:(CGContextRef)context
|
||||
{
|
||||
CGMutablePathRef path = CGPathCreateMutable();
|
||||
CGFloat x = [self relativeOnWidth:self.x];
|
||||
CGFloat y = [self relativeOnHeight:self.y];
|
||||
CGFloat width = [self relativeOnWidth:self.rectwidth];
|
||||
CGFloat height = [self relativeOnHeight:self.rectheight];
|
||||
CGMutablePathRef path = CGPathCreateMutable();
|
||||
CGFloat x = [self relativeOnWidth:self.x];
|
||||
CGFloat y = [self relativeOnHeight:self.y];
|
||||
CGFloat width = [self relativeOnWidth:self.rectwidth];
|
||||
CGFloat height = [self relativeOnHeight:self.rectheight];
|
||||
|
||||
if (self.rx != nil || self.ry != nil) {
|
||||
CGFloat rx = 0;
|
||||
CGFloat ry = 0;
|
||||
if (self.rx == nil) {
|
||||
ry = [self relativeOnHeight:self.ry];
|
||||
rx = ry;
|
||||
} else if (self.ry == nil) {
|
||||
rx = [self relativeOnWidth:self.rx];
|
||||
ry = rx;
|
||||
} else {
|
||||
rx = [self relativeOnWidth:self.rx];
|
||||
ry = [self relativeOnHeight:self.ry];
|
||||
}
|
||||
|
||||
if (rx > width / 2) {
|
||||
rx = width / 2;
|
||||
}
|
||||
|
||||
if (ry > height / 2) {
|
||||
ry = height / 2;
|
||||
}
|
||||
|
||||
CGPathAddRoundedRect(path, nil, CGRectMake(x, y, width, height), rx, ry);
|
||||
if (self.rx != nil || self.ry != nil) {
|
||||
CGFloat rx = 0;
|
||||
CGFloat ry = 0;
|
||||
if (self.rx == nil) {
|
||||
ry = [self relativeOnHeight:self.ry];
|
||||
rx = ry;
|
||||
} else if (self.ry == nil) {
|
||||
rx = [self relativeOnWidth:self.rx];
|
||||
ry = rx;
|
||||
} else {
|
||||
CGPathAddRect(path, nil, CGRectMake(x, y, width, height));
|
||||
rx = [self relativeOnWidth:self.rx];
|
||||
ry = [self relativeOnHeight:self.ry];
|
||||
}
|
||||
|
||||
return (CGPathRef)CFAutorelease(path);
|
||||
if (rx > width / 2) {
|
||||
rx = width / 2;
|
||||
}
|
||||
|
||||
if (ry > height / 2) {
|
||||
ry = height / 2;
|
||||
}
|
||||
|
||||
CGPathAddRoundedRect(path, nil, CGRectMake(x, y, width, height), rx, ry);
|
||||
} else {
|
||||
CGPathAddRect(path, nil, CGRectMake(x, y, width, height));
|
||||
}
|
||||
|
||||
return (CGPathRef)CFAutorelease(path);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -2,35 +2,33 @@
|
||||
|
||||
#import "RNSVGUIKit.h"
|
||||
|
||||
#import "RNSVGTextProperties.h"
|
||||
#import "RNSVGPropHelper.h"
|
||||
#import "RNSVGTextProperties.h"
|
||||
|
||||
@interface RNSVGFontData : NSObject {
|
||||
@public
|
||||
CGFloat fontSize;
|
||||
NSString * fontSize_;
|
||||
NSString *fontFamily;
|
||||
enum RNSVGFontStyle fontStyle;
|
||||
NSDictionary * fontData;
|
||||
enum RNSVGFontWeight fontWeight;
|
||||
int absoluteFontWeight;
|
||||
NSString *fontFeatureSettings;
|
||||
enum RNSVGFontVariantLigatures fontVariantLigatures;
|
||||
enum RNSVGTextAnchor textAnchor;
|
||||
enum RNSVGTextDecoration textDecoration;
|
||||
CGFloat kerning;
|
||||
CGFloat wordSpacing;
|
||||
CGFloat letterSpacing;
|
||||
bool manualKerning;
|
||||
@public
|
||||
CGFloat fontSize;
|
||||
NSString *fontSize_;
|
||||
NSString *fontFamily;
|
||||
enum RNSVGFontStyle fontStyle;
|
||||
NSDictionary *fontData;
|
||||
enum RNSVGFontWeight fontWeight;
|
||||
int absoluteFontWeight;
|
||||
NSString *fontFeatureSettings;
|
||||
enum RNSVGFontVariantLigatures fontVariantLigatures;
|
||||
enum RNSVGTextAnchor textAnchor;
|
||||
enum RNSVGTextDecoration textDecoration;
|
||||
CGFloat kerning;
|
||||
CGFloat wordSpacing;
|
||||
CGFloat letterSpacing;
|
||||
bool manualKerning;
|
||||
}
|
||||
|
||||
+ (instancetype)Defaults;
|
||||
|
||||
+ (CGFloat)toAbsoluteWithNSString:(NSString *)string
|
||||
fontSize:(CGFloat)fontSize;
|
||||
+ (CGFloat)toAbsoluteWithNSString:(NSString *)string fontSize:(CGFloat)fontSize;
|
||||
|
||||
+ (instancetype)initWithNSDictionary:(NSDictionary *)font
|
||||
parent:(RNSVGFontData *)parent;
|
||||
+ (instancetype)initWithNSDictionary:(NSDictionary *)font parent:(RNSVGFontData *)parent;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#import "RNSVGFontData.h"
|
||||
#import "RNSVGNode.h"
|
||||
#import "RNSVGPropHelper.h"
|
||||
#import "RNSVGTextProperties.h"
|
||||
#import "RNSVGNode.h"
|
||||
|
||||
#define RNSVG_DEFAULT_KERNING 0.0
|
||||
#define RNSVG_DEFAULT_WORD_SPACING 0.0
|
||||
@@ -23,178 +23,174 @@ RNSVGFontData *RNSVGFontData_Defaults;
|
||||
|
||||
@implementation RNSVGFontData
|
||||
|
||||
+ (instancetype)Defaults {
|
||||
if (!RNSVGFontData_Defaults) {
|
||||
RNSVGFontData *self = [RNSVGFontData alloc];
|
||||
self->fontData = nil;
|
||||
self->fontFamily = @"";
|
||||
self->fontStyle = RNSVGFontStyleNormal;
|
||||
self->fontWeight = RNSVGFontWeightNormal;
|
||||
self->absoluteFontWeight = 400;
|
||||
self->fontFeatureSettings = @"";
|
||||
self->fontVariantLigatures = RNSVGFontVariantLigaturesNormal;
|
||||
self->textAnchor = RNSVGTextAnchorStart;
|
||||
self->textDecoration = RNSVGTextDecorationNone;
|
||||
self->manualKerning = false;
|
||||
self->kerning = RNSVG_DEFAULT_KERNING;
|
||||
self->fontSize = RNSVG_DEFAULT_FONT_SIZE;
|
||||
self->wordSpacing = RNSVG_DEFAULT_WORD_SPACING;
|
||||
self->letterSpacing = RNSVG_DEFAULT_LETTER_SPACING;
|
||||
RNSVGFontData_Defaults = self;
|
||||
}
|
||||
return RNSVGFontData_Defaults;
|
||||
+ (instancetype)Defaults
|
||||
{
|
||||
if (!RNSVGFontData_Defaults) {
|
||||
RNSVGFontData *self = [RNSVGFontData alloc];
|
||||
self->fontData = nil;
|
||||
self->fontFamily = @"";
|
||||
self->fontStyle = RNSVGFontStyleNormal;
|
||||
self->fontWeight = RNSVGFontWeightNormal;
|
||||
self->absoluteFontWeight = 400;
|
||||
self->fontFeatureSettings = @"";
|
||||
self->fontVariantLigatures = RNSVGFontVariantLigaturesNormal;
|
||||
self->textAnchor = RNSVGTextAnchorStart;
|
||||
self->textDecoration = RNSVGTextDecorationNone;
|
||||
self->manualKerning = false;
|
||||
self->kerning = RNSVG_DEFAULT_KERNING;
|
||||
self->fontSize = RNSVG_DEFAULT_FONT_SIZE;
|
||||
self->wordSpacing = RNSVG_DEFAULT_WORD_SPACING;
|
||||
self->letterSpacing = RNSVG_DEFAULT_LETTER_SPACING;
|
||||
RNSVGFontData_Defaults = self;
|
||||
}
|
||||
return RNSVGFontData_Defaults;
|
||||
}
|
||||
|
||||
+ (CGFloat)toAbsoluteWithNSString:(NSString *)string
|
||||
fontSize:(CGFloat)fontSize {
|
||||
return [RNSVGPropHelper fromRelativeWithNSString:string
|
||||
relative:0
|
||||
fontSize:fontSize];
|
||||
+ (CGFloat)toAbsoluteWithNSString:(NSString *)string fontSize:(CGFloat)fontSize
|
||||
{
|
||||
return [RNSVGPropHelper fromRelativeWithNSString:string relative:0 fontSize:fontSize];
|
||||
}
|
||||
|
||||
- (void)setInheritedWeight:(RNSVGFontData*) parent {
|
||||
absoluteFontWeight = parent->absoluteFontWeight;
|
||||
fontWeight = parent->fontWeight;
|
||||
- (void)setInheritedWeight:(RNSVGFontData *)parent
|
||||
{
|
||||
absoluteFontWeight = parent->absoluteFontWeight;
|
||||
fontWeight = parent->fontWeight;
|
||||
}
|
||||
|
||||
RNSVGFontWeight nearestFontWeight(int absoluteFontWeight) {
|
||||
return RNSVGFontWeights[(int)round(absoluteFontWeight / 100.0)];
|
||||
RNSVGFontWeight nearestFontWeight(int absoluteFontWeight)
|
||||
{
|
||||
return RNSVGFontWeights[(int)round(absoluteFontWeight / 100.0)];
|
||||
}
|
||||
|
||||
- (void)handleNumericWeight:(RNSVGFontData*)parent weight:(double)weight {
|
||||
long roundWeight = round(weight);
|
||||
if (roundWeight >= 1 && roundWeight <= 1000) {
|
||||
absoluteFontWeight = (int)roundWeight;
|
||||
fontWeight = nearestFontWeight(absoluteFontWeight);
|
||||
} else {
|
||||
[self setInheritedWeight:parent];
|
||||
}
|
||||
- (void)handleNumericWeight:(RNSVGFontData *)parent weight:(double)weight
|
||||
{
|
||||
long roundWeight = round(weight);
|
||||
if (roundWeight >= 1 && roundWeight <= 1000) {
|
||||
absoluteFontWeight = (int)roundWeight;
|
||||
fontWeight = nearestFontWeight(absoluteFontWeight);
|
||||
} else {
|
||||
[self setInheritedWeight:parent];
|
||||
}
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-fonts-4/#relative-weights
|
||||
int AbsoluteFontWeight(RNSVGFontWeight fontWeight, RNSVGFontData* parent) {
|
||||
if (fontWeight == RNSVGFontWeightBolder) {
|
||||
return bolder(parent->absoluteFontWeight);
|
||||
} else if (fontWeight == RNSVGFontWeightLighter) {
|
||||
return lighter(parent->absoluteFontWeight);
|
||||
} else {
|
||||
return RNSVGAbsoluteFontWeights[fontWeight];
|
||||
}
|
||||
int AbsoluteFontWeight(RNSVGFontWeight fontWeight, RNSVGFontData *parent)
|
||||
{
|
||||
if (fontWeight == RNSVGFontWeightBolder) {
|
||||
return bolder(parent->absoluteFontWeight);
|
||||
} else if (fontWeight == RNSVGFontWeightLighter) {
|
||||
return lighter(parent->absoluteFontWeight);
|
||||
} else {
|
||||
return RNSVGAbsoluteFontWeights[fontWeight];
|
||||
}
|
||||
}
|
||||
|
||||
int bolder(int inherited) {
|
||||
if (inherited < 350) {
|
||||
return 400;
|
||||
} else if (inherited < 550) {
|
||||
return 700;
|
||||
} else if (inherited < 900) {
|
||||
return 900;
|
||||
} else {
|
||||
return inherited;
|
||||
}
|
||||
int bolder(int inherited)
|
||||
{
|
||||
if (inherited < 350) {
|
||||
return 400;
|
||||
} else if (inherited < 550) {
|
||||
return 700;
|
||||
} else if (inherited < 900) {
|
||||
return 900;
|
||||
} else {
|
||||
return inherited;
|
||||
}
|
||||
}
|
||||
|
||||
int lighter(int inherited) {
|
||||
if (inherited < 100) {
|
||||
return inherited;
|
||||
} else if (inherited < 550) {
|
||||
return 100;
|
||||
} else if (inherited < 750) {
|
||||
return 400;
|
||||
} else {
|
||||
return 700;
|
||||
}
|
||||
int lighter(int inherited)
|
||||
{
|
||||
if (inherited < 100) {
|
||||
return inherited;
|
||||
} else if (inherited < 550) {
|
||||
return 100;
|
||||
} else if (inherited < 750) {
|
||||
return 400;
|
||||
} else {
|
||||
return 700;
|
||||
}
|
||||
}
|
||||
|
||||
+ (instancetype)initWithNSDictionary:(NSDictionary *)font
|
||||
parent:(RNSVGFontData *)parent {
|
||||
RNSVGFontData *data = [RNSVGFontData alloc];
|
||||
CGFloat parentFontSize = parent->fontSize;
|
||||
if ([font objectForKey:FONT_SIZE]) {
|
||||
id fontSize = [font objectForKey:FONT_SIZE];
|
||||
if ([fontSize isKindOfClass:NSNumber.class]) {
|
||||
NSNumber* fs = fontSize;
|
||||
data->fontSize = (CGFloat)[fs doubleValue];
|
||||
} else {
|
||||
data->fontSize = [RNSVGPropHelper fromRelativeWithNSString:fontSize
|
||||
relative:parentFontSize
|
||||
fontSize:parentFontSize];
|
||||
}
|
||||
}
|
||||
else {
|
||||
data->fontSize = parentFontSize;
|
||||
}
|
||||
|
||||
if ([font objectForKey:FONT_WEIGHT]) {
|
||||
id fontWeight = [font objectForKey:FONT_WEIGHT];
|
||||
if ([fontWeight isKindOfClass:NSNumber.class]) {
|
||||
[data handleNumericWeight:parent weight:[fontWeight doubleValue]];
|
||||
} else {
|
||||
NSString* weight = fontWeight;
|
||||
NSInteger fw = RNSVGFontWeightFromString(weight);
|
||||
if (fw != -1) {
|
||||
data->absoluteFontWeight = AbsoluteFontWeight((RNSVGFontWeight)fw, parent);
|
||||
data->fontWeight = nearestFontWeight(data->absoluteFontWeight);
|
||||
} else if ([weight length] != 0) {
|
||||
[data handleNumericWeight:parent weight:[weight doubleValue]];
|
||||
} else {
|
||||
[data setInheritedWeight:parent];
|
||||
}
|
||||
}
|
||||
+ (instancetype)initWithNSDictionary:(NSDictionary *)font parent:(RNSVGFontData *)parent
|
||||
{
|
||||
RNSVGFontData *data = [RNSVGFontData alloc];
|
||||
CGFloat parentFontSize = parent->fontSize;
|
||||
if ([font objectForKey:FONT_SIZE]) {
|
||||
id fontSize = [font objectForKey:FONT_SIZE];
|
||||
if ([fontSize isKindOfClass:NSNumber.class]) {
|
||||
NSNumber *fs = fontSize;
|
||||
data->fontSize = (CGFloat)[fs doubleValue];
|
||||
} else {
|
||||
data->fontSize = [RNSVGPropHelper fromRelativeWithNSString:fontSize
|
||||
relative:parentFontSize
|
||||
fontSize:parentFontSize];
|
||||
}
|
||||
} else {
|
||||
data->fontSize = parentFontSize;
|
||||
}
|
||||
|
||||
if ([font objectForKey:FONT_WEIGHT]) {
|
||||
id fontWeight = [font objectForKey:FONT_WEIGHT];
|
||||
if ([fontWeight isKindOfClass:NSNumber.class]) {
|
||||
[data handleNumericWeight:parent weight:[fontWeight doubleValue]];
|
||||
} else {
|
||||
NSString *weight = fontWeight;
|
||||
NSInteger fw = RNSVGFontWeightFromString(weight);
|
||||
if (fw != -1) {
|
||||
data->absoluteFontWeight = AbsoluteFontWeight((RNSVGFontWeight)fw, parent);
|
||||
data->fontWeight = nearestFontWeight(data->absoluteFontWeight);
|
||||
} else if ([weight length] != 0) {
|
||||
[data handleNumericWeight:parent weight:[weight doubleValue]];
|
||||
} else {
|
||||
[data setInheritedWeight:parent];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
[data setInheritedWeight:parent];
|
||||
}
|
||||
|
||||
data->fontData = [font objectForKey:FONT_DATA] ? [font objectForKey:FONT_DATA] : parent->fontData;
|
||||
data->fontFamily = [font objectForKey:FONT_FAMILY] ? [font objectForKey:FONT_FAMILY] : parent->fontFamily;
|
||||
NSString* style = [font objectForKey:FONT_STYLE];
|
||||
data->fontStyle = style ? RNSVGFontStyleFromString(style) : parent->fontStyle;
|
||||
NSString* feature = [font objectForKey:FONT_FEATURE_SETTINGS];
|
||||
data->fontFeatureSettings = feature ? [font objectForKey:FONT_FEATURE_SETTINGS] : parent->fontFeatureSettings;
|
||||
NSString* variant = [font objectForKey:FONT_VARIANT_LIGATURES];
|
||||
data->fontVariantLigatures = variant ? RNSVGFontVariantLigaturesFromString(variant) : parent->fontVariantLigatures;
|
||||
NSString* anchor = [font objectForKey:TEXT_ANCHOR];
|
||||
data->textAnchor = anchor ? RNSVGTextAnchorFromString(anchor) : parent->textAnchor;
|
||||
NSString* decoration = [font objectForKey:TEXT_DECORATION];
|
||||
data->textDecoration = decoration ? RNSVGTextDecorationFromString(decoration) : parent->textDecoration;
|
||||
data->fontData = [font objectForKey:FONT_DATA] ? [font objectForKey:FONT_DATA] : parent->fontData;
|
||||
data->fontFamily = [font objectForKey:FONT_FAMILY] ? [font objectForKey:FONT_FAMILY] : parent->fontFamily;
|
||||
NSString *style = [font objectForKey:FONT_STYLE];
|
||||
data->fontStyle = style ? RNSVGFontStyleFromString(style) : parent->fontStyle;
|
||||
NSString *feature = [font objectForKey:FONT_FEATURE_SETTINGS];
|
||||
data->fontFeatureSettings = feature ? [font objectForKey:FONT_FEATURE_SETTINGS] : parent->fontFeatureSettings;
|
||||
NSString *variant = [font objectForKey:FONT_VARIANT_LIGATURES];
|
||||
data->fontVariantLigatures = variant ? RNSVGFontVariantLigaturesFromString(variant) : parent->fontVariantLigatures;
|
||||
NSString *anchor = [font objectForKey:TEXT_ANCHOR];
|
||||
data->textAnchor = anchor ? RNSVGTextAnchorFromString(anchor) : parent->textAnchor;
|
||||
NSString *decoration = [font objectForKey:TEXT_DECORATION];
|
||||
data->textDecoration = decoration ? RNSVGTextDecorationFromString(decoration) : parent->textDecoration;
|
||||
|
||||
CGFloat fontSize = data->fontSize;
|
||||
id kerning = [font objectForKey:KERNING];
|
||||
data->manualKerning = (kerning || parent->manualKerning );
|
||||
if ([kerning isKindOfClass:NSNumber.class]) {
|
||||
NSNumber* kern = kerning;
|
||||
data->kerning = (CGFloat)[kern doubleValue];
|
||||
} else {
|
||||
data->kerning = kerning ?
|
||||
[RNSVGFontData toAbsoluteWithNSString:kerning
|
||||
fontSize:fontSize]
|
||||
: parent->kerning;
|
||||
}
|
||||
CGFloat fontSize = data->fontSize;
|
||||
id kerning = [font objectForKey:KERNING];
|
||||
data->manualKerning = (kerning || parent->manualKerning);
|
||||
if ([kerning isKindOfClass:NSNumber.class]) {
|
||||
NSNumber *kern = kerning;
|
||||
data->kerning = (CGFloat)[kern doubleValue];
|
||||
} else {
|
||||
data->kerning = kerning ? [RNSVGFontData toAbsoluteWithNSString:kerning fontSize:fontSize] : parent->kerning;
|
||||
}
|
||||
|
||||
id wordSpacing = [font objectForKey:WORD_SPACING];
|
||||
if ([wordSpacing isKindOfClass:NSNumber.class]) {
|
||||
NSNumber* ws = wordSpacing;
|
||||
data->wordSpacing = (CGFloat)[ws doubleValue];
|
||||
} else {
|
||||
data->wordSpacing = wordSpacing ?
|
||||
[RNSVGFontData toAbsoluteWithNSString:wordSpacing
|
||||
fontSize:fontSize]
|
||||
: parent->wordSpacing;
|
||||
}
|
||||
id wordSpacing = [font objectForKey:WORD_SPACING];
|
||||
if ([wordSpacing isKindOfClass:NSNumber.class]) {
|
||||
NSNumber *ws = wordSpacing;
|
||||
data->wordSpacing = (CGFloat)[ws doubleValue];
|
||||
} else {
|
||||
data->wordSpacing =
|
||||
wordSpacing ? [RNSVGFontData toAbsoluteWithNSString:wordSpacing fontSize:fontSize] : parent->wordSpacing;
|
||||
}
|
||||
|
||||
id letterSpacing = [font objectForKey:LETTER_SPACING];
|
||||
if ([letterSpacing isKindOfClass:NSNumber.class]) {
|
||||
NSNumber* ls = letterSpacing;
|
||||
data->wordSpacing = (CGFloat)[ls doubleValue];
|
||||
} else {
|
||||
data->letterSpacing = letterSpacing ?
|
||||
[RNSVGFontData toAbsoluteWithNSString:letterSpacing
|
||||
fontSize:fontSize]
|
||||
: parent->letterSpacing;
|
||||
}
|
||||
id letterSpacing = [font objectForKey:LETTER_SPACING];
|
||||
if ([letterSpacing isKindOfClass:NSNumber.class]) {
|
||||
NSNumber *ls = letterSpacing;
|
||||
data->wordSpacing = (CGFloat)[ls doubleValue];
|
||||
} else {
|
||||
data->letterSpacing =
|
||||
letterSpacing ? [RNSVGFontData toAbsoluteWithNSString:letterSpacing fontSize:fontSize] : parent->letterSpacing;
|
||||
}
|
||||
|
||||
return data;
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#import <React/UIView+React.h>
|
||||
#import <CoreText/CoreText.h>
|
||||
#import <React/UIView+React.h>
|
||||
#import "RNSVGFontData.h"
|
||||
|
||||
@class RNSVGText;
|
||||
@@ -10,8 +10,7 @@
|
||||
|
||||
- (CTFontRef)getGlyphFont;
|
||||
|
||||
- (instancetype)initWithWidth:(CGFloat)width
|
||||
height:(CGFloat)height;
|
||||
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;
|
||||
|
||||
- (RNSVGFontData *)getFont;
|
||||
|
||||
@@ -33,17 +32,16 @@
|
||||
|
||||
- (void)popContext;
|
||||
|
||||
- (void)pushContext:(RNSVGText*)node
|
||||
font:(NSDictionary*)font
|
||||
x:(NSArray<RNSVGLength*>*)x
|
||||
y:(NSArray<RNSVGLength*>*)y
|
||||
deltaX:(NSArray<RNSVGLength*>*)deltaX
|
||||
deltaY:(NSArray<RNSVGLength*>*)deltaY
|
||||
rotate:(NSArray<RNSVGLength*>*)rotate;
|
||||
- (void)pushContext:(RNSVGText *)node
|
||||
font:(NSDictionary *)font
|
||||
x:(NSArray<RNSVGLength *> *)x
|
||||
y:(NSArray<RNSVGLength *> *)y
|
||||
deltaX:(NSArray<RNSVGLength *> *)deltaX
|
||||
deltaY:(NSArray<RNSVGLength *> *)deltaY
|
||||
rotate:(NSArray<RNSVGLength *> *)rotate;
|
||||
|
||||
- (void)pushContext:(RNSVGGroup*)node
|
||||
font:(NSDictionary *)font;
|
||||
- (void)pushContext:(RNSVGGroup *)node font:(NSDictionary *)font;
|
||||
|
||||
- (NSArray*)getFontContext;
|
||||
- (NSArray *)getFontContext;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,414 +1,414 @@
|
||||
#import "RNSVGGlyphContext.h"
|
||||
#import <React/RCTFont.h>
|
||||
#import "RNSVGFontData.h"
|
||||
#import "RNSVGNode.h"
|
||||
#import "RNSVGPropHelper.h"
|
||||
#import "RNSVGFontData.h"
|
||||
#import "RNSVGText.h"
|
||||
|
||||
// https://www.w3.org/TR/SVG/text.html#TSpanElement
|
||||
@interface RNSVGGlyphContext () {
|
||||
@public
|
||||
// Current stack (one per node push/pop)
|
||||
NSMutableArray *mFontContext_;
|
||||
@public
|
||||
// Current stack (one per node push/pop)
|
||||
NSMutableArray *mFontContext_;
|
||||
|
||||
// Unique input attribute lists (only added if node sets a value)
|
||||
NSMutableArray<NSArray<RNSVGLength*>*> *mXsContext_;
|
||||
NSMutableArray<NSArray<RNSVGLength*>*> *mYsContext_;
|
||||
NSMutableArray<NSArray<RNSVGLength*>*> *mDXsContext_;
|
||||
NSMutableArray<NSArray<RNSVGLength*>*> *mDYsContext_;
|
||||
NSMutableArray<NSArray<RNSVGLength*>*> *mRsContext_;
|
||||
// Unique input attribute lists (only added if node sets a value)
|
||||
NSMutableArray<NSArray<RNSVGLength *> *> *mXsContext_;
|
||||
NSMutableArray<NSArray<RNSVGLength *> *> *mYsContext_;
|
||||
NSMutableArray<NSArray<RNSVGLength *> *> *mDXsContext_;
|
||||
NSMutableArray<NSArray<RNSVGLength *> *> *mDYsContext_;
|
||||
NSMutableArray<NSArray<RNSVGLength *> *> *mRsContext_;
|
||||
|
||||
// Unique index into attribute list (one per unique list)
|
||||
NSMutableArray<NSNumber*> *mXIndices_;
|
||||
NSMutableArray<NSNumber*> *mYIndices_;
|
||||
NSMutableArray<NSNumber*> *mDXIndices_;
|
||||
NSMutableArray<NSNumber*> *mDYIndices_;
|
||||
NSMutableArray<NSNumber*> *mRIndices_;
|
||||
// Unique index into attribute list (one per unique list)
|
||||
NSMutableArray<NSNumber *> *mXIndices_;
|
||||
NSMutableArray<NSNumber *> *mYIndices_;
|
||||
NSMutableArray<NSNumber *> *mDXIndices_;
|
||||
NSMutableArray<NSNumber *> *mDYIndices_;
|
||||
NSMutableArray<NSNumber *> *mRIndices_;
|
||||
|
||||
// Index of unique context used (one per node push/pop)
|
||||
NSMutableArray<NSNumber*> *mXsIndices_;
|
||||
NSMutableArray<NSNumber*> *mYsIndices_;
|
||||
NSMutableArray<NSNumber*> *mDXsIndices_;
|
||||
NSMutableArray<NSNumber*> *mDYsIndices_;
|
||||
NSMutableArray<NSNumber*> *mRsIndices_;
|
||||
// Index of unique context used (one per node push/pop)
|
||||
NSMutableArray<NSNumber *> *mXsIndices_;
|
||||
NSMutableArray<NSNumber *> *mYsIndices_;
|
||||
NSMutableArray<NSNumber *> *mDXsIndices_;
|
||||
NSMutableArray<NSNumber *> *mDYsIndices_;
|
||||
NSMutableArray<NSNumber *> *mRsIndices_;
|
||||
|
||||
// Calculated on push context, percentage and em length depends on parent font size
|
||||
CGFloat mFontSize_;
|
||||
RNSVGFontData *topFont_;
|
||||
// Calculated on push context, percentage and em length depends on parent font size
|
||||
CGFloat mFontSize_;
|
||||
RNSVGFontData *topFont_;
|
||||
|
||||
// Current accumulated values
|
||||
// https://www.w3.org/TR/SVG/types.html#DataTypeCoordinate
|
||||
// <coordinate> syntax is the same as that for <length>
|
||||
CGFloat mX_;
|
||||
CGFloat mY_;
|
||||
// Current accumulated values
|
||||
// https://www.w3.org/TR/SVG/types.html#DataTypeCoordinate
|
||||
// <coordinate> syntax is the same as that for <length>
|
||||
CGFloat mX_;
|
||||
CGFloat mY_;
|
||||
|
||||
// https://www.w3.org/TR/SVG/types.html#Length
|
||||
CGFloat mDX_;
|
||||
CGFloat mDY_;
|
||||
// https://www.w3.org/TR/SVG/types.html#Length
|
||||
CGFloat mDX_;
|
||||
CGFloat mDY_;
|
||||
|
||||
// Current <list-of-coordinates> SVGLengthList
|
||||
// https://www.w3.org/TR/SVG/types.html#InterfaceSVGLengthList
|
||||
// https://www.w3.org/TR/SVG/types.html#DataTypeCoordinates
|
||||
// Current <list-of-coordinates> SVGLengthList
|
||||
// https://www.w3.org/TR/SVG/types.html#InterfaceSVGLengthList
|
||||
// https://www.w3.org/TR/SVG/types.html#DataTypeCoordinates
|
||||
|
||||
// https://www.w3.org/TR/SVG/text.html#TSpanElementXAttribute
|
||||
NSArray<RNSVGLength*> *mXs_;
|
||||
// https://www.w3.org/TR/SVG/text.html#TSpanElementXAttribute
|
||||
NSArray<RNSVGLength *> *mXs_;
|
||||
|
||||
// https://www.w3.org/TR/SVG/text.html#TSpanElementYAttribute
|
||||
NSArray<RNSVGLength*> *mYs_;
|
||||
// https://www.w3.org/TR/SVG/text.html#TSpanElementYAttribute
|
||||
NSArray<RNSVGLength *> *mYs_;
|
||||
|
||||
// Current <list-of-lengths> SVGLengthList
|
||||
// https://www.w3.org/TR/SVG/types.html#DataTypeLengths
|
||||
// Current <list-of-lengths> SVGLengthList
|
||||
// https://www.w3.org/TR/SVG/types.html#DataTypeLengths
|
||||
|
||||
// https://www.w3.org/TR/SVG/text.html#TSpanElementDXAttribute
|
||||
NSArray<RNSVGLength*> *mDXs_;
|
||||
// https://www.w3.org/TR/SVG/text.html#TSpanElementDXAttribute
|
||||
NSArray<RNSVGLength *> *mDXs_;
|
||||
|
||||
// https://www.w3.org/TR/SVG/text.html#TSpanElementDYAttribute
|
||||
NSArray<RNSVGLength*> *mDYs_;
|
||||
// https://www.w3.org/TR/SVG/text.html#TSpanElementDYAttribute
|
||||
NSArray<RNSVGLength *> *mDYs_;
|
||||
|
||||
// Current <list-of-numbers> SVGLengthList
|
||||
// https://www.w3.org/TR/SVG/types.html#DataTypeNumbers
|
||||
// Current <list-of-numbers> SVGLengthList
|
||||
// https://www.w3.org/TR/SVG/types.html#DataTypeNumbers
|
||||
|
||||
// https://www.w3.org/TR/SVG/text.html#TSpanElementRotateAttribute
|
||||
NSArray<RNSVGLength*> *mRs_;
|
||||
// https://www.w3.org/TR/SVG/text.html#TSpanElementRotateAttribute
|
||||
NSArray<RNSVGLength *> *mRs_;
|
||||
|
||||
// Current attribute list index
|
||||
long mXsIndex_;
|
||||
long mYsIndex_;
|
||||
long mDXsIndex_;
|
||||
long mDYsIndex_;
|
||||
long mRsIndex_;
|
||||
// Current attribute list index
|
||||
long mXsIndex_;
|
||||
long mYsIndex_;
|
||||
long mDXsIndex_;
|
||||
long mDYsIndex_;
|
||||
long mRsIndex_;
|
||||
|
||||
// Current value index in current attribute list
|
||||
long mXIndex_;
|
||||
long mYIndex_;
|
||||
long mDXIndex_;
|
||||
long mDYIndex_;
|
||||
long mRIndex_;
|
||||
// Current value index in current attribute list
|
||||
long mXIndex_;
|
||||
long mYIndex_;
|
||||
long mDXIndex_;
|
||||
long mDYIndex_;
|
||||
long mRIndex_;
|
||||
|
||||
// Top index of stack
|
||||
long mTop_;
|
||||
// Top index of stack
|
||||
long mTop_;
|
||||
|
||||
// Constructor parameters
|
||||
CGFloat mWidth_;
|
||||
CGFloat mHeight_;
|
||||
// Constructor parameters
|
||||
CGFloat mWidth_;
|
||||
CGFloat mHeight_;
|
||||
}
|
||||
|
||||
- (void)pushContext:(RNSVGText*)node
|
||||
font:(NSDictionary*)font
|
||||
x:(NSArray<RNSVGLength*>*)x
|
||||
y:(NSArray<RNSVGLength*>*)y
|
||||
deltaX:(NSArray<RNSVGLength*>*)deltaX
|
||||
deltaY:(NSArray<RNSVGLength*>*)deltaY
|
||||
rotate:(NSArray<RNSVGLength*>*)rotate;
|
||||
- (void)pushContext:(RNSVGText *)node
|
||||
font:(NSDictionary *)font
|
||||
x:(NSArray<RNSVGLength *> *)x
|
||||
y:(NSArray<RNSVGLength *> *)y
|
||||
deltaX:(NSArray<RNSVGLength *> *)deltaX
|
||||
deltaY:(NSArray<RNSVGLength *> *)deltaY
|
||||
rotate:(NSArray<RNSVGLength *> *)rotate;
|
||||
|
||||
- (void)pushContext:(RNSVGGroup*)node
|
||||
font:(NSDictionary *)font;
|
||||
- (void)pushContext:(RNSVGGroup *)node font:(NSDictionary *)font;
|
||||
@end
|
||||
|
||||
@implementation RNSVGGlyphContext
|
||||
|
||||
- (NSArray*)getFontContext {
|
||||
return mFontContext_;
|
||||
- (NSArray *)getFontContext
|
||||
{
|
||||
return mFontContext_;
|
||||
}
|
||||
|
||||
- (CTFontRef)getGlyphFont
|
||||
{
|
||||
CGFloat size = topFont_->fontSize;
|
||||
NSString *fontFamily = topFont_->fontFamily;
|
||||
NSString *fontStyle = RNSVGFontStyleStrings[topFont_->fontStyle];
|
||||
NSString *fontWeight = RNSVGFontWeightStrings[topFont_->fontWeight];
|
||||
UIFont *font = [RCTFont updateFont:nil
|
||||
withFamily:[fontFamily isEqualToString:@""] ? nil : fontFamily
|
||||
size:@(isnan(size) ? 0 : size)
|
||||
weight:fontWeight
|
||||
style:fontStyle
|
||||
variant:nil
|
||||
scaleMultiplier:1.0];
|
||||
CTFontRef ref = (__bridge CTFontRef)font;
|
||||
CGFloat size = topFont_->fontSize;
|
||||
NSString *fontFamily = topFont_->fontFamily;
|
||||
NSString *fontStyle = RNSVGFontStyleStrings[topFont_->fontStyle];
|
||||
NSString *fontWeight = RNSVGFontWeightStrings[topFont_->fontWeight];
|
||||
UIFont *font = [RCTFont updateFont:nil
|
||||
withFamily:[fontFamily isEqualToString:@""] ? nil : fontFamily
|
||||
size:@(isnan(size) ? 0 : size)
|
||||
weight:fontWeight
|
||||
style:fontStyle
|
||||
variant:nil
|
||||
scaleMultiplier:1.0];
|
||||
CTFontRef ref = (__bridge CTFontRef)font;
|
||||
|
||||
double weight = topFont_->absoluteFontWeight;
|
||||
if (weight == 400) {
|
||||
return ref;
|
||||
double weight = topFont_->absoluteFontWeight;
|
||||
if (weight == 400) {
|
||||
return ref;
|
||||
}
|
||||
|
||||
CFArrayRef cgAxes = CTFontCopyVariationAxes(ref);
|
||||
if (cgAxes == 0) {
|
||||
return ref;
|
||||
}
|
||||
CFIndex cgAxisCount = CFArrayGetCount(cgAxes);
|
||||
CFNumberRef wght_id = 0;
|
||||
|
||||
for (CFIndex i = 0; i < cgAxisCount; ++i) {
|
||||
CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes, i);
|
||||
if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CFArrayRef cgAxes = CTFontCopyVariationAxes(ref);
|
||||
if (cgAxes == 0) {
|
||||
return ref;
|
||||
CFDictionaryRef cgAxisDict = (CFDictionaryRef)cgAxis;
|
||||
CFTypeRef axisName = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisNameKey);
|
||||
|
||||
if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
|
||||
continue;
|
||||
}
|
||||
CFIndex cgAxisCount = CFArrayGetCount(cgAxes);
|
||||
CFNumberRef wght_id = 0;
|
||||
|
||||
for (CFIndex i = 0; i < cgAxisCount; ++i) {
|
||||
CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes, i);
|
||||
if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CFDictionaryRef cgAxisDict = (CFDictionaryRef)cgAxis;
|
||||
CFTypeRef axisName = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisNameKey);
|
||||
|
||||
if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
|
||||
continue;
|
||||
}
|
||||
CFStringRef axisNameString = (CFStringRef)axisName;
|
||||
NSString *axisNameNSString = (__bridge NSString *)(axisNameString);
|
||||
if (![@"Weight" isEqualToString:axisNameNSString] && ![@"Size" isEqualToString:axisNameNSString]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CFTypeRef axisMinValue = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisMinimumValueKey);
|
||||
if (axisMinValue && CFGetTypeID(axisMinValue) == CFNumberGetTypeID()) {
|
||||
CFNumberRef axisMinValueNumber = (CFNumberRef)axisMinValue;
|
||||
double axisMinValueDouble;
|
||||
if (CFNumberGetValue(axisMinValueNumber, kCFNumberDoubleType, &axisMinValueDouble))
|
||||
{
|
||||
weight = fmax(axisMinValueDouble, weight);
|
||||
}
|
||||
}
|
||||
|
||||
CFTypeRef axisMaxValue = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisMaximumValueKey);
|
||||
if (axisMaxValue && CFGetTypeID(axisMaxValue) == CFNumberGetTypeID()) {
|
||||
CFNumberRef axisMaxValueNumber = (CFNumberRef)axisMaxValue;
|
||||
double axisMaxValueDouble;
|
||||
if (CFNumberGetValue(axisMaxValueNumber, kCFNumberDoubleType, &axisMaxValueDouble))
|
||||
{
|
||||
weight = fmin(axisMaxValueDouble, weight);
|
||||
}
|
||||
}
|
||||
|
||||
CFTypeRef axisId = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisIdentifierKey);
|
||||
if (!axisId || CFGetTypeID(axisId) != CFNumberGetTypeID()) {
|
||||
continue;
|
||||
}
|
||||
wght_id = (CFNumberRef)axisId;
|
||||
break;
|
||||
CFStringRef axisNameString = (CFStringRef)axisName;
|
||||
NSString *axisNameNSString = (__bridge NSString *)(axisNameString);
|
||||
if (![@"Weight" isEqualToString:axisNameNSString] && ![@"Size" isEqualToString:axisNameNSString]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wght_id == 0) {
|
||||
return ref;
|
||||
CFTypeRef axisMinValue = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisMinimumValueKey);
|
||||
if (axisMinValue && CFGetTypeID(axisMinValue) == CFNumberGetTypeID()) {
|
||||
CFNumberRef axisMinValueNumber = (CFNumberRef)axisMinValue;
|
||||
double axisMinValueDouble;
|
||||
if (CFNumberGetValue(axisMinValueNumber, kCFNumberDoubleType, &axisMinValueDouble)) {
|
||||
weight = fmax(axisMinValueDouble, weight);
|
||||
}
|
||||
}
|
||||
UIFontDescriptor *uifd = font.fontDescriptor;
|
||||
CTFontDescriptorRef ctfd = (__bridge CTFontDescriptorRef)(uifd);
|
||||
CTFontDescriptorRef newfd = CTFontDescriptorCreateCopyWithVariation(ctfd, wght_id, (CGFloat)weight);
|
||||
CTFontRef newfont = CTFontCreateCopyWithAttributes(ref, size, nil, newfd);
|
||||
|
||||
CFRelease(newfd);
|
||||
CFTypeRef axisMaxValue = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisMaximumValueKey);
|
||||
if (axisMaxValue && CFGetTypeID(axisMaxValue) == CFNumberGetTypeID()) {
|
||||
CFNumberRef axisMaxValueNumber = (CFNumberRef)axisMaxValue;
|
||||
double axisMaxValueDouble;
|
||||
if (CFNumberGetValue(axisMaxValueNumber, kCFNumberDoubleType, &axisMaxValueDouble)) {
|
||||
weight = fmin(axisMaxValueDouble, weight);
|
||||
}
|
||||
}
|
||||
|
||||
return (CTFontRef)CFAutorelease(newfont);
|
||||
CFTypeRef axisId = CFDictionaryGetValue(cgAxisDict, kCTFontVariationAxisIdentifierKey);
|
||||
if (!axisId || CFGetTypeID(axisId) != CFNumberGetTypeID()) {
|
||||
continue;
|
||||
}
|
||||
wght_id = (CFNumberRef)axisId;
|
||||
break;
|
||||
}
|
||||
|
||||
if (wght_id == 0) {
|
||||
return ref;
|
||||
}
|
||||
UIFontDescriptor *uifd = font.fontDescriptor;
|
||||
CTFontDescriptorRef ctfd = (__bridge CTFontDescriptorRef)(uifd);
|
||||
CTFontDescriptorRef newfd = CTFontDescriptorCreateCopyWithVariation(ctfd, wght_id, (CGFloat)weight);
|
||||
CTFontRef newfont = CTFontCreateCopyWithAttributes(ref, size, nil, newfd);
|
||||
|
||||
CFRelease(newfd);
|
||||
|
||||
return (CTFontRef)CFAutorelease(newfont);
|
||||
}
|
||||
|
||||
- (void)pushIndices
|
||||
{
|
||||
[self->mXsIndices_ addObject:[NSNumber numberWithLong:self->mXsIndex_]];
|
||||
[self->mYsIndices_ addObject:[NSNumber numberWithLong:self->mYsIndex_]];
|
||||
[self->mDXsIndices_ addObject:[NSNumber numberWithLong:self->mDXsIndex_]];
|
||||
[self->mDYsIndices_ addObject:[NSNumber numberWithLong:self->mDYsIndex_]];
|
||||
[self->mRsIndices_ addObject:[NSNumber numberWithLong:self->mRsIndex_]];
|
||||
[self->mXsIndices_ addObject:[NSNumber numberWithLong:self->mXsIndex_]];
|
||||
[self->mYsIndices_ addObject:[NSNumber numberWithLong:self->mYsIndex_]];
|
||||
[self->mDXsIndices_ addObject:[NSNumber numberWithLong:self->mDXsIndex_]];
|
||||
[self->mDYsIndices_ addObject:[NSNumber numberWithLong:self->mDYsIndex_]];
|
||||
[self->mRsIndices_ addObject:[NSNumber numberWithLong:self->mRsIndex_]];
|
||||
}
|
||||
|
||||
- (instancetype)initWithWidth:(CGFloat)width
|
||||
height:(CGFloat)height {
|
||||
self = [super init];
|
||||
self->mFontContext_ = [[NSMutableArray alloc]init];
|
||||
self->mXsContext_ = [[NSMutableArray alloc]init];
|
||||
self->mYsContext_ = [[NSMutableArray alloc]init];
|
||||
self->mDXsContext_ = [[NSMutableArray alloc]init];
|
||||
self->mDYsContext_ = [[NSMutableArray alloc]init];
|
||||
self->mRsContext_ = [[NSMutableArray alloc]init];
|
||||
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height
|
||||
{
|
||||
self = [super init];
|
||||
self->mFontContext_ = [[NSMutableArray alloc] init];
|
||||
self->mXsContext_ = [[NSMutableArray alloc] init];
|
||||
self->mYsContext_ = [[NSMutableArray alloc] init];
|
||||
self->mDXsContext_ = [[NSMutableArray alloc] init];
|
||||
self->mDYsContext_ = [[NSMutableArray alloc] init];
|
||||
self->mRsContext_ = [[NSMutableArray alloc] init];
|
||||
|
||||
self->mXIndices_ = [[NSMutableArray alloc]init];
|
||||
self->mYIndices_ = [[NSMutableArray alloc]init];
|
||||
self->mDXIndices_ = [[NSMutableArray alloc]init];
|
||||
self->mDYIndices_ = [[NSMutableArray alloc]init];
|
||||
self->mRIndices_ = [[NSMutableArray alloc]init];
|
||||
self->mXIndices_ = [[NSMutableArray alloc] init];
|
||||
self->mYIndices_ = [[NSMutableArray alloc] init];
|
||||
self->mDXIndices_ = [[NSMutableArray alloc] init];
|
||||
self->mDYIndices_ = [[NSMutableArray alloc] init];
|
||||
self->mRIndices_ = [[NSMutableArray alloc] init];
|
||||
|
||||
self->mXsIndices_ = [[NSMutableArray alloc]init];
|
||||
self->mYsIndices_ = [[NSMutableArray alloc]init];
|
||||
self->mDXsIndices_ = [[NSMutableArray alloc]init];
|
||||
self->mDYsIndices_ = [[NSMutableArray alloc]init];
|
||||
self->mRsIndices_ = [[NSMutableArray alloc]init];
|
||||
self->mXsIndices_ = [[NSMutableArray alloc] init];
|
||||
self->mYsIndices_ = [[NSMutableArray alloc] init];
|
||||
self->mDXsIndices_ = [[NSMutableArray alloc] init];
|
||||
self->mDYsIndices_ = [[NSMutableArray alloc] init];
|
||||
self->mRsIndices_ = [[NSMutableArray alloc] init];
|
||||
|
||||
self->mFontSize_ = RNSVGFontData_DEFAULT_FONT_SIZE;
|
||||
self->topFont_ = [RNSVGFontData Defaults];
|
||||
self->mFontSize_ = RNSVGFontData_DEFAULT_FONT_SIZE;
|
||||
self->topFont_ = [RNSVGFontData Defaults];
|
||||
|
||||
self->mXs_ = [[NSArray alloc]init];
|
||||
self->mYs_ = [[NSArray alloc]init];
|
||||
self->mDXs_ = [[NSArray alloc]init];
|
||||
self->mDYs_ = [[NSArray alloc]init];
|
||||
self->mRs_ = [[NSArray alloc]initWithObjects:[RNSVGLength lengthWithNumber:0], nil];
|
||||
self->mXs_ = [[NSArray alloc] init];
|
||||
self->mYs_ = [[NSArray alloc] init];
|
||||
self->mDXs_ = [[NSArray alloc] init];
|
||||
self->mDYs_ = [[NSArray alloc] init];
|
||||
self->mRs_ = [[NSArray alloc] initWithObjects:[RNSVGLength lengthWithNumber:0], nil];
|
||||
|
||||
self->mXIndex_ = -1;
|
||||
self->mYIndex_ = -1;
|
||||
self->mDXIndex_ = -1;
|
||||
self->mDYIndex_ = -1;
|
||||
self->mRIndex_ = -1;
|
||||
self->mXIndex_ = -1;
|
||||
self->mYIndex_ = -1;
|
||||
self->mDXIndex_ = -1;
|
||||
self->mDYIndex_ = -1;
|
||||
self->mRIndex_ = -1;
|
||||
|
||||
self->mWidth_ = width;
|
||||
self->mHeight_ = height;
|
||||
self->mWidth_ = width;
|
||||
self->mHeight_ = height;
|
||||
|
||||
[self->mXsContext_ addObject:self->mXs_];
|
||||
[self->mYsContext_ addObject:self->mYs_];
|
||||
[self->mDXsContext_ addObject:self->mDXs_];
|
||||
[self->mDYsContext_ addObject:self->mDYs_];
|
||||
[self->mRsContext_ addObject:self->mRs_];
|
||||
[self->mXsContext_ addObject:self->mXs_];
|
||||
[self->mYsContext_ addObject:self->mYs_];
|
||||
[self->mDXsContext_ addObject:self->mDXs_];
|
||||
[self->mDYsContext_ addObject:self->mDYs_];
|
||||
[self->mRsContext_ addObject:self->mRs_];
|
||||
|
||||
[self->mXIndices_ addObject:[NSNumber numberWithLong:self->mXIndex_]];
|
||||
[self->mYIndices_ addObject:[NSNumber numberWithLong:self->mYIndex_]];
|
||||
[self->mDXIndices_ addObject:[NSNumber numberWithLong:self->mDXIndex_]];
|
||||
[self->mDYIndices_ addObject:[NSNumber numberWithLong:self->mDYIndex_]];
|
||||
[self->mRIndices_ addObject:[NSNumber numberWithLong:self->mRIndex_]];
|
||||
[self->mXIndices_ addObject:[NSNumber numberWithLong:self->mXIndex_]];
|
||||
[self->mYIndices_ addObject:[NSNumber numberWithLong:self->mYIndex_]];
|
||||
[self->mDXIndices_ addObject:[NSNumber numberWithLong:self->mDXIndex_]];
|
||||
[self->mDYIndices_ addObject:[NSNumber numberWithLong:self->mDYIndex_]];
|
||||
[self->mRIndices_ addObject:[NSNumber numberWithLong:self->mRIndex_]];
|
||||
|
||||
[self->mFontContext_ addObject:self->topFont_];
|
||||
[self pushIndices];
|
||||
return self;
|
||||
[self->mFontContext_ addObject:self->topFont_];
|
||||
[self pushIndices];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (RNSVGFontData *)getFont {
|
||||
return topFont_;
|
||||
- (RNSVGFontData *)getFont
|
||||
{
|
||||
return topFont_;
|
||||
}
|
||||
|
||||
- (RNSVGFontData *)getTopOrParentFont:(RNSVGGroup *)child
|
||||
{
|
||||
if (self->mTop_ > 0) {
|
||||
return self->topFont_;
|
||||
} else {
|
||||
RNSVGGroup *parentRoot = [child getParentTextRoot];
|
||||
RNSVGFontData *Defaults = [RNSVGFontData Defaults];
|
||||
while (parentRoot != nil) {
|
||||
RNSVGFontData *map = [[parentRoot getGlyphContext] getFont];
|
||||
if (map != Defaults) {
|
||||
return map;
|
||||
}
|
||||
parentRoot = [parentRoot getParentTextRoot];
|
||||
}
|
||||
return Defaults;
|
||||
if (self->mTop_ > 0) {
|
||||
return self->topFont_;
|
||||
} else {
|
||||
RNSVGGroup *parentRoot = [child getParentTextRoot];
|
||||
RNSVGFontData *Defaults = [RNSVGFontData Defaults];
|
||||
while (parentRoot != nil) {
|
||||
RNSVGFontData *map = [[parentRoot getGlyphContext] getFont];
|
||||
if (map != Defaults) {
|
||||
return map;
|
||||
}
|
||||
parentRoot = [parentRoot getParentTextRoot];
|
||||
}
|
||||
return Defaults;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pushNode:(RNSVGGroup *)node andFont:(NSDictionary *)font
|
||||
{
|
||||
RNSVGFontData *parent = [self getTopOrParentFont:node];
|
||||
self->mTop_++;
|
||||
if (font == nil) {
|
||||
[self->mFontContext_ addObject:parent];
|
||||
return;
|
||||
}
|
||||
RNSVGFontData *data = [RNSVGFontData initWithNSDictionary:font
|
||||
parent:parent];
|
||||
self->mFontSize_ = data->fontSize;
|
||||
[self->mFontContext_ addObject:data];
|
||||
self->topFont_ = data;
|
||||
RNSVGFontData *parent = [self getTopOrParentFont:node];
|
||||
self->mTop_++;
|
||||
if (font == nil) {
|
||||
[self->mFontContext_ addObject:parent];
|
||||
return;
|
||||
}
|
||||
RNSVGFontData *data = [RNSVGFontData initWithNSDictionary:font parent:parent];
|
||||
self->mFontSize_ = data->fontSize;
|
||||
[self->mFontContext_ addObject:data];
|
||||
self->topFont_ = data;
|
||||
}
|
||||
|
||||
- (void)pushContext:(RNSVGGroup*)node
|
||||
font:(NSDictionary*)font {
|
||||
[self pushNode:node andFont:font];
|
||||
[self pushIndices];
|
||||
- (void)pushContext:(RNSVGGroup *)node font:(NSDictionary *)font
|
||||
{
|
||||
[self pushNode:node andFont:font];
|
||||
[self pushIndices];
|
||||
}
|
||||
|
||||
- (void)pushContext:(RNSVGText*)node
|
||||
font:(NSDictionary*)font
|
||||
x:(NSArray<RNSVGLength*>*)x
|
||||
y:(NSArray<RNSVGLength*>*)y
|
||||
deltaX:(NSArray<RNSVGLength*>*)deltaX
|
||||
deltaY:(NSArray<RNSVGLength*>*)deltaY
|
||||
rotate:(NSArray<RNSVGLength*>*)rotate {
|
||||
[self pushNode:(RNSVGGroup*)node andFont:font];
|
||||
if (x != nil && [x count] != 0) {
|
||||
mXsIndex_++;
|
||||
mXIndex_ = -1;
|
||||
[mXIndices_ addObject:[NSNumber numberWithLong:mXIndex_]];
|
||||
mXs_ = x;
|
||||
[mXsContext_ addObject:mXs_];
|
||||
}
|
||||
if (y != nil && [y count] != 0) {
|
||||
mYsIndex_++;
|
||||
mYIndex_ = -1;
|
||||
[mYIndices_ addObject:[NSNumber numberWithLong:mYIndex_]];
|
||||
mYs_ = y;
|
||||
[mYsContext_ addObject:mYs_];
|
||||
}
|
||||
if (deltaX != nil && [deltaX count] != 0) {
|
||||
mDXsIndex_++;
|
||||
mDXIndex_ = -1;
|
||||
[mDXIndices_ addObject:[NSNumber numberWithLong:mDXIndex_]];
|
||||
mDXs_ = deltaX;
|
||||
[mDXsContext_ addObject:mDXs_];
|
||||
}
|
||||
if (deltaY != nil && [deltaY count] != 0) {
|
||||
mDYsIndex_++;
|
||||
mDYIndex_ = -1;
|
||||
[mDYIndices_ addObject:[NSNumber numberWithLong:mDYIndex_]];
|
||||
mDYs_ = deltaY;
|
||||
[mDYsContext_ addObject:mDYs_];
|
||||
}
|
||||
if (rotate != nil && [rotate count] != 0) {
|
||||
mRsIndex_++;
|
||||
mRIndex_ = -1;
|
||||
[mRIndices_ addObject:[NSNumber numberWithLong:mRIndex_]];
|
||||
mRs_ = rotate;
|
||||
[mRsContext_ addObject:mRs_];
|
||||
}
|
||||
[self pushIndices];
|
||||
- (void)pushContext:(RNSVGText *)node
|
||||
font:(NSDictionary *)font
|
||||
x:(NSArray<RNSVGLength *> *)x
|
||||
y:(NSArray<RNSVGLength *> *)y
|
||||
deltaX:(NSArray<RNSVGLength *> *)deltaX
|
||||
deltaY:(NSArray<RNSVGLength *> *)deltaY
|
||||
rotate:(NSArray<RNSVGLength *> *)rotate
|
||||
{
|
||||
[self pushNode:(RNSVGGroup *)node andFont:font];
|
||||
if (x != nil && [x count] != 0) {
|
||||
mXsIndex_++;
|
||||
mXIndex_ = -1;
|
||||
[mXIndices_ addObject:[NSNumber numberWithLong:mXIndex_]];
|
||||
mXs_ = x;
|
||||
[mXsContext_ addObject:mXs_];
|
||||
}
|
||||
if (y != nil && [y count] != 0) {
|
||||
mYsIndex_++;
|
||||
mYIndex_ = -1;
|
||||
[mYIndices_ addObject:[NSNumber numberWithLong:mYIndex_]];
|
||||
mYs_ = y;
|
||||
[mYsContext_ addObject:mYs_];
|
||||
}
|
||||
if (deltaX != nil && [deltaX count] != 0) {
|
||||
mDXsIndex_++;
|
||||
mDXIndex_ = -1;
|
||||
[mDXIndices_ addObject:[NSNumber numberWithLong:mDXIndex_]];
|
||||
mDXs_ = deltaX;
|
||||
[mDXsContext_ addObject:mDXs_];
|
||||
}
|
||||
if (deltaY != nil && [deltaY count] != 0) {
|
||||
mDYsIndex_++;
|
||||
mDYIndex_ = -1;
|
||||
[mDYIndices_ addObject:[NSNumber numberWithLong:mDYIndex_]];
|
||||
mDYs_ = deltaY;
|
||||
[mDYsContext_ addObject:mDYs_];
|
||||
}
|
||||
if (rotate != nil && [rotate count] != 0) {
|
||||
mRsIndex_++;
|
||||
mRIndex_ = -1;
|
||||
[mRIndices_ addObject:[NSNumber numberWithLong:mRIndex_]];
|
||||
mRs_ = rotate;
|
||||
[mRsContext_ addObject:mRs_];
|
||||
}
|
||||
[self pushIndices];
|
||||
}
|
||||
|
||||
- (void)popContext {
|
||||
[mFontContext_ removeLastObject];
|
||||
[mXsIndices_ removeLastObject];
|
||||
[mYsIndices_ removeLastObject];
|
||||
[mDXsIndices_ removeLastObject];
|
||||
[mDYsIndices_ removeLastObject];
|
||||
[mRsIndices_ removeLastObject];
|
||||
- (void)popContext
|
||||
{
|
||||
[mFontContext_ removeLastObject];
|
||||
[mXsIndices_ removeLastObject];
|
||||
[mYsIndices_ removeLastObject];
|
||||
[mDXsIndices_ removeLastObject];
|
||||
[mDYsIndices_ removeLastObject];
|
||||
[mRsIndices_ removeLastObject];
|
||||
|
||||
mTop_--;
|
||||
mTop_--;
|
||||
|
||||
long x = mXsIndex_;
|
||||
long y = mYsIndex_;
|
||||
long dx = mDXsIndex_;
|
||||
long dy = mDYsIndex_;
|
||||
long r = mRsIndex_;
|
||||
long x = mXsIndex_;
|
||||
long y = mYsIndex_;
|
||||
long dx = mDXsIndex_;
|
||||
long dy = mDYsIndex_;
|
||||
long r = mRsIndex_;
|
||||
|
||||
topFont_ = [mFontContext_ lastObject];
|
||||
topFont_ = [mFontContext_ lastObject];
|
||||
|
||||
mXsIndex_ = [[mXsIndices_ lastObject] longValue];
|
||||
mYsIndex_ = [[mYsIndices_ lastObject] longValue];
|
||||
mDXsIndex_ = [[mDXsIndices_ lastObject] longValue];
|
||||
mDYsIndex_ = [[mDYsIndices_ lastObject] longValue];
|
||||
mRsIndex_ = [[mRsIndices_ lastObject] longValue];
|
||||
mXsIndex_ = [[mXsIndices_ lastObject] longValue];
|
||||
mYsIndex_ = [[mYsIndices_ lastObject] longValue];
|
||||
mDXsIndex_ = [[mDXsIndices_ lastObject] longValue];
|
||||
mDYsIndex_ = [[mDYsIndices_ lastObject] longValue];
|
||||
mRsIndex_ = [[mRsIndices_ lastObject] longValue];
|
||||
|
||||
if (x != mXsIndex_) {
|
||||
[mXsContext_ removeObjectAtIndex:x];
|
||||
mXs_ = [mXsContext_ objectAtIndex:mXsIndex_];
|
||||
mXIndex_ = [[mXIndices_ objectAtIndex:mXsIndex_] longValue];
|
||||
}
|
||||
if (y != mYsIndex_) {
|
||||
[mYsContext_ removeObjectAtIndex:y];
|
||||
mYs_ = [mYsContext_ objectAtIndex:mYsIndex_];
|
||||
mYIndex_ = [[mYIndices_ objectAtIndex:mYsIndex_] longValue];
|
||||
}
|
||||
if (dx != mDXsIndex_) {
|
||||
[mDXsContext_ removeObjectAtIndex:dx];
|
||||
mDXs_ = [mDXsContext_ objectAtIndex:mDXsIndex_];
|
||||
mDXIndex_ = [[mDXIndices_ objectAtIndex:mDXsIndex_] longValue];
|
||||
}
|
||||
if (dy != mDYsIndex_) {
|
||||
[mDYsContext_ removeObjectAtIndex:dy];
|
||||
mDYs_ = [mDYsContext_ objectAtIndex:mDYsIndex_];
|
||||
mDYIndex_ = [[mDYIndices_ objectAtIndex:mDYsIndex_] longValue];
|
||||
}
|
||||
if (r != mRsIndex_) {
|
||||
[mRsContext_ removeObjectAtIndex:r];
|
||||
mRs_ = [mRsContext_ objectAtIndex:mRsIndex_];
|
||||
mRIndex_ = [[mRIndices_ objectAtIndex:mRsIndex_] longValue];
|
||||
}
|
||||
if (x != mXsIndex_) {
|
||||
[mXsContext_ removeObjectAtIndex:x];
|
||||
mXs_ = [mXsContext_ objectAtIndex:mXsIndex_];
|
||||
mXIndex_ = [[mXIndices_ objectAtIndex:mXsIndex_] longValue];
|
||||
}
|
||||
if (y != mYsIndex_) {
|
||||
[mYsContext_ removeObjectAtIndex:y];
|
||||
mYs_ = [mYsContext_ objectAtIndex:mYsIndex_];
|
||||
mYIndex_ = [[mYIndices_ objectAtIndex:mYsIndex_] longValue];
|
||||
}
|
||||
if (dx != mDXsIndex_) {
|
||||
[mDXsContext_ removeObjectAtIndex:dx];
|
||||
mDXs_ = [mDXsContext_ objectAtIndex:mDXsIndex_];
|
||||
mDXIndex_ = [[mDXIndices_ objectAtIndex:mDXsIndex_] longValue];
|
||||
}
|
||||
if (dy != mDYsIndex_) {
|
||||
[mDYsContext_ removeObjectAtIndex:dy];
|
||||
mDYs_ = [mDYsContext_ objectAtIndex:mDYsIndex_];
|
||||
mDYIndex_ = [[mDYIndices_ objectAtIndex:mDYsIndex_] longValue];
|
||||
}
|
||||
if (r != mRsIndex_) {
|
||||
[mRsContext_ removeObjectAtIndex:r];
|
||||
mRs_ = [mRsContext_ objectAtIndex:mRsIndex_];
|
||||
mRIndex_ = [[mRIndices_ objectAtIndex:mRsIndex_] longValue];
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)incrementIndices:(NSMutableArray *)indices topIndex:(long)topIndex
|
||||
{
|
||||
for (long index = topIndex; index >= 0; index--) {
|
||||
long xIndex = [[indices objectAtIndex:index] longValue];
|
||||
[indices setObject:[NSNumber numberWithLong:xIndex + 1] atIndexedSubscript:index];
|
||||
}
|
||||
for (long index = topIndex; index >= 0; index--) {
|
||||
long xIndex = [[indices objectAtIndex:index] longValue];
|
||||
[indices setObject:[NSNumber numberWithLong:xIndex + 1] atIndexedSubscript:index];
|
||||
}
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/SVG11/text.html#FontSizeProperty
|
||||
@@ -442,84 +442,84 @@
|
||||
* Except for any additional information provided in this specification,
|
||||
* the normative definition of the property is in CSS2 ([CSS2], section 15.2.4).
|
||||
*/
|
||||
- (CGFloat)getFontSize {
|
||||
return mFontSize_;
|
||||
- (CGFloat)getFontSize
|
||||
{
|
||||
return mFontSize_;
|
||||
}
|
||||
|
||||
- (CGFloat)nextXWithDouble:(CGFloat)advance {
|
||||
[RNSVGGlyphContext incrementIndices:mXIndices_ topIndex:mXsIndex_];
|
||||
long nextIndex = mXIndex_ + 1;
|
||||
if (nextIndex < [mXs_ count]) {
|
||||
mDX_ = 0;
|
||||
mXIndex_ = nextIndex;
|
||||
RNSVGLength *length = [mXs_ objectAtIndex:nextIndex];
|
||||
mX_ = [RNSVGPropHelper fromRelative:length
|
||||
relative:mWidth_
|
||||
fontSize:mFontSize_];
|
||||
}
|
||||
mX_ += advance;
|
||||
return mX_;
|
||||
- (CGFloat)nextXWithDouble:(CGFloat)advance
|
||||
{
|
||||
[RNSVGGlyphContext incrementIndices:mXIndices_ topIndex:mXsIndex_];
|
||||
long nextIndex = mXIndex_ + 1;
|
||||
if (nextIndex < [mXs_ count]) {
|
||||
mDX_ = 0;
|
||||
mXIndex_ = nextIndex;
|
||||
RNSVGLength *length = [mXs_ objectAtIndex:nextIndex];
|
||||
mX_ = [RNSVGPropHelper fromRelative:length relative:mWidth_ fontSize:mFontSize_];
|
||||
}
|
||||
mX_ += advance;
|
||||
return mX_;
|
||||
}
|
||||
|
||||
- (CGFloat)nextY {
|
||||
[RNSVGGlyphContext incrementIndices:mYIndices_ topIndex:mYsIndex_];
|
||||
long nextIndex = mYIndex_ + 1;
|
||||
if (nextIndex < [mYs_ count]) {
|
||||
mDY_ = 0;
|
||||
mYIndex_ = nextIndex;
|
||||
RNSVGLength *length = [mYs_ objectAtIndex:nextIndex];
|
||||
mY_ = [RNSVGPropHelper fromRelative:length
|
||||
relative:mHeight_
|
||||
fontSize:mFontSize_];
|
||||
}
|
||||
return mY_;
|
||||
- (CGFloat)nextY
|
||||
{
|
||||
[RNSVGGlyphContext incrementIndices:mYIndices_ topIndex:mYsIndex_];
|
||||
long nextIndex = mYIndex_ + 1;
|
||||
if (nextIndex < [mYs_ count]) {
|
||||
mDY_ = 0;
|
||||
mYIndex_ = nextIndex;
|
||||
RNSVGLength *length = [mYs_ objectAtIndex:nextIndex];
|
||||
mY_ = [RNSVGPropHelper fromRelative:length relative:mHeight_ fontSize:mFontSize_];
|
||||
}
|
||||
return mY_;
|
||||
}
|
||||
|
||||
- (CGFloat)nextDeltaX {
|
||||
[RNSVGGlyphContext incrementIndices:mDXIndices_ topIndex:mDXsIndex_];
|
||||
long nextIndex = mDXIndex_ + 1;
|
||||
if (nextIndex < [mDXs_ count]) {
|
||||
mDXIndex_ = nextIndex;
|
||||
RNSVGLength *length = [mDXs_ objectAtIndex:nextIndex];
|
||||
CGFloat val = [RNSVGPropHelper fromRelative:length
|
||||
relative:mWidth_
|
||||
fontSize:mFontSize_];
|
||||
mDX_ += val;
|
||||
}
|
||||
return mDX_;
|
||||
- (CGFloat)nextDeltaX
|
||||
{
|
||||
[RNSVGGlyphContext incrementIndices:mDXIndices_ topIndex:mDXsIndex_];
|
||||
long nextIndex = mDXIndex_ + 1;
|
||||
if (nextIndex < [mDXs_ count]) {
|
||||
mDXIndex_ = nextIndex;
|
||||
RNSVGLength *length = [mDXs_ objectAtIndex:nextIndex];
|
||||
CGFloat val = [RNSVGPropHelper fromRelative:length relative:mWidth_ fontSize:mFontSize_];
|
||||
mDX_ += val;
|
||||
}
|
||||
return mDX_;
|
||||
}
|
||||
|
||||
- (CGFloat)nextDeltaY {
|
||||
[RNSVGGlyphContext incrementIndices:mDYIndices_ topIndex:mDYsIndex_];
|
||||
long nextIndex = mDYIndex_ + 1;
|
||||
if (nextIndex < [mDYs_ count]) {
|
||||
mDYIndex_ = nextIndex;
|
||||
RNSVGLength *length = [mDYs_ objectAtIndex:nextIndex];
|
||||
CGFloat val = [RNSVGPropHelper fromRelative:length
|
||||
relative:mHeight_
|
||||
fontSize:mFontSize_];
|
||||
mDY_ += val;
|
||||
}
|
||||
return mDY_;
|
||||
- (CGFloat)nextDeltaY
|
||||
{
|
||||
[RNSVGGlyphContext incrementIndices:mDYIndices_ topIndex:mDYsIndex_];
|
||||
long nextIndex = mDYIndex_ + 1;
|
||||
if (nextIndex < [mDYs_ count]) {
|
||||
mDYIndex_ = nextIndex;
|
||||
RNSVGLength *length = [mDYs_ objectAtIndex:nextIndex];
|
||||
CGFloat val = [RNSVGPropHelper fromRelative:length relative:mHeight_ fontSize:mFontSize_];
|
||||
mDY_ += val;
|
||||
}
|
||||
return mDY_;
|
||||
}
|
||||
|
||||
- (CGFloat)nextRotation {
|
||||
[RNSVGGlyphContext incrementIndices:mRIndices_ topIndex:mRsIndex_];
|
||||
long nextIndex = mRIndex_ + 1;
|
||||
long count = [mRs_ count];
|
||||
if (nextIndex < count) {
|
||||
mRIndex_ = nextIndex;
|
||||
} else {
|
||||
mRIndex_ = count - 1;
|
||||
}
|
||||
return [mRs_[mRIndex_] value];
|
||||
- (CGFloat)nextRotation
|
||||
{
|
||||
[RNSVGGlyphContext incrementIndices:mRIndices_ topIndex:mRsIndex_];
|
||||
long nextIndex = mRIndex_ + 1;
|
||||
long count = [mRs_ count];
|
||||
if (nextIndex < count) {
|
||||
mRIndex_ = nextIndex;
|
||||
} else {
|
||||
mRIndex_ = count - 1;
|
||||
}
|
||||
return [mRs_[mRIndex_] value];
|
||||
}
|
||||
|
||||
- (CGFloat)getWidth {
|
||||
return mWidth_;
|
||||
- (CGFloat)getWidth
|
||||
{
|
||||
return mWidth_;
|
||||
}
|
||||
|
||||
- (CGFloat)getHeight {
|
||||
return mHeight_;
|
||||
- (CGFloat)getHeight
|
||||
{
|
||||
return mHeight_;
|
||||
}
|
||||
@end
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user