react-native-photo-video-editor 0.2.0 → 0.2.2
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 +17 -0
- package/README.md +17 -0
- package/android/src/main/java/com/photovideoeditor/editor/PhotoVideoEditorActivity.kt +45 -0
- package/android/src/main/java/com/photovideoeditor/files/SourceResolver.kt +7 -0
- package/ios/Editor/PhotoVideoEditorViewController.swift +397 -149
- package/ios/Editor/UI/CropPanel.swift +10 -3
- package/ios/Editor/UI/EditorComponents.swift +53 -10
- package/ios/Editor/UI/EditorPanel.swift +228 -155
- package/ios/Editor/UI/EditorToolSlider.swift +4 -2
- package/ios/Editor/UI/TextEditorSheet.swift +4 -4
- package/ios/Files/SourceResolver.swift +131 -3
- package/ios/Photo/Export/PhotoExporter.swift +2 -2
- package/ios/Photo/Rendering/PhotoAdjustmentRenderer.swift +57 -63
- package/ios/Photo/Rendering/PixelBuffer.swift +24 -18
- package/ios/Photo/UI/FilterThumbnailLoader.swift +3 -3
- package/ios/PhotoVideoEditor.mm +33 -2
- package/ios/PhotoVideoEditor.swift +22 -18
- package/ios/Stickers/OnlineStickerSheetViewController.swift +1 -1
- package/ios/Video/Render/VideoEditSession.swift +30 -11
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +2 -1
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +20 -0
- package/src/types.ts +2 -1
|
@@ -1,13 +1,141 @@
|
|
|
1
|
+
import AVFoundation
|
|
1
2
|
import Foundation
|
|
3
|
+
import Photos
|
|
4
|
+
import UIKit
|
|
2
5
|
|
|
3
|
-
/// Resolves an editor source
|
|
4
|
-
/// filesystem path that ImageIO can read directly.
|
|
5
|
-
/// (ph://) and remote https:// sources are not yet supported.
|
|
6
|
+
/// Resolves an editor source URI (file://, bare path, ph://, or remote/data URI)
|
|
7
|
+
/// to a local filesystem path that ImageIO / AVFoundation can read directly.
|
|
6
8
|
enum SourceResolver {
|
|
7
9
|
static func resolvePath(sourceUri: String, tempPrefix: String) -> String? {
|
|
10
|
+
if sourceUri.hasPrefix("data:image/png;base64,") {
|
|
11
|
+
guard let separator = sourceUri.firstIndex(of: ","),
|
|
12
|
+
let data = Data(base64Encoded: String(sourceUri[sourceUri.index(after: separator)...])) else { return nil }
|
|
13
|
+
let file = FileManager.default.temporaryDirectory.appendingPathComponent("\(tempPrefix)_\(UUID().uuidString).png")
|
|
14
|
+
do {
|
|
15
|
+
try data.write(to: file)
|
|
16
|
+
return file.path
|
|
17
|
+
} catch { return nil }
|
|
18
|
+
}
|
|
8
19
|
guard let url = URL(string: sourceUri) else { return nil }
|
|
9
20
|
if url.isFileURL { return url.path }
|
|
10
21
|
if url.scheme == nil { return sourceUri }
|
|
11
22
|
return nil
|
|
23
|
+
let trimmed = sourceUri.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
24
|
+
guard !trimmed.isEmpty else { return nil }
|
|
25
|
+
|
|
26
|
+
// 1. Direct filesystem path
|
|
27
|
+
if trimmed.hasPrefix("/") {
|
|
28
|
+
if FileManager.default.fileExists(atPath: trimmed) { return trimmed }
|
|
29
|
+
if let decoded = trimmed.removingPercentEncoding, FileManager.default.fileExists(atPath: decoded) { return decoded }
|
|
30
|
+
return trimmed
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 2. file:// URI
|
|
34
|
+
if trimmed.hasPrefix("file://") {
|
|
35
|
+
if let url = URL(string: trimmed), url.isFileURL {
|
|
36
|
+
return url.path
|
|
37
|
+
}
|
|
38
|
+
let stripped = String(trimmed.dropFirst(7))
|
|
39
|
+
if let decoded = stripped.removingPercentEncoding, FileManager.default.fileExists(atPath: decoded) {
|
|
40
|
+
return decoded
|
|
41
|
+
}
|
|
42
|
+
return stripped
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 3. ph:// Photo Library identifier (from react-native-image-picker or Photos)
|
|
46
|
+
if trimmed.hasPrefix("ph://") {
|
|
47
|
+
let localId = String(trimmed.dropFirst(5))
|
|
48
|
+
return resolvePHAsset(localIdentifier: localId, tempPrefix: tempPrefix)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 4. assets-library:// legacy identifier
|
|
52
|
+
if trimmed.hasPrefix("assets-library://") {
|
|
53
|
+
if let url = URL(string: trimmed) {
|
|
54
|
+
let fetchResult = PHAsset.fetchAssets(withALAssetURLs: [url], options: nil)
|
|
55
|
+
if let asset = fetchResult.firstObject {
|
|
56
|
+
return exportPHAsset(asset, tempPrefix: tempPrefix)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 5. Remote http:// or https:// URL
|
|
62
|
+
if trimmed.hasPrefix("http://") || trimmed.hasPrefix("https://") {
|
|
63
|
+
if let url = URL(string: trimmed) {
|
|
64
|
+
return downloadRemoteURL(url, tempPrefix: tempPrefix)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 6. Base64 data: URI
|
|
69
|
+
if trimmed.hasPrefix("data:") {
|
|
70
|
+
return resolveDataURI(trimmed, tempPrefix: tempPrefix)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 7. Fallback for un-schemed relative or encoded path
|
|
74
|
+
if let url = URL(string: trimmed), url.isFileURL {
|
|
75
|
+
return url.path
|
|
76
|
+
}
|
|
77
|
+
if let decoded = trimmed.removingPercentEncoding, FileManager.default.fileExists(atPath: decoded) {
|
|
78
|
+
return decoded
|
|
79
|
+
}
|
|
80
|
+
return trimmed
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private static func resolvePHAsset(localIdentifier: String, tempPrefix: String) -> String? {
|
|
84
|
+
let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [localIdentifier], options: nil)
|
|
85
|
+
guard let asset = fetchResult.firstObject else { return nil }
|
|
86
|
+
return exportPHAsset(asset, tempPrefix: tempPrefix)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private static func exportPHAsset(_ asset: PHAsset, tempPrefix: String) -> String? {
|
|
90
|
+
let tempDir = FileManager.default.temporaryDirectory
|
|
91
|
+
if asset.mediaType == .video {
|
|
92
|
+
let semaphore = DispatchSemaphore(value: 0)
|
|
93
|
+
var resolvedPath: String?
|
|
94
|
+
let options = PHVideoRequestOptions()
|
|
95
|
+
options.isNetworkAccessAllowed = true
|
|
96
|
+
options.version = .current
|
|
97
|
+
options.deliveryMode = .highQualityFormat
|
|
98
|
+
PHImageManager.default().requestAVAsset(forVideo: asset, options: options) { avAsset, _, _ in
|
|
99
|
+
defer { semaphore.signal() }
|
|
100
|
+
if let urlAsset = avAsset as? AVURLAsset {
|
|
101
|
+
resolvedPath = urlAsset.url.path
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
_ = semaphore.wait(timeout: .now() + 5)
|
|
105
|
+
return resolvedPath
|
|
106
|
+
} else {
|
|
107
|
+
let options = PHImageRequestOptions()
|
|
108
|
+
options.isNetworkAccessAllowed = true
|
|
109
|
+
options.isSynchronous = true
|
|
110
|
+
options.deliveryMode = .highQualityFormat
|
|
111
|
+
var resolvedPath: String?
|
|
112
|
+
PHImageManager.default().requestImageDataAndOrientation(for: asset, options: options) { data, _, _, _ in
|
|
113
|
+
if let data {
|
|
114
|
+
let ext = (data.prefix(3) == Data([0xFF, 0xD8, 0xFF])) ? "jpg" : "png"
|
|
115
|
+
let outURL = tempDir.appendingPathComponent("\(tempPrefix)_\(UUID().uuidString).\(ext)")
|
|
116
|
+
try? data.write(to: outURL)
|
|
117
|
+
resolvedPath = outURL.path
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return resolvedPath
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private static func downloadRemoteURL(_ url: URL, tempPrefix: String) -> String? {
|
|
125
|
+
let ext = url.pathExtension.isEmpty ? "tmp" : url.pathExtension
|
|
126
|
+
let outURL = FileManager.default.temporaryDirectory.appendingPathComponent("\(tempPrefix)_\(UUID().uuidString).\(ext)")
|
|
127
|
+
guard let data = try? Data(contentsOf: url) else { return nil }
|
|
128
|
+
try? data.write(to: outURL)
|
|
129
|
+
return outURL.path
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private static func resolveDataURI(_ dataUri: String, tempPrefix: String) -> String? {
|
|
133
|
+
guard let commaIndex = dataUri.firstIndex(of: ",") else { return nil }
|
|
134
|
+
let base64String = String(dataUri[dataUri.index(after: commaIndex)...])
|
|
135
|
+
guard let data = Data(base64Encoded: base64String, options: .ignoreUnknownCharacters) else { return nil }
|
|
136
|
+
let outURL = FileManager.default.temporaryDirectory.appendingPathComponent("\(tempPrefix)_\(UUID().uuidString).png")
|
|
137
|
+
try? data.write(to: outURL)
|
|
138
|
+
return outURL.path
|
|
12
139
|
}
|
|
13
140
|
}
|
|
141
|
+
|
|
@@ -36,8 +36,8 @@ enum PhotoExporter {
|
|
|
36
36
|
throw PhotoExportError(code: "E_SOURCE_UNREADABLE", message: "The selected photo could not be decoded.")
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
let maxWidth = (exportOptions?["maxWidth"] as? NSNumber)
|
|
40
|
-
let maxHeight = (exportOptions?["maxHeight"] as? NSNumber)
|
|
39
|
+
let maxWidth = (exportOptions?["maxWidth"] as? NSNumber).map { CGFloat($0.doubleValue) }
|
|
40
|
+
let maxHeight = (exportOptions?["maxHeight"] as? NSNumber).map { CGFloat($0.doubleValue) }
|
|
41
41
|
let decodeTarget = max(maxWidth ?? defaultMaxDimension, maxHeight ?? defaultMaxDimension) * 2
|
|
42
42
|
|
|
43
43
|
let thumbnailOptions: [CFString: Any] = [
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import Accelerate
|
|
1
2
|
import UIKit
|
|
2
3
|
|
|
3
4
|
/// Applies the non-destructive `PhotoAdjustments` stack to a `UIImage`. The
|
|
@@ -32,62 +33,50 @@ enum PhotoAdjustmentRenderer {
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
private static func applyColorAdjustments(_ buffer: inout PixelBuffer, _ adjustments: PhotoAdjustments) {
|
|
35
|
-
let exposureScale = pow(2, adjustments.exposure / 100)
|
|
36
|
-
let brightnessOffset = adjustments.brightness / 100 * 80
|
|
37
|
-
let contrastScale = (adjustments.contrast + 100) / 100
|
|
38
|
-
let contrastTranslate = 128 * (1 - contrastScale)
|
|
39
|
-
let saturationFactor = (adjustments.saturation + 100) / 100
|
|
40
|
-
let tempShift = adjustments.temperature / 100 * 40
|
|
36
|
+
let exposureScale = Float(pow(2.0, adjustments.exposure / 100))
|
|
37
|
+
let brightnessOffset = Float(adjustments.brightness / 100 * 80)
|
|
38
|
+
let contrastScale = Float((adjustments.contrast + 100) / 100)
|
|
39
|
+
let contrastTranslate = Float(128 * (1 - contrastScale))
|
|
40
|
+
let saturationFactor = Float((adjustments.saturation + 100) / 100)
|
|
41
|
+
let tempShift = Float(adjustments.temperature / 100 * 40)
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
let
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
43
|
+
let combScale = exposureScale * contrastScale
|
|
44
|
+
let combOffset = brightnessOffset * contrastScale + contrastTranslate
|
|
45
|
+
|
|
46
|
+
buffer.mutateBytes { ptr, _, width, height in
|
|
47
|
+
let totalPixels = width * height
|
|
48
|
+
for i in 0..<totalPixels {
|
|
49
|
+
let offset = i * 4
|
|
50
|
+
let rVal = Float(ptr[offset])
|
|
51
|
+
let gVal = Float(ptr[offset + 1])
|
|
52
|
+
let bVal = Float(ptr[offset + 2])
|
|
53
|
+
let rBase = rVal * combScale + combOffset
|
|
54
|
+
let gBase = gVal * combScale + combOffset
|
|
55
|
+
let bBase = bVal * combScale + combOffset
|
|
56
|
+
let luminance = 0.2126 * rBase + 0.7152 * gBase + 0.0722 * bBase
|
|
57
|
+
let r = luminance + (rBase - luminance) * saturationFactor + tempShift
|
|
58
|
+
let g = luminance + (gBase - luminance) * saturationFactor
|
|
59
|
+
let b = luminance + (bBase - luminance) * saturationFactor - tempShift
|
|
60
|
+
ptr[offset] = UInt8(min(max(r, 0), 255))
|
|
61
|
+
ptr[offset + 1] = UInt8(min(max(g, 0), 255))
|
|
62
|
+
ptr[offset + 2] = UInt8(min(max(b, 0), 255))
|
|
58
63
|
}
|
|
59
64
|
}
|
|
60
65
|
}
|
|
61
66
|
|
|
62
|
-
///
|
|
67
|
+
/// Hardware-accelerated box blur using Apple Accelerate vImage.
|
|
63
68
|
private static func applyBoxBlur(_ buffer: inout PixelBuffer, radius: Int) {
|
|
64
69
|
guard radius > 0 else { return }
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
let innerLimit = horizontal ? buffer.width : buffer.height
|
|
75
|
-
for outer in 0..<outerLimit {
|
|
76
|
-
for inner in 0..<innerLimit {
|
|
77
|
-
var r = 0
|
|
78
|
-
var g = 0
|
|
79
|
-
var b = 0
|
|
80
|
-
var a = 0
|
|
81
|
-
var count = 0
|
|
82
|
-
for offset in -radius...radius {
|
|
83
|
-
let sample = min(max(inner + offset, 0), innerLimit - 1)
|
|
84
|
-
let (x, y) = horizontal ? (sample, outer) : (outer, sample)
|
|
85
|
-
let pixel = source[x, y]
|
|
86
|
-
r += Int(pixel.r); g += Int(pixel.g); b += Int(pixel.b); a += Int(pixel.a)
|
|
87
|
-
count += 1
|
|
70
|
+
let kernel = UInt32(max(3, (radius * 2 + 1) | 1))
|
|
71
|
+
buffer.mutateBytes { ptr, count, width, height in
|
|
72
|
+
var src = vImage_Buffer(data: ptr, height: vImagePixelCount(height), width: vImagePixelCount(width), rowBytes: width * 4)
|
|
73
|
+
var temp = [UInt8](repeating: 0, count: count)
|
|
74
|
+
temp.withUnsafeMutableBufferPointer { tempBuf in
|
|
75
|
+
var dest = vImage_Buffer(data: tempBuf.baseAddress, height: vImagePixelCount(height), width: vImagePixelCount(width), rowBytes: width * 4)
|
|
76
|
+
for _ in 0..<3 {
|
|
77
|
+
vImageBoxConvolve_ARGB8888(&src, &dest, nil, 0, 0, kernel, kernel, nil, vImage_Flags(kvImageEdgeExtend))
|
|
78
|
+
memcpy(src.data, dest.data, count)
|
|
88
79
|
}
|
|
89
|
-
let (ox, oy) = horizontal ? (inner, outer) : (outer, inner)
|
|
90
|
-
buffer[ox, oy] = (UInt8(r / count), UInt8(g / count), UInt8(b / count), UInt8(a / count))
|
|
91
80
|
}
|
|
92
81
|
}
|
|
93
82
|
}
|
|
@@ -112,17 +101,25 @@ enum PhotoAdjustmentRenderer {
|
|
|
112
101
|
|
|
113
102
|
private static func applyPreset(_ image: UIImage, preset: String, strength: Double) -> UIImage {
|
|
114
103
|
guard let recipe = PhotoFilterPresets.recipe(for: preset), var buffer = PixelBuffer(image: image) else { return image }
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
104
|
+
let sat = Float(recipe.saturation)
|
|
105
|
+
let scale = Float(recipe.scale)
|
|
106
|
+
let rOff = Float(recipe.redOffset)
|
|
107
|
+
let gOff = Float(recipe.greenOffset)
|
|
108
|
+
let bOff = Float(recipe.blueOffset)
|
|
109
|
+
buffer.mutateBytes { ptr, _, width, height in
|
|
110
|
+
let totalPixels = width * height
|
|
111
|
+
for i in 0..<totalPixels {
|
|
112
|
+
let offset = i * 4
|
|
113
|
+
let rVal = Float(ptr[offset])
|
|
114
|
+
let gVal = Float(ptr[offset + 1])
|
|
115
|
+
let bVal = Float(ptr[offset + 2])
|
|
116
|
+
let luminance = 0.2126 * rVal + 0.7152 * gVal + 0.0722 * bVal
|
|
117
|
+
let r = (luminance + (rVal - luminance) * sat) * scale + rOff
|
|
118
|
+
let g = (luminance + (gVal - luminance) * sat) * scale + gOff
|
|
119
|
+
let b = (luminance + (bVal - luminance) * sat) * scale + bOff
|
|
120
|
+
ptr[offset] = UInt8(min(max(r, 0), 255))
|
|
121
|
+
ptr[offset + 1] = UInt8(min(max(g, 0), 255))
|
|
122
|
+
ptr[offset + 2] = UInt8(min(max(b, 0), 255))
|
|
126
123
|
}
|
|
127
124
|
}
|
|
128
125
|
guard let filtered = buffer.makeImage() else { return image }
|
|
@@ -134,8 +131,5 @@ enum PhotoAdjustmentRenderer {
|
|
|
134
131
|
filtered.draw(in: CGRect(origin: .zero, size: image.size), blendMode: .normal, alpha: CGFloat(strength))
|
|
135
132
|
}
|
|
136
133
|
}
|
|
137
|
-
|
|
138
|
-
private static func clampByte(_ value: Double) -> UInt8 {
|
|
139
|
-
UInt8(min(max(value.rounded(), 0), 255))
|
|
140
|
-
}
|
|
141
134
|
}
|
|
135
|
+
|
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
import CoreGraphics
|
|
2
2
|
import UIKit
|
|
3
3
|
|
|
4
|
-
/// Raw RGBA8 pixel access for a `UIImage`,
|
|
5
|
-
///
|
|
6
|
-
///
|
|
7
|
-
/// Important: a bitmap `CGContext` created via `CGContext(data:...)` has its
|
|
8
|
-
/// origin at the bottom-left with Y increasing upward (Quartz-native), which
|
|
9
|
-
/// is the OPPOSITE of the top-down row order `CGImage`/`UIImage` assume.
|
|
10
|
-
/// Drawing into it without first flipping the CTM produces a vertically
|
|
11
|
-
/// flipped image once the raw bytes are wrapped back into a `CGImage`. Both
|
|
12
|
-
/// `init` and `makeImage()` apply that flip so round-tripping through this
|
|
13
|
-
/// buffer preserves the image's orientation.
|
|
4
|
+
/// Raw RGBA8 pixel access for a `UIImage`, optimized for high-throughput
|
|
5
|
+
/// contiguous memory access.
|
|
14
6
|
struct PixelBuffer {
|
|
15
|
-
|
|
7
|
+
var data: [UInt8]
|
|
16
8
|
let width: Int
|
|
17
9
|
let height: Int
|
|
18
10
|
private static let bytesPerPixel = 4
|
|
@@ -32,23 +24,36 @@ struct PixelBuffer {
|
|
|
32
24
|
space: CGColorSpaceCreateDeviceRGB(),
|
|
33
25
|
bitmapInfo: Self.bitmapInfo
|
|
34
26
|
) else { return nil }
|
|
35
|
-
context.translateBy(x: 0, y: CGFloat(height))
|
|
36
|
-
context.scaleBy(x: 1, y: -1)
|
|
37
27
|
context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height))
|
|
38
28
|
data = buffer
|
|
39
29
|
}
|
|
40
30
|
|
|
31
|
+
mutating func mutateBytes(_ body: (UnsafeMutablePointer<UInt8>, Int, Int, Int) -> Void) {
|
|
32
|
+
let count = data.count
|
|
33
|
+
let w = width
|
|
34
|
+
let h = height
|
|
35
|
+
data.withUnsafeMutableBufferPointer { buffer in
|
|
36
|
+
if let base = buffer.baseAddress {
|
|
37
|
+
body(base, count, w, h)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
41
42
|
func makeImage() -> UIImage? {
|
|
42
|
-
|
|
43
|
-
guard let
|
|
44
|
-
data: &buffer,
|
|
43
|
+
guard let provider = CGDataProvider(data: Data(data) as CFData) else { return nil }
|
|
44
|
+
guard let cgImage = CGImage(
|
|
45
45
|
width: width,
|
|
46
46
|
height: height,
|
|
47
47
|
bitsPerComponent: 8,
|
|
48
|
+
bitsPerPixel: 32,
|
|
48
49
|
bytesPerRow: width * Self.bytesPerPixel,
|
|
49
50
|
space: CGColorSpaceCreateDeviceRGB(),
|
|
50
|
-
bitmapInfo: Self.bitmapInfo
|
|
51
|
-
|
|
51
|
+
bitmapInfo: CGBitmapInfo(rawValue: Self.bitmapInfo),
|
|
52
|
+
provider: provider,
|
|
53
|
+
decode: nil,
|
|
54
|
+
shouldInterpolate: false,
|
|
55
|
+
intent: .defaultIntent
|
|
56
|
+
) else { return nil }
|
|
52
57
|
return UIImage(cgImage: cgImage)
|
|
53
58
|
}
|
|
54
59
|
|
|
@@ -66,3 +71,4 @@ struct PixelBuffer {
|
|
|
66
71
|
}
|
|
67
72
|
}
|
|
68
73
|
}
|
|
74
|
+
|
|
@@ -20,14 +20,14 @@ final class FilterThumbnailLoader {
|
|
|
20
20
|
deliver(cached)
|
|
21
21
|
return
|
|
22
22
|
}
|
|
23
|
-
// Scale once and reuse for every preset rather than per thumbnail.
|
|
24
|
-
let square = squareSource(from: source, size: size)
|
|
25
23
|
queue.async { [weak self] in
|
|
24
|
+
guard let self else { return }
|
|
25
|
+
let square = self.squareSource(from: source, size: size)
|
|
26
26
|
let rendered = PhotoAdjustmentRenderer.apply(
|
|
27
27
|
square,
|
|
28
28
|
adjustments: PhotoAdjustments(filterPreset: presetID, filterStrength: 100)
|
|
29
29
|
)
|
|
30
|
-
self
|
|
30
|
+
self.cache.setObject(rendered, forKey: presetID as NSString)
|
|
31
31
|
DispatchQueue.main.async { deliver(rendered) }
|
|
32
32
|
}
|
|
33
33
|
}
|
package/ios/PhotoVideoEditor.mm
CHANGED
|
@@ -1,10 +1,41 @@
|
|
|
1
1
|
#import <PhotoVideoEditorSpec/PhotoVideoEditorSpec.h>
|
|
2
2
|
#import "PhotoVideoEditor-Swift.h"
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// Objective-C cannot subclass a Swift class, so the TurboModule holds a
|
|
5
|
+
// `PhotoVideoEditorSwift` instance and forwards the spec methods to it.
|
|
6
|
+
@interface PhotoVideoEditor : NSObject <NativePhotoVideoEditorSpec>
|
|
5
7
|
@end
|
|
6
8
|
|
|
7
|
-
@implementation PhotoVideoEditor
|
|
9
|
+
@implementation PhotoVideoEditor {
|
|
10
|
+
PhotoVideoEditorSwift *_impl;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
- (instancetype)init
|
|
14
|
+
{
|
|
15
|
+
if (self = [super init]) {
|
|
16
|
+
_impl = [PhotoVideoEditorSwift new];
|
|
17
|
+
}
|
|
18
|
+
return self;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
- (void)openEditor:(NSString *)request
|
|
22
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
23
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
24
|
+
{
|
|
25
|
+
[_impl openEditor:request resolve:resolve reject:reject];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
- (void)cancelExport:(NSString *)jobId
|
|
29
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
30
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
31
|
+
{
|
|
32
|
+
[_impl cancelExport:jobId resolve:resolve reject:reject];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
- (NSNumber *)isAvailable
|
|
36
|
+
{
|
|
37
|
+
return [_impl isAvailable];
|
|
38
|
+
}
|
|
8
39
|
|
|
9
40
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
10
41
|
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
@@ -30,28 +30,32 @@ public class PhotoVideoEditorSwift: NSObject {
|
|
|
30
30
|
features: payload["features"] as? [String: Any] ?? [:],
|
|
31
31
|
theme: payload["theme"] as? [String: Any] ?? [:],
|
|
32
32
|
exportOptions: payload["export"] as? [String: Any] ?? [:],
|
|
33
|
-
stickerAssets: payload["stickerAssets"] as? [[String: Any]] ?? []
|
|
33
|
+
stickerAssets: payload["stickerAssets"] as? [[String: Any]] ?? [],
|
|
34
|
+
initialStickerId: payload["initialStickerId"] as? String
|
|
34
35
|
)
|
|
35
36
|
controller.completion = { [weak self, weak controller] outcome in
|
|
36
37
|
guard let self else { return }
|
|
37
38
|
self.editorOpen = false
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
39
|
+
DispatchQueue.main.async {
|
|
40
|
+
controller?.dismiss(animated: true) {
|
|
41
|
+
switch outcome {
|
|
42
|
+
case .cancelled:
|
|
43
|
+
var result: [String: Any] = ["uri": uri, "type": type, "cancelled": true]
|
|
44
|
+
if let mimeType = source["mimeType"] as? String, !mimeType.isEmpty { result["mimeType"] = mimeType }
|
|
45
|
+
PhotoVideoEditorSwift.resolveResult(result, resolve: resolve, reject: reject)
|
|
46
|
+
case let .success(resultUri, mimeType, width, height, fileSize, durationMs):
|
|
47
|
+
var result: [String: Any] = ["uri": resultUri, "type": type, "cancelled": false]
|
|
48
|
+
let resolvedMimeType = mimeType ?? (source["mimeType"] as? String)
|
|
49
|
+
if let resolvedMimeType, !resolvedMimeType.isEmpty { result["mimeType"] = resolvedMimeType }
|
|
50
|
+
if let width { result["width"] = width }
|
|
51
|
+
if let height { result["height"] = height }
|
|
52
|
+
if let fileSize { result["fileSize"] = fileSize }
|
|
53
|
+
if let durationMs { result["duration"] = durationMs }
|
|
54
|
+
PhotoVideoEditorSwift.resolveResult(result, resolve: resolve, reject: reject)
|
|
55
|
+
case let .failure(code, message):
|
|
56
|
+
reject(code, message, nil)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
55
59
|
}
|
|
56
60
|
}
|
|
57
61
|
DispatchQueue.main.async { presenter.present(controller, animated: true) }
|
|
@@ -236,7 +236,7 @@ final class OnlineStickerSheetViewController: UIViewController {
|
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
-
|
|
239
|
+
func loadRuntimeSticker(_ sticker: RuntimeSticker, completion: @escaping (URL?, UIImage?) -> Void) {
|
|
240
240
|
if !sticker.uri.lowercased().hasPrefix("https://") {
|
|
241
241
|
guard let path = SourceResolver.resolvePath(sourceUri: sticker.uri, tempPrefix: "pve_runtime_sticker") else {
|
|
242
242
|
completion(nil, nil)
|
|
@@ -24,19 +24,33 @@ final class VideoEditSession {
|
|
|
24
24
|
/// Fires once the source is probed and the initial single-clip timeline is ready.
|
|
25
25
|
var onClipsReady: (() -> Void)?
|
|
26
26
|
|
|
27
|
+
private(set) var naturalSize: CGSize = .zero
|
|
28
|
+
private var cachedAsset: AVURLAsset?
|
|
27
29
|
private var statusObservation: NSKeyValueObservation?
|
|
30
|
+
private let sourceUri: String
|
|
28
31
|
|
|
29
32
|
init(sourceUri: String) {
|
|
30
|
-
|
|
33
|
+
self.sourceUri = sourceUri
|
|
34
|
+
let resolvedPath = SourceResolver.resolvePath(sourceUri: sourceUri, tempPrefix: "pve_video_preview")
|
|
35
|
+
let url = resolvedPath.map { URL(fileURLWithPath: $0) }
|
|
36
|
+
?? (URL(string: sourceUri)?.scheme != nil ? URL(string: sourceUri)! : URL(fileURLWithPath: sourceUri))
|
|
31
37
|
let asset = AVURLAsset(url: url)
|
|
38
|
+
cachedAsset = asset
|
|
32
39
|
let probeItem = AVPlayerItem(asset: asset)
|
|
33
|
-
statusObservation = probeItem.observe(\.status, options: [.new]) { [weak self] observedItem, _ in
|
|
34
|
-
guard let self, observedItem.status == .readyToPlay
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
statusObservation = probeItem.observe(\.status, options: [.initial, .new]) { [weak self] observedItem, _ in
|
|
41
|
+
guard let self, observedItem.status == .readyToPlay else { return }
|
|
42
|
+
DispatchQueue.main.async { [weak self] in
|
|
43
|
+
guard let self, self.clips.isEmpty else { return }
|
|
44
|
+
if let track = asset.tracks(withMediaType: .video).first {
|
|
45
|
+
let size = track.naturalSize.applying(track.preferredTransform)
|
|
46
|
+
self.naturalSize = CGSize(width: max(1, abs(size.width)), height: max(1, abs(size.height)))
|
|
47
|
+
}
|
|
48
|
+
let durationSeconds = CMTimeGetSeconds(asset.duration)
|
|
49
|
+
guard durationSeconds.isFinite, durationSeconds > 0 else { return }
|
|
50
|
+
self.clips = [VideoClip(sourceUri: sourceUri, originalDurationMs: Int64(durationSeconds * 1000))]
|
|
51
|
+
self.rebuildPlayerComposition()
|
|
52
|
+
self.onClipsReady?()
|
|
53
|
+
}
|
|
40
54
|
}
|
|
41
55
|
player.replaceCurrentItem(with: probeItem)
|
|
42
56
|
}
|
|
@@ -59,8 +73,13 @@ final class VideoEditSession {
|
|
|
59
73
|
|
|
60
74
|
/// The `AVURLAsset` for one clip's own source file (all clips share one source this milestone).
|
|
61
75
|
func asset(for clip: VideoClip) -> AVURLAsset {
|
|
62
|
-
let
|
|
63
|
-
|
|
76
|
+
if let cached = cachedAsset, clip.sourceUri == sourceUri { return cached }
|
|
77
|
+
let resolvedPath = SourceResolver.resolvePath(sourceUri: clip.sourceUri, tempPrefix: "pve_video_preview")
|
|
78
|
+
let url = resolvedPath.map { URL(fileURLWithPath: $0) }
|
|
79
|
+
?? (URL(string: clip.sourceUri)?.scheme != nil ? URL(string: clip.sourceUri)! : URL(fileURLWithPath: clip.sourceUri))
|
|
80
|
+
let asset = AVURLAsset(url: url)
|
|
81
|
+
if clip.sourceUri == sourceUri { cachedAsset = asset }
|
|
82
|
+
return asset
|
|
64
83
|
}
|
|
65
84
|
|
|
66
85
|
private func rebuildPlayerComposition() {
|
|
@@ -95,5 +114,5 @@ final class VideoEditSession {
|
|
|
95
114
|
}
|
|
96
115
|
|
|
97
116
|
private extension Int64 {
|
|
98
|
-
func clamped(to range: ClosedRange<Int64>) -> Int64 { min(max(self, range.lowerBound), range.upperBound) }
|
|
117
|
+
func clamped(to range: ClosedRange<Int64>) -> Int64 { Swift.min(Swift.max(self, range.lowerBound), range.upperBound) }
|
|
99
118
|
}
|
package/lib/module/index.js
CHANGED
|
@@ -39,6 +39,11 @@ function normalizeOptions(input) {
|
|
|
39
39
|
positive(output.frameRate, 'export.frameRate');
|
|
40
40
|
if (input.source.type === 'photo' && output.videoFormat !== undefined) invalid('videoFormat is only valid for video sources.');
|
|
41
41
|
if (input.source.type === 'video' && output.imageFormat !== undefined) invalid('imageFormat is only valid for photo sources.');
|
|
42
|
+
if (input.initialStickerId !== undefined) {
|
|
43
|
+
if (typeof input.initialStickerId !== 'string' || !input.initialStickerId.trim()) invalid('initialStickerId must be a non-empty string.');
|
|
44
|
+
const matches = Array.isArray(input.stickerAssets) ? input.stickerAssets.filter(asset => asset?.id === input.initialStickerId) : [];
|
|
45
|
+
if (matches.length !== 1 || typeof matches[0]?.uri !== 'string' || !matches[0].uri.trim()) invalid('initialStickerId must match exactly one stickerAssets entry with a non-empty uri.');
|
|
46
|
+
}
|
|
42
47
|
const features = {
|
|
43
48
|
...defaultFeatures,
|
|
44
49
|
...input.features
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["NativePhotoVideoEditor","PhotoVideoEditorError","normalizeEditorError","addEditorEventListener","addExportProgressListener","EDITOR_PROJECT_SCHEMA_VERSION","mediaTypes","Set","qualities","imageFormats","videoFormats","defaultFeatures","crop","rotate","trim","filters","text","stickers","draw","overlays","invalid","message","positive","value","name","undefined","Number","isFinite","normalizeOptions","input","source","uri","has","type","output","export","quality","imageFormat","videoFormat","maxWidth","maxHeight","videoBitrate","frameRate","features","preserveMetadata","saveToGallery","openEditor","options","JSON","parse","stringify","error","openPhotoEditor","openVideoEditor","cancelExport","jobId","isAvailable"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":"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
|
|
1
|
+
{"version":3,"names":["NativePhotoVideoEditor","PhotoVideoEditorError","normalizeEditorError","addEditorEventListener","addExportProgressListener","EDITOR_PROJECT_SCHEMA_VERSION","mediaTypes","Set","qualities","imageFormats","videoFormats","defaultFeatures","crop","rotate","trim","filters","text","stickers","draw","overlays","invalid","message","positive","value","name","undefined","Number","isFinite","normalizeOptions","input","source","uri","has","type","output","export","quality","imageFormat","videoFormat","maxWidth","maxHeight","videoBitrate","frameRate","initialStickerId","matches","Array","isArray","stickerAssets","filter","asset","id","length","features","preserveMetadata","saveToGallery","openEditor","options","JSON","parse","stringify","error","openPhotoEditor","openVideoEditor","cancelExport","jobId","isAvailable"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EAIZ,kBAAkB,EAElB,kBAAkB,EACnB,MAAM,YAAS,CAAC;AAEjB,OAAO,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,MAAM,aAAU,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAU,CAAC;AACjD,mBAAmB,YAAS,CAAC;AAC7B,OAAO,EAAE,6BAA6B,EAAE,MAAM,cAAW,CAAC;AAC1D,YAAY,EAAE,aAAa,EAAE,MAAM,cAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EAIZ,kBAAkB,EAElB,kBAAkB,EACnB,MAAM,YAAS,CAAC;AAEjB,OAAO,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,MAAM,aAAU,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAU,CAAC;AACjD,mBAAmB,YAAS,CAAC;AAC7B,OAAO,EAAE,6BAA6B,EAAE,MAAM,cAAW,CAAC;AAC1D,YAAY,EAAE,aAAa,EAAE,MAAM,cAAW,CAAC;AA0F/C,mEAAmE;AACnE,wBAAsB,UAAU,CAC9B,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,YAAY,CAAC,CAUvB;AACD,wBAAgB,eAAe,CAC7B,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CAEvB;AACD,wBAAgB,eAAe,CAC7B,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CAEvB;AACD,0FAA0F;AAC1F,wBAAsB,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAMnE;AACD,4EAA4E;AAC5E,wBAAgB,WAAW,IAAI,OAAO,CAErC"}
|
|
@@ -53,7 +53,7 @@ export interface FontAsset {
|
|
|
53
53
|
/** A consumer-provided sticker image, selectable from the Stickers tool. */
|
|
54
54
|
export interface StickerAsset {
|
|
55
55
|
/** Stable identifier, echoed back on layers created from this asset. */ id: string;
|
|
56
|
-
/** Sticker image URI. Supports HTTPS URLs
|
|
56
|
+
/** Sticker image URI. Supports HTTPS URLs, local device file/content URIs, and data:image/png;base64 URIs. */ uri: string;
|
|
57
57
|
}
|
|
58
58
|
/** Options accepted by {@link openEditor}. */
|
|
59
59
|
export interface EditorOptions {
|
|
@@ -62,6 +62,7 @@ export interface EditorOptions {
|
|
|
62
62
|
/** Native UI theme. */ theme?: EditorTheme;
|
|
63
63
|
/** Export preferences. */ export?: ExportOptions;
|
|
64
64
|
/** Requests saving to the gallery. Currently ignored by the placeholder. */ saveToGallery?: boolean;
|
|
65
|
+
/** ID in stickerAssets to insert at the media center when the editor opens. */ initialStickerId?: string;
|
|
65
66
|
/** Consumer-provided stickers offered by the Stickers tool, in addition to the small built-in set and end-user uploads. */ stickerAssets?: StickerAsset[];
|
|
66
67
|
/** Consumer-provided fonts selectable from the Text tool. */ fonts?: FontAsset[];
|
|
67
68
|
}
|