├── .github └── workflows │ ├── npm-publish.yml │ └── windows-app-test.yml ├── .gitignore ├── .prettierrc ├── .prettierrc.js ├── LICENSE ├── README.md ├── RNSketchCanvas.podspec ├── android ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── terrylinla │ └── rnsketchcanvas │ ├── SketchCanvas.java │ ├── SketchCanvasManager.java │ ├── SketchCanvasModule.java │ ├── SketchCanvasPackage.java │ ├── SketchData.java │ └── Utility.java ├── example ├── .buckconfig ├── .eslintrc.js ├── .flowconfig ├── .gitattributes ├── .gitignore ├── .prettierrc.js ├── .watchmanconfig ├── App.js ├── __tests__ │ └── App-test.js ├── __windows_tests__ │ ├── Control-render-test.js │ └── Draw-color-test.js ├── android │ ├── app │ │ ├── _BUCK │ │ ├── build.gradle │ │ ├── build_defs.bzl │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── debug │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── ReactNativeFlipper.java │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── assets │ │ │ └── fonts │ │ │ │ └── IndieFlower.ttf │ │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.java │ │ │ └── res │ │ │ ├── drawable-xxhdpi │ │ │ ├── bulb.png │ │ │ └── whale.png │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── app.json ├── babel.config.js ├── index.js ├── ios │ ├── Images │ │ ├── bulb.png │ │ └── whale.png │ ├── Podfile │ ├── Podfile.lock │ ├── example-tvOS │ │ └── Info.plist │ ├── example-tvOSTests │ │ └── Info.plist │ ├── example.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ ├── example-tvOS.xcscheme │ │ │ └── example.xcscheme │ ├── example.xcworkspace │ │ └── contents.xcworkspacedata │ ├── example │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── LaunchScreen.storyboard │ │ └── main.m │ └── exampleTests │ │ ├── Info.plist │ │ └── exampleTests.m ├── jest-windows │ ├── driver.setup.js │ └── jest.setup.js ├── jest.windows.config.js ├── jsconfig.json ├── metro.config.js ├── package.json ├── windows │ ├── .gitignore │ ├── example.sln │ └── example │ │ ├── .gitignore │ │ ├── App.cpp │ │ ├── App.h │ │ ├── App.idl │ │ ├── App.xaml │ │ ├── Assets │ │ ├── LockScreenLogo.scale-200.png │ │ ├── SplashScreen.scale-200.png │ │ ├── Square150x150Logo.scale-200.png │ │ ├── Square44x44Logo.scale-200.png │ │ ├── Square44x44Logo.targetsize-24_altform-unplated.png │ │ ├── StoreLogo.png │ │ └── Wide310x150Logo.scale-200.png │ │ ├── AutolinkedNativeModules.g.cpp │ │ ├── AutolinkedNativeModules.g.h │ │ ├── AutolinkedNativeModules.g.targets │ │ ├── MainPage.cpp │ │ ├── MainPage.h │ │ ├── MainPage.idl │ │ ├── MainPage.xaml │ │ ├── Package.appxmanifest │ │ ├── PropertySheet.props │ │ ├── ReactPackageProvider.cpp │ │ ├── ReactPackageProvider.h │ │ ├── bulb.png │ │ ├── example.vcxproj │ │ ├── example.vcxproj.filters │ │ ├── example_TemporaryKey.pfx │ │ ├── fonts │ │ └── IndieFlower.ttf │ │ ├── packages.config │ │ ├── pch.cpp │ │ ├── pch.h │ │ └── whale.png └── yarn.lock ├── index.d.ts ├── index.js ├── ios └── RNSketchCanvas │ ├── RNSketchCanvas.xcodeproj │ └── project.pbxproj │ └── RNSketchCanvas │ ├── RNSketchCanvas.h │ ├── RNSketchCanvas.m │ ├── RNSketchCanvasManager.h │ ├── RNSketchCanvasManager.m │ ├── RNSketchData.h │ ├── RNSketchData.m │ ├── Utility.h │ └── Utility.m ├── package.json ├── src ├── SketchCanvas.js └── handlePermissions.js ├── windows ├── .gitignore ├── README.md ├── RNSketchCanvas │ ├── PropertySheet.props │ ├── RNSketchCanvas.cpp │ ├── RNSketchCanvas.def │ ├── RNSketchCanvas.h │ ├── RNSketchCanvas.idl │ ├── RNSketchCanvas.vcxproj │ ├── RNSketchCanvas.vcxproj.filters │ ├── RNSketchCanvasModule.h │ ├── RNSketchCanvasViewManager.cpp │ ├── RNSketchCanvasViewManager.h │ ├── ReactPackageProvider.cpp │ ├── ReactPackageProvider.h │ ├── ReactPackageProvider.idl │ ├── SketchData.cpp │ ├── SketchData.h │ ├── Utility.cpp │ ├── Utility.h │ ├── packages.config │ ├── pch.cpp │ └── pch.h ├── RNSketchCanvas62.sln └── RNSketchCanvas63.sln └── yarn.lock /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | publish-npm: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: actions/setup-node@v3 16 | with: 17 | node-version: 16 18 | registry-url: https://registry.npmjs.org/ 19 | - run: yarn 20 | - run: yarn publish --access public 21 | env: 22 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 23 | -------------------------------------------------------------------------------- /.github/workflows/windows-app-test.yml: -------------------------------------------------------------------------------- 1 | name: Windows CI 2 | on: [pull_request] 3 | 4 | jobs: 5 | run-windows-tests: 6 | name: Build & run tests 7 | runs-on: windows-2019 8 | 9 | steps: 10 | - uses: actions/checkout@v2 11 | name: Checkout Code 12 | 13 | - name: Setup Node.js 14 | uses: actions/setup-node@v1 15 | with: 16 | node-version: '12.18.3' 17 | 18 | - name: Setup MSBuild 19 | uses: microsoft/setup-msbuild@v1 20 | with: 21 | vs-version: 16.5 22 | 23 | - name: Install example modules 24 | run: | 25 | cd example 26 | yarn --pure-lockfile 27 | - name: Build release 28 | run: | 29 | cd example 30 | npx react-native run-windows --release --no-packager --no-launch --logging 31 | - name: Start Appium server 32 | shell: powershell 33 | run: | 34 | cd example 35 | Start-Process PowerShell -ArgumentList "yarn appium" 36 | - name: Run tests 37 | run: | 38 | cd example 39 | yarn test:windows 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | 38 | # BUCK 39 | buck-out/ 40 | \.buckd/ 41 | android/app/libs 42 | *.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 50 | 51 | fastlane/report.xml 52 | fastlane/Preview.html 53 | fastlane/screenshots 54 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "tabWidth": 4, 4 | "arrowParens": "always", 5 | "trailingComma": "none" 6 | } -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | printWidth: 120, 3 | tabWidth: 4, 4 | arrowParens: "always", 5 | trailingComma: "none" 6 | }; 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Terry Lin 4 | Copyright (c) 2022 W&W Immo Informatik AG 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /RNSketchCanvas.podspec: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) 4 | 5 | Pod::Spec.new do |s| 6 | s.name = 'RNSketchCanvas' 7 | s.version = package['version'] 8 | s.summary = package['description'] 9 | s.homepage = 'https://github.com/wwimmo/react-native-sketch-canvas' 10 | s.license = package['license'] 11 | s.authors = package['author'] 12 | s.source = { :git => package['repository']['url'] } 13 | s.platform = :ios, '8.0' 14 | s.source_files = 'ios/**/*.{h,m}' 15 | s.dependency 'React' 16 | end 17 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | def safeExtGet(prop, fallback) { 4 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback 5 | } 6 | 7 | android { 8 | compileSdkVersion safeExtGet('compileSdkVersion', 27) 9 | buildToolsVersion safeExtGet('buildToolsVersion', '27.0.3') 10 | 11 | defaultConfig { 12 | minSdkVersion safeExtGet('minSdkVersion', 16) 13 | targetSdkVersion safeExtGet('targetSdkVersion', 27) 14 | versionCode 1 15 | versionName "1.0" 16 | ndk { 17 | abiFilters "armeabi-v7a", "x86" 18 | } 19 | } 20 | splits { 21 | abi { 22 | reset() 23 | enable safeExtGet('enableSeparateBuildPerCPUArchitecture', false) 24 | universalApk safeExtGet('universalApk', false) // If true, also generate a universal APK 25 | include "armeabi-v7a", "x86" 26 | } 27 | } 28 | } 29 | 30 | dependencies { 31 | compileOnly "com.facebook.react:react-native:+" 32 | } 33 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/java/com/terrylinla/rnsketchcanvas/SketchCanvasManager.java: -------------------------------------------------------------------------------- 1 | package com.terrylinla.rnsketchcanvas; 2 | 3 | import android.app.Activity; 4 | import android.content.BroadcastReceiver; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.IntentFilter; 8 | import android.content.pm.ActivityInfo; 9 | import android.content.res.Configuration; 10 | import android.util.Log; 11 | 12 | import com.facebook.common.logging.FLog; 13 | import com.facebook.react.bridge.Callback; 14 | import com.facebook.react.bridge.ReadableArray; 15 | import com.facebook.react.bridge.ReadableMap; 16 | import com.facebook.react.bridge.ReactMethod; 17 | import com.facebook.react.common.ReactConstants; 18 | import com.facebook.react.common.MapBuilder; 19 | import com.facebook.react.modules.core.DeviceEventManagerModule; 20 | import com.facebook.react.uimanager.SimpleViewManager; 21 | import com.facebook.react.uimanager.ThemedReactContext; 22 | import com.facebook.react.uimanager.annotations.ReactProp; 23 | 24 | import java.util.HashMap; 25 | import java.util.Map; 26 | import java.util.ArrayList; 27 | import android.graphics.PointF; 28 | 29 | import javax.annotation.Nullable; 30 | 31 | public class SketchCanvasManager extends SimpleViewManager { 32 | public static final int COMMAND_ADD_POINT = 1; 33 | public static final int COMMAND_NEW_PATH = 2; 34 | public static final int COMMAND_CLEAR = 3; 35 | public static final int COMMAND_ADD_PATH = 4; 36 | public static final int COMMAND_DELETE_PATH = 5; 37 | public static final int COMMAND_SAVE = 6; 38 | public static final int COMMAND_END_PATH = 7; 39 | 40 | public static SketchCanvas Canvas = null; 41 | 42 | private static final String PROPS_LOCAL_SOURCE_IMAGE = "localSourceImage"; 43 | 44 | @Override 45 | public String getName() { 46 | return "RNSketchCanvas"; 47 | } 48 | 49 | @Override 50 | protected SketchCanvas createViewInstance(ThemedReactContext context) { 51 | SketchCanvasManager.Canvas = new SketchCanvas(context); 52 | return SketchCanvasManager.Canvas; 53 | } 54 | 55 | @ReactProp(name = PROPS_LOCAL_SOURCE_IMAGE) 56 | public void setLocalSourceImage(SketchCanvas viewContainer, ReadableMap localSourceImage) { 57 | if (localSourceImage != null && localSourceImage.getString("filename") != null) { 58 | viewContainer.openImageFile( 59 | localSourceImage.hasKey("filename") ? localSourceImage.getString("filename") : null, 60 | localSourceImage.hasKey("directory") ? localSourceImage.getString("directory") : "", 61 | localSourceImage.hasKey("mode") ? localSourceImage.getString("mode") : "" 62 | ); 63 | } 64 | } 65 | 66 | @Override 67 | public Map getCommandsMap() { 68 | Map map = new HashMap<>(); 69 | 70 | map.put("addPoint", COMMAND_ADD_POINT); 71 | map.put("newPath", COMMAND_NEW_PATH); 72 | map.put("clear", COMMAND_CLEAR); 73 | map.put("addPath", COMMAND_ADD_PATH); 74 | map.put("deletePath", COMMAND_DELETE_PATH); 75 | map.put("save", COMMAND_SAVE); 76 | map.put("endPath", COMMAND_END_PATH); 77 | 78 | return map; 79 | } 80 | 81 | @Override 82 | protected void addEventEmitters(ThemedReactContext reactContext, SketchCanvas view) { 83 | 84 | } 85 | 86 | @Override 87 | public void receiveCommand(SketchCanvas view, int commandType, @Nullable ReadableArray args) { 88 | switch (commandType) { 89 | case COMMAND_ADD_POINT: { 90 | view.addPoint((float)args.getDouble(0), (float)args.getDouble(1)); 91 | return; 92 | } 93 | case COMMAND_NEW_PATH: { 94 | view.newPath(args.getInt(0), args.getInt(1), (float)args.getDouble(2)); 95 | return; 96 | } 97 | case COMMAND_CLEAR: { 98 | view.clear(); 99 | return; 100 | } 101 | case COMMAND_ADD_PATH: { 102 | ReadableArray path = args.getArray(3); 103 | ArrayList pointPath = new ArrayList(path.size()); 104 | for (int i=0; i createNativeModules(ReactApplicationContext reactContext) { 21 | return Arrays.asList( 22 | new SketchCanvasModule(reactContext) 23 | ); 24 | } 25 | 26 | @Override 27 | public List createViewManagers(ReactApplicationContext reactContext) { 28 | return Arrays.asList( 29 | new SketchCanvasManager() 30 | ); 31 | } 32 | 33 | public List> createJSModules() { 34 | return Collections.emptyList(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /android/src/main/java/com/terrylinla/rnsketchcanvas/SketchData.java: -------------------------------------------------------------------------------- 1 | package com.terrylinla.rnsketchcanvas; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Color; 5 | import android.graphics.Paint; 6 | import android.graphics.Path; 7 | import android.graphics.PointF; 8 | import android.graphics.PorterDuff; 9 | import android.graphics.PorterDuffXfermode; 10 | import android.graphics.Rect; 11 | import android.graphics.RectF; 12 | 13 | import java.util.ArrayList; 14 | 15 | public class SketchData { 16 | public final ArrayList points = new ArrayList(); 17 | public final int id, strokeColor; 18 | public final float strokeWidth; 19 | public final boolean isTranslucent; 20 | 21 | private Paint mPaint; 22 | private Path mPath; 23 | private RectF mDirty = null; 24 | 25 | public static PointF midPoint(PointF p1, PointF p2) { 26 | return new PointF((p1.x + p2.x) * 0.5f, (p1.y + p2.y) * 0.5f); 27 | } 28 | 29 | public SketchData(int id, int strokeColor, float strokeWidth) { 30 | this.id = id; 31 | this.strokeColor = strokeColor; 32 | this.strokeWidth = strokeWidth; 33 | this.isTranslucent = ((strokeColor >> 24) & 0xff) != 255 && strokeColor != Color.TRANSPARENT; 34 | mPath = this.isTranslucent ? new Path() : null; 35 | } 36 | 37 | public SketchData(int id, int strokeColor, float strokeWidth, ArrayList points) { 38 | this.id = id; 39 | this.strokeColor = strokeColor; 40 | this.strokeWidth = strokeWidth; 41 | this.points.addAll(points); 42 | this.isTranslucent = ((strokeColor >> 24) & 0xff) != 255 && strokeColor != Color.TRANSPARENT; 43 | mPath = this.isTranslucent ? evaluatePath() : null; 44 | } 45 | 46 | public Rect addPoint(PointF p) { 47 | points.add(p); 48 | 49 | RectF updateRect; 50 | 51 | int pointsCount = points.size(); 52 | 53 | if (this.isTranslucent) { 54 | if (pointsCount >= 3) { 55 | addPointToPath(mPath, 56 | this.points.get(pointsCount - 3), 57 | this.points.get(pointsCount - 2), 58 | p); 59 | } else if (pointsCount >= 2) { 60 | addPointToPath(mPath, this.points.get(0), this.points.get(0), p); 61 | } else { 62 | addPointToPath(mPath, p, p, p); 63 | } 64 | 65 | float x = p.x, y = p.y; 66 | if (mDirty == null) { 67 | mDirty = new RectF(x, y, x + 1, y + 1); 68 | updateRect = new RectF(x - this.strokeWidth, y - this.strokeWidth, 69 | x + this.strokeWidth, y + this.strokeWidth); 70 | } else { 71 | mDirty.union(x, y); 72 | updateRect = new RectF( 73 | mDirty.left - this.strokeWidth, mDirty.top - this.strokeWidth, 74 | mDirty.right + this.strokeWidth, mDirty.bottom + this.strokeWidth 75 | ); 76 | } 77 | } else { 78 | if (pointsCount >= 3) { 79 | PointF a = points.get(pointsCount - 3); 80 | PointF b = points.get(pointsCount - 2); 81 | PointF c = p; 82 | PointF prevMid = midPoint(a, b); 83 | PointF currentMid = midPoint(b, c); 84 | 85 | updateRect = new RectF(prevMid.x, prevMid.y, prevMid.x, prevMid.y); 86 | updateRect.union(b.x, b.y); 87 | updateRect.union(currentMid.x, currentMid.y); 88 | } else if (pointsCount >= 2) { 89 | PointF a = points.get(pointsCount - 2); 90 | PointF b = p; 91 | PointF mid = midPoint(a, b); 92 | 93 | updateRect = new RectF(a.x, a.y, a.x, a.y); 94 | updateRect.union(mid.x, mid.y); 95 | } else { 96 | updateRect = new RectF(p.x, p.y, p.x, p.y); 97 | } 98 | 99 | updateRect.inset(-strokeWidth * 2, -strokeWidth * 2); 100 | 101 | } 102 | Rect integralRect = new Rect(); 103 | updateRect.roundOut(integralRect); 104 | 105 | return integralRect; 106 | } 107 | 108 | public void drawLastPoint(Canvas canvas) { 109 | int pointsCount = points.size(); 110 | if (pointsCount < 1) { 111 | return; 112 | } 113 | 114 | draw(canvas, pointsCount - 1); 115 | } 116 | 117 | public void draw(Canvas canvas) { 118 | if (this.isTranslucent) { 119 | canvas.drawPath(mPath, getPaint()); 120 | } else { 121 | int pointsCount = points.size(); 122 | for (int i = 0; i < pointsCount; i++) { 123 | draw(canvas, i); 124 | } 125 | } 126 | } 127 | 128 | private Paint getPaint() { 129 | if (mPaint == null) { 130 | boolean isErase = strokeColor == Color.TRANSPARENT; 131 | 132 | mPaint = new Paint(); 133 | mPaint.setColor(strokeColor); 134 | mPaint.setStrokeWidth(strokeWidth); 135 | mPaint.setStyle(Paint.Style.STROKE); 136 | mPaint.setStrokeCap(Paint.Cap.ROUND); 137 | mPaint.setStrokeJoin(Paint.Join.ROUND); 138 | mPaint.setAntiAlias(true); 139 | mPaint.setXfermode(new PorterDuffXfermode(isErase ? PorterDuff.Mode.CLEAR : PorterDuff.Mode.SRC_OVER)); 140 | } 141 | return mPaint; 142 | } 143 | 144 | private void draw(Canvas canvas, int pointIndex) { 145 | int pointsCount = points.size(); 146 | if (pointIndex >= pointsCount) { 147 | return; 148 | } 149 | 150 | if (pointsCount >= 3 && pointIndex >= 2) { 151 | PointF a = points.get(pointIndex - 2); 152 | PointF b = points.get(pointIndex - 1); 153 | PointF c = points.get(pointIndex); 154 | PointF prevMid = midPoint(a, b); 155 | PointF currentMid = midPoint(b, c); 156 | 157 | // Draw a curve 158 | Path path = new Path(); 159 | path.moveTo(prevMid.x, prevMid.y); 160 | path.quadTo(b.x, b.y, currentMid.x, currentMid.y); 161 | 162 | canvas.drawPath(path, getPaint()); 163 | } else if (pointsCount >= 2 && pointIndex >= 1) { 164 | PointF a = points.get(pointIndex - 1); 165 | PointF b = points.get(pointIndex); 166 | PointF mid = midPoint(a, b); 167 | 168 | // Draw a line to the middle of points a and b 169 | // This is so the next draw which uses a curve looks correct and continues from there 170 | canvas.drawLine(a.x, a.y, mid.x, mid.y, getPaint()); 171 | } else if (pointsCount >= 1) { 172 | PointF a = points.get(pointIndex); 173 | 174 | // Draw a single point 175 | canvas.drawPoint(a.x, a.y, getPaint()); 176 | } 177 | } 178 | 179 | private Path evaluatePath() { 180 | int pointsCount = points.size(); 181 | Path path = new Path(); 182 | 183 | for(int pointIndex=0; pointIndex= 3 && pointIndex >= 2) { 185 | PointF a = points.get(pointIndex - 2); 186 | PointF b = points.get(pointIndex - 1); 187 | PointF c = points.get(pointIndex); 188 | PointF prevMid = midPoint(a, b); 189 | PointF currentMid = midPoint(b, c); 190 | 191 | // Draw a curve 192 | path.moveTo(prevMid.x, prevMid.y); 193 | path.quadTo(b.x, b.y, currentMid.x, currentMid.y); 194 | } else if (pointsCount >= 2 && pointIndex >= 1) { 195 | PointF a = points.get(pointIndex - 1); 196 | PointF b = points.get(pointIndex); 197 | PointF mid = midPoint(a, b); 198 | 199 | // Draw a line to the middle of points a and b 200 | // This is so the next draw which uses a curve looks correct and continues from there 201 | path.moveTo(a.x, a.y); 202 | path.lineTo(mid.x, mid.y); 203 | } else if (pointsCount >= 1) { 204 | PointF a = points.get(pointIndex); 205 | 206 | // Draw a single point 207 | path.moveTo(a.x, a.y); 208 | path.lineTo(a.x, a.y); 209 | } 210 | } 211 | return path; 212 | } 213 | 214 | private void addPointToPath(Path path, PointF tPoint, PointF pPoint, PointF point) { 215 | PointF mid1 = new PointF((pPoint.x + tPoint.x) * 0.5f, (pPoint.y + tPoint.y) * 0.5f); 216 | PointF mid2 = new PointF((point.x + pPoint.x) * 0.5f, (point.y + pPoint.y) * 0.5f); 217 | path.moveTo(mid1.x, mid1.y); 218 | path.quadTo(pPoint.x, pPoint.y, mid2.x, mid2.y); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /android/src/main/java/com/terrylinla/rnsketchcanvas/Utility.java: -------------------------------------------------------------------------------- 1 | package com.terrylinla.rnsketchcanvas; 2 | 3 | import android.util.Log; 4 | import android.graphics.RectF; 5 | 6 | public final class Utility { 7 | public static RectF fillImage(float imgWidth, float imgHeight, float targetWidth, float targetHeight, String mode) { 8 | float imageAspectRatio = imgWidth / imgHeight; 9 | float targetAspectRatio = targetWidth / targetHeight; 10 | switch (mode) { 11 | case "AspectFill": { 12 | float scaleFactor = targetAspectRatio < imageAspectRatio ? targetHeight / imgHeight : targetWidth / imgWidth; 13 | float w = imgWidth * scaleFactor, h = imgHeight * scaleFactor; 14 | return new RectF((targetWidth - w) / 2, (targetHeight - h) / 2, 15 | w + (targetWidth - w) / 2, h + (targetHeight - h) / 2); 16 | } 17 | case "AspectFit": 18 | default: { 19 | float scaleFactor = targetAspectRatio > imageAspectRatio ? targetHeight / imgHeight : targetWidth / imgWidth; 20 | float w = imgWidth * scaleFactor, h = imgHeight * scaleFactor; 21 | return new RectF((targetWidth - w) / 2, (targetHeight - h) / 2, 22 | w + (targetWidth - w) / 2, h + (targetHeight - h) / 2); 23 | } 24 | case "ScaleToFill": { 25 | return new RectF(0, 0, targetWidth, targetHeight); 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /example/.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /example/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: '@react-native-community', 4 | }; 5 | -------------------------------------------------------------------------------- /example/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore polyfills 9 | node_modules/react-native/Libraries/polyfills/.* 10 | 11 | ; These should not be required directly 12 | ; require from fbjs/lib instead: require('fbjs/lib/warning') 13 | node_modules/warning/.* 14 | 15 | ; Flow doesn't support platforms 16 | .*/Libraries/Utilities/LoadingView.js 17 | 18 | [untyped] 19 | .*/node_modules/@react-native-community/cli/.*/.* 20 | 21 | [include] 22 | 23 | [libs] 24 | node_modules/react-native/interface.js 25 | node_modules/react-native/flow/ 26 | 27 | [options] 28 | emoji=true 29 | 30 | esproposal.optional_chaining=enable 31 | esproposal.nullish_coalescing=enable 32 | 33 | module.file_ext=.js 34 | module.file_ext=.json 35 | module.file_ext=.ios.js 36 | 37 | munge_underscores=true 38 | 39 | module.name_mapper='^react-native/\(.*\)$' -> '/node_modules/react-native/\1' 40 | module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '/node_modules/react-native/Libraries/Image/RelativeImageStub' 41 | 42 | suppress_type=$FlowIssue 43 | suppress_type=$FlowFixMe 44 | suppress_type=$FlowFixMeProps 45 | suppress_type=$FlowFixMeState 46 | 47 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\) 48 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+ 49 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 50 | 51 | [lints] 52 | sketchy-null-number=warn 53 | sketchy-null-mixed=warn 54 | sketchy-number=warn 55 | untyped-type-import=warn 56 | nonstrict-import=warn 57 | deprecated-type=warn 58 | unsafe-getters-setters=warn 59 | unnecessary-invariant=warn 60 | signature-verification-failure=warn 61 | deprecated-utility=error 62 | 63 | [strict] 64 | deprecated-type 65 | nonstrict-import 66 | sketchy-null 67 | unclear-type 68 | unsafe-getters-setters 69 | untyped-import 70 | untyped-type-import 71 | 72 | [version] 73 | ^0.122.0 74 | -------------------------------------------------------------------------------- /example/.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | 24 | # Android/IntelliJ 25 | # 26 | build/ 27 | .idea 28 | .gradle 29 | local.properties 30 | *.iml 31 | 32 | # node.js 33 | # 34 | node_modules/ 35 | npm-debug.log 36 | yarn-error.log 37 | 38 | # BUCK 39 | buck-out/ 40 | \.buckd/ 41 | *.keystore 42 | !debug.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | 58 | # CocoaPods 59 | /ios/Pods/ 60 | 61 | # MSBuild Binary and Structured Log 62 | *.binlog 63 | 64 | # Debug log 65 | debug.log 66 | -------------------------------------------------------------------------------- /example/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bracketSpacing: false, 3 | jsxBracketSameLine: true, 4 | singleQuote: true, 5 | trailingComma: 'all', 6 | }; 7 | -------------------------------------------------------------------------------- /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /example/__tests__/App-test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import 'react-native'; 6 | import React from 'react'; 7 | import App from '../App'; 8 | 9 | // Note: test renderer must be required after react-native. 10 | import renderer from 'react-test-renderer'; 11 | 12 | it('renders correctly', () => { 13 | renderer.create(); 14 | }); 15 | -------------------------------------------------------------------------------- /example/__windows_tests__/Control-render-test.js: -------------------------------------------------------------------------------- 1 | import { driver, By2 } from 'selenium-appium'; 2 | import { until } from 'selenium-webdriver'; 3 | 4 | const setup = require('../jest-windows/driver.setup'); 5 | jest.setTimeout(60000); 6 | 7 | beforeAll(() => { 8 | return driver.startWithCapabilities(setup.capabilites); 9 | }); 10 | 11 | afterAll(() => { 12 | return driver.quit(); 13 | }); 14 | 15 | describe('Control Renders', () => { 16 | 17 | test('Renders default buttons', async () => { 18 | await driver.wait(until.elementLocated(By2.nativeName('- Example 1 -'))); 19 | await By2.nativeName('- Example 1 -').click(); 20 | await(driver.sleep(1000)); 21 | await driver.wait(until.elementLocated(By2.nativeName('Undo'))); 22 | await driver.wait(until.elementLocated(By2.nativeName('Clear'))); 23 | await driver.wait(until.elementLocated(By2.nativeName('Eraser'))); 24 | await By2.nativeName('Close').click(); 25 | await(driver.sleep(1000)); 26 | }); 27 | 28 | }) 29 | -------------------------------------------------------------------------------- /example/__windows_tests__/Draw-color-test.js: -------------------------------------------------------------------------------- 1 | import { driver, By2 } from 'selenium-appium'; 2 | import { until, Origin } from 'selenium-webdriver'; 3 | import { PNG } from 'pngjs'; 4 | import pixelmatch from 'pixelmatch'; 5 | 6 | const setup = require('../jest-windows/driver.setup'); 7 | jest.setTimeout(60000); 8 | 9 | 10 | function pngFromBase64(base64) { 11 | const pngBuffer = Buffer.from(base64, 'base64'); 12 | return PNG.sync.read(pngBuffer); 13 | }; 14 | 15 | const pixelThreshold = 10; // Allow 10 pixel difference, to account for anti-aliasing differences. 16 | function pixelDiffPNGs(img1, img2) { 17 | return pixelmatch(img1.data, img2.data, null, img1.width, img1.height); 18 | } 19 | 20 | beforeAll(() => { 21 | return driver.startWithCapabilities(setup.capabilites); 22 | }); 23 | 24 | afterAll(() => { 25 | return driver.quit(); 26 | }); 27 | 28 | describe('Test draw', () => { 29 | 30 | test('Draw line', async () => { 31 | await driver.wait(until.elementLocated(By2.nativeName('- Example 2 -'))); 32 | 33 | // Set to specific size, so that drawing is expectable. 34 | await driver.manage().window().setRect({x:0,y:0,width:500,height:500}); 35 | await By2.nativeName('- Example 2 -').click(); 36 | await(driver.sleep(1000)); 37 | 38 | await By2.nativeName('Red').click(); 39 | await(driver.sleep(1000)); 40 | 41 | let screenshot_before_drawing = pngFromBase64(await driver.takeScreenshot()); 42 | 43 | // Have to use bridge, since WinAppDriver doesn't support mouse movement directly. 44 | let builder = driver.actions({bridge: true}); 45 | 46 | let drawAction = builder 47 | .move({x:100, y:-100, origin: Origin.POINTER}) 48 | .press() 49 | .move({x:100, y:-100, origin: Origin.POINTER}) 50 | .release(); 51 | drawAction.perform(); 52 | await(driver.sleep(1000)); 53 | 54 | let screenshot_after_drawing = pngFromBase64(await driver.takeScreenshot()); 55 | expect(pixelDiffPNGs(screenshot_before_drawing, screenshot_after_drawing)).toBeGreaterThanOrEqual(pixelThreshold); 56 | 57 | await By2.nativeName('Close').click(); 58 | await(driver.sleep(1000)); 59 | }); 60 | 61 | test('Clear clears line', async () => { 62 | await driver.wait(until.elementLocated(By2.nativeName('- Example 1 -'))); 63 | 64 | // Set to specific size, so that drawing is expectable. 65 | await driver.manage().window().setRect({x:0,y:0,width:500,height:500}); 66 | await By2.nativeName('- Example 1 -').click(); 67 | await(driver.sleep(1000)); 68 | 69 | let screenshot_before_drawing = pngFromBase64(await driver.takeScreenshot()); 70 | 71 | // Have to use bridge, since WinAppDriver doesn't support mouse movement directly. 72 | let builder = driver.actions({bridge: true}); 73 | 74 | let drawAction = builder 75 | .move({x:0, y:50, origin: Origin.POINTER}) 76 | .press() 77 | .move({x:100, y:100, origin: Origin.POINTER}) 78 | .release(); 79 | drawAction.perform(); 80 | await(driver.sleep(1000)); 81 | 82 | let screenshot_after_drawing = pngFromBase64(await driver.takeScreenshot()); 83 | expect(pixelDiffPNGs(screenshot_before_drawing, screenshot_after_drawing)).toBeGreaterThanOrEqual(pixelThreshold); 84 | 85 | await By2.nativeName('Clear').click(); 86 | await(driver.sleep(1000)); 87 | 88 | let screenshot_after_clearing = pngFromBase64(await driver.takeScreenshot()); 89 | expect(pixelDiffPNGs(screenshot_before_drawing, screenshot_after_clearing)).toBeLessThanOrEqual(pixelThreshold); 90 | 91 | await By2.nativeName('Close').click(); 92 | await(driver.sleep(1000)); 93 | }); 94 | 95 | test('Undo line works', async () => { 96 | await driver.wait(until.elementLocated(By2.nativeName('- Example 1 -'))); 97 | 98 | // Set to specific size, so that drawing is expectable. 99 | await driver.manage().window().setRect({x:0,y:0,width:500,height:500}); 100 | await By2.nativeName('- Example 1 -').click(); 101 | await(driver.sleep(1000)); 102 | 103 | let screenshot_before_drawing = pngFromBase64(await driver.takeScreenshot()); 104 | 105 | // Have to use bridge, since WinAppDriver doesn't support mouse movement directly. 106 | let builder = driver.actions({bridge: true}); 107 | 108 | let drawAction = builder 109 | .move({x:-50, y:0, origin: Origin.POINTER}) 110 | .press() 111 | .move({x:50, y:50, origin: Origin.POINTER}) 112 | .release(); 113 | drawAction.perform(); 114 | await(driver.sleep(1000)); 115 | 116 | let screenshot_after_drawing_first_line = pngFromBase64(await driver.takeScreenshot()); 117 | expect(pixelDiffPNGs(screenshot_before_drawing, screenshot_after_drawing_first_line)).toBeGreaterThanOrEqual(pixelThreshold); 118 | 119 | drawAction.perform(); 120 | await(driver.sleep(1000)); 121 | 122 | let screenshot_after_drawing_second_line = pngFromBase64(await driver.takeScreenshot()); 123 | 124 | expect(pixelDiffPNGs(screenshot_before_drawing, screenshot_after_drawing_second_line)).toBeGreaterThanOrEqual(pixelThreshold); 125 | expect(pixelDiffPNGs(screenshot_after_drawing_first_line, screenshot_after_drawing_second_line)).toBeGreaterThanOrEqual(pixelThreshold); 126 | 127 | await By2.nativeName('Undo').click(); 128 | await(driver.sleep(1000)); 129 | 130 | let screenshot_after_first_undo = pngFromBase64(await driver.takeScreenshot()); 131 | expect(pixelDiffPNGs(screenshot_after_first_undo, screenshot_after_drawing_first_line)).toBeLessThanOrEqual(pixelThreshold); 132 | 133 | await By2.nativeName('Undo').click(); 134 | await(driver.sleep(1000)); 135 | 136 | let screenshot_after_second_undo = pngFromBase64(await driver.takeScreenshot()); 137 | expect(pixelDiffPNGs(screenshot_after_second_undo, screenshot_before_drawing)).toBeLessThanOrEqual(pixelThreshold); 138 | 139 | await By2.nativeName('Close').click(); 140 | await(driver.sleep(1000)); 141 | }); 142 | 143 | }) 144 | -------------------------------------------------------------------------------- /example/android/app/_BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets") 12 | 13 | lib_deps = [] 14 | 15 | create_aar_targets(glob(["libs/*.aar"])) 16 | 17 | create_jar_targets(glob(["libs/*.jar"])) 18 | 19 | android_library( 20 | name = "all-libs", 21 | exported_deps = lib_deps, 22 | ) 23 | 24 | android_library( 25 | name = "app-code", 26 | srcs = glob([ 27 | "src/main/java/**/*.java", 28 | ]), 29 | deps = [ 30 | ":all-libs", 31 | ":build_config", 32 | ":res", 33 | ], 34 | ) 35 | 36 | android_build_config( 37 | name = "build_config", 38 | package = "com.example", 39 | ) 40 | 41 | android_resource( 42 | name = "res", 43 | package = "com.example", 44 | res = "src/main/res", 45 | ) 46 | 47 | android_binary( 48 | name = "app", 49 | keystore = "//android/keystores:debug", 50 | manifest = "src/main/AndroidManifest.xml", 51 | package_type = "debug", 52 | deps = [ 53 | ":app-code", 54 | ], 55 | ) 56 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | 3 | import com.android.build.OutputFile 4 | 5 | /** 6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets 7 | * and bundleReleaseJsAndAssets). 8 | * These basically call `react-native bundle` with the correct arguments during the Android build 9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the 10 | * bundle directly from the development server. Below you can see all the possible configurations 11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the 12 | * `apply from: "../../node_modules/react-native/react.gradle"` line. 13 | * 14 | * project.ext.react = [ 15 | * // the name of the generated asset file containing your JS bundle 16 | * bundleAssetName: "index.android.bundle", 17 | * 18 | * // the entry file for bundle generation. If none specified and 19 | * // "index.android.js" exists, it will be used. Otherwise "index.js" is 20 | * // default. Can be overridden with ENTRY_FILE environment variable. 21 | * entryFile: "index.android.js", 22 | * 23 | * // https://reactnative.dev/docs/performance#enable-the-ram-format 24 | * bundleCommand: "ram-bundle", 25 | * 26 | * // whether to bundle JS and assets in debug mode 27 | * bundleInDebug: false, 28 | * 29 | * // whether to bundle JS and assets in release mode 30 | * bundleInRelease: true, 31 | * 32 | * // whether to bundle JS and assets in another build variant (if configured). 33 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants 34 | * // The configuration property can be in the following formats 35 | * // 'bundleIn${productFlavor}${buildType}' 36 | * // 'bundleIn${buildType}' 37 | * // bundleInFreeDebug: true, 38 | * // bundleInPaidRelease: true, 39 | * // bundleInBeta: true, 40 | * 41 | * // whether to disable dev mode in custom build variants (by default only disabled in release) 42 | * // for example: to disable dev mode in the staging build type (if configured) 43 | * devDisabledInStaging: true, 44 | * // The configuration property can be in the following formats 45 | * // 'devDisabledIn${productFlavor}${buildType}' 46 | * // 'devDisabledIn${buildType}' 47 | * 48 | * // the root of your project, i.e. where "package.json" lives 49 | * root: "../../", 50 | * 51 | * // where to put the JS bundle asset in debug mode 52 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", 53 | * 54 | * // where to put the JS bundle asset in release mode 55 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release", 56 | * 57 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 58 | * // require('./image.png')), in debug mode 59 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", 60 | * 61 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 62 | * // require('./image.png')), in release mode 63 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", 64 | * 65 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means 66 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to 67 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle 68 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ 69 | * // for example, you might want to remove it from here. 70 | * inputExcludes: ["android/**", "ios/**"], 71 | * 72 | * // override which node gets called and with what additional arguments 73 | * nodeExecutableAndArgs: ["node"], 74 | * 75 | * // supply additional arguments to the packager 76 | * extraPackagerArgs: [] 77 | * ] 78 | */ 79 | 80 | project.ext.react = [ 81 | enableHermes: false, // clean and rebuild if changing 82 | ] 83 | 84 | apply from: "../../node_modules/react-native/react.gradle" 85 | 86 | /** 87 | * Set this to true to create two separate APKs instead of one: 88 | * - An APK that only works on ARM devices 89 | * - An APK that only works on x86 devices 90 | * The advantage is the size of the APK is reduced by about 4MB. 91 | * Upload all the APKs to the Play Store and people will download 92 | * the correct one based on the CPU architecture of their device. 93 | */ 94 | def enableSeparateBuildPerCPUArchitecture = false 95 | 96 | /** 97 | * Run Proguard to shrink the Java bytecode in release builds. 98 | */ 99 | def enableProguardInReleaseBuilds = false 100 | 101 | /** 102 | * The preferred build flavor of JavaScriptCore. 103 | * 104 | * For example, to use the international variant, you can use: 105 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` 106 | * 107 | * The international variant includes ICU i18n library and necessary data 108 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that 109 | * give correct results when using with locales other than en-US. Note that 110 | * this variant is about 6MiB larger per architecture than default. 111 | */ 112 | def jscFlavor = 'org.webkit:android-jsc:+' 113 | 114 | /** 115 | * Whether to enable the Hermes VM. 116 | * 117 | * This should be set on project.ext.react and mirrored here. If it is not set 118 | * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode 119 | * and the benefits of using Hermes will therefore be sharply reduced. 120 | */ 121 | def enableHermes = project.ext.react.get("enableHermes", false); 122 | 123 | android { 124 | compileSdkVersion rootProject.ext.compileSdkVersion 125 | 126 | compileOptions { 127 | sourceCompatibility JavaVersion.VERSION_1_8 128 | targetCompatibility JavaVersion.VERSION_1_8 129 | } 130 | 131 | defaultConfig { 132 | applicationId "com.example" 133 | minSdkVersion rootProject.ext.minSdkVersion 134 | targetSdkVersion rootProject.ext.targetSdkVersion 135 | versionCode 1 136 | versionName "1.0" 137 | missingDimensionStrategy 'react-native-camera', 'general' 138 | } 139 | splits { 140 | abi { 141 | reset() 142 | enable enableSeparateBuildPerCPUArchitecture 143 | universalApk false // If true, also generate a universal APK 144 | include "armeabi-v7a", "x86", "arm64-v8a", "x86_64" 145 | } 146 | } 147 | signingConfigs { 148 | debug { 149 | storeFile file('debug.keystore') 150 | storePassword 'android' 151 | keyAlias 'androiddebugkey' 152 | keyPassword 'android' 153 | } 154 | } 155 | buildTypes { 156 | debug { 157 | signingConfig signingConfigs.debug 158 | } 159 | release { 160 | // Caution! In production, you need to generate your own keystore file. 161 | // see https://reactnative.dev/docs/signed-apk-android. 162 | signingConfig signingConfigs.debug 163 | minifyEnabled enableProguardInReleaseBuilds 164 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 165 | } 166 | } 167 | 168 | // applicationVariants are e.g. debug, release 169 | applicationVariants.all { variant -> 170 | variant.outputs.each { output -> 171 | // For each separate APK per architecture, set a unique version code as described here: 172 | // https://developer.android.com/studio/build/configure-apk-splits.html 173 | def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] 174 | def abi = output.getFilter(OutputFile.ABI) 175 | if (abi != null) { // null for the universal-debug, universal-release variants 176 | output.versionCodeOverride = 177 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode 178 | } 179 | 180 | } 181 | } 182 | } 183 | 184 | dependencies { 185 | implementation fileTree(dir: "libs", include: ["*.jar"]) 186 | //noinspection GradleDynamicVersion 187 | implementation "com.facebook.react:react-native:+" // From node_modules 188 | 189 | implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" 190 | 191 | debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") { 192 | exclude group:'com.facebook.fbjni' 193 | } 194 | 195 | debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { 196 | exclude group:'com.facebook.flipper' 197 | exclude group:'com.squareup.okhttp3', module:'okhttp' 198 | } 199 | 200 | debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") { 201 | exclude group:'com.facebook.flipper' 202 | } 203 | 204 | if (enableHermes) { 205 | def hermesPath = "../../node_modules/hermes-engine/android/"; 206 | debugImplementation files(hermesPath + "hermes-debug.aar") 207 | releaseImplementation files(hermesPath + "hermes-release.aar") 208 | } else { 209 | implementation jscFlavor 210 | } 211 | } 212 | 213 | // Run this once to be able to run the application with BUCK 214 | // puts all compile dependencies into folder libs for BUCK to use 215 | task copyDownloadableDepsToLibs(type: Copy) { 216 | from configurations.compile 217 | into 'libs' 218 | } 219 | 220 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) 221 | -------------------------------------------------------------------------------- /example/android/app/build_defs.bzl: -------------------------------------------------------------------------------- 1 | """Helper definitions to glob .aar and .jar targets""" 2 | 3 | def create_aar_targets(aarfiles): 4 | for aarfile in aarfiles: 5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")] 6 | lib_deps.append(":" + name) 7 | android_prebuilt_aar( 8 | name = name, 9 | aar = aarfile, 10 | ) 11 | 12 | def create_jar_targets(jarfiles): 13 | for jarfile in jarfiles: 14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")] 15 | lib_deps.append(":" + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | -------------------------------------------------------------------------------- /example/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/android/app/debug.keystore -------------------------------------------------------------------------------- /example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/app/src/debug/java/com/example/ReactNativeFlipper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | *

This source code is licensed under the MIT license found in the LICENSE file in the root 5 | * directory of this source tree. 6 | */ 7 | package com.example; 8 | 9 | import android.content.Context; 10 | import com.facebook.flipper.android.AndroidFlipperClient; 11 | import com.facebook.flipper.android.utils.FlipperUtils; 12 | import com.facebook.flipper.core.FlipperClient; 13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; 14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; 15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin; 16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping; 17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; 18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; 19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; 20 | import com.facebook.flipper.plugins.react.ReactFlipperPlugin; 21 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; 22 | import com.facebook.react.ReactInstanceManager; 23 | import com.facebook.react.bridge.ReactContext; 24 | import com.facebook.react.modules.network.NetworkingModule; 25 | import okhttp3.OkHttpClient; 26 | 27 | public class ReactNativeFlipper { 28 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 29 | if (FlipperUtils.shouldEnableFlipper(context)) { 30 | final FlipperClient client = AndroidFlipperClient.getInstance(context); 31 | 32 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); 33 | client.addPlugin(new ReactFlipperPlugin()); 34 | client.addPlugin(new DatabasesFlipperPlugin(context)); 35 | client.addPlugin(new SharedPreferencesFlipperPlugin(context)); 36 | client.addPlugin(CrashReporterPlugin.getInstance()); 37 | 38 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); 39 | NetworkingModule.setCustomClientBuilder( 40 | new NetworkingModule.CustomClientBuilder() { 41 | @Override 42 | public void apply(OkHttpClient.Builder builder) { 43 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); 44 | } 45 | }); 46 | client.addPlugin(networkFlipperPlugin); 47 | client.start(); 48 | 49 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized 50 | // Hence we run if after all native modules have been initialized 51 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); 52 | if (reactContext == null) { 53 | reactInstanceManager.addReactInstanceEventListener( 54 | new ReactInstanceManager.ReactInstanceEventListener() { 55 | @Override 56 | public void onReactContextInitialized(ReactContext reactContext) { 57 | reactInstanceManager.removeReactInstanceEventListener(this); 58 | reactContext.runOnNativeModulesQueueThread( 59 | new Runnable() { 60 | @Override 61 | public void run() { 62 | client.addPlugin(new FrescoFlipperPlugin()); 63 | } 64 | }); 65 | } 66 | }); 67 | } else { 68 | client.addPlugin(new FrescoFlipperPlugin()); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 16 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /example/android/app/src/main/assets/fonts/IndieFlower.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/android/app/src/main/assets/fonts/IndieFlower.ttf -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. This is used to schedule 9 | * rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "example"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import com.facebook.react.PackageList; 6 | import com.facebook.react.ReactApplication; 7 | import com.facebook.react.ReactInstanceManager; 8 | import com.facebook.react.ReactNativeHost; 9 | import com.facebook.react.ReactPackage; 10 | import com.facebook.soloader.SoLoader; 11 | import java.lang.reflect.InvocationTargetException; 12 | import java.util.List; 13 | 14 | public class MainApplication extends Application implements ReactApplication { 15 | 16 | private final ReactNativeHost mReactNativeHost = 17 | new ReactNativeHost(this) { 18 | @Override 19 | public boolean getUseDeveloperSupport() { 20 | return BuildConfig.DEBUG; 21 | } 22 | 23 | @Override 24 | protected List getPackages() { 25 | @SuppressWarnings("UnnecessaryLocalVariable") 26 | List packages = new PackageList(this).getPackages(); 27 | // Packages that cannot be autolinked yet can be added manually here, for example: 28 | // packages.add(new MyReactNativePackage()); 29 | return packages; 30 | } 31 | 32 | @Override 33 | protected String getJSMainModuleName() { 34 | return "index"; 35 | } 36 | }; 37 | 38 | @Override 39 | public ReactNativeHost getReactNativeHost() { 40 | return mReactNativeHost; 41 | } 42 | 43 | @Override 44 | public void onCreate() { 45 | super.onCreate(); 46 | SoLoader.init(this, /* native exopackage */ false); 47 | initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 48 | } 49 | 50 | /** 51 | * Loads Flipper in React Native templates. Call this in the onCreate method with something like 52 | * initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 53 | * 54 | * @param context 55 | * @param reactInstanceManager 56 | */ 57 | private static void initializeFlipper( 58 | Context context, ReactInstanceManager reactInstanceManager) { 59 | if (BuildConfig.DEBUG) { 60 | try { 61 | /* 62 | We use reflection here to pick up the class that initializes Flipper, 63 | since Flipper library is not available in release mode 64 | */ 65 | Class aClass = Class.forName("com.example.ReactNativeFlipper"); 66 | aClass 67 | .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class) 68 | .invoke(null, context, reactInstanceManager); 69 | } catch (ClassNotFoundException e) { 70 | e.printStackTrace(); 71 | } catch (NoSuchMethodException e) { 72 | e.printStackTrace(); 73 | } catch (IllegalAccessException e) { 74 | e.printStackTrace(); 75 | } catch (InvocationTargetException e) { 76 | e.printStackTrace(); 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-xxhdpi/bulb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/android/app/src/main/res/drawable-xxhdpi/bulb.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-xxhdpi/whale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/android/app/src/main/res/drawable-xxhdpi/whale.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | example 3 | 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "29.0.2" 6 | minSdkVersion = 16 7 | compileSdkVersion = 29 8 | targetSdkVersion = 29 9 | } 10 | repositories { 11 | google() 12 | jcenter() 13 | } 14 | dependencies { 15 | classpath("com.android.tools.build:gradle:3.5.3") 16 | // NOTE: Do not place your application dependencies here; they belong 17 | // in the individual module build.gradle files 18 | } 19 | } 20 | 21 | allprojects { 22 | repositories { 23 | mavenLocal() 24 | maven { 25 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 26 | url("$rootDir/../node_modules/react-native/android") 27 | } 28 | maven { 29 | // Android JSC is installed from npm 30 | url("$rootDir/../node_modules/jsc-android/dist") 31 | } 32 | 33 | google() 34 | jcenter() 35 | maven { url 'https://www.jitpack.io' } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | # AndroidX package structure to make it clearer which packages are bundled with the 21 | # Android operating system, and which are packaged with your app's APK 22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 23 | android.useAndroidX=true 24 | # Automatically convert third-party libraries to use AndroidX 25 | android.enableJetifier=true 26 | 27 | # Version of flipper SDK to use with React Native 28 | FLIPPER_VERSION=0.37.0 29 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=`expr $i + 1` 158 | done 159 | case $i in 160 | 0) set -- ;; 161 | 1) set -- "$args0" ;; 162 | 2) set -- "$args0" "$args1" ;; 163 | 3) set -- "$args0" "$args1" "$args2" ;; 164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=`save "$@"` 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | exec "$JAVACMD" "$@" 184 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto init 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto init 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | @rem Execute Gradle 88 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 89 | 90 | :end 91 | @rem End local scope for the variables with windows NT shell 92 | if "%ERRORLEVEL%"=="0" goto mainEnd 93 | 94 | :fail 95 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 96 | rem the _cmd.exe /c_ return code! 97 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 98 | exit /b 1 99 | 100 | :mainEnd 101 | if "%OS%"=="Windows_NT" endlocal 102 | 103 | :omega 104 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'example' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "displayName": "example" 4 | } -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import {AppRegistry} from 'react-native'; 6 | import App from './App'; 7 | import {name as appName} from './app.json'; 8 | 9 | AppRegistry.registerComponent(appName, () => App); 10 | -------------------------------------------------------------------------------- /example/ios/Images/bulb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/ios/Images/bulb.png -------------------------------------------------------------------------------- /example/ios/Images/whale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/ios/Images/whale.png -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../node_modules/react-native/scripts/react_native_pods' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | platform :ios, '10.0' 5 | 6 | target 'example' do 7 | config = use_native_modules! 8 | 9 | use_react_native!(:path => config["reactNativePath"]) 10 | 11 | target 'exampleTests' do 12 | inherit! :complete 13 | # Pods for testing 14 | end 15 | 16 | # Enables Flipper. 17 | # 18 | # Note that if you have use_frameworks! enabled, Flipper will not work and 19 | # you should disable these next few lines. 20 | use_flipper! 21 | post_install do |installer| 22 | flipper_post_install(installer) 23 | end 24 | end 25 | 26 | target 'example-tvOS' do 27 | # Pods for example-tvOS 28 | 29 | target 'example-tvOSTests' do 30 | inherit! :search_paths 31 | # Pods for testing 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /example/ios/example-tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSExceptionDomains 28 | 29 | localhost 30 | 31 | NSExceptionAllowsInsecureHTTPLoads 32 | 33 | 34 | 35 | 36 | NSLocationWhenInUseUsageDescription 37 | 38 | UILaunchStoryboardName 39 | LaunchScreen 40 | UIRequiredDeviceCapabilities 41 | 42 | armv7 43 | 44 | UISupportedInterfaceOrientations 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | UIViewControllerBasedStatusBarAppearance 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /example/ios/example-tvOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/ios/example.xcodeproj/xcshareddata/xcschemes/example-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /example/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /example/ios/example.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/example/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : UIResponder 5 | 6 | @property (nonatomic, strong) UIWindow *window; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /example/ios/example/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | #import 5 | #import 6 | 7 | #ifdef FB_SONARKIT_ENABLED 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | 15 | static void InitializeFlipper(UIApplication *application) { 16 | FlipperClient *client = [FlipperClient sharedClient]; 17 | SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; 18 | [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]]; 19 | [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; 20 | [client addPlugin:[FlipperKitReactPlugin new]]; 21 | [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; 22 | [client start]; 23 | } 24 | #endif 25 | 26 | @implementation AppDelegate 27 | 28 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 29 | { 30 | #ifdef FB_SONARKIT_ENABLED 31 | InitializeFlipper(application); 32 | #endif 33 | 34 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; 35 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge 36 | moduleName:@"example" 37 | initialProperties:nil]; 38 | 39 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 40 | 41 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 42 | UIViewController *rootViewController = [UIViewController new]; 43 | rootViewController.view = rootView; 44 | self.window.rootViewController = rootViewController; 45 | [self.window makeKeyAndVisible]; 46 | return YES; 47 | } 48 | 49 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 50 | { 51 | #if DEBUG 52 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 53 | #else 54 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 55 | #endif 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/ios/example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | NSExceptionDomains 32 | 33 | localhost 34 | 35 | NSExceptionAllowsInsecureHTTPLoads 36 | 37 | 38 | 39 | 40 | NSLocationWhenInUseUsageDescription 41 | 42 | NSPhotoLibraryAddUsageDescription 43 | Use Photo Library 44 | NSCameraUsageDescription 45 | Your message to user when the camera is accessed for the first time 46 | 47 | NSPhotoLibraryUsageDescription 48 | Your message to user when the photo library is accessed for the first time 49 | 50 | NSMicrophoneUsageDescription 51 | Your message to user when the microphone is accessed for the first time 52 | UILaunchStoryboardName 53 | LaunchScreen 54 | UIRequiredDeviceCapabilities 55 | 56 | armv7 57 | 58 | UISupportedInterfaceOrientations 59 | 60 | UIInterfaceOrientationPortrait 61 | UIInterfaceOrientationLandscapeLeft 62 | UIInterfaceOrientationLandscapeRight 63 | 64 | UIViewControllerBasedStatusBarAppearance 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /example/ios/example/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /example/ios/example/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/ios/exampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/ios/exampleTests/exampleTests.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import 5 | #import 6 | 7 | #define TIMEOUT_SECONDS 600 8 | #define TEXT_TO_LOOK_FOR @"Welcome to React" 9 | 10 | @interface exampleTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation exampleTests 15 | 16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 17 | { 18 | if (test(view)) { 19 | return YES; 20 | } 21 | for (UIView *subview in [view subviews]) { 22 | if ([self findSubviewInView:subview matching:test]) { 23 | return YES; 24 | } 25 | } 26 | return NO; 27 | } 28 | 29 | - (void)testRendersWelcomeScreen 30 | { 31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 33 | BOOL foundElement = NO; 34 | 35 | __block NSString *redboxError = nil; 36 | #ifdef DEBUG 37 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 38 | if (level >= RCTLogLevelError) { 39 | redboxError = message; 40 | } 41 | }); 42 | #endif 43 | 44 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 45 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 46 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 47 | 48 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 49 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 50 | return YES; 51 | } 52 | return NO; 53 | }]; 54 | } 55 | 56 | #ifdef DEBUG 57 | RCTSetLogFunction(RCTDefaultLogFunction); 58 | #endif 59 | 60 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 61 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 62 | } 63 | 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /example/jest-windows/driver.setup.js: -------------------------------------------------------------------------------- 1 | import { windowsAppDriverCapabilities } from 'selenium-appium' 2 | 3 | switch (platform) { 4 | case "windows": 5 | const webViewWindowsAppId = 'RNSketchCanvasExample_tzd3rs38zxb7w!App'; 6 | module.exports = { 7 | capabilites: windowsAppDriverCapabilities(webViewWindowsAppId) 8 | } 9 | break; 10 | default: 11 | throw "Unknown platform: " + platform; 12 | } 13 | -------------------------------------------------------------------------------- /example/jest-windows/jest.setup.js: -------------------------------------------------------------------------------- 1 | platform = "windows" -------------------------------------------------------------------------------- /example/jest.windows.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testMatch: ['**/__windows_tests__/**/*.[jt]s?(x)'], 3 | setupFiles: ['./jest-windows/jest.setup.js'] 4 | } 5 | -------------------------------------------------------------------------------- /example/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "allowSyntheticDefaultImports": true 5 | }, 6 | "exclude": [ 7 | "node_modules" 8 | ] 9 | } -------------------------------------------------------------------------------- /example/metro.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Metro configuration for React Native 3 | * https://github.com/facebook/react-native 4 | * 5 | * @format 6 | */ 7 | const path = require('path'); 8 | const blacklist = require('metro-config/src/defaults/blacklist'); 9 | 10 | module.exports = { 11 | resolver: { 12 | blacklistRE: blacklist([ 13 | // This stops "react-native run-windows" from causing the metro server to crash if its already running 14 | new RegExp( 15 | `${path.resolve(__dirname, 'windows').replace(/[/\\]/g, '/')}.*`, 16 | ), 17 | // This prevents "react-native run-windows" from hitting: EBUSY: resource busy or locked, open msbuild.ProjectImports.zip 18 | /.*\.ProjectImports\.zip/, 19 | ]), 20 | }, 21 | transformer: { 22 | getTransformOptions: async () => ({ 23 | transform: { 24 | experimentalImportSupport: false, 25 | inlineRequires: false, 26 | }, 27 | }), 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "android": "react-native run-android", 7 | "appium": "appium", 8 | "ios": "react-native run-ios", 9 | "start": "react-native start", 10 | "test": "jest", 11 | "test:windows": "yarn jest --config=./jest.windows.config.js --runInBand", 12 | "lint": "eslint .", 13 | "windows": "react-native run-windows" 14 | }, 15 | "dependencies": { 16 | "@wwimmo/react-native-sketch-canvas": "file:..", 17 | "react": "16.13.1", 18 | "react-native": "0.63.2", 19 | "react-native-camera": "^3.40.0", 20 | "react-native-windows": "^0.63.0-0" 21 | }, 22 | "devDependencies": { 23 | "@babel/core": "^7.8.4", 24 | "@babel/runtime": "^7.8.4", 25 | "@react-native-community/eslint-config": "^1.1.0", 26 | "appium": "1.18.3", 27 | "babel-jest": "^25.1.0", 28 | "eslint": "^6.5.1", 29 | "jest": "^25.1.0", 30 | "metro-react-native-babel-preset": "^0.59.0", 31 | "pixelmatch": "^5.2.1", 32 | "pngjs": "6.0.0", 33 | "react-test-renderer": "16.13.1", 34 | "selenium-appium": "0.0.15", 35 | "selenium-webdriver": "4.0.0-alpha.7" 36 | }, 37 | "jest": { 38 | "preset": "react-native" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /example/windows/.gitignore: -------------------------------------------------------------------------------- 1 | *AppPackages* 2 | *BundleArtifacts* 3 | 4 | #OS junk files 5 | [Tt]humbs.db 6 | *.DS_Store 7 | 8 | #Visual Studio files 9 | *.[Oo]bj 10 | *.user 11 | *.aps 12 | *.pch 13 | *.vspscc 14 | *.vssscc 15 | *_i.c 16 | *_p.c 17 | *.ncb 18 | *.suo 19 | *.tlb 20 | *.tlh 21 | *.bak 22 | *.[Cc]ache 23 | *.ilk 24 | *.log 25 | *.lib 26 | *.sbr 27 | *.sdf 28 | *.opensdf 29 | *.opendb 30 | *.unsuccessfulbuild 31 | ipch/ 32 | [Oo]bj/ 33 | [Bb]in 34 | [Dd]ebug*/ 35 | [Rr]elease*/ 36 | Ankh.NoLoad 37 | 38 | # Visual C++ cache files 39 | ipch/ 40 | *.aps 41 | *.ncb 42 | *.opendb 43 | *.opensdf 44 | *.sdf 45 | *.cachefile 46 | *.VC.db 47 | *.VC.VC.opendb 48 | 49 | #MonoDevelop 50 | *.pidb 51 | *.userprefs 52 | 53 | #Tooling 54 | _ReSharper*/ 55 | *.resharper 56 | [Tt]est[Rr]esult* 57 | *.sass-cache 58 | 59 | #Project files 60 | [Bb]uild/ 61 | 62 | #Subversion files 63 | .svn 64 | 65 | # Office Temp Files 66 | ~$* 67 | 68 | # vim Temp Files 69 | *~ 70 | 71 | #NuGet 72 | packages/ 73 | *.nupkg 74 | 75 | #ncrunch 76 | *ncrunch* 77 | *crunch*.local.xml 78 | 79 | # visual studio database projects 80 | *.dbmdl 81 | 82 | #Test files 83 | *.testsettings 84 | 85 | #Other files 86 | *.DotSettings 87 | .vs/ 88 | *project.lock.json 89 | 90 | #Files generated by the VS build 91 | **/Generated Files/** 92 | 93 | -------------------------------------------------------------------------------- /example/windows/example/.gitignore: -------------------------------------------------------------------------------- 1 | /Bundle 2 | -------------------------------------------------------------------------------- /example/windows/example/App.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "App.h" 4 | 5 | #include "AutolinkedNativeModules.g.h" 6 | #include "ReactPackageProvider.h" 7 | 8 | 9 | using namespace winrt::example; 10 | using namespace winrt::example::implementation; 11 | using namespace winrt; 12 | using namespace Windows::UI::Xaml; 13 | using namespace Windows::UI::Xaml::Controls; 14 | using namespace Windows::UI::Xaml::Navigation; 15 | using namespace Windows::ApplicationModel; 16 | 17 | ///

18 | /// Initializes the singleton application object. This is the first line of 19 | /// authored code executed, and as such is the logical equivalent of main() or 20 | /// WinMain(). 21 | /// 22 | App::App() noexcept 23 | { 24 | #if BUNDLE 25 | JavaScriptBundleFile(L"index.windows"); 26 | InstanceSettings().UseWebDebugger(false); 27 | InstanceSettings().UseFastRefresh(false); 28 | #else 29 | JavaScriptMainModuleName(L"index"); 30 | InstanceSettings().UseWebDebugger(true); 31 | InstanceSettings().UseFastRefresh(true); 32 | #endif 33 | 34 | #if _DEBUG 35 | InstanceSettings().UseDeveloperSupport(true); 36 | #else 37 | InstanceSettings().UseDeveloperSupport(false); 38 | #endif 39 | 40 | RegisterAutolinkedNativeModulePackages(PackageProviders()); // Includes any autolinked modules 41 | 42 | PackageProviders().Append(make()); // Includes all modules in this project 43 | 44 | InitializeComponent(); 45 | } 46 | 47 | /// 48 | /// Invoked when the application is launched normally by the end user. Other entry points 49 | /// will be used such as when the application is launched to open a specific file. 50 | /// 51 | /// Details about the launch request and process. 52 | void App::OnLaunched(activation::LaunchActivatedEventArgs const& e) 53 | { 54 | super::OnLaunched(e); 55 | 56 | Frame rootFrame = Window::Current().Content().as(); 57 | rootFrame.Navigate(xaml_typename(), box_value(e.Arguments())); 58 | } 59 | 60 | /// 61 | /// Invoked when application execution is being suspended. Application state is saved 62 | /// without knowing whether the application will be terminated or resumed with the contents 63 | /// of memory still intact. 64 | /// 65 | /// The source of the suspend request. 66 | /// Details about the suspend request. 67 | void App::OnSuspending([[maybe_unused]] IInspectable const& sender, [[maybe_unused]] SuspendingEventArgs const& e) 68 | { 69 | // Save application state and stop any background activity 70 | } 71 | 72 | /// 73 | /// Invoked when Navigation to a certain page fails 74 | /// 75 | /// The Frame which failed navigation 76 | /// Details about the navigation failure 77 | void App::OnNavigationFailed(IInspectable const&, NavigationFailedEventArgs const& e) 78 | { 79 | throw hresult_error(E_FAIL, hstring(L"Failed to load Page ") + e.SourcePageType().Name); 80 | } 81 | -------------------------------------------------------------------------------- /example/windows/example/App.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "App.xaml.g.h" 4 | 5 | namespace activation = winrt::Windows::ApplicationModel::Activation; 6 | 7 | namespace winrt::example::implementation 8 | { 9 | struct App : AppT 10 | { 11 | App() noexcept; 12 | void OnLaunched(activation::LaunchActivatedEventArgs const&); 13 | void OnSuspending(IInspectable const&, Windows::ApplicationModel::SuspendingEventArgs const&); 14 | void OnNavigationFailed(IInspectable const&, Windows::UI::Xaml::Navigation::NavigationFailedEventArgs const&); 15 | private: 16 | using super = AppT; 17 | }; 18 | } // namespace winrt::example::implementation 19 | 20 | 21 | -------------------------------------------------------------------------------- /example/windows/example/App.idl: -------------------------------------------------------------------------------- 1 | namespace example 2 | { 3 | } 4 | -------------------------------------------------------------------------------- /example/windows/example/App.xaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/windows/example/Assets/LockScreenLogo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/windows/example/Assets/LockScreenLogo.scale-200.png -------------------------------------------------------------------------------- /example/windows/example/Assets/SplashScreen.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/windows/example/Assets/SplashScreen.scale-200.png -------------------------------------------------------------------------------- /example/windows/example/Assets/Square150x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/windows/example/Assets/Square150x150Logo.scale-200.png -------------------------------------------------------------------------------- /example/windows/example/Assets/Square44x44Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/windows/example/Assets/Square44x44Logo.scale-200.png -------------------------------------------------------------------------------- /example/windows/example/Assets/Square44x44Logo.targetsize-24_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/windows/example/Assets/Square44x44Logo.targetsize-24_altform-unplated.png -------------------------------------------------------------------------------- /example/windows/example/Assets/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/windows/example/Assets/StoreLogo.png -------------------------------------------------------------------------------- /example/windows/example/Assets/Wide310x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/windows/example/Assets/Wide310x150Logo.scale-200.png -------------------------------------------------------------------------------- /example/windows/example/AutolinkedNativeModules.g.cpp: -------------------------------------------------------------------------------- 1 | // AutolinkedNativeModules.g.cpp contents generated by "react-native autolink-windows" 2 | 3 | #include "pch.h" 4 | #include "AutolinkedNativeModules.g.h" 5 | 6 | namespace winrt::Microsoft::ReactNative 7 | { 8 | 9 | void RegisterAutolinkedNativeModulePackages(winrt::Windows::Foundation::Collections::IVector const& packageProviders) 10 | { 11 | UNREFERENCED_PARAMETER(packageProviders); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /example/windows/example/AutolinkedNativeModules.g.h: -------------------------------------------------------------------------------- 1 | // AutolinkedNativeModules.g.h contents generated by "react-native autolink-windows" 2 | 3 | #pragma once 4 | 5 | namespace winrt::Microsoft::ReactNative 6 | { 7 | 8 | void RegisterAutolinkedNativeModulePackages(winrt::Windows::Foundation::Collections::IVector const& packageProviders); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /example/windows/example/AutolinkedNativeModules.g.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /example/windows/example/MainPage.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "MainPage.h" 3 | #if __has_include("MainPage.g.cpp") 4 | #include "MainPage.g.cpp" 5 | #endif 6 | 7 | #include "App.h" 8 | 9 | 10 | 11 | using namespace winrt; 12 | using namespace Windows::UI::Xaml; 13 | 14 | namespace winrt::example::implementation 15 | { 16 | MainPage::MainPage() 17 | { 18 | InitializeComponent(); 19 | auto app = Application::Current().as(); 20 | ReactRootView().ReactNativeHost(app->Host()); 21 | } 22 | } 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/windows/example/MainPage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "MainPage.g.h" 3 | #include 4 | 5 | 6 | namespace winrt::example::implementation 7 | { 8 | struct MainPage : MainPageT 9 | { 10 | MainPage(); 11 | }; 12 | } 13 | 14 | namespace winrt::example::factory_implementation 15 | { 16 | struct MainPage : MainPageT 17 | { 18 | }; 19 | } 20 | 21 | 22 | -------------------------------------------------------------------------------- /example/windows/example/MainPage.idl: -------------------------------------------------------------------------------- 1 | namespace example 2 | { 3 | [default_interface] 4 | runtimeclass MainPage : Windows.UI.Xaml.Controls.Page 5 | { 6 | MainPage(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /example/windows/example/MainPage.xaml: -------------------------------------------------------------------------------- 1 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /example/windows/example/Package.appxmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 8 | 9 | 13 | 14 | 15 | 16 | 17 | example 18 | Jaime 19 | Assets\StoreLogo.png 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 35 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /example/windows/example/PropertySheet.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /example/windows/example/ReactPackageProvider.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "ReactPackageProvider.h" 3 | #include "NativeModules.h" 4 | 5 | 6 | using namespace winrt::Microsoft::ReactNative; 7 | 8 | namespace winrt::example::implementation 9 | { 10 | 11 | void ReactPackageProvider::CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept 12 | { 13 | AddAttributedModules(packageBuilder); 14 | } 15 | 16 | } // namespace winrt::example::implementation 17 | 18 | 19 | -------------------------------------------------------------------------------- /example/windows/example/ReactPackageProvider.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "winrt/Microsoft.ReactNative.h" 4 | 5 | 6 | namespace winrt::example::implementation 7 | { 8 | struct ReactPackageProvider : winrt::implements 9 | { 10 | public: // IReactPackageProvider 11 | void CreatePackage(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) noexcept; 12 | }; 13 | } // namespace winrt::example::implementation 14 | 15 | 16 | -------------------------------------------------------------------------------- /example/windows/example/bulb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/windows/example/bulb.png -------------------------------------------------------------------------------- /example/windows/example/example.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | true 6 | true 7 | true 8 | {bbf8c8ac-a17b-4eed-ba00-c3aedccd6f74} 9 | example 10 | example 11 | en-US 12 | 16.0 13 | true 14 | Windows Store 15 | 10.0 16 | 10.0.18362.0 17 | 10.0.16299.0 18 | example_TemporaryKey.pfx 19 | FE631A250ECAB1AA1F9EFF207E4A076F3C22D958 20 | password 21 | 22 | 23 | 24 | $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), 'node_modules\react-native-windows\package.json'))\node_modules\react-native-windows\ 25 | 26 | 27 | 28 | Debug 29 | ARM 30 | 31 | 32 | Debug 33 | ARM64 34 | 35 | 36 | Debug 37 | Win32 38 | 39 | 40 | Debug 41 | x64 42 | 43 | 44 | Release 45 | ARM 46 | 47 | 48 | Release 49 | ARM64 50 | 51 | 52 | Release 53 | Win32 54 | 55 | 56 | Release 57 | x64 58 | 59 | 60 | 61 | Application 62 | Unicode 63 | 64 | 65 | true 66 | true 67 | 68 | 69 | false 70 | true 71 | false 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | Use 90 | pch.h 91 | $(IntDir)pch.pch 92 | Level4 93 | %(AdditionalOptions) /bigobj 94 | 4453;28204 95 | 96 | 97 | 98 | 99 | _DEBUG;%(PreprocessorDefinitions) 100 | 101 | 102 | 103 | 104 | NDEBUG;%(PreprocessorDefinitions) 105 | 106 | 107 | 108 | 109 | MainPage.xaml 110 | Code 111 | 112 | 113 | 114 | 115 | 116 | App.xaml 117 | 118 | 119 | 120 | 121 | Designer 122 | 123 | 124 | 125 | 126 | Designer 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | MainPage.xaml 143 | Code 144 | 145 | 146 | 147 | 148 | Create 149 | 150 | 151 | App.xaml 152 | 153 | 154 | 155 | 156 | 157 | App.xaml 158 | 159 | 160 | MainPage.xaml 161 | Code 162 | 163 | 164 | 165 | 166 | 167 | 168 | false 169 | 170 | 171 | 172 | 173 | Designer 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | This project references targets in your node_modules\react-native-windows folder. The missing file is {0}. 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 197 | 198 | 199 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /example/windows/example/example.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Assets 25 | 26 | 27 | Assets 28 | 29 | 30 | Assets 31 | 32 | 33 | Assets 34 | 35 | 36 | Assets 37 | 38 | 39 | Assets 40 | 41 | 42 | Assets 43 | 44 | 45 | Assets 46 | 47 | 48 | Assets 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | {e48dc53e-40b1-40cb-970a-f89935452892} 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | Assets 72 | 73 | 74 | -------------------------------------------------------------------------------- /example/windows/example/example_TemporaryKey.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/windows/example/example_TemporaryKey.pfx -------------------------------------------------------------------------------- /example/windows/example/fonts/IndieFlower.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/windows/example/fonts/IndieFlower.ttf -------------------------------------------------------------------------------- /example/windows/example/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /example/windows/example/pch.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | -------------------------------------------------------------------------------- /example/windows/example/pch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define NOMINMAX 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | -------------------------------------------------------------------------------- /example/windows/example/whale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwimmo/react-native-sketch-canvas/15ad2d6ae423e08df85dea70fd438e9020caef73/example/windows/example/whale.png -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { StyleProp, ViewStyle, ViewProps } from "react-native"; 3 | 4 | type ImageType = "png" | "jpg"; 5 | 6 | type Size = { 7 | width: number; 8 | height: number; 9 | }; 10 | 11 | type PathData = { 12 | id: number; 13 | color: string; 14 | width: number; 15 | data: string[]; 16 | }; 17 | 18 | type Path = { 19 | drawer?: string; 20 | size: Size; 21 | path: PathData; 22 | }; 23 | 24 | export interface SavePreference { 25 | folder: string; 26 | filename: string; 27 | transparent: boolean; 28 | imageType: ImageType; 29 | includeImage?: boolean; 30 | cropToImageSize?: boolean; 31 | } 32 | 33 | export interface LocalSourceImage { 34 | path: string; 35 | directory?: string; 36 | mode?: "AspectFill" | "AspectFit" | "ScaleToFill"; 37 | } 38 | 39 | export interface SketchCanvasProps { 40 | style?: StyleProp; 41 | strokeColor?: string; 42 | strokeWidth?: number; 43 | user?: string; 44 | 45 | localSourceImage?: LocalSourceImage; 46 | touchEnabled?: boolean; 47 | 48 | /** 49 | * Android Only: Provide a Dialog Title for the Image Saving PermissionDialog. Defaults to empty string if not set 50 | */ 51 | permissionDialogTitle?: string; 52 | 53 | /** 54 | * Android Only: Provide a Dialog Message for the Image Saving PermissionDialog. Defaults to empty string if not set 55 | */ 56 | permissionDialogMessage?: string; 57 | 58 | onStrokeStart?: () => void; 59 | onStrokeChanged?: () => void; 60 | onStrokeEnd?: (path: Path) => void; 61 | onSketchSaved?: (result: boolean, path: string) => void; 62 | onPathsChange?: (pathsCount: number) => void; 63 | } 64 | 65 | export class SketchCanvas extends React.Component { 66 | clear(): void; 67 | undo(): number; 68 | addPath(data: Path): void; 69 | deletePath(id: number): void; 70 | 71 | /** 72 | * @param imageType "png" or "jpg" 73 | * @param includeImage Set to `true` to include the image loaded from `LocalSourceImage` 74 | * @param cropToImageSize Set to `true` to crop output image to the image loaded from `LocalSourceImage` 75 | */ 76 | save( 77 | imageType: ImageType, 78 | transparent: boolean, 79 | folder: string, 80 | filename: string, 81 | includeImage: boolean, 82 | cropToImageSize: boolean 83 | ): void; 84 | getPaths(): Path[]; 85 | 86 | /** 87 | * @param imageType "png" or "jpg" 88 | * @param includeImage Set to `true` to include the image loaded from `LocalSourceImage` 89 | * @param cropToImageSize Set to `true` to crop output image to the image loaded from `LocalSourceImage` 90 | */ 91 | getBase64( 92 | imageType: ImageType, 93 | transparent: boolean, 94 | includeImage: boolean, 95 | cropToImageSize: boolean, 96 | callback: (error: any, result?: string) => void 97 | ): void; 98 | 99 | static MAIN_BUNDLE: string; 100 | static DOCUMENT: string; 101 | static LIBRARY: string; 102 | static CACHES: string; 103 | static TEMPORARY: string; 104 | static ROAMING: string; 105 | static LOCAL: string; 106 | } 107 | 108 | export interface RNSketchCanvasProps { 109 | containerStyle?: StyleProp; 110 | canvasStyle?: StyleProp; 111 | onStrokeStart?: () => void; 112 | onStrokeChanged?: () => void; 113 | onStrokeEnd?: (path: Path) => void; 114 | onClosePressed?: () => void; 115 | onUndoPressed?: (id: number) => void; 116 | onClearPressed?: () => void; 117 | onPathsChange?: (pathsCount: number) => void; 118 | user?: string; 119 | 120 | closeComponent?: JSX.Element; 121 | eraseComponent?: JSX.Element; 122 | undoComponent?: JSX.Element; 123 | clearComponent?: JSX.Element; 124 | saveComponent?: JSX.Element; 125 | strokeComponent?: (color: string) => JSX.Element; 126 | strokeSelectedComponent?: (color: string, index: number, changed: boolean) => JSX.Element; 127 | strokeWidthComponent?: (width: number) => JSX.Element; 128 | 129 | strokeColors?: { color: string }[]; 130 | defaultStrokeIndex?: number; 131 | defaultStrokeWidth?: number; 132 | 133 | minStrokeWidth?: number; 134 | maxStrokeWidth?: number; 135 | strokeWidthStep?: number; 136 | 137 | /** 138 | * @param imageType "png" or "jpg" 139 | * @param includeImage default true 140 | * @param cropToImageSize default false 141 | */ 142 | savePreference?: () => { 143 | folder: string; 144 | filename: string; 145 | transparent: boolean; 146 | imageType: ImageType; 147 | includeImage?: boolean; 148 | cropToImageSize?: boolean; 149 | }; 150 | onSketchSaved?: (result: boolean, path: string) => void; 151 | 152 | /** 153 | * { 154 | * path: string, 155 | * directory: string, 156 | * mode: 'AspectFill' | 'AspectFit' | 'ScaleToFill' 157 | * } 158 | */ 159 | localSourceImage?: LocalSourceImage; 160 | } 161 | 162 | export default class RNSketchCanvas extends React.Component { 163 | clear(): void; 164 | undo(): number; 165 | addPath(data: Path): void; 166 | deletePath(id: number): void; 167 | save(): void; 168 | nextStrokeWidth(): void; 169 | 170 | static MAIN_BUNDLE: string; 171 | static DOCUMENT: string; 172 | static LIBRARY: string; 173 | static CACHES: string; 174 | static TEMPORARY: string; 175 | static ROAMING: string; 176 | static LOCAL: string; 177 | } 178 | -------------------------------------------------------------------------------- /ios/RNSketchCanvas/RNSketchCanvas/RNSketchCanvas.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class RCTEventDispatcher; 4 | 5 | @interface RNSketchCanvas : UIView 6 | 7 | @property (nonatomic, copy) RCTBubblingEventBlock onChange; 8 | 9 | - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher; 10 | 11 | - (BOOL)openSketchFile:(NSString *)filename directory:(NSString*) directory contentMode:(NSString*)mode; 12 | - (void)newPath:(int) pathId strokeColor:(UIColor*) strokeColor strokeWidth:(int) strokeWidth; 13 | - (void)addPath:(int) pathId strokeColor:(UIColor*) strokeColor strokeWidth:(int) strokeWidth points:(NSArray*) points; 14 | - (void)deletePath:(int) pathId; 15 | - (void)addPointX: (float)x Y: (float)y; 16 | - (void)endPath; 17 | - (void)clear; 18 | - (void)saveImageOfType:(NSString*) type folder:(NSString*) folder filename:(NSString*) filename withTransparentBackground:(BOOL) transparent includeImage:(BOOL)includeImage cropToImageSize:(BOOL)cropToImageSize; 19 | - (NSString*) transferToBase64OfType: (NSString*) type withTransparentBackground: (BOOL) transparent includeImage:(BOOL)includeImage cropToImageSize:(BOOL)cropToImageSize; 20 | 21 | @end -------------------------------------------------------------------------------- /ios/RNSketchCanvas/RNSketchCanvas/RNSketchCanvasManager.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "RNSketchCanvas.h" 3 | 4 | @interface RNSketchCanvasManager : RCTViewManager 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /ios/RNSketchCanvas/RNSketchCanvas/RNSketchCanvasManager.m: -------------------------------------------------------------------------------- 1 | #import "RNSketchCanvasManager.h" 2 | #import "RNSketchCanvas.h" 3 | #import 4 | #import 5 | #import 6 | #import 7 | 8 | @implementation RNSketchCanvasManager 9 | 10 | RCT_EXPORT_MODULE() 11 | 12 | + (BOOL)requiresMainQueueSetup 13 | { 14 | return YES; 15 | } 16 | 17 | -(NSDictionary *)constantsToExport { 18 | return @{ 19 | @"MainBundlePath": [[NSBundle mainBundle] bundlePath], 20 | @"NSDocumentDirectory": [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject], 21 | @"NSLibraryDirectory": [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) firstObject], 22 | @"NSCachesDirectory": [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject], 23 | }; 24 | } 25 | 26 | #pragma mark - Events 27 | 28 | RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock); 29 | 30 | #pragma mark - Props 31 | RCT_CUSTOM_VIEW_PROPERTY(localSourceImage, NSDictionary, RNSketchCanvas) 32 | { 33 | RNSketchCanvas *currentView = !view ? defaultView : view; 34 | NSDictionary *dict = [RCTConvert NSDictionary:json]; 35 | dispatch_async(dispatch_get_main_queue(), ^{ 36 | [currentView openSketchFile:dict[@"filename"] 37 | directory:[dict[@"directory"] isEqual: [NSNull null]] ? @"" : dict[@"directory"] 38 | contentMode:[dict[@"mode"] isEqual: [NSNull null]] ? @"" : dict[@"mode"]]; 39 | }); 40 | } 41 | 42 | #pragma mark - Lifecycle 43 | 44 | - (UIView *)view 45 | { 46 | return [[RNSketchCanvas alloc] initWithEventDispatcher: self.bridge.eventDispatcher]; 47 | } 48 | 49 | #pragma mark - Exported methods 50 | 51 | 52 | RCT_EXPORT_METHOD(save:(nonnull NSNumber *)reactTag type:(NSString*) type folder:(NSString*) folder filename:(NSString*) filename withTransparentBackground:(BOOL) transparent includeImage:(BOOL)includeImage cropToImageSize:(BOOL)cropToImageSize) 53 | { 54 | [self runCanvas:reactTag block:^(RNSketchCanvas *canvas) { 55 | [canvas saveImageOfType:type folder:folder filename:filename withTransparentBackground:transparent includeImage:includeImage cropToImageSize:cropToImageSize]; 56 | }]; 57 | } 58 | 59 | RCT_EXPORT_METHOD(addPoint:(nonnull NSNumber *)reactTag x: (float)x y: (float)y) 60 | { 61 | [self runCanvas:reactTag block:^(RNSketchCanvas *canvas) { 62 | [canvas addPointX:x Y:y]; 63 | }]; 64 | } 65 | 66 | RCT_EXPORT_METHOD(addPath:(nonnull NSNumber *)reactTag pathId: (int) pathId strokeColor: (UIColor*) strokeColor strokeWidth: (int) strokeWidth points: (NSArray*) points) 67 | { 68 | NSMutableArray *cgPoints = [[NSMutableArray alloc] initWithCapacity: points.count]; 69 | for (NSString *coor in points) { 70 | @autoreleasepool { 71 | NSArray *coorInNumber = [coor componentsSeparatedByString: @","]; 72 | [cgPoints addObject: [NSValue valueWithCGPoint: CGPointMake([coorInNumber[0] floatValue], [coorInNumber[1] floatValue])]]; 73 | [self runCanvas:reactTag block:^(RNSketchCanvas *canvas) { 74 | [canvas addPath: pathId strokeColor: strokeColor strokeWidth: strokeWidth points: cgPoints]; 75 | }]; 76 | } 77 | } 78 | } 79 | 80 | RCT_EXPORT_METHOD(newPath:(nonnull NSNumber *)reactTag pathId: (int) pathId strokeColor: (UIColor*) strokeColor strokeWidth: (int) strokeWidth) 81 | { 82 | [self runCanvas:reactTag block:^(RNSketchCanvas *canvas) { 83 | [canvas newPath: pathId strokeColor: strokeColor strokeWidth: strokeWidth]; 84 | }]; 85 | } 86 | 87 | RCT_EXPORT_METHOD(deletePath:(nonnull NSNumber *)reactTag pathId: (int) pathId) 88 | { 89 | [self runCanvas:reactTag block:^(RNSketchCanvas *canvas) { 90 | [canvas deletePath: pathId]; 91 | }]; 92 | } 93 | 94 | RCT_EXPORT_METHOD(endPath:(nonnull NSNumber *)reactTag) 95 | { 96 | [self runCanvas:reactTag block:^(RNSketchCanvas *canvas) { 97 | [canvas endPath]; 98 | }]; 99 | } 100 | 101 | RCT_EXPORT_METHOD(clear:(nonnull NSNumber *)reactTag) 102 | { 103 | [self runCanvas:reactTag block:^(RNSketchCanvas *canvas) { 104 | [canvas clear]; 105 | }]; 106 | } 107 | 108 | RCT_EXPORT_METHOD(transferToBase64:(nonnull NSNumber *)reactTag type: (NSString*) type withTransparentBackground:(BOOL) transparent includeImage:(BOOL)includeImage cropToImageSize:(BOOL)cropToImageSize :(RCTResponseSenderBlock)callback) 109 | { 110 | [self runCanvas:reactTag block:^(RNSketchCanvas *canvas) { 111 | callback(@[[NSNull null], [canvas transferToBase64OfType: type withTransparentBackground: transparent includeImage:includeImage cropToImageSize:cropToImageSize]]); 112 | }]; 113 | } 114 | 115 | #pragma mark - Utils 116 | 117 | - (void)runCanvas:(nonnull NSNumber *)reactTag block:(void (^)(RNSketchCanvas *canvas))block { 118 | [self.bridge.uiManager addUIBlock: 119 | ^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry){ 120 | 121 | RNSketchCanvas *view = viewRegistry[reactTag]; 122 | if (!view || ![view isKindOfClass:[RNSketchCanvas class]]) { 123 | RCTLogError(@"Cannot find RNSketchCanvas with tag #%@", reactTag); 124 | return; 125 | } 126 | 127 | block(view); 128 | }]; 129 | } 130 | 131 | @end 132 | -------------------------------------------------------------------------------- /ios/RNSketchCanvas/RNSketchCanvas/RNSketchData.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNSketchCanvasData.h 3 | // RNSketchCanvas 4 | // 5 | // Created by terry on 03/08/2017. 6 | // Copyright © 2017 Terry. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface RNSketchData : NSObject 13 | 14 | @property (nonatomic, readonly) int pathId; 15 | @property (nonatomic, readonly) CGFloat strokeWidth; 16 | @property (nonatomic, readonly) UIColor* strokeColor; 17 | @property (nonatomic, readonly) NSArray *points; 18 | @property (nonatomic, readonly) BOOL isTranslucent; 19 | 20 | - (instancetype)initWithId:(int) pathId strokeColor:(UIColor*) strokeColor strokeWidth:(int) strokeWidth points: (NSArray*) points; 21 | - (instancetype)initWithId:(int) pathId strokeColor:(UIColor*) strokeColor strokeWidth:(int) strokeWidth; 22 | 23 | - (CGRect)addPoint:(CGPoint) point; 24 | 25 | - (void)drawLastPointInContext:(CGContextRef)context; 26 | - (void)drawInContext:(CGContextRef)context; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /ios/RNSketchCanvas/RNSketchCanvas/RNSketchData.m: -------------------------------------------------------------------------------- 1 | // 2 | // RNSketchCanvasData.m 3 | // RNSketchCanvas 4 | // 5 | // Created by terry on 03/08/2017. 6 | // Copyright © 2017 Terry. All rights reserved. 7 | // 8 | 9 | #import "RNSketchData.h" 10 | #import "Utility.h" 11 | 12 | @interface RNSketchData () 13 | 14 | @property (nonatomic, readwrite) int pathId; 15 | @property (nonatomic, readwrite) CGFloat strokeWidth; 16 | @property (nonatomic, readwrite) UIColor* strokeColor; 17 | @property (nonatomic, readwrite) NSMutableArray *points; 18 | 19 | @end 20 | 21 | @implementation RNSketchData 22 | { 23 | CGRect _dirty; 24 | UIBezierPath *_path; 25 | } 26 | 27 | - (instancetype)initWithId:(int) pathId strokeColor:(UIColor*) strokeColor strokeWidth:(int) strokeWidth { 28 | self = [super init]; 29 | if (self) { 30 | _pathId = pathId; 31 | _strokeColor = strokeColor; 32 | _strokeWidth = strokeWidth; 33 | _points = [NSMutableArray new]; 34 | _isTranslucent = CGColorGetComponents(strokeColor.CGColor)[3] != 1.0 && 35 | ![Utility isSameColor:strokeColor color:[UIColor clearColor]]; 36 | _path = _isTranslucent ? [UIBezierPath new] : nil; 37 | _dirty = CGRectZero; 38 | } 39 | return self; 40 | } 41 | 42 | - (instancetype)initWithId:(int) pathId strokeColor:(UIColor*) strokeColor strokeWidth:(int) strokeWidth points: (NSArray*) points { 43 | self = [super init]; 44 | if (self) { 45 | _pathId = pathId; 46 | _strokeColor = strokeColor; 47 | _strokeWidth = strokeWidth; 48 | _points = [points mutableCopy]; 49 | _isTranslucent = CGColorGetComponents(strokeColor.CGColor)[3] != 1.0 && 50 | ![Utility isSameColor:strokeColor color:[UIColor clearColor]]; 51 | _path = _isTranslucent ? [self evaluatePath] : nil; 52 | _dirty = CGRectZero; 53 | } 54 | return self; 55 | } 56 | 57 | - (CGRect)addPoint:(CGPoint) point { 58 | [_points addObject: [NSValue valueWithCGPoint: point]]; 59 | 60 | CGRect updateRect; 61 | 62 | NSUInteger pointsCount = _points.count; 63 | 64 | if (_isTranslucent) { 65 | if (pointsCount >= 3) { 66 | [Utility addPointToPath: _path 67 | toPoint: point 68 | tertiaryPoint: [_points[_points.count - 3] CGPointValue] 69 | previousPoint:[_points[_points.count - 2] CGPointValue]]; 70 | } else if (pointsCount >= 2) { 71 | [Utility addPointToPath: _path 72 | toPoint: point 73 | tertiaryPoint: [_points[0] CGPointValue] 74 | previousPoint: [_points[0] CGPointValue]]; 75 | } else { 76 | [Utility addPointToPath: _path toPoint: point tertiaryPoint: point previousPoint: point]; 77 | } 78 | 79 | CGFloat x = point.x, y = point.y; 80 | _dirty = CGRectIsEmpty(_dirty) ? CGRectMake(x, y, 1, 1) : CGRectUnion(_dirty, CGRectMake(x, y, 1, 1)); 81 | updateRect = CGRectInset(_dirty, -_strokeWidth * 2, -_strokeWidth * 2); 82 | } else { 83 | if (pointsCount >= 3) { 84 | CGPoint a = _points[pointsCount - 3].CGPointValue; 85 | CGPoint b = _points[pointsCount - 2].CGPointValue; 86 | CGPoint c = point; 87 | CGPoint prevMid = midPoint(a, b); 88 | CGPoint currentMid = midPoint(b, c); 89 | 90 | updateRect = CGRectMake(prevMid.x, prevMid.y, 0, 0); 91 | updateRect = CGRectUnion(updateRect, CGRectMake(b.x, b.y, 0, 0)); 92 | updateRect = CGRectUnion(updateRect, CGRectMake(currentMid.x, currentMid.y, 0, 0)); 93 | } else if (pointsCount >= 2) { 94 | CGPoint a = _points[pointsCount - 2].CGPointValue; 95 | CGPoint b = point; 96 | CGPoint mid = midPoint(a, b); 97 | 98 | updateRect = CGRectMake(a.x, a.y, 0, 0); 99 | updateRect = CGRectUnion(updateRect, CGRectMake(mid.x, mid.y, 0, 0)); 100 | } else { 101 | updateRect = CGRectMake(point.x, point.y, 0, 0); 102 | } 103 | 104 | updateRect = CGRectInset(updateRect, -_strokeWidth * 2, -_strokeWidth * 2); 105 | } 106 | 107 | return updateRect; 108 | } 109 | 110 | - (void)drawLastPointInContext:(CGContextRef)context { 111 | NSUInteger pointsCount = _points.count; 112 | if (pointsCount < 1) { 113 | return; 114 | }; 115 | 116 | [self drawInContext:context pointIndex:pointsCount - 1]; 117 | } 118 | - (void)drawInContext:(CGContextRef)context { 119 | if (_isTranslucent) { 120 | CGContextSetLineWidth(context, _strokeWidth); 121 | CGContextSetLineCap(context, kCGLineCapRound); 122 | CGContextSetLineJoin(context, kCGLineJoinRound); 123 | CGContextSetStrokeColorWithColor(context, [_strokeColor CGColor]); 124 | CGContextSetBlendMode(context, kCGBlendModeNormal); 125 | 126 | CGContextAddPath(context, _path.CGPath); 127 | CGContextStrokePath(context); 128 | } else { 129 | NSUInteger pointsCount = _points.count; 130 | for (NSUInteger i = 0; i < pointsCount; i++) { 131 | @autoreleasepool { 132 | [self drawInContext:context pointIndex:i]; 133 | } 134 | } 135 | } 136 | } 137 | 138 | - (void)drawInContext:(CGContextRef)context pointIndex:(NSUInteger)pointIndex { 139 | NSUInteger pointsCount = _points.count; 140 | if (pointIndex >= pointsCount) { 141 | return; 142 | }; 143 | 144 | BOOL isErase = [Utility isSameColor:_strokeColor color:[UIColor clearColor]]; 145 | 146 | CGContextSetStrokeColorWithColor(context, _strokeColor.CGColor); 147 | CGContextSetLineWidth(context, _strokeWidth); 148 | CGContextSetLineCap(context, kCGLineCapRound); 149 | CGContextSetLineJoin(context, kCGLineJoinRound); 150 | CGContextSetBlendMode(context, isErase ? kCGBlendModeClear : kCGBlendModeNormal); 151 | CGContextBeginPath(context); 152 | 153 | if (pointsCount >= 3 && pointIndex >= 2) { 154 | CGPoint a = _points[pointIndex - 2].CGPointValue; 155 | CGPoint b = _points[pointIndex - 1].CGPointValue; 156 | CGPoint c = _points[pointIndex].CGPointValue; 157 | CGPoint prevMid = midPoint(a, b); 158 | CGPoint currentMid = midPoint(b, c); 159 | 160 | // Draw a curve 161 | CGContextMoveToPoint(context, prevMid.x, prevMid.y); 162 | CGContextAddQuadCurveToPoint(context, b.x, b.y, currentMid.x, currentMid.y); 163 | } else if (pointsCount >= 2 && pointIndex >= 1) { 164 | CGPoint a = _points[pointIndex - 1].CGPointValue; 165 | CGPoint b = _points[pointIndex].CGPointValue; 166 | CGPoint mid = midPoint(a, b); 167 | 168 | // Draw a line to the middle of points a and b 169 | // This is so the next draw which uses a curve looks correct and continues from there 170 | CGContextMoveToPoint(context, a.x, a.y); 171 | CGContextAddLineToPoint(context, mid.x, mid.y); 172 | } else if (pointsCount >= 1) { 173 | CGPoint a = _points[pointIndex].CGPointValue; 174 | 175 | // Draw a single point 176 | CGContextMoveToPoint(context, a.x, a.y); 177 | CGContextAddLineToPoint(context, a.x, a.y); 178 | } 179 | 180 | CGContextStrokePath(context); 181 | } 182 | 183 | // Translucent 184 | - (UIBezierPath*) evaluatePath { 185 | NSUInteger pointsCount = _points.count; 186 | UIBezierPath *path = [UIBezierPath new]; 187 | 188 | for(NSUInteger pointIndex=0; pointIndex= 3 && pointIndex >= 2) { 190 | CGPoint a = _points[pointIndex - 2].CGPointValue; 191 | CGPoint b = _points[pointIndex - 1].CGPointValue; 192 | CGPoint c = _points[pointIndex].CGPointValue; 193 | CGPoint prevMid = midPoint(a, b); 194 | CGPoint currentMid = midPoint(b, c); 195 | 196 | // Draw a curve 197 | [path moveToPoint:prevMid]; 198 | [path addQuadCurveToPoint:currentMid controlPoint:b]; 199 | } else if (pointsCount >= 2 && pointIndex >= 1) { 200 | CGPoint a = _points[pointIndex - 1].CGPointValue; 201 | CGPoint b = _points[pointIndex].CGPointValue; 202 | CGPoint mid = midPoint(a, b); 203 | 204 | // Draw a line to the middle of points a and b 205 | // This is so the next draw which uses a curve looks correct and continues from there 206 | [path moveToPoint:a]; 207 | [path addLineToPoint:mid]; 208 | } else if (pointsCount >= 1) { 209 | CGPoint a = _points[pointIndex].CGPointValue; 210 | 211 | // Draw a single point 212 | [path moveToPoint:a]; 213 | [path addLineToPoint:a]; 214 | } 215 | } 216 | return path; 217 | } 218 | 219 | 220 | @end 221 | -------------------------------------------------------------------------------- /ios/RNSketchCanvas/RNSketchCanvas/Utility.h: -------------------------------------------------------------------------------- 1 | // 2 | // Utility.h 3 | // RNSketchCanvas 4 | // 5 | // Created by TERRY on 2018/5/8. 6 | // Copyright © 2018年 Terry. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "RNSketchData.h" 12 | 13 | CGPoint midPoint (CGPoint p1, CGPoint p2); 14 | 15 | @interface Utility : NSObject 16 | 17 | + (void)addPointToPath: (UIBezierPath*)path 18 | toPoint: (CGPoint)point 19 | tertiaryPoint: (CGPoint)tPoint 20 | previousPoint: (CGPoint) pPoint; 21 | + (BOOL)isSameColor:(UIColor *)color1 color:(UIColor *)color2; 22 | + (CGRect)fillImageWithSize:(CGSize)imgSize toSize:(CGSize)targetSize contentMode:(NSString*)mode; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /ios/RNSketchCanvas/RNSketchCanvas/Utility.m: -------------------------------------------------------------------------------- 1 | // 2 | // Utility.m 3 | // RNSketchCanvas 4 | // 5 | // Created by TERRY on 2018/5/8. 6 | // Copyright © 2018年 Terry. All rights reserved. 7 | // 8 | 9 | #import "Utility.h" 10 | 11 | CGPoint midPoint (CGPoint p1, CGPoint p2) { 12 | return CGPointMake((p1.x + p2.x) * 0.5, (p1.y + p2.y) * 0.5); 13 | } 14 | 15 | @implementation Utility 16 | 17 | + (void)addPointToPath: (UIBezierPath*)path 18 | toPoint: (CGPoint)point 19 | tertiaryPoint: (CGPoint)tPoint 20 | previousPoint: (CGPoint) pPoint { 21 | CGPoint mid1 = midPoint(pPoint, tPoint); 22 | CGPoint mid2 = midPoint(point, pPoint); 23 | [path moveToPoint: mid1]; 24 | [path addQuadCurveToPoint: mid2 controlPoint: pPoint]; 25 | } 26 | 27 | + (BOOL)isSameColor:(UIColor *)color1 color:(UIColor *)color2 { 28 | CGFloat red1, green1, blue1, alpha1; 29 | [color1 getRed:&red1 green:&green1 blue:&blue1 alpha:&alpha1]; 30 | CGFloat red2, green2, blue2, alpha2; 31 | [color2 getRed:&red2 green:&green2 blue:&blue2 alpha:&alpha2]; 32 | if (red1 == red2 && green1 == green2 && blue1 == blue2 && alpha1 == alpha2) { 33 | return true; 34 | } 35 | return false; 36 | } 37 | 38 | + (CGRect)fillImageWithSize:(CGSize)imgSize toSize:(CGSize)targetSize contentMode:(NSString*)mode { 39 | CGFloat imageAspectRatio = imgSize.width / imgSize.height; 40 | CGFloat targetAspectRatio = targetSize.width / targetSize.height; 41 | switch ([@[@"AspectFill", @"AspectFit", @"ScaleToFill"] indexOfObject: mode]) { 42 | case 0: { 43 | CGFloat scaleFactor = targetAspectRatio < imageAspectRatio ? targetSize.height / imgSize.height : targetSize.width / imgSize.width; 44 | CGFloat w = imgSize.width * scaleFactor, h = imgSize.height * scaleFactor; 45 | return CGRectMake((targetSize.width - w) / 2, (targetSize.height - h) / 2, w, h); 46 | } 47 | case 1: 48 | case NSNotFound: 49 | default: { 50 | CGFloat scaleFactor = targetAspectRatio > imageAspectRatio ? targetSize.height / imgSize.height : targetSize.width / imgSize.width; 51 | CGFloat w = imgSize.width * scaleFactor, h = imgSize.height * scaleFactor; 52 | return CGRectMake((targetSize.width - w) / 2, (targetSize.height - h) / 2, w, h); 53 | } 54 | case 2: { 55 | return CGRectMake(0, 0, targetSize.width, targetSize.height); 56 | } 57 | } 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@wwimmo/react-native-sketch-canvas", 3 | "repository": { 4 | "type": "git", 5 | "url": "https://github.com/wwimmo/react-native-sketch-canvas" 6 | }, 7 | "version": "0.8.6", 8 | "description": "react-native-sketch-canvas allows you to draw / sketch on both iOS and Android devices and sync the drawing data between users. Of course you can save as image.", 9 | "author": "Terry Lin", 10 | "main": "index.js", 11 | "keywords": [ 12 | "react-native", 13 | "react-native-sketch", 14 | "react-native-svg", 15 | "react", 16 | "native", 17 | "sketch", 18 | "svg", 19 | "draw" 20 | ], 21 | "nativePackage": true, 22 | "license": "MIT", 23 | "devDependencies": { 24 | "react": "16.13.1", 25 | "react-native": "0.63.2", 26 | "react-native-windows": "^0.63.7", 27 | "prettier": "^2.7.1" 28 | }, 29 | "dependencies": { 30 | "deprecated-react-native-prop-types": "^2.3.0" 31 | } 32 | } -------------------------------------------------------------------------------- /src/handlePermissions.js: -------------------------------------------------------------------------------- 1 | import { PermissionsAndroid, Platform } from "react-native"; 2 | 3 | export const requestPermissions = async (permissionDialogTitle, permissionDialogMessage) => { 4 | if (Platform.OS === "android") { 5 | const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE, { 6 | title: permissionDialogTitle, 7 | message: permissionDialogMessage 8 | }); 9 | 10 | // On devices before SDK version 23, the permissions are automatically granted if they appear in the manifest, 11 | // so check and request should always be true. 12 | // https://github.com/facebook/react-native-website/blob/master/docs/permissionsandroid.md 13 | const isAuthorized = Platform.Version >= 23 ? granted === PermissionsAndroid.RESULTS.GRANTED : granted === true; 14 | return isAuthorized; 15 | } 16 | return true; 17 | }; 18 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | *AppPackages* 2 | *BundleArtifacts* 3 | 4 | #OS junk files 5 | [Tt]humbs.db 6 | *.DS_Store 7 | 8 | #Visual Studio files 9 | *.[Oo]bj 10 | *.user 11 | *.aps 12 | *.pch 13 | *.vspscc 14 | *.vssscc 15 | *_i.c 16 | *_p.c 17 | *.ncb 18 | *.suo 19 | *.tlb 20 | *.tlh 21 | *.bak 22 | *.[Cc]ache 23 | *.ilk 24 | *.log 25 | *.lib 26 | *.sbr 27 | *.sdf 28 | *.opensdf 29 | *.opendb 30 | *.unsuccessfulbuild 31 | ipch/ 32 | [Oo]bj/ 33 | [Bb]in 34 | [Dd]ebug*/ 35 | [Rr]elease*/ 36 | Ankh.NoLoad 37 | 38 | # Visual C++ cache files 39 | ipch/ 40 | *.aps 41 | *.ncb 42 | *.opendb 43 | *.opensdf 44 | *.sdf 45 | *.cachefile 46 | *.VC.db 47 | *.VC.VC.opendb 48 | 49 | #MonoDevelop 50 | *.pidb 51 | *.userprefs 52 | 53 | #Tooling 54 | _ReSharper*/ 55 | *.resharper 56 | [Tt]est[Rr]esult* 57 | *.sass-cache 58 | 59 | #Project files 60 | [Bb]uild/ 61 | 62 | #Subversion files 63 | .svn 64 | 65 | # Office Temp Files 66 | ~$* 67 | 68 | # vim Temp Files 69 | *~ 70 | 71 | #NuGet 72 | packages/ 73 | *.nupkg 74 | 75 | #ncrunch 76 | *ncrunch* 77 | *crunch*.local.xml 78 | 79 | # visual studio database projects 80 | *.dbmdl 81 | 82 | #Test files 83 | *.testsettings 84 | 85 | #Other files 86 | *.DotSettings 87 | .vs/ 88 | *project.lock.json 89 | 90 | #Files generated by the VS build 91 | **/Generated Files/** 92 | 93 | -------------------------------------------------------------------------------- /windows/README.md: -------------------------------------------------------------------------------- 1 | # [module name here] Windows Implementation 2 | 3 | ## Module Installation 4 | 5 | You can either use autolinking on react-native-windows 0.63 and later or manually link the module on earlier releases. 6 | 7 | ### Automatic install with autolinking on RNW >= 0.63 8 | 9 | RNSketchCanvas supports autolinking. Just install the library: `yarn @wwimmo/react-native-sketch-canvas` 10 | 11 | ### Manual installation on RNW >= 0.62 12 | 13 | 1. Install with `yarn @wwimmo/react-native-sketch-canvas` 14 | 2. Open your solution in Visual Studio 2019 (eg. `windows\yourapp.sln`) 15 | 3. Right-click Solution icon in Solution Explorer > Add > Existing Project... 16 | 4. Add `node_modules\@wwimmo\react-native-sketch-canvas\windows\RNSketchCanvas\RNSketchCanvas.vcxproj` 17 | 5. Right-click main application project > Add > Reference... 18 | 6. Select `RNSketchCanvas` in Solution Projects 19 | 7. In app `pch.h` add `#include "winrt/RNSketchCanvas.h"` 20 | 8. In `App.cpp` add `PackageProviders().Append(winrt::RNSketchCanvas::ReactPackageProvider());` before `InitializeComponent();` 21 | 22 | ### Using save on Windows 23 | 24 | On Windows, `save()` will save the resulting image in the TemporaryDirectory folder of the application. 25 | 26 | ## Module development 27 | 28 | If you want to contribute to this module Windows implementation, first you must install the [Windows Development Dependencies](https://aka.ms/rnw-deps). 29 | 30 | You must temporarily install the `react-native-windows` package. Versions of `react-native-windows` and `react-native` must match, e.g. if the module uses `react-native@0.62`, install `yarn add react-native-windows@^0.62 --dev`. 31 | 32 | Now, you will be able to open corresponding `RNSketchCanvas...sln` file, e.g. `RNSketchCanvas62.sln` for `react-native-windows@0.62`. 33 | -------------------------------------------------------------------------------- /windows/RNSketchCanvas/PropertySheet.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /windows/RNSketchCanvas/RNSketchCanvas.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE 3 | DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE 4 | -------------------------------------------------------------------------------- /windows/RNSketchCanvas/RNSketchCanvas.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pch.h" 4 | #include "winrt/Microsoft.ReactNative.h" 5 | #include "NativeModules.h" 6 | #include "RNSketchCanvasView.g.h" 7 | #include "SketchData.h" 8 | 9 | namespace winrt::RNSketchCanvas::implementation 10 | { 11 | class RNSketchCanvasView : public RNSketchCanvasViewT 12 | { 13 | public: 14 | RNSketchCanvasView(Microsoft::ReactNative::IReactContext const& reactContext); 15 | 16 | void openImageFile(std::string filename, std::string directory, std::string mode); 17 | 18 | static winrt::Windows::Foundation::Collections:: 19 | IMapView 20 | NativeProps() noexcept; 21 | void UpdateProperties(winrt::Microsoft::ReactNative::IJSValueReader const& propertyMapReader) noexcept; 22 | 23 | 24 | static winrt::Microsoft::ReactNative::ConstantProviderDelegate 25 | ExportedViewConstants() noexcept; 26 | 27 | static winrt::Microsoft::ReactNative::ConstantProviderDelegate 28 | ExportedCustomBubblingEventTypeConstants() noexcept; 29 | static winrt::Microsoft::ReactNative::ConstantProviderDelegate 30 | ExportedCustomDirectEventTypeConstants() noexcept; 31 | 32 | static winrt::Windows::Foundation::Collections::IVectorView Commands() noexcept; 33 | void DispatchCommand( 34 | winrt::hstring const& commandId, 35 | winrt::Microsoft::ReactNative::IJSValueReader const& commandArgsReader) noexcept; 36 | 37 | void clear(); 38 | void newPath(int32_t id, uint32_t strokeColor, float strokeWidth); 39 | void addPoint(float x, float y); 40 | void addPath(int32_t id, uint32_t strokeColor, float strokeWidth, std::vector points); 41 | void deletePath(int32_t id); 42 | void end(); 43 | void save(std::string format, std::string folder, std::string filename, bool transparent, bool includeImage, bool cropToImageSize); 44 | 45 | IAsyncOperation getBase64(std::string format, bool transparent, bool includeImage, bool cropToImageSize); 46 | 47 | private: 48 | std::vector> mPaths; 49 | std::shared_ptr mCurrentPath = nullptr; 50 | 51 | Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl mCanvasControl; 52 | 53 | bool mNeedsFullRedraw = true; 54 | std::optional mDrawingCanvas = std::nullopt; 55 | std::optional mTranslucentDrawingCanvas = std::nullopt; 56 | 57 | std::optional mBackgroundImage; 58 | int mOriginalWidth, mOriginalHeight; 59 | std::string mContentMode; 60 | 61 | IAsyncOperation saveHelper(std::string format, std::string folder, std::string filename, bool transparent, bool includeImage, bool cropToImageSize); 62 | 63 | Microsoft::ReactNative::IReactContext m_reactContext{ nullptr }; 64 | void OnCanvasDraw(Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl const&, Microsoft::Graphics::Canvas::UI::Xaml::CanvasDrawEventArgs const&); 65 | void OnCanvasSizeChanged(const winrt::Windows::Foundation::IInspectable, Windows::UI::Xaml::SizeChangedEventArgs const&); 66 | Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl::Draw_revoker mCanvasDrawRevoker{}; 67 | Microsoft::Graphics::Canvas::UI::Xaml::CanvasControl::SizeChanged_revoker mCanvaSizeChangedRevoker{}; 68 | 69 | void onSaved(bool success, std::string path); 70 | 71 | void invalidateCanvas(bool shouldDispatchEvent); 72 | Microsoft::Graphics::Canvas::CanvasBitmap createImage(bool transparent, bool includeImage, bool cropToImageSize); 73 | }; 74 | } 75 | 76 | namespace winrt::RNSketchCanvas::factory_implementation 77 | { 78 | struct RNSketchCanvasView : RNSketchCanvasViewT {}; 79 | } 80 | -------------------------------------------------------------------------------- /windows/RNSketchCanvas/RNSketchCanvas.idl: -------------------------------------------------------------------------------- 1 | namespace RNSketchCanvas { 2 | [default_interface] 3 | runtimeclass RNSketchCanvasView : Windows.UI.Xaml.Controls.Grid { 4 | RNSketchCanvasView(Microsoft.ReactNative.IReactContext context); 5 | void UpdateProperties(Microsoft.ReactNative.IJSValueReader reader); 6 | void DispatchCommand(String commandId, Microsoft.ReactNative.IJSValueReader commandArgsReader); 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /windows/RNSketchCanvas/RNSketchCanvas.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | true 6 | true 7 | true 8 | {f96593a0-46b4-47da-a15a-7a135934ce1f} 9 | RNSketchCanvas 10 | RNSketchCanvas 11 | en-US 12 | 16.0 13 | true 14 | Windows Store 15 | 10.0 16 | 10.0.18362.0 17 | 10.0.17763.0 18 | 19 | 20 | 21 | $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), 'node_modules\react-native-windows\package.json'))\node_modules\react-native-windows\ 22 | 23 | 24 | 25 | Debug 26 | ARM 27 | 28 | 29 | Debug 30 | ARM64 31 | 32 | 33 | Debug 34 | Win32 35 | 36 | 37 | Debug 38 | x64 39 | 40 | 41 | Release 42 | ARM 43 | 44 | 45 | Release 46 | ARM64 47 | 48 | 49 | Release 50 | Win32 51 | 52 | 53 | Release 54 | x64 55 | 56 | 57 | 58 | DynamicLibrary 59 | Unicode 60 | false 61 | 62 | 63 | true 64 | true 65 | 66 | 67 | false 68 | true 69 | false 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | Use 88 | pch.h 89 | $(IntDir)pch.pch 90 | Level4 91 | %(AdditionalOptions) /bigobj 92 | 93 | /DWINRT_NO_MAKE_DETECTION %(AdditionalOptions) 94 | 28204 95 | _WINRT_DLL;%(PreprocessorDefinitions) 96 | $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) 97 | 98 | 99 | Console 100 | true 101 | RNSketchCanvas.def 102 | 103 | 104 | 105 | 106 | _DEBUG;%(PreprocessorDefinitions) 107 | ProgramDatabase 108 | 109 | 110 | 111 | 112 | NDEBUG;%(PreprocessorDefinitions) 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | ReactPackageProvider.idl 121 | 122 | 123 | RNSketchCanvas.idl 124 | 125 | 126 | 127 | 128 | 129 | 130 | Create 131 | 132 | 133 | 134 | ReactPackageProvider.idl 135 | 136 | 137 | RNSketchCanvas.idl 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | This project references targets in your node_modules\react-native-windows folder. The missing file is {0}. 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 174 | 175 | 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /windows/RNSketchCanvas/RNSketchCanvas.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | accd3aa8-1ba0-4223-9bbe-0c431709210b 6 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms 7 | 8 | 9 | {926ab91d-31b5-48c3-b9a4-e681349f27f0} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /windows/RNSketchCanvas/RNSketchCanvasModule.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "pch.h" 3 | #include "winrt/Microsoft.ReactNative.h" 4 | #include "NativeModules.h" 5 | #include "RNSketchCanvas.h" 6 | #include 7 | 8 | 9 | using namespace winrt::Microsoft::ReactNative; 10 | 11 | namespace winrt::RNSketchCanvas 12 | { 13 | REACT_MODULE(RNSketchCanvasModule, L"SketchCanvasModule"); 14 | struct RNSketchCanvasModule 15 | { 16 | const std::string Name = "SketchCanvasModule"; 17 | 18 | ReactContext reactContext = nullptr; 19 | 20 | REACT_INIT(RNSketchCanvasModule_Init); 21 | void RNSketchCanvasModule_Init(ReactContext const& context) noexcept 22 | { 23 | reactContext = context; 24 | } 25 | 26 | REACT_METHOD(transferToBase64); 27 | void transferToBase64( 28 | int tag, 29 | std::string type, 30 | bool transparent, 31 | bool includeImage, 32 | bool cropToImageSize, 33 | std::function callback 34 | ) noexcept 35 | { 36 | reactContext.UIDispatcher().Post([=]() 37 | { 38 | XamlUIService uiService = XamlUIService::FromContext(reactContext.Handle()); 39 | auto sketchCanvasInstance = uiService.ElementFromReactTag(tag).as(); 40 | IAsyncOperation asyncOp = sketchCanvasInstance->getBase64(type, transparent, includeImage, cropToImageSize); 41 | asyncOp.Completed([=](IAsyncOperation const& sender, AsyncStatus const asyncStatus) 42 | { 43 | if (asyncStatus == AsyncStatus::Error) 44 | { 45 | std::string error = "HRESULT " + std::to_string(sender.ErrorCode()) + ": " + std::system_category().message(sender.ErrorCode()); 46 | callback(error, nullptr); 47 | } else if (asyncStatus == AsyncStatus::Completed) 48 | { 49 | callback(nullptr, winrt::to_string(sender.GetResults())); 50 | } 51 | } 52 | ); 53 | }); 54 | } 55 | 56 | }; 57 | 58 | } 59 | 60 | -------------------------------------------------------------------------------- /windows/RNSketchCanvas/RNSketchCanvasViewManager.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "NativeModules.h" 3 | #include "JSValueXaml.h" 4 | #include "RNSketchCanvasViewManager.h" 5 | #include "RNSketchCanvas.h" 6 | 7 | namespace winrt 8 | { 9 | using namespace Microsoft::ReactNative; 10 | using namespace Windows::Foundation; 11 | using namespace Windows::Foundation::Collections; 12 | using namespace Windows::UI; 13 | using namespace Windows::UI::Xaml; 14 | using namespace Windows::UI::Xaml::Controls; 15 | } 16 | 17 | namespace winrt::RNSketchCanvas::implementation 18 | { 19 | // IViewManager 20 | winrt::hstring RNSketchCanvasViewManager::Name() noexcept 21 | { 22 | return L"RNSketchCanvas"; 23 | } 24 | 25 | winrt::FrameworkElement RNSketchCanvasViewManager::CreateView() noexcept 26 | { 27 | return winrt::RNSketchCanvas::RNSketchCanvasView(m_reactContext); 28 | } 29 | 30 | // IViewManagerWithReactContext 31 | winrt::IReactContext RNSketchCanvasViewManager::ReactContext() noexcept 32 | { 33 | return m_reactContext; 34 | } 35 | 36 | void RNSketchCanvasViewManager::ReactContext(IReactContext reactContext) noexcept 37 | { 38 | m_reactContext = reactContext; 39 | } 40 | 41 | // IViewManagerWithNativeProperties 42 | IMapView RNSketchCanvasViewManager::NativeProps() noexcept 43 | { 44 | return winrt::RNSketchCanvas::implementation::RNSketchCanvasView::NativeProps(); 45 | } 46 | 47 | void RNSketchCanvasViewManager::UpdateProperties( 48 | FrameworkElement const& view, 49 | IJSValueReader const& propertyMapReader) noexcept 50 | { 51 | if (auto module = view.try_as()) 52 | { 53 | module.UpdateProperties(propertyMapReader); 54 | } 55 | } 56 | winrt::Microsoft::ReactNative::ConstantProviderDelegate RNSketchCanvasViewManager::ExportedViewConstants() noexcept 57 | { 58 | return winrt::RNSketchCanvas::implementation::RNSketchCanvasView::ExportedViewConstants(); 59 | } 60 | // IViewManagerWithExportedEventTypeConstants 61 | ConstantProviderDelegate RNSketchCanvasViewManager::ExportedCustomBubblingEventTypeConstants() noexcept 62 | { 63 | return winrt::RNSketchCanvas::implementation::RNSketchCanvasView::ExportedCustomBubblingEventTypeConstants(); 64 | } 65 | 66 | ConstantProviderDelegate RNSketchCanvasViewManager::ExportedCustomDirectEventTypeConstants() noexcept 67 | { 68 | return winrt::RNSketchCanvas::implementation::RNSketchCanvasView::ExportedCustomDirectEventTypeConstants(); 69 | } 70 | 71 | // IViewManagerWithCommands 72 | IVectorView RNSketchCanvasViewManager::Commands() noexcept 73 | { 74 | return winrt::RNSketchCanvas::implementation::RNSketchCanvasView::Commands(); 75 | } 76 | 77 | void RNSketchCanvasViewManager::DispatchCommand( 78 | FrameworkElement const& view, 79 | winrt::hstring const& commandId, 80 | winrt::IJSValueReader const& commandArgsReader) noexcept 81 | { 82 | if (auto module = view.try_as()) 83 | { 84 | module.DispatchCommand(commandId, commandArgsReader); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /windows/RNSketchCanvas/RNSketchCanvasViewManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "winrt/Microsoft.ReactNative.h" 3 | #include "NativeModules.h" 4 | 5 | 6 | namespace winrt::RNSketchCanvas::implementation 7 | { 8 | 9 | class RNSketchCanvasViewManager : public winrt::implements< 10 | RNSketchCanvasViewManager, 11 | winrt::Microsoft::ReactNative::IViewManager, 12 | winrt::Microsoft::ReactNative::IViewManagerWithReactContext, 13 | winrt::Microsoft::ReactNative::IViewManagerWithNativeProperties, 14 | winrt::Microsoft::ReactNative::IViewManagerWithExportedViewConstants, 15 | winrt::Microsoft::ReactNative::IViewManagerWithExportedEventTypeConstants, 16 | winrt::Microsoft::ReactNative::IViewManagerWithCommands> 17 | { 18 | public: 19 | RNSketchCanvasViewManager() = default; 20 | 21 | // IViewManager 22 | winrt::hstring Name() noexcept; 23 | winrt::Windows::UI::Xaml::FrameworkElement CreateView() noexcept; 24 | 25 | // IViewManagerWithReactContext 26 | winrt::Microsoft::ReactNative::IReactContext ReactContext() noexcept; 27 | void ReactContext(winrt::Microsoft::ReactNative::IReactContext reactContext) noexcept; 28 | 29 | // IViewManagerWithNativeProperties 30 | winrt::Windows::Foundation::Collections:: 31 | IMapView 32 | NativeProps() noexcept; 33 | 34 | void UpdateProperties( 35 | winrt::Windows::UI::Xaml::FrameworkElement const& view, 36 | winrt::Microsoft::ReactNative::IJSValueReader const& propertyMapReader) noexcept; 37 | 38 | // IViewManagerWithExportedViewConstants 39 | winrt::Microsoft::ReactNative::ConstantProviderDelegate 40 | ExportedViewConstants() noexcept; 41 | 42 | // IViewManagerWithExportedEventTypeConstants 43 | winrt::Microsoft::ReactNative::ConstantProviderDelegate ExportedCustomBubblingEventTypeConstants() noexcept; 44 | winrt::Microsoft::ReactNative::ConstantProviderDelegate ExportedCustomDirectEventTypeConstants() noexcept; 45 | 46 | 47 | // IViewManagerWithCommands 48 | winrt::Windows::Foundation::Collections::IVectorView Commands() noexcept; 49 | 50 | void DispatchCommand( 51 | winrt::Windows::UI::Xaml::FrameworkElement const& view, 52 | winrt::hstring const& commandId, 53 | winrt::Microsoft::ReactNative::IJSValueReader const& commandArgsReader) noexcept; 54 | 55 | private: 56 | winrt::Microsoft::ReactNative::IReactContext m_reactContext{ nullptr }; 57 | }; 58 | } 59 | -------------------------------------------------------------------------------- /windows/RNSketchCanvas/ReactPackageProvider.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "ReactPackageProvider.h" 3 | #if __has_include("ReactPackageProvider.g.cpp") 4 | # include "ReactPackageProvider.g.cpp" 5 | #endif 6 | 7 | #include "RNSketchCanvasModule.h" 8 | #include "RNSketchCanvasViewManager.h" 9 | 10 | using namespace winrt::Microsoft::ReactNative; 11 | 12 | namespace winrt::RNSketchCanvas::implementation { 13 | void ReactPackageProvider::CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept { 14 | AddAttributedModules(packageBuilder); 15 | packageBuilder.AddViewManager(L"RNSketchCanvasViewManager", []() { return winrt::make(); }); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /windows/RNSketchCanvas/ReactPackageProvider.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ReactPackageProvider.g.h" 3 | 4 | using namespace winrt::Microsoft::ReactNative; 5 | 6 | namespace winrt::RNSketchCanvas::implementation { 7 | struct ReactPackageProvider : ReactPackageProviderT { 8 | ReactPackageProvider() = default; 9 | void CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept; 10 | }; 11 | } 12 | 13 | namespace winrt::RNSketchCanvas::factory_implementation { 14 | struct ReactPackageProvider : ReactPackageProviderT {}; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /windows/RNSketchCanvas/ReactPackageProvider.idl: -------------------------------------------------------------------------------- 1 | namespace RNSketchCanvas 2 | { 3 | [webhosthidden] 4 | [default_interface] 5 | runtimeclass ReactPackageProvider : Microsoft.ReactNative.IReactPackageProvider 6 | { 7 | ReactPackageProvider(); 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /windows/RNSketchCanvas/SketchData.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "SketchData.h" 3 | #include "Utility.h" 4 | 5 | namespace winrt 6 | { 7 | using namespace Microsoft::Graphics::Canvas; 8 | using namespace Microsoft::Graphics::Canvas::Brushes; 9 | using namespace Microsoft::Graphics::Canvas::Geometry; 10 | using namespace Windows::Foundation; 11 | using namespace Windows::Foundation::Numerics; 12 | using namespace Windows::UI; 13 | using namespace Windows::UI::Xaml; 14 | /*using namespace Windows::UI::Xaml::Shapes; 15 | using namespace Windows::UI::Xaml::Controls; 16 | using namespace Windows::UI::Xaml::Media;*/ 17 | } 18 | 19 | namespace winrt::RNSketchCanvas::implementation 20 | { 21 | 22 | std::optional SketchData::mStrokeStyle = std::nullopt; 23 | 24 | float2 SketchData::midPoint(const float2& p1, const float2& p2) 25 | { 26 | return float2((p1.x + p2.x) * .5f, (p1.y + p2.y) * .5f); 27 | } 28 | 29 | SketchData::SketchData(int id, Color strokeColor, float strokeWidth) 30 | { 31 | this->id = id; 32 | this->strokeColor = strokeColor; 33 | this->strokeWidth = strokeWidth; 34 | this->isTranslucent = strokeColor.A != 255 && !Utility::isColorTransparent(strokeColor); 35 | mPath.reset(); // Geometry has to be created with something. 36 | } 37 | 38 | SketchData::SketchData(int id, Color strokeColor, float strokeWidth, std::vector points) 39 | { 40 | this->id = id; 41 | this->strokeColor = strokeColor; 42 | this->strokeWidth = strokeWidth; 43 | this->points.insert(std::end(this->points), std::begin(points), std::end(points)); 44 | this->isTranslucent = strokeColor.A != 255 && !Utility::isColorTransparent(strokeColor); 45 | mPath = this->isTranslucent ? evaluatePath() : nullptr; 46 | } 47 | 48 | void SketchData::addPoint(const float2& p) 49 | { 50 | points.push_back(p); 51 | int pointsCount = points.size(); 52 | if (isTranslucent) 53 | { 54 | if (pointsCount >= 3) 55 | { 56 | addPointToPath( 57 | points[pointsCount - 3], 58 | points[pointsCount - 2], 59 | p); 60 | } else if (pointsCount >= 2) 61 | { 62 | addPointToPath(points[0], points[0], p); 63 | } else 64 | { 65 | addPointToPath(p, p, p); 66 | } 67 | } else 68 | { 69 | if (pointsCount >= 3) 70 | { 71 | float2 a = points[pointsCount - 3]; 72 | float2 b = points[pointsCount - 2]; 73 | float2 c = p; 74 | float2 prevMid = midPoint(a, b); 75 | float2 currentMid = midPoint(b, c); 76 | 77 | } else if (pointsCount >= 2) 78 | { 79 | float2 a = points[pointsCount - 2]; 80 | float2 b = p; 81 | float2 mid = midPoint(a, b); 82 | } 83 | } 84 | } 85 | 86 | void SketchData::drawLastPoint(const CanvasDrawingSession& canvasDS) 87 | { 88 | int pointsCount = points.size(); 89 | if (pointsCount < 1) 90 | { 91 | return; 92 | } 93 | draw(canvasDS, pointsCount - 1); 94 | } 95 | 96 | void SketchData::draw(const CanvasDrawingSession& canvasDS) 97 | { 98 | canvasDS.Blend(CanvasBlend::SourceOver); 99 | bool isErase = Utility::isColorTransparent(strokeColor); 100 | canvasDS.Blend(isErase ? CanvasBlend::Copy : CanvasBlend::SourceOver); 101 | 102 | if (this->isTranslucent) 103 | { 104 | canvasDS.DrawGeometry(mPath.value(), strokeColor, strokeWidth, getStrokeStyle()); 105 | } else 106 | { 107 | int pointsCount = points.size(); 108 | for (int i = 0; i < pointsCount; i++) 109 | { 110 | draw(canvasDS, i); 111 | } 112 | } 113 | } 114 | 115 | void SketchData::draw(const CanvasDrawingSession& canvasDS, int pointIndex) 116 | { 117 | int pointsCount = points.size(); 118 | if (pointIndex >= pointsCount) 119 | { 120 | return; 121 | } 122 | 123 | bool isErase = Utility::isColorTransparent(strokeColor); 124 | canvasDS.Blend(isErase ? CanvasBlend::Copy : CanvasBlend::SourceOver); 125 | 126 | if (pointsCount >= 3 && pointIndex >= 2) 127 | { 128 | float2 a = points[pointIndex - 2]; 129 | float2 b = points[pointIndex - 1]; 130 | float2 c = points[pointIndex]; 131 | float2 prevMid = midPoint(a, b); 132 | float2 currMid = midPoint(b, c); 133 | 134 | // Draw a curve 135 | CanvasPathBuilder path = CanvasPathBuilder(canvasDS.Device()); 136 | path.BeginFigure(prevMid); 137 | path.AddQuadraticBezier(b, currMid); 138 | path.EndFigure(CanvasFigureLoop::Open); 139 | canvasDS.DrawGeometry(CanvasGeometry::CreatePath(path), strokeColor, strokeWidth, getStrokeStyle()); 140 | } else if (pointsCount >= 2 && pointIndex >= 1) 141 | { 142 | float2 a = points[pointIndex - 1]; 143 | float2 b = points[pointIndex]; 144 | float2 mid = midPoint(a, b); 145 | 146 | // Draw a line to the middle of points a and b 147 | // This is so the next draw which uses a curve looks correct and continues from there 148 | canvasDS.DrawLine(a, mid, strokeColor, strokeWidth, getStrokeStyle()); 149 | } else if (pointsCount >= 1) 150 | { 151 | float2 a = points[pointIndex]; 152 | 153 | // Draw a single point 154 | canvasDS.DrawLine(a, a, strokeColor, strokeWidth, getStrokeStyle()); 155 | } 156 | } 157 | 158 | CanvasStrokeStyle SketchData::getStrokeStyle() 159 | { 160 | if (!SketchData::mStrokeStyle.has_value()) 161 | { 162 | 163 | CanvasStrokeStyle style; 164 | style.DashStyle(CanvasDashStyle::Solid); 165 | style.StartCap(CanvasCapStyle::Round); 166 | style.EndCap(CanvasCapStyle::Round); 167 | style.DashCap(CanvasCapStyle::Round); 168 | SketchData::mStrokeStyle = style; 169 | } 170 | return SketchData::mStrokeStyle.value(); 171 | } 172 | 173 | CanvasGeometry SketchData::evaluatePath() 174 | { 175 | int pointsCount = points.size(); 176 | CanvasPathBuilder path(CanvasDevice::GetSharedDevice()); 177 | for (int pointIndex = 0; pointIndex < pointsCount; pointIndex++) 178 | { 179 | if (pointsCount >= 3 && pointIndex >= 2) 180 | { 181 | float2 a = points[pointIndex - 2]; 182 | float2 b = points[pointIndex - 1]; 183 | float2 c = points[pointIndex]; 184 | float2 prevMid = midPoint(a, b); 185 | float2 currMid = midPoint(b, c); 186 | 187 | // Draw a curve 188 | path.BeginFigure(prevMid); 189 | path.AddQuadraticBezier(b, currMid); 190 | path.EndFigure(CanvasFigureLoop::Open); 191 | } else if (pointsCount >= 2 && pointIndex >= 1) 192 | { 193 | float2 a = points[pointIndex - 1]; 194 | float2 b = points[pointIndex]; 195 | float2 mid = midPoint(a, b); 196 | 197 | // Draw a line to the middle of points a and b 198 | // This is so the next draw which uses a curve looks correct and continues from there 199 | path.BeginFigure(a); 200 | path.AddLine(mid); 201 | path.EndFigure(CanvasFigureLoop::Open); 202 | } else if (pointsCount >= 1) 203 | { 204 | float2 a = points[pointIndex]; 205 | 206 | // Draw a single point 207 | path.BeginFigure(a); 208 | path.AddLine(a); 209 | path.EndFigure(CanvasFigureLoop::Open); 210 | } 211 | } 212 | 213 | return CanvasGeometry::CreatePath(path); 214 | } 215 | 216 | void SketchData::addPointToPath( 217 | const float2& tPoint, 218 | const float2& pPoint, 219 | const float2& point 220 | ) 221 | { 222 | CanvasPathBuilder path = CanvasPathBuilder(CanvasDevice::GetSharedDevice()); 223 | float2 mid1 = midPoint(pPoint, tPoint); 224 | float2 mid2 = midPoint(point, pPoint); 225 | path.BeginFigure(mid1); 226 | path.AddQuadraticBezier(pPoint, mid2); 227 | path.EndFigure(CanvasFigureLoop::Open); 228 | CanvasGeometry geometry = CanvasGeometry::CreatePath(path); 229 | if (mPath.has_value()) 230 | { 231 | mPath = CanvasGeometry::CreateGroup(CanvasDevice::GetSharedDevice(), { mPath.value(), geometry }); 232 | } else 233 | { 234 | mPath = geometry; 235 | } 236 | } 237 | 238 | } 239 | -------------------------------------------------------------------------------- /windows/RNSketchCanvas/SketchData.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace winrt::RNSketchCanvas::implementation 5 | { 6 | class SketchData 7 | { 8 | public: 9 | std::vector points; 10 | int id; 11 | winrt::Windows::UI::Color strokeColor; 12 | float strokeWidth; 13 | bool isTranslucent; 14 | 15 | SketchData(int id, winrt::Windows::UI::Color strokeColor, float strokeWidth); 16 | SketchData(int id, winrt::Windows::UI::Color strokeColor, float strokeWidth, std::vector points); 17 | 18 | static winrt::Windows::Foundation::Numerics::float2 midPoint(const winrt::Windows::Foundation::Numerics::float2&, const winrt::Windows::Foundation::Numerics::float2&); 19 | 20 | void addPoint(const winrt::Windows::Foundation::Numerics::float2& p); 21 | 22 | void drawLastPoint(const winrt::Microsoft::Graphics::Canvas::CanvasDrawingSession&); 23 | void draw(const winrt::Microsoft::Graphics::Canvas::CanvasDrawingSession&); 24 | void draw(const winrt::Microsoft::Graphics::Canvas::CanvasDrawingSession&, int); 25 | 26 | private: 27 | std::optional mPath; 28 | static std::optional mStrokeStyle; 29 | 30 | static winrt::Microsoft::Graphics::Canvas::Geometry::CanvasStrokeStyle getStrokeStyle(); 31 | 32 | winrt::Microsoft::Graphics::Canvas::Geometry::CanvasGeometry evaluatePath(); 33 | void addPointToPath( 34 | const winrt::Windows::Foundation::Numerics::float2& tPoint, 35 | const winrt::Windows::Foundation::Numerics::float2& pPoint, 36 | const winrt::Windows::Foundation::Numerics::float2& point 37 | ); 38 | 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /windows/RNSketchCanvas/Utility.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "Utility.h" 3 | #include 4 | 5 | namespace winrt::RNSketchCanvas::implementation 6 | { 7 | bool Utility::isSameColor(winrt::Windows::UI::Color color1, winrt::Windows::UI::Color color2) noexcept 8 | { 9 | if (color1.R == color2.R && color1.G == color2.G && color1.B == color2.B && color1.A == color2.A) 10 | { 11 | return true; 12 | } 13 | return false; 14 | } 15 | 16 | bool Utility::isColorTransparent(winrt::Windows::UI::Color color) noexcept 17 | { 18 | // Windows Colors::Transparent is #00FFFFFF but JavaScript code uses #00000000. Let's consider both. 19 | return 20 | (color.A == 0) && 21 | ( 22 | (color.R == 0 && color.G == 0 && color.R == 0) || 23 | (color.R == 255 && color.G == 255 && color.R == 255) 24 | ); 25 | } 26 | 27 | winrt::Windows::UI::Color Utility::uint32ToColor(uint32_t color) noexcept 28 | { 29 | winrt::Windows::UI::Color result; 30 | result.A = (color >> 24) & 0xff; 31 | result.R = (color >> 16) & 0xff; 32 | result.G = (color >> 8) & 0xff; 33 | result.B = color & 0xff; 34 | return result; 35 | } 36 | 37 | Windows::Foundation::Rect Utility::fillImage(float imgWidth, float imgHeight, float targetWidth, float targetHeight, std::string mode) 38 | { 39 | float imageAspectRatio = imgWidth / imgHeight; 40 | float targetAspectRatio = targetWidth / targetHeight; 41 | if (mode == "AspectFill") 42 | { 43 | float scaleFactor = targetAspectRatio < imageAspectRatio ? targetHeight / imgHeight : targetWidth / imgWidth; 44 | float w = imgWidth * scaleFactor; 45 | float h = imgHeight * scaleFactor; 46 | return Windows::Foundation::Rect((targetWidth - w) / 2, (targetHeight - h) / 2, w, h); 47 | } else if (mode == "ScaleToFill") 48 | { 49 | return Windows::Foundation::Rect(0, 0, targetWidth, targetHeight); 50 | } else 51 | { 52 | // AspectFit 53 | float scaleFactor = targetAspectRatio > imageAspectRatio ? targetHeight / imgHeight : targetWidth / imgWidth; 54 | float w = imgWidth * scaleFactor; 55 | float h = imgHeight * scaleFactor; 56 | return Windows::Foundation::Rect((targetWidth - w) / 2, (targetHeight - h) / 2, w, h); 57 | } 58 | } 59 | 60 | std::vector Utility::splitLines(std::string input) 61 | { 62 | std::stringstream ss(input); 63 | std::vector result; 64 | std::string line; 65 | while (std::getline(ss, line, '\n')) 66 | { 67 | result.push_back(line); 68 | } 69 | return result; 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /windows/RNSketchCanvas/Utility.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | namespace winrt::RNSketchCanvas::implementation 3 | { 4 | class Utility 5 | { 6 | public: 7 | static bool isSameColor(winrt::Windows::UI::Color, winrt::Windows::UI::Color) noexcept; 8 | static bool isColorTransparent(winrt::Windows::UI::Color) noexcept; 9 | static winrt::Windows::UI::Color uint32ToColor(uint32_t) noexcept; 10 | static Windows::Foundation::Rect fillImage(float imgWidth, float imgHeight, float targetWidth, float targetHeight, std::string mode); 11 | static std::vector splitLines(std::string input); 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /windows/RNSketchCanvas/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /windows/RNSketchCanvas/pch.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | -------------------------------------------------------------------------------- /windows/RNSketchCanvas/pch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | --------------------------------------------------------------------------------