expo-image-manipulator 10.2.0 → 10.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/README.md +3 -3
- package/android/build.gradle +38 -25
- package/build/ExpoImageManipulator.d.ts +1 -0
- package/build/ExpoImageManipulator.d.ts.map +1 -0
- package/build/ExpoImageManipulator.web.d.ts +1 -0
- package/build/ExpoImageManipulator.web.d.ts.map +1 -0
- package/build/ExpoImageManipulator.web.js +5 -5
- package/build/ExpoImageManipulator.web.js.map +1 -1
- package/build/ImageManipulator.d.ts +1 -0
- package/build/ImageManipulator.d.ts.map +1 -0
- package/build/ImageManipulator.types.d.ts +1 -0
- package/build/ImageManipulator.types.d.ts.map +1 -0
- package/build/actions/CropAction.web.d.ts +1 -0
- package/build/actions/CropAction.web.d.ts.map +1 -0
- package/build/actions/FlipAction.web.d.ts +1 -0
- package/build/actions/FlipAction.web.d.ts.map +1 -0
- package/build/actions/ResizeAction.web.d.ts +1 -0
- package/build/actions/ResizeAction.web.d.ts.map +1 -0
- package/build/actions/RotateAction.web.d.ts +1 -0
- package/build/actions/RotateAction.web.d.ts.map +1 -0
- package/build/actions/index.web.d.ts +1 -0
- package/build/actions/index.web.d.ts.map +1 -0
- package/build/utils/getContext.web.d.ts +1 -0
- package/build/utils/getContext.web.d.ts.map +1 -0
- package/build/validators.d.ts +1 -0
- package/build/validators.d.ts.map +1 -0
- package/expo-module.config.json +7 -0
- package/ios/{EXImageManipulator.podspec → ExpoImageManipulator.podspec} +10 -3
- package/ios/ImageManipulations.swift +209 -0
- package/ios/ImageManipulatorArguments.swift +93 -0
- package/ios/ImageManipulatorExceptions.swift +82 -0
- package/ios/ImageManipulatorModule.swift +133 -0
- package/package.json +3 -3
- package/src/ExpoImageManipulator.web.ts +5 -5
- package/ios/EXImageManipulator/EXImageManipulatorModule.h +0 -7
- package/ios/EXImageManipulator/EXImageManipulatorModule.m +0 -373
- package/unimodule.json +0 -4
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,33 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 10.3.1 — 2022-04-20
|
|
14
|
+
|
|
15
|
+
### 🐛 Bug fixes
|
|
16
|
+
|
|
17
|
+
- Fix `base64` result of `manipulateAsync` being always `null` on iOS. ([#17122](https://github.com/expo/expo/pull/17122) by [@barthap](https://github.com/barthap))
|
|
18
|
+
|
|
19
|
+
## 10.3.0 — 2022-04-18
|
|
20
|
+
|
|
21
|
+
### 🎉 New features
|
|
22
|
+
|
|
23
|
+
- Native module on iOS is now written in Swift using the new API. ([#15277](https://github.com/expo/expo/pull/15277) by [@tsapeta](https://github.com/tsapeta))
|
|
24
|
+
|
|
25
|
+
### 🐛 Bug fixes
|
|
26
|
+
|
|
27
|
+
- Remove `data:image` part in web base64 result. ([#16191](https://github.com/expo/expo/pull/16191) by [@AllanChain](https://github.com/AllanChain))
|
|
28
|
+
- On iOS fix rotation causing extra image borders. ([#16669](https://github.com/expo/expo/pull/16669) by [@mnightingale](https://github.com/mnightingale))
|
|
29
|
+
|
|
30
|
+
### ⚠️ Notices
|
|
31
|
+
|
|
32
|
+
- On Android bump `compileSdkVersion` to `31`, `targetSdkVersion` to `31` and `Java` version to `11`. ([#16941](https://github.com/expo/expo/pull/16941) by [@bbarthec](https://github.com/bbarthec))
|
|
33
|
+
|
|
34
|
+
## 10.2.1 - 2022-02-01
|
|
35
|
+
|
|
36
|
+
### 🐛 Bug fixes
|
|
37
|
+
|
|
38
|
+
- Fix `Plugin with id 'maven' not found` build error from Android Gradle 7. ([#16080](https://github.com/expo/expo/pull/16080) by [@kudo](https://github.com/kudo))
|
|
39
|
+
|
|
13
40
|
## 10.2.0 — 2021-12-03
|
|
14
41
|
|
|
15
42
|
### 🐛 Bug fixes
|
package/README.md
CHANGED
|
@@ -4,12 +4,12 @@ Provides functions that let you manipulation images on the local file system, eg
|
|
|
4
4
|
|
|
5
5
|
# API documentation
|
|
6
6
|
|
|
7
|
-
- [Documentation for the
|
|
8
|
-
- [Documentation for the latest stable release](https://docs.expo.
|
|
7
|
+
- [Documentation for the main branch](https://github.com/expo/expo/blob/main/docs/pages/versions/unversioned/sdk/imagemanipulator.md)
|
|
8
|
+
- [Documentation for the latest stable release](https://docs.expo.dev/versions/latest/sdk/imagemanipulator/)
|
|
9
9
|
|
|
10
10
|
# Installation in managed Expo projects
|
|
11
11
|
|
|
12
|
-
For
|
|
12
|
+
For [managed](https://docs.expo.dev/versions/latest/introduction/managed-vs-bare/) Expo projects, please follow the installation instructions in the [API documentation for the latest stable release](https://docs.expo.dev/versions/latest/sdk/imagemanipulator/).
|
|
13
13
|
|
|
14
14
|
# Installation in bare React Native projects
|
|
15
15
|
|
package/android/build.gradle
CHANGED
|
@@ -1,67 +1,80 @@
|
|
|
1
1
|
apply plugin: 'com.android.library'
|
|
2
2
|
apply plugin: 'kotlin-android'
|
|
3
|
-
apply plugin: 'maven'
|
|
3
|
+
apply plugin: 'maven-publish'
|
|
4
4
|
|
|
5
5
|
group = 'host.exp.exponent'
|
|
6
|
-
version = '10.
|
|
6
|
+
version = '10.3.1'
|
|
7
7
|
|
|
8
8
|
buildscript {
|
|
9
|
+
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
10
|
+
if (expoModulesCorePlugin.exists()) {
|
|
11
|
+
apply from: expoModulesCorePlugin
|
|
12
|
+
applyKotlinExpoModulesCorePlugin()
|
|
13
|
+
}
|
|
14
|
+
|
|
9
15
|
// Simple helper that allows the root project to override versions declared by this library.
|
|
10
16
|
ext.safeExtGet = { prop, fallback ->
|
|
11
17
|
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
12
18
|
}
|
|
13
19
|
|
|
20
|
+
// Ensures backward compatibility
|
|
21
|
+
ext.getKotlinVersion = {
|
|
22
|
+
if (ext.has("kotlinVersion")) {
|
|
23
|
+
ext.kotlinVersion()
|
|
24
|
+
} else {
|
|
25
|
+
ext.safeExtGet("kotlinVersion", "1.6.10")
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
14
29
|
repositories {
|
|
15
30
|
mavenCentral()
|
|
16
31
|
}
|
|
17
32
|
|
|
18
33
|
dependencies {
|
|
19
|
-
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${
|
|
34
|
+
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${getKotlinVersion()}")
|
|
20
35
|
}
|
|
21
36
|
}
|
|
22
37
|
|
|
23
|
-
// Upload android library to maven with javadoc and android sources
|
|
24
|
-
configurations {
|
|
25
|
-
deployerJars
|
|
26
|
-
}
|
|
27
|
-
|
|
28
38
|
// Creating sources with comments
|
|
29
39
|
task androidSourcesJar(type: Jar) {
|
|
30
40
|
classifier = 'sources'
|
|
31
41
|
from android.sourceSets.main.java.srcDirs
|
|
32
42
|
}
|
|
33
43
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
afterEvaluate {
|
|
45
|
+
publishing {
|
|
46
|
+
publications {
|
|
47
|
+
release(MavenPublication) {
|
|
48
|
+
from components.release
|
|
49
|
+
// Add additional sourcesJar to artifacts
|
|
50
|
+
artifact(androidSourcesJar)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
repositories {
|
|
54
|
+
maven {
|
|
55
|
+
url = mavenLocal().url
|
|
56
|
+
}
|
|
44
57
|
}
|
|
45
58
|
}
|
|
46
59
|
}
|
|
47
60
|
|
|
48
61
|
android {
|
|
49
|
-
compileSdkVersion safeExtGet("compileSdkVersion",
|
|
62
|
+
compileSdkVersion safeExtGet("compileSdkVersion", 31)
|
|
50
63
|
|
|
51
64
|
compileOptions {
|
|
52
|
-
sourceCompatibility JavaVersion.
|
|
53
|
-
targetCompatibility JavaVersion.
|
|
65
|
+
sourceCompatibility JavaVersion.VERSION_11
|
|
66
|
+
targetCompatibility JavaVersion.VERSION_11
|
|
54
67
|
}
|
|
55
68
|
|
|
56
69
|
kotlinOptions {
|
|
57
|
-
jvmTarget = JavaVersion.
|
|
70
|
+
jvmTarget = JavaVersion.VERSION_11.majorVersion
|
|
58
71
|
}
|
|
59
72
|
|
|
60
73
|
defaultConfig {
|
|
61
74
|
minSdkVersion safeExtGet("minSdkVersion", 21)
|
|
62
|
-
targetSdkVersion safeExtGet("targetSdkVersion",
|
|
75
|
+
targetSdkVersion safeExtGet("targetSdkVersion", 31)
|
|
63
76
|
versionCode 23
|
|
64
|
-
versionName "10.
|
|
77
|
+
versionName "10.3.1"
|
|
65
78
|
}
|
|
66
79
|
lintOptions {
|
|
67
80
|
abortOnError false
|
|
@@ -73,5 +86,5 @@ dependencies {
|
|
|
73
86
|
|
|
74
87
|
api "androidx.annotation:annotation:1.0.0"
|
|
75
88
|
|
|
76
|
-
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${
|
|
89
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
|
|
77
90
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpoImageManipulator.d.ts","sourceRoot":"","sources":["../src/ExpoImageManipulator.ts"],"names":[],"mappings":";AACA,wBAA6D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpoImageManipulator.web.d.ts","sourceRoot":"","sources":["../src/ExpoImageManipulator.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;;;yBAiDnE,MAAM,0CAEF,WAAW,GACnB,QAAQ,WAAW,CAAC;;AARzB,wBA2BE"}
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import { crop, flip, resize, rotate } from './actions/index.web';
|
|
2
2
|
import { getContext } from './utils/getContext.web';
|
|
3
3
|
function getResults(canvas, options) {
|
|
4
|
-
let
|
|
4
|
+
let uri;
|
|
5
5
|
if (options) {
|
|
6
6
|
const { format = 'png' } = options;
|
|
7
7
|
if (options.format === 'png' && options.compress !== undefined) {
|
|
8
8
|
console.warn('compress is not supported with png format.');
|
|
9
9
|
}
|
|
10
10
|
const quality = Math.min(1, Math.max(0, options.compress ?? 1));
|
|
11
|
-
|
|
11
|
+
uri = canvas.toDataURL('image/' + format, quality);
|
|
12
12
|
}
|
|
13
13
|
else {
|
|
14
14
|
// defaults to PNG with no loss
|
|
15
|
-
|
|
15
|
+
uri = canvas.toDataURL();
|
|
16
16
|
}
|
|
17
17
|
return {
|
|
18
|
-
uri
|
|
18
|
+
uri,
|
|
19
19
|
width: canvas.width,
|
|
20
20
|
height: canvas.height,
|
|
21
|
-
base64,
|
|
21
|
+
base64: uri.replace(/^data:image\/\w+;base64,/, ''),
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
24
|
function loadImageAsync(uri) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoImageManipulator.web.js","sourceRoot":"","sources":["../src/ExpoImageManipulator.web.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,SAAS,UAAU,CAAC,MAAyB,EAAE,OAAqB;IAClE,IAAI,
|
|
1
|
+
{"version":3,"file":"ExpoImageManipulator.web.js","sourceRoot":"","sources":["../src/ExpoImageManipulator.web.ts"],"names":[],"mappings":"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","sourcesContent":["import { ImageResult, SaveOptions, Action } from './ImageManipulator.types';\nimport { crop, flip, resize, rotate } from './actions/index.web';\nimport { getContext } from './utils/getContext.web';\n\nfunction getResults(canvas: HTMLCanvasElement, options?: SaveOptions): ImageResult {\n let uri: string;\n if (options) {\n const { format = 'png' } = options;\n if (options.format === 'png' && options.compress !== undefined) {\n console.warn('compress is not supported with png format.');\n }\n const quality = Math.min(1, Math.max(0, options.compress ?? 1));\n uri = canvas.toDataURL('image/' + format, quality);\n } else {\n // defaults to PNG with no loss\n uri = canvas.toDataURL();\n }\n return {\n uri,\n width: canvas.width,\n height: canvas.height,\n base64: uri.replace(/^data:image\\/\\w+;base64,/, ''),\n };\n}\n\nfunction loadImageAsync(uri: string): Promise<HTMLCanvasElement> {\n return new Promise((resolve, reject) => {\n const imageSource = new Image();\n imageSource.crossOrigin = 'anonymous';\n const canvas = document.createElement('canvas');\n imageSource.onload = () => {\n canvas.width = imageSource.naturalWidth;\n canvas.height = imageSource.naturalHeight;\n\n const context = getContext(canvas);\n context.drawImage(imageSource, 0, 0, imageSource.naturalWidth, imageSource.naturalHeight);\n\n resolve(canvas);\n };\n imageSource.onerror = () => reject(canvas);\n imageSource.src = uri;\n });\n}\n\nexport default {\n get name(): string {\n return 'ExpoImageManipulator';\n },\n async manipulateAsync(\n uri: string,\n actions: Action[] = [],\n options: SaveOptions\n ): Promise<ImageResult> {\n const originalCanvas = await loadImageAsync(uri);\n\n const resultCanvas = actions.reduce((canvas, action) => {\n if ('crop' in action) {\n return crop(canvas, action.crop);\n } else if ('resize' in action) {\n return resize(canvas, action.resize);\n } else if ('flip' in action) {\n return flip(canvas, action.flip);\n } else if ('rotate' in action) {\n return rotate(canvas, action.rotate);\n } else {\n return canvas;\n }\n }, originalCanvas);\n\n return getResults(resultCanvas, options);\n },\n};\n"]}
|
|
@@ -12,3 +12,4 @@ import { Action, ImageResult, SaveOptions } from './ImageManipulator.types';
|
|
|
12
12
|
*/
|
|
13
13
|
export declare function manipulateAsync(uri: string, actions?: Action[], saveOptions?: SaveOptions): Promise<ImageResult>;
|
|
14
14
|
export * from './ImageManipulator.types';
|
|
15
|
+
//# sourceMappingURL=ImageManipulator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageManipulator.d.ts","sourceRoot":"","sources":["../src/ImageManipulator.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,WAAW,EAAc,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAIxF;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,MAAM,EAAO,EACtB,WAAW,GAAE,WAAgB,GAC5B,OAAO,CAAC,WAAW,CAAC,CAStB;AAED,cAAc,0BAA0B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageManipulator.types.d.ts","sourceRoot":"","sources":["../src/ImageManipulator.types.ts"],"names":[],"mappings":"AACA,oBAAY,WAAW,GAAG;IACxB;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAGF,oBAAY,YAAY,GAAG;IACzB;;;OAGG;IACH,MAAM,EAAE;QACN,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACH,CAAC;AAGF,oBAAY,YAAY,GAAG;IACzB;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAGF,oBAAY,QAAQ;IAClB,QAAQ,aAAa;IACrB,UAAU,eAAe;CAC1B;AAGD,oBAAY,UAAU,GAAG;IACvB;;;OAGG;IACH,IAAI,EAAE,QAAQ,CAAC;CAChB,CAAC;AAGF,oBAAY,UAAU,GAAG;IACvB;;OAEG;IACH,IAAI,EAAE;QACJ,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAAC;AAGF,oBAAY,MAAM,GAAG,YAAY,GAAG,YAAY,GAAG,UAAU,GAAG,UAAU,CAAC;AAG3E,oBAAY,UAAU;IACpB,IAAI,SAAS;IACb,GAAG,QAAQ;IACX;;OAEG;IACH,IAAI,SAAS;CACd;AAGD;;GAEG;AACH,oBAAY,WAAW,GAAG;IACxB;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CropAction.web.d.ts","sourceRoot":"","sources":["../../src/actions/CropAction.web.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;iCAG/B,iBAAiB,WAAW,UAAU,CAAC,MAAM,CAAC;AAAtE,wBA6BE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FlipAction.web.d.ts","sourceRoot":"","sources":["../../src/actions/FlipAction.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAY,MAAM,2BAA2B,CAAC;iCAGzC,iBAAiB,QAAQ,UAAU,CAAC,MAAM,CAAC;AAAnE,wBAsBE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResizeAction.web.d.ts","sourceRoot":"","sources":["../../src/actions/ResizeAction.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;iCAqGjC,iBAAiB,qBAAqB,YAAY,CAAC,QAAQ,CAAC;AAApF,wBAiBE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RotateAction.web.d.ts","sourceRoot":"","sources":["../../src/actions/RotateAction.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;iCAoBjC,iBAAiB,WAAW,YAAY,CAAC,QAAQ,CAAC;AAA1E,wBAoBE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../src/actions/index.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getContext.web.d.ts","sourceRoot":"","sources":["../../src/utils/getContext.web.ts"],"names":[],"mappings":"AAEA,wBAAgB,UAAU,CAAC,MAAM,EAAE,iBAAiB,GAAG,wBAAwB,CAM9E"}
|
package/build/validators.d.ts
CHANGED
|
@@ -3,3 +3,4 @@ export declare function validateArguments(uri: string, actions: Action[], saveOp
|
|
|
3
3
|
export declare function validateUri(uri: string): void;
|
|
4
4
|
export declare function validateActions(actions: Action[]): void;
|
|
5
5
|
export declare function validateSaveOptions({ base64, compress, format }: SaveOptions): void;
|
|
6
|
+
//# sourceMappingURL=validators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../src/validators.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EAON,WAAW,EACZ,MAAM,0BAA0B,CAAC;AAElC,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,WAAW,QAIzF;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAI7C;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CA8BvD;AA6CD,wBAAgB,mBAAmB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,WAAW,GAAG,IAAI,CAgBnF"}
|
|
@@ -3,7 +3,7 @@ require 'json'
|
|
|
3
3
|
package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
|
|
4
4
|
|
|
5
5
|
Pod::Spec.new do |s|
|
|
6
|
-
s.name = '
|
|
6
|
+
s.name = 'ExpoImageManipulator'
|
|
7
7
|
s.version = package['version']
|
|
8
8
|
s.summary = package['description']
|
|
9
9
|
s.description = package['description']
|
|
@@ -11,16 +11,23 @@ Pod::Spec.new do |s|
|
|
|
11
11
|
s.author = package['author']
|
|
12
12
|
s.homepage = package['homepage']
|
|
13
13
|
s.platform = :ios, '12.0'
|
|
14
|
+
s.swift_version = '5.4'
|
|
14
15
|
s.source = { git: 'https://github.com/expo/expo.git' }
|
|
15
16
|
s.static_framework = true
|
|
16
17
|
|
|
17
18
|
s.dependency 'ExpoModulesCore'
|
|
18
19
|
s.dependency 'EXImageLoader'
|
|
19
20
|
|
|
21
|
+
# Swift/Objective-C compatibility
|
|
22
|
+
s.pod_target_xcconfig = {
|
|
23
|
+
'DEFINES_MODULE' => 'YES',
|
|
24
|
+
'SWIFT_COMPILATION_MODE' => 'wholemodule'
|
|
25
|
+
}
|
|
26
|
+
|
|
20
27
|
if !$ExpoUseSources&.include?(package['name']) && ENV['EXPO_USE_SOURCE'].to_i == 0 && File.exist?("#{s.name}.xcframework") && Gem::Version.new(Pod::VERSION) >= Gem::Version.new('1.10.0')
|
|
21
|
-
s.source_files = "
|
|
28
|
+
s.source_files = "**/*.h"
|
|
22
29
|
s.vendored_frameworks = "#{s.name}.xcframework"
|
|
23
30
|
else
|
|
24
|
-
s.source_files = "
|
|
31
|
+
s.source_files = "**/*.{h,m,swift}"
|
|
25
32
|
end
|
|
26
33
|
end
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// Copyright 2021-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import UIKit
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
Main `manipulate` function that takes an array of any supported actions to apply.
|
|
7
|
+
*/
|
|
8
|
+
internal func manipulate(image initialImage: UIImage, actions: [ManipulateAction]) throws -> UIImage {
|
|
9
|
+
var image: UIImage = initialImage
|
|
10
|
+
|
|
11
|
+
image = try fixImageOrientation(image)
|
|
12
|
+
|
|
13
|
+
for action in actions {
|
|
14
|
+
if let resize = action.resize {
|
|
15
|
+
image = try manipulate(image: image, resize: resize)
|
|
16
|
+
} else if let rotate = action.rotate {
|
|
17
|
+
image = try manipulate(image: image, rotate: rotate)
|
|
18
|
+
} else if let flip = action.flip {
|
|
19
|
+
image = try manipulate(image: image, flip: flip)
|
|
20
|
+
} else if let crop = action.crop {
|
|
21
|
+
image = try manipulate(image: image, crop: crop)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return image
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
Draws a new image by resizing given image to specified size.
|
|
29
|
+
*/
|
|
30
|
+
internal func manipulate(image: UIImage, resize: ResizeOptions) throws -> UIImage {
|
|
31
|
+
let imageWidth = image.size.width
|
|
32
|
+
let imageHeight = image.size.height
|
|
33
|
+
let imageRatio = imageWidth / imageHeight
|
|
34
|
+
|
|
35
|
+
var targetSize = CGSize.zero
|
|
36
|
+
|
|
37
|
+
if let width = resize.width {
|
|
38
|
+
targetSize.width = width
|
|
39
|
+
targetSize.height = width / imageRatio
|
|
40
|
+
}
|
|
41
|
+
if let height = resize.height {
|
|
42
|
+
targetSize.height = height
|
|
43
|
+
targetSize.width = targetSize.width == 0 ? imageRatio * targetSize.height : targetSize.width
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
UIGraphicsBeginImageContextWithOptions(targetSize, false, 1.0)
|
|
47
|
+
image.draw(in: CGRect(origin: .zero, size: targetSize))
|
|
48
|
+
|
|
49
|
+
guard let newImage = UIGraphicsGetImageFromCurrentImageContext() else {
|
|
50
|
+
UIGraphicsEndImageContext()
|
|
51
|
+
throw NoImageInContextException()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
UIGraphicsEndImageContext()
|
|
55
|
+
return newImage
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
Creates a new image by rotating given image by the rotate angle.
|
|
60
|
+
*/
|
|
61
|
+
internal func manipulate(image: UIImage, rotate: Double) throws -> UIImage {
|
|
62
|
+
guard let cgImage = image.cgImage else {
|
|
63
|
+
throw ImageNotFoundException()
|
|
64
|
+
}
|
|
65
|
+
let rads = rotate * Double.pi / 180
|
|
66
|
+
let rotatedView = UIView(frame: CGRect(origin: .zero, size: image.size))
|
|
67
|
+
|
|
68
|
+
rotatedView.transform = CGAffineTransform(rotationAngle: rads)
|
|
69
|
+
|
|
70
|
+
let rotatedSize = CGSize(width: rotatedView.frame.size.width.rounded(.down), height: rotatedView.frame.size.height.rounded(.down))
|
|
71
|
+
let origin = CGPoint(x: -image.size.width / 2, y: -image.size.height / 2)
|
|
72
|
+
|
|
73
|
+
return try drawInNewContext(size: rotatedSize) { context in
|
|
74
|
+
context.translateBy(x: rotatedSize.width / 2, y: rotatedSize.height / 2)
|
|
75
|
+
context.rotate(by: rads)
|
|
76
|
+
context.scaleBy(x: 1.0, y: -1.0)
|
|
77
|
+
context.draw(cgImage, in: CGRect(origin: origin, size: image.size))
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
Creates a new image by flipping given image vertically or horizontally.
|
|
83
|
+
*/
|
|
84
|
+
internal func manipulate(image: UIImage, flip: FlipType) throws -> UIImage {
|
|
85
|
+
let imageView = UIImageView(image: image)
|
|
86
|
+
|
|
87
|
+
return try drawInNewContext(size: imageView.frame.size) { context in
|
|
88
|
+
switch flip {
|
|
89
|
+
case .vertical:
|
|
90
|
+
let transform = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: imageView.frame.size.height)
|
|
91
|
+
context.concatenate(transform)
|
|
92
|
+
case .horizontal:
|
|
93
|
+
let transform = CGAffineTransform(a: -1, b: 0, c: 0, d: 1, tx: imageView.frame.size.width, ty: 0)
|
|
94
|
+
context.concatenate(transform)
|
|
95
|
+
}
|
|
96
|
+
imageView.layer.render(in: context)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
Creates a new image by cropping given image to specified width and height.
|
|
102
|
+
*/
|
|
103
|
+
internal func manipulate(image: UIImage, crop: CropRect) throws -> UIImage {
|
|
104
|
+
let rect = crop.toRect()
|
|
105
|
+
let isOutOfBounds = rect.origin.x > image.size.width
|
|
106
|
+
|| rect.origin.y > image.size.height
|
|
107
|
+
|| rect.width > image.size.width
|
|
108
|
+
|| rect.height > image.size.height
|
|
109
|
+
|
|
110
|
+
guard !isOutOfBounds else {
|
|
111
|
+
throw ImageInvalidCropException()
|
|
112
|
+
}
|
|
113
|
+
guard let cgImage = image.cgImage?.cropping(to: rect) else {
|
|
114
|
+
throw ImageCropFailedException(rect)
|
|
115
|
+
}
|
|
116
|
+
return UIImage(cgImage: cgImage, scale: image.scale, orientation: image.imageOrientation)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
Makes sure the image is oriented up and not mirrored.
|
|
121
|
+
Guarantees that the original pixel data matches the displayed orientation.
|
|
122
|
+
*/
|
|
123
|
+
internal func fixImageOrientation(_ image: UIImage) throws -> UIImage {
|
|
124
|
+
guard let cgImage = image.cgImage else {
|
|
125
|
+
throw ImageNotFoundException()
|
|
126
|
+
}
|
|
127
|
+
guard let colorSpace = cgImage.colorSpace else {
|
|
128
|
+
// That should never happen as `colorSpace` is empty only when the image is a mask.
|
|
129
|
+
throw ImageColorSpaceNotFoundException()
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
var transform = CGAffineTransform.identity
|
|
133
|
+
|
|
134
|
+
switch image.imageOrientation {
|
|
135
|
+
case .down, .downMirrored:
|
|
136
|
+
transform = transform.translatedBy(x: image.size.width, y: image.size.height)
|
|
137
|
+
transform = transform.rotated(by: Double.pi)
|
|
138
|
+
case .left, .leftMirrored:
|
|
139
|
+
transform = transform.translatedBy(x: image.size.width, y: 0)
|
|
140
|
+
transform = transform.rotated(by: Double.pi / 2)
|
|
141
|
+
case .right, .rightMirrored:
|
|
142
|
+
transform = transform.translatedBy(x: 0, y: image.size.height)
|
|
143
|
+
transform = transform.rotated(by: -Double.pi / 2)
|
|
144
|
+
default:
|
|
145
|
+
break
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
switch image.imageOrientation {
|
|
149
|
+
case .upMirrored, .downMirrored:
|
|
150
|
+
transform = transform.translatedBy(x: image.size.width, y: 0)
|
|
151
|
+
transform = transform.scaledBy(x: -1, y: 1)
|
|
152
|
+
case .leftMirrored, .rightMirrored:
|
|
153
|
+
transform = transform.translatedBy(x: image.size.height, y: 0)
|
|
154
|
+
transform = transform.scaledBy(x: -1, y: 1)
|
|
155
|
+
default:
|
|
156
|
+
break
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
let context = CGContext(
|
|
160
|
+
data: nil,
|
|
161
|
+
width: Int(image.size.width),
|
|
162
|
+
height: Int(image.size.height),
|
|
163
|
+
bitsPerComponent: cgImage.bitsPerComponent,
|
|
164
|
+
bytesPerRow: 0,
|
|
165
|
+
space: colorSpace,
|
|
166
|
+
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
guard let context = context else {
|
|
170
|
+
throw ImageContextLostException()
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
context.concatenate(transform)
|
|
174
|
+
|
|
175
|
+
switch image.imageOrientation {
|
|
176
|
+
case .left, .leftMirrored, .right, .rightMirrored:
|
|
177
|
+
context.draw(cgImage, in: CGRect(x: 0, y: 0, width: image.size.height, height: image.size.width))
|
|
178
|
+
default:
|
|
179
|
+
context.draw(cgImage, in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
guard let newCGImage = context.makeImage() else {
|
|
183
|
+
throw ImageDrawingFailedException()
|
|
184
|
+
}
|
|
185
|
+
return UIImage(cgImage: newCGImage)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
Helper function for drawing the image in graphics context.
|
|
190
|
+
Throws appropriate exceptions when the context is missing or the image couldn't be rendered.
|
|
191
|
+
*/
|
|
192
|
+
private func drawInNewContext(size: CGSize, drawing: (CGContext) -> Void) throws -> UIImage {
|
|
193
|
+
UIGraphicsBeginImageContext(size)
|
|
194
|
+
|
|
195
|
+
guard let context = UIGraphicsGetCurrentContext() else {
|
|
196
|
+
UIGraphicsEndImageContext()
|
|
197
|
+
throw ImageContextLostException()
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
drawing(context)
|
|
201
|
+
|
|
202
|
+
guard let newImage = UIGraphicsGetImageFromCurrentImageContext() else {
|
|
203
|
+
UIGraphicsEndImageContext()
|
|
204
|
+
throw NoImageInContextException()
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
UIGraphicsEndImageContext()
|
|
208
|
+
return newImage
|
|
209
|
+
}
|