react-native-photo-video-editor 0.1.0 → 0.2.0
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 +20 -0
- package/PhotoVideoEditor.podspec +1 -0
- package/README.md +10 -6
- package/THIRD_PARTY_NOTICES.md +20 -0
- package/android/build.gradle +1 -1
- package/android/src/main/AndroidManifest.xml +1 -1
- package/android/src/main/assets/Stickers/AUTUMN_1.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_10.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_11.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_12.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_13.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_14.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_15.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_16.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_17.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_18.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_2.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_3.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_4.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_5.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_6.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_7.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_8.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_9.png +0 -0
- package/android/src/main/assets/Stickers/LOL_1.png +0 -0
- package/android/src/main/assets/Stickers/LOL_10.png +0 -0
- package/android/src/main/assets/Stickers/LOL_11.png +0 -0
- package/android/src/main/assets/Stickers/LOL_12.png +0 -0
- package/android/src/main/assets/Stickers/LOL_13.png +0 -0
- package/android/src/main/assets/Stickers/LOL_14.png +0 -0
- package/android/src/main/assets/Stickers/LOL_15.png +0 -0
- package/android/src/main/assets/Stickers/LOL_16.png +0 -0
- package/android/src/main/assets/Stickers/LOL_17.png +0 -0
- package/android/src/main/assets/Stickers/LOL_18.png +0 -0
- package/android/src/main/assets/Stickers/LOL_19.png +0 -0
- package/android/src/main/assets/Stickers/LOL_2.png +0 -0
- package/android/src/main/assets/Stickers/LOL_3.png +0 -0
- package/android/src/main/assets/Stickers/LOL_4.png +0 -0
- package/android/src/main/assets/Stickers/LOL_5.png +0 -0
- package/android/src/main/assets/Stickers/LOL_6.png +0 -0
- package/android/src/main/assets/Stickers/LOL_7.png +0 -0
- package/android/src/main/assets/Stickers/LOL_8.png +0 -0
- package/android/src/main/assets/Stickers/LOL_9.png +0 -0
- package/android/src/main/assets/Stickers/SPRING_1.png +0 -0
- package/android/src/main/assets/Stickers/SPRING_2.png +0 -0
- package/android/src/main/assets/Stickers/SPRING_3.png +0 -0
- package/android/src/main/assets/Stickers/SPRING_4.png +0 -0
- package/android/src/main/assets/Stickers/SPRING_5.png +0 -0
- package/android/src/main/assets/Stickers/SPRING_6.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_1.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_10.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_11.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_12.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_13.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_14.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_15.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_16.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_17.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_18.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_19.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_2.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_20.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_21.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_3.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_4.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_5.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_6.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_7.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_8.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_9.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_1.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_12.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_13.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_14.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_15.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_16.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_2.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_25.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_26.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_27.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_28.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_29.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_3.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_30.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_31.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_32.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_7.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_8.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_9.png +0 -0
- package/android/src/main/java/com/photovideoeditor/editor/PhotoVideoEditorActivity.kt +901 -651
- package/android/src/main/java/com/photovideoeditor/photo/export/PhotoExporter.kt +8 -23
- package/android/src/main/java/com/photovideoeditor/photo/render/CropGeometry.kt +71 -0
- package/android/src/main/java/com/photovideoeditor/photo/render/OverlayGeometry.kt +36 -0
- package/android/src/main/java/com/photovideoeditor/photo/render/PhotoEditSession.kt +5 -7
- package/android/src/main/java/com/photovideoeditor/photo/render/PhotoFilterPresets.kt +71 -41
- package/android/src/main/java/com/photovideoeditor/photo/render/PhotoLayer.kt +5 -1
- package/android/src/main/java/com/photovideoeditor/photo/render/PhotoLayerRenderer.kt +24 -1
- package/android/src/main/java/com/photovideoeditor/photo/render/PhotoTransformRenderer.kt +33 -0
- package/android/src/main/java/com/photovideoeditor/photo/render/PhotoTransformState.kt +6 -1
- package/android/src/main/java/com/photovideoeditor/photo/ui/CropOverlayView.kt +112 -122
- package/android/src/main/java/com/photovideoeditor/photo/ui/DrawOverlayView.kt +66 -0
- package/android/src/main/java/com/photovideoeditor/photo/ui/LayerOverlayView.kt +63 -14
- package/android/src/main/java/com/photovideoeditor/photo/ui/ZoomableImageView.kt +10 -0
- package/android/src/main/java/com/photovideoeditor/stickers/OnlineStickerSheet.kt +135 -104
- package/android/src/main/java/com/photovideoeditor/ui/components/CropPanel.kt +122 -0
- package/android/src/main/java/com/photovideoeditor/ui/components/EditorPanel.kt +250 -0
- package/android/src/main/java/com/photovideoeditor/ui/components/EditorToolSlider.kt +103 -0
- package/android/src/main/java/com/photovideoeditor/video/export/VideoExporter.kt +38 -11
- package/android/src/main/java/com/photovideoeditor/video/render/VideoCropEffects.kt +35 -0
- package/android/src/main/java/com/photovideoeditor/video/render/VideoEditSession.kt +10 -0
- package/android/src/main/java/com/photovideoeditor/video/render/VideoTransformState.kt +1 -0
- package/android/src/main/res/drawable/ic_draw.xml +6 -0
- package/android/src/main/res/drawable/ic_pause.xml +7 -0
- package/android/src/main/res/drawable/ic_play.xml +7 -0
- package/docs/api.md +1 -1
- package/docs/editor-ui-validation.md +22 -0
- package/docs/photo-editor.md +5 -6
- package/ios/Editor/PhotoVideoEditorViewController.swift +918 -494
- package/ios/Editor/UI/CropPanel.swift +97 -0
- package/ios/Editor/UI/EditorPanel.swift +334 -0
- package/ios/Editor/UI/EditorToolSlider.swift +22 -0
- package/ios/Editor/UI/TextEditorSheet.swift +84 -0
- package/ios/Photo/Export/PhotoExporter.swift +3 -3
- package/ios/Photo/Rendering/CropGeometry.swift +48 -0
- package/ios/Photo/Rendering/PhotoEditSession.swift +21 -10
- package/ios/Photo/Rendering/PhotoFilterPresets.swift +53 -23
- package/ios/Photo/Rendering/PhotoLayer.swift +14 -2
- package/ios/Photo/Rendering/PhotoLayerRenderer.swift +16 -1
- package/ios/Photo/Rendering/PhotoTransformState.swift +5 -0
- package/ios/Photo/UI/CropOverlayView.swift +98 -134
- package/ios/Photo/UI/DrawOverlayView.swift +48 -0
- package/ios/Photo/UI/FilterThumbnailLoader.swift +62 -0
- package/ios/Photo/UI/LayerOverlayView.swift +36 -11
- package/ios/Photo/UI/ZoomableImageView.swift +19 -1
- package/ios/Resources/Stickers/AUTUMN_1.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_10.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_11.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_12.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_13.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_14.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_15.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_16.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_17.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_18.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_2.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_3.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_4.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_5.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_6.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_7.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_8.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_9.png +0 -0
- package/ios/Resources/Stickers/LOL_1.png +0 -0
- package/ios/Resources/Stickers/LOL_10.png +0 -0
- package/ios/Resources/Stickers/LOL_11.png +0 -0
- package/ios/Resources/Stickers/LOL_12.png +0 -0
- package/ios/Resources/Stickers/LOL_13.png +0 -0
- package/ios/Resources/Stickers/LOL_14.png +0 -0
- package/ios/Resources/Stickers/LOL_15.png +0 -0
- package/ios/Resources/Stickers/LOL_16.png +0 -0
- package/ios/Resources/Stickers/LOL_17.png +0 -0
- package/ios/Resources/Stickers/LOL_18.png +0 -0
- package/ios/Resources/Stickers/LOL_19.png +0 -0
- package/ios/Resources/Stickers/LOL_2.png +0 -0
- package/ios/Resources/Stickers/LOL_3.png +0 -0
- package/ios/Resources/Stickers/LOL_4.png +0 -0
- package/ios/Resources/Stickers/LOL_5.png +0 -0
- package/ios/Resources/Stickers/LOL_6.png +0 -0
- package/ios/Resources/Stickers/LOL_7.png +0 -0
- package/ios/Resources/Stickers/LOL_8.png +0 -0
- package/ios/Resources/Stickers/LOL_9.png +0 -0
- package/ios/Resources/Stickers/SPRING_1.png +0 -0
- package/ios/Resources/Stickers/SPRING_2.png +0 -0
- package/ios/Resources/Stickers/SPRING_3.png +0 -0
- package/ios/Resources/Stickers/SPRING_4.png +0 -0
- package/ios/Resources/Stickers/SPRING_5.png +0 -0
- package/ios/Resources/Stickers/SPRING_6.png +0 -0
- package/ios/Resources/Stickers/SUMMER_1.png +0 -0
- package/ios/Resources/Stickers/SUMMER_10.png +0 -0
- package/ios/Resources/Stickers/SUMMER_11.png +0 -0
- package/ios/Resources/Stickers/SUMMER_12.png +0 -0
- package/ios/Resources/Stickers/SUMMER_13.png +0 -0
- package/ios/Resources/Stickers/SUMMER_14.png +0 -0
- package/ios/Resources/Stickers/SUMMER_15.png +0 -0
- package/ios/Resources/Stickers/SUMMER_16.png +0 -0
- package/ios/Resources/Stickers/SUMMER_17.png +0 -0
- package/ios/Resources/Stickers/SUMMER_18.png +0 -0
- package/ios/Resources/Stickers/SUMMER_19.png +0 -0
- package/ios/Resources/Stickers/SUMMER_2.png +0 -0
- package/ios/Resources/Stickers/SUMMER_20.png +0 -0
- package/ios/Resources/Stickers/SUMMER_21.png +0 -0
- package/ios/Resources/Stickers/SUMMER_3.png +0 -0
- package/ios/Resources/Stickers/SUMMER_4.png +0 -0
- package/ios/Resources/Stickers/SUMMER_5.png +0 -0
- package/ios/Resources/Stickers/SUMMER_6.png +0 -0
- package/ios/Resources/Stickers/SUMMER_7.png +0 -0
- package/ios/Resources/Stickers/SUMMER_8.png +0 -0
- package/ios/Resources/Stickers/SUMMER_9.png +0 -0
- package/ios/Resources/Stickers/TEXT_1.png +0 -0
- package/ios/Resources/Stickers/TEXT_12.png +0 -0
- package/ios/Resources/Stickers/TEXT_13.png +0 -0
- package/ios/Resources/Stickers/TEXT_14.png +0 -0
- package/ios/Resources/Stickers/TEXT_15.png +0 -0
- package/ios/Resources/Stickers/TEXT_16.png +0 -0
- package/ios/Resources/Stickers/TEXT_2.png +0 -0
- package/ios/Resources/Stickers/TEXT_25.png +0 -0
- package/ios/Resources/Stickers/TEXT_26.png +0 -0
- package/ios/Resources/Stickers/TEXT_27.png +0 -0
- package/ios/Resources/Stickers/TEXT_28.png +0 -0
- package/ios/Resources/Stickers/TEXT_29.png +0 -0
- package/ios/Resources/Stickers/TEXT_3.png +0 -0
- package/ios/Resources/Stickers/TEXT_30.png +0 -0
- package/ios/Resources/Stickers/TEXT_31.png +0 -0
- package/ios/Resources/Stickers/TEXT_32.png +0 -0
- package/ios/Resources/Stickers/TEXT_7.png +0 -0
- package/ios/Resources/Stickers/TEXT_8.png +0 -0
- package/ios/Resources/Stickers/TEXT_9.png +0 -0
- package/ios/Resources/Stickers/a_pety_icon.png +0 -0
- package/ios/Stickers/OnlineStickerSheetViewController.swift +102 -159
- package/ios/Video/Export/VideoExporter.swift +10 -1
- package/ios/Video/Render/VideoCropGeometry.swift +48 -0
- package/ios/Video/Render/VideoTransformState.swift +1 -0
- package/lib/module/index.js +2 -2
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/types.d.ts +2 -2
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/index.ts +1 -1
- package/src/types.ts +2 -2
- package/android/src/main/java/com/photovideoeditor/stickers/OpenMojiCatalog.kt +0 -172
- package/android/src/main/java/com/photovideoeditor/stickers/RemoteImageLoader.kt +0 -110
- package/ios/Stickers/OpenMojiCatalog.swift +0 -188
- package/ios/Stickers/RemoteImageLoader.swift +0 -127
|
@@ -1,49 +1,42 @@
|
|
|
1
1
|
import UIKit
|
|
2
2
|
|
|
3
|
-
/// Bottom sheet: search + category filter + a
|
|
4
|
-
///
|
|
3
|
+
/// Bottom sheet: search + category filter + a 5-column grid of bundled stickers. Tapping one
|
|
4
|
+
/// writes the PNG to a temp file and hands `(URL, UIImage)` back to
|
|
5
5
|
/// `onStickerPicked` — the caller feeds that straight into the existing
|
|
6
6
|
/// `handlePickedImage(_:image:purpose:)` insertion path (photo sticker chip / video sticker
|
|
7
7
|
/// action sheet), so this file has zero knowledge of `PhotoLayer`/session internals.
|
|
8
8
|
///
|
|
9
9
|
/// Built entirely in code (no storyboard/xib), consistent with the rest of this codebase.
|
|
10
10
|
final class OnlineStickerSheetViewController: UIViewController {
|
|
11
|
-
|
|
11
|
+
struct RuntimeSticker { let id: String; let uri: String }
|
|
12
|
+
/// Fired once after the bundled image is written to a temp file.
|
|
12
13
|
/// The sheet dismisses itself first, then invokes this closure.
|
|
13
14
|
var onStickerPicked: ((URL, UIImage) -> Void)?
|
|
15
|
+
var onDismiss: (() -> Void)?
|
|
16
|
+
private var didNotifyDismiss = false
|
|
14
17
|
|
|
15
|
-
private
|
|
16
|
-
private
|
|
17
|
-
|
|
18
|
-
private
|
|
19
|
-
case loading, loaded, error(String)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
private var loadState: LoadState = .loading
|
|
23
|
-
private var allEntries: [OpenMojiEntry] = []
|
|
24
|
-
private var displayedEntries: [OpenMojiEntry] = []
|
|
18
|
+
private var localStickerNames: [String] = []
|
|
19
|
+
private var displayedLocalStickerNames: [String] = []
|
|
20
|
+
private let runtimeStickers: [RuntimeSticker]
|
|
21
|
+
private var displayedRuntimeStickers: [RuntimeSticker] = []
|
|
25
22
|
private var selectedGroup: String? // nil == "All"
|
|
26
23
|
private var searchQuery: String = ""
|
|
27
24
|
private var searchDebounceTimer: Timer?
|
|
28
|
-
private var downloadInFlightHexcode: String?
|
|
29
25
|
|
|
30
26
|
private let searchBar = UISearchBar()
|
|
31
27
|
private let categoryScroll = UIScrollView()
|
|
32
28
|
private let categoryStack = UIStackView()
|
|
33
29
|
private var categoryButtons: [String: UIButton] = [:] // key "" == All
|
|
34
30
|
private let collectionView: UICollectionView
|
|
35
|
-
private let spinner = UIActivityIndicatorView(style: .large)
|
|
36
31
|
private let emptyLabel = UILabel()
|
|
37
|
-
private let errorLabel = UILabel()
|
|
38
|
-
private let retryButton = UIButton(type: .system)
|
|
39
|
-
private let attributionLabel = UILabel()
|
|
40
32
|
private let stateContainer = UIView()
|
|
41
33
|
|
|
42
|
-
private static let cellReuseID = "
|
|
43
|
-
private static let columns =
|
|
44
|
-
private static let cellSpacing: CGFloat =
|
|
34
|
+
private static let cellReuseID = "BundledStickerCell"
|
|
35
|
+
private static let columns = 5
|
|
36
|
+
private static let cellSpacing: CGFloat = 6
|
|
45
37
|
|
|
46
|
-
init() {
|
|
38
|
+
init(runtimeStickers: [RuntimeSticker] = []) {
|
|
39
|
+
self.runtimeStickers = runtimeStickers
|
|
47
40
|
let layout = UICollectionViewFlowLayout()
|
|
48
41
|
layout.minimumInteritemSpacing = Self.cellSpacing
|
|
49
42
|
layout.minimumLineSpacing = Self.cellSpacing
|
|
@@ -56,9 +49,16 @@ final class OnlineStickerSheetViewController: UIViewController {
|
|
|
56
49
|
|
|
57
50
|
override func viewDidLoad() {
|
|
58
51
|
super.viewDidLoad()
|
|
59
|
-
view.backgroundColor = DesignTokens.
|
|
52
|
+
view.backgroundColor = DesignTokens.elevatedPanel
|
|
60
53
|
buildUI()
|
|
61
|
-
|
|
54
|
+
loadLocalStickers()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
override func viewDidDisappear(_ animated: Bool) {
|
|
58
|
+
super.viewDidDisappear(animated)
|
|
59
|
+
guard (isBeingDismissed || presentingViewController == nil), !didNotifyDismiss else { return }
|
|
60
|
+
didNotifyDismiss = true
|
|
61
|
+
onDismiss?()
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
// MARK: - UI construction
|
|
@@ -86,9 +86,7 @@ final class OnlineStickerSheetViewController: UIViewController {
|
|
|
86
86
|
collectionView.backgroundColor = .clear
|
|
87
87
|
collectionView.dataSource = self
|
|
88
88
|
collectionView.delegate = self
|
|
89
|
-
collectionView.register(
|
|
90
|
-
|
|
91
|
-
spinner.hidesWhenStopped = true
|
|
89
|
+
collectionView.register(BundledStickerCell.self, forCellWithReuseIdentifier: Self.cellReuseID)
|
|
92
90
|
|
|
93
91
|
emptyLabel.text = "No stickers found."
|
|
94
92
|
emptyLabel.textColor = DesignTokens.onSurfaceVariant
|
|
@@ -96,19 +94,7 @@ final class OnlineStickerSheetViewController: UIViewController {
|
|
|
96
94
|
emptyLabel.font = .systemFont(ofSize: 14)
|
|
97
95
|
emptyLabel.isHidden = true
|
|
98
96
|
|
|
99
|
-
|
|
100
|
-
errorLabel.textColor = DesignTokens.onSurfaceVariant
|
|
101
|
-
errorLabel.textAlignment = .center
|
|
102
|
-
errorLabel.font = .systemFont(ofSize: 14)
|
|
103
|
-
errorLabel.numberOfLines = 0
|
|
104
|
-
errorLabel.isHidden = true
|
|
105
|
-
|
|
106
|
-
retryButton.setTitle("Retry", for: .normal)
|
|
107
|
-
retryButton.setTitleColor(DesignTokens.primaryContainer, for: .normal)
|
|
108
|
-
retryButton.isHidden = true
|
|
109
|
-
retryButton.addAction(UIAction { [weak self] _ in self?.loadCatalog(forceRefresh: true) }, for: .touchUpInside)
|
|
110
|
-
|
|
111
|
-
let stateStack = UIStackView(arrangedSubviews: [spinner, emptyLabel, errorLabel, retryButton])
|
|
97
|
+
let stateStack = UIStackView(arrangedSubviews: [emptyLabel])
|
|
112
98
|
stateStack.axis = .vertical
|
|
113
99
|
stateStack.alignment = .center
|
|
114
100
|
stateStack.spacing = 12
|
|
@@ -121,14 +107,9 @@ final class OnlineStickerSheetViewController: UIViewController {
|
|
|
121
107
|
stateStack.trailingAnchor.constraint(lessThanOrEqualTo: stateContainer.trailingAnchor, constant: -24),
|
|
122
108
|
])
|
|
123
109
|
|
|
124
|
-
attributionLabel.text = "Stickers by OpenMoji (CC BY-SA 4.0)"
|
|
125
|
-
attributionLabel.font = .systemFont(ofSize: 11)
|
|
126
|
-
attributionLabel.textColor = DesignTokens.outline
|
|
127
|
-
attributionLabel.textAlignment = .center
|
|
128
|
-
|
|
129
110
|
// stateContainer (spinner/empty/error) overlays the collection view's area rather than
|
|
130
111
|
// sharing stack space, since it's shown/hidden independently of the grid's own layout.
|
|
131
|
-
let root = UIStackView(arrangedSubviews: [searchBar, categoryScroll, collectionView
|
|
112
|
+
let root = UIStackView(arrangedSubviews: [searchBar, categoryScroll, collectionView])
|
|
132
113
|
root.axis = .vertical
|
|
133
114
|
root.translatesAutoresizingMaskIntoConstraints = false
|
|
134
115
|
|
|
@@ -142,8 +123,6 @@ final class OnlineStickerSheetViewController: UIViewController {
|
|
|
142
123
|
root.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 8),
|
|
143
124
|
root.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -8),
|
|
144
125
|
categoryScroll.heightAnchor.constraint(equalToConstant: 40),
|
|
145
|
-
attributionLabel.heightAnchor.constraint(equalToConstant: 20),
|
|
146
|
-
|
|
147
126
|
stateContainer.leadingAnchor.constraint(equalTo: collectionView.leadingAnchor),
|
|
148
127
|
stateContainer.trailingAnchor.constraint(equalTo: collectionView.trailingAnchor),
|
|
149
128
|
stateContainer.topAnchor.constraint(equalTo: collectionView.topAnchor),
|
|
@@ -155,7 +134,9 @@ final class OnlineStickerSheetViewController: UIViewController {
|
|
|
155
134
|
categoryStack.arrangedSubviews.forEach { $0.removeFromSuperview() }
|
|
156
135
|
categoryButtons.removeAll()
|
|
157
136
|
|
|
158
|
-
let
|
|
137
|
+
let localGroups = Set(localStickerNames.compactMap { $0.split(separator: "_").first.map { "local:" + $0.lowercased() } }).sorted()
|
|
138
|
+
let runtimeGroups = runtimeStickers.isEmpty ? [] : [("runtime", "My stickers")]
|
|
139
|
+
let all = [("", "All")] + runtimeGroups + localGroups.map { ($0, Self.displayName(for: $0.replacingOccurrences(of: "local:", with: ""))) }
|
|
159
140
|
all.forEach { key, title in
|
|
160
141
|
let chip = makeChip(title: title) { [weak self] in self?.selectCategory(key) }
|
|
161
142
|
categoryButtons[key] = chip
|
|
@@ -200,47 +181,22 @@ final class OnlineStickerSheetViewController: UIViewController {
|
|
|
200
181
|
}
|
|
201
182
|
}
|
|
202
183
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
self.applyFilter()
|
|
213
|
-
self.setLoadState(.loaded)
|
|
214
|
-
} else {
|
|
215
|
-
self.setLoadState(.error((error as NSError?)?.localizedDescription ?? "Couldn't load stickers — check your connection."))
|
|
216
|
-
}
|
|
217
|
-
}
|
|
184
|
+
private func loadLocalStickers() {
|
|
185
|
+
let hostBundle = Bundle(for: OnlineStickerSheetViewController.self)
|
|
186
|
+
let bundleURL = Bundle.main.url(forResource: "PhotoVideoEditorStickers", withExtension: "bundle")
|
|
187
|
+
?? hostBundle.url(forResource: "PhotoVideoEditorStickers", withExtension: "bundle")
|
|
188
|
+
guard let bundleURL, let bundle = Bundle(url: bundleURL) else { return }
|
|
189
|
+
localStickerNames = (bundle.urls(forResourcesWithExtension: "png", subdirectory: nil) ?? []).map(\.lastPathComponent).sorted()
|
|
190
|
+
displayedLocalStickerNames = localStickerNames
|
|
191
|
+
rebuildCategoryChips()
|
|
192
|
+
applyFilter()
|
|
218
193
|
}
|
|
219
194
|
|
|
220
|
-
private func
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
spinner.startAnimating()
|
|
226
|
-
emptyLabel.isHidden = true
|
|
227
|
-
errorLabel.isHidden = true
|
|
228
|
-
retryButton.isHidden = true
|
|
229
|
-
case .loaded:
|
|
230
|
-
spinner.stopAnimating()
|
|
231
|
-
let isEmpty = displayedEntries.isEmpty
|
|
232
|
-
stateContainer.isHidden = !isEmpty
|
|
233
|
-
emptyLabel.isHidden = !isEmpty
|
|
234
|
-
errorLabel.isHidden = true
|
|
235
|
-
retryButton.isHidden = true
|
|
236
|
-
case .error(let message):
|
|
237
|
-
spinner.stopAnimating()
|
|
238
|
-
stateContainer.isHidden = false
|
|
239
|
-
emptyLabel.isHidden = true
|
|
240
|
-
errorLabel.isHidden = false
|
|
241
|
-
errorLabel.text = message
|
|
242
|
-
retryButton.isHidden = false
|
|
243
|
-
}
|
|
195
|
+
private func localStickerURL(named name: String) -> URL? {
|
|
196
|
+
let hostBundle = Bundle(for: OnlineStickerSheetViewController.self)
|
|
197
|
+
let bundleURL = Bundle.main.url(forResource: "PhotoVideoEditorStickers", withExtension: "bundle")
|
|
198
|
+
?? hostBundle.url(forResource: "PhotoVideoEditorStickers", withExtension: "bundle")
|
|
199
|
+
return bundleURL.flatMap(Bundle.init(url:))?.url(forResource: (name as NSString).deletingPathExtension, withExtension: "png")
|
|
244
200
|
}
|
|
245
201
|
|
|
246
202
|
// MARK: - Filtering
|
|
@@ -254,47 +210,19 @@ final class OnlineStickerSheetViewController: UIViewController {
|
|
|
254
210
|
/// Search takes precedence over category when non-empty; otherwise filter by selected
|
|
255
211
|
/// category, with "All" (nil) meaning no filter.
|
|
256
212
|
private func applyFilter() {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
displayedEntries = allEntries
|
|
213
|
+
let normalizedQuery = searchQuery.lowercased()
|
|
214
|
+
displayedLocalStickerNames = localStickerNames.filter { name in
|
|
215
|
+
let matchesQuery = normalizedQuery.isEmpty || name.lowercased().contains(normalizedQuery)
|
|
216
|
+
let matchesGroup = selectedGroup == nil || (selectedGroup?.hasPrefix("local:") == true && name.lowercased().hasPrefix(selectedGroup!.replacingOccurrences(of: "local:", with: "")))
|
|
217
|
+
return matchesQuery && matchesGroup
|
|
263
218
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// MARK: - Sticker selection / download
|
|
269
|
-
|
|
270
|
-
private func stickerTapped(_ entry: OpenMojiEntry, at indexPath: IndexPath) {
|
|
271
|
-
guard downloadInFlightHexcode == nil else { return }
|
|
272
|
-
downloadInFlightHexcode = entry.hexcode
|
|
273
|
-
if let cell = collectionView.cellForItem(at: indexPath) as? OpenMojiCell {
|
|
274
|
-
cell.setDownloading(true)
|
|
275
|
-
}
|
|
276
|
-
let url = catalog.fullURL(entry.hexcode)
|
|
277
|
-
loader.downloadFull(url: url) { [weak self] fileURL, error in
|
|
278
|
-
guard let self else { return }
|
|
279
|
-
self.downloadInFlightHexcode = nil
|
|
280
|
-
if let cell = self.collectionView.cellForItem(at: indexPath) as? OpenMojiCell {
|
|
281
|
-
cell.setDownloading(false)
|
|
282
|
-
}
|
|
283
|
-
guard let fileURL, let data = try? Data(contentsOf: fileURL), let image = UIImage(data: data) else {
|
|
284
|
-
self.showInlineDownloadError()
|
|
285
|
-
return
|
|
286
|
-
}
|
|
287
|
-
// Write to a fresh temp file (consistent with the "Upload" flow's `writeTempImage`) so the
|
|
288
|
-
// caller always receives a `file://` URL it can treat like any other picked image, decoupled
|
|
289
|
-
// from our own disk cache's lifetime.
|
|
290
|
-
guard let tempURL = self.writeTempImage(image) else {
|
|
291
|
-
self.showInlineDownloadError()
|
|
292
|
-
return
|
|
293
|
-
}
|
|
294
|
-
self.dismiss(animated: true) { [weak self] in
|
|
295
|
-
self?.onStickerPicked?(tempURL, image)
|
|
296
|
-
}
|
|
219
|
+
displayedRuntimeStickers = runtimeStickers.filter { sticker in
|
|
220
|
+
(selectedGroup == nil || selectedGroup == "runtime") && (normalizedQuery.isEmpty || sticker.id.lowercased().contains(normalizedQuery))
|
|
297
221
|
}
|
|
222
|
+
collectionView.reloadData()
|
|
223
|
+
let isEmpty = displayedLocalStickerNames.isEmpty && displayedRuntimeStickers.isEmpty
|
|
224
|
+
stateContainer.isHidden = !isEmpty
|
|
225
|
+
emptyLabel.isHidden = !isEmpty
|
|
298
226
|
}
|
|
299
227
|
|
|
300
228
|
private func writeTempImage(_ image: UIImage) -> URL? {
|
|
@@ -308,11 +236,26 @@ final class OnlineStickerSheetViewController: UIViewController {
|
|
|
308
236
|
}
|
|
309
237
|
}
|
|
310
238
|
|
|
311
|
-
private func
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
239
|
+
private func loadRuntimeSticker(_ sticker: RuntimeSticker, completion: @escaping (URL?, UIImage?) -> Void) {
|
|
240
|
+
if !sticker.uri.lowercased().hasPrefix("https://") {
|
|
241
|
+
guard let path = SourceResolver.resolvePath(sourceUri: sticker.uri, tempPrefix: "pve_runtime_sticker") else {
|
|
242
|
+
completion(nil, nil)
|
|
243
|
+
return
|
|
244
|
+
}
|
|
245
|
+
let localURL = URL(fileURLWithPath: path)
|
|
246
|
+
completion(localURL, UIImage(contentsOfFile: path))
|
|
247
|
+
return
|
|
248
|
+
}
|
|
249
|
+
guard let url = URL(string: sticker.uri) else { completion(nil, nil); return }
|
|
250
|
+
URLSession.shared.dataTask(with: url) { [weak self] data, _, _ in
|
|
251
|
+
guard let self, let data, let image = UIImage(data: data), let localURL = self.writeTempImage(image) else {
|
|
252
|
+
DispatchQueue.main.async { completion(nil, nil) }
|
|
253
|
+
return
|
|
254
|
+
}
|
|
255
|
+
DispatchQueue.main.async { completion(localURL, image) }
|
|
256
|
+
}.resume()
|
|
315
257
|
}
|
|
258
|
+
|
|
316
259
|
}
|
|
317
260
|
|
|
318
261
|
// MARK: - UISearchBarDelegate (debounced)
|
|
@@ -344,20 +287,38 @@ extension OnlineStickerSheetViewController: UISearchBarDelegate {
|
|
|
344
287
|
|
|
345
288
|
extension OnlineStickerSheetViewController: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
|
|
346
289
|
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
|
347
|
-
|
|
290
|
+
displayedRuntimeStickers.count + displayedLocalStickerNames.count
|
|
348
291
|
}
|
|
349
292
|
|
|
350
293
|
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
|
351
|
-
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Self.cellReuseID, for: indexPath) as!
|
|
352
|
-
|
|
353
|
-
|
|
294
|
+
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Self.cellReuseID, for: indexPath) as! BundledStickerCell
|
|
295
|
+
if indexPath.item < displayedRuntimeStickers.count {
|
|
296
|
+
let sticker = displayedRuntimeStickers[indexPath.item]
|
|
297
|
+
cell.configure(image: nil)
|
|
298
|
+
loadRuntimeSticker(sticker) { [weak collectionView] _, image in
|
|
299
|
+
guard collectionView?.indexPath(for: cell) == indexPath else { return }
|
|
300
|
+
cell.configure(image: image)
|
|
301
|
+
}
|
|
302
|
+
} else {
|
|
303
|
+
let name = displayedLocalStickerNames[indexPath.item - displayedRuntimeStickers.count]
|
|
304
|
+
let image = localStickerURL(named: name).flatMap { UIImage(contentsOfFile: $0.path) }
|
|
305
|
+
cell.configure(image: image)
|
|
306
|
+
}
|
|
354
307
|
return cell
|
|
355
308
|
}
|
|
356
309
|
|
|
357
310
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
|
358
311
|
collectionView.deselectItem(at: indexPath, animated: true)
|
|
359
|
-
|
|
360
|
-
|
|
312
|
+
if indexPath.item < displayedRuntimeStickers.count {
|
|
313
|
+
loadRuntimeSticker(displayedRuntimeStickers[indexPath.item]) { [weak self] url, image in
|
|
314
|
+
guard let self, let url, let image else { return }
|
|
315
|
+
self.dismiss(animated: true) { [weak self] in self?.onStickerPicked?(url, image) }
|
|
316
|
+
}
|
|
317
|
+
} else {
|
|
318
|
+
let name = displayedLocalStickerNames[indexPath.item - displayedRuntimeStickers.count]
|
|
319
|
+
guard let url = localStickerURL(named: name), let image = UIImage(contentsOfFile: url.path), let tempURL = writeTempImage(image) else { return }
|
|
320
|
+
dismiss(animated: true) { [weak self] in self?.onStickerPicked?(tempURL, image) }
|
|
321
|
+
}
|
|
361
322
|
}
|
|
362
323
|
|
|
363
324
|
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
|
|
@@ -367,15 +328,13 @@ extension OnlineStickerSheetViewController: UICollectionViewDataSource, UICollec
|
|
|
367
328
|
}
|
|
368
329
|
}
|
|
369
330
|
|
|
370
|
-
/// One grid cell
|
|
371
|
-
|
|
372
|
-
private final class OpenMojiCell: UICollectionViewCell {
|
|
331
|
+
/// One grid cell for a bundled sticker image.
|
|
332
|
+
private final class BundledStickerCell: UICollectionViewCell {
|
|
373
333
|
private let imageView = UIImageView()
|
|
374
|
-
private let spinner = UIActivityIndicatorView(style: .medium)
|
|
375
334
|
|
|
376
335
|
override init(frame: CGRect) {
|
|
377
336
|
super.init(frame: frame)
|
|
378
|
-
contentView.backgroundColor = DesignTokens.
|
|
337
|
+
contentView.backgroundColor = DesignTokens.surfaceContainerLow
|
|
379
338
|
contentView.layer.cornerRadius = DesignTokens.radiusMd
|
|
380
339
|
contentView.clipsToBounds = true
|
|
381
340
|
|
|
@@ -389,30 +348,14 @@ private final class OpenMojiCell: UICollectionViewCell {
|
|
|
389
348
|
imageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -4),
|
|
390
349
|
])
|
|
391
350
|
|
|
392
|
-
spinner.hidesWhenStopped = true
|
|
393
|
-
contentView.addSubview(spinner)
|
|
394
|
-
spinner.translatesAutoresizingMaskIntoConstraints = false
|
|
395
|
-
NSLayoutConstraint.activate([
|
|
396
|
-
spinner.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
|
|
397
|
-
spinner.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
|
|
398
|
-
])
|
|
399
351
|
}
|
|
400
352
|
|
|
401
353
|
required init?(coder: NSCoder) { nil }
|
|
402
354
|
|
|
403
|
-
func configure(
|
|
404
|
-
loader.loadThumbnail(url: url, into: imageView)
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
func setDownloading(_ downloading: Bool) {
|
|
408
|
-
if downloading { spinner.startAnimating() } else { spinner.stopAnimating() }
|
|
409
|
-
imageView.alpha = downloading ? 0.4 : 1.0
|
|
410
|
-
}
|
|
355
|
+
func configure(image: UIImage?) { imageView.image = image }
|
|
411
356
|
|
|
412
357
|
override func prepareForReuse() {
|
|
413
358
|
super.prepareForReuse()
|
|
414
359
|
imageView.image = nil
|
|
415
|
-
spinner.stopAnimating()
|
|
416
|
-
imageView.alpha = 1.0
|
|
417
360
|
}
|
|
418
361
|
}
|
|
@@ -131,6 +131,10 @@ final class VideoExporter {
|
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
let cropPlan = VideoCropGeometry.plan(size:CGSize(width:renderWidth,height:renderHeight),state:state.crop,includeCrop:true)
|
|
135
|
+
transform = transform.concatenating(cropPlan.transform)
|
|
136
|
+
renderWidth = cropPlan.size.width; renderHeight = cropPlan.size.height
|
|
137
|
+
|
|
134
138
|
let videoComposition = AVMutableVideoComposition()
|
|
135
139
|
videoComposition.renderSize = CGSize(width: renderWidth, height: renderHeight)
|
|
136
140
|
videoComposition.frameDuration = CMTime(value: 1, timescale: 30)
|
|
@@ -145,7 +149,12 @@ final class VideoExporter {
|
|
|
145
149
|
if !layers.isEmpty {
|
|
146
150
|
let renderSize = CGSize(width: renderWidth, height: renderHeight)
|
|
147
151
|
let longestEdge = max(renderSize.width, renderSize.height)
|
|
148
|
-
|
|
152
|
+
// Geometry is independent of this cap: each overlay surface below is a full-`renderSize`
|
|
153
|
+
// CALayer with `contentsGravity = .resizeAspect`, so a smaller (same-aspect) image is scaled
|
|
154
|
+
// back up to cover the whole frame. The cap only trades sharpness for peak memory. (Android's
|
|
155
|
+
// Media3 path has no such scale-to-fit — it composites overlay textures 1:1 in pixels — which
|
|
156
|
+
// is why `VideoExporter.kt` has to restore the same invariant explicitly via OverlaySettings.)
|
|
157
|
+
let overlayScale = min(1, 2560 / max(longestEdge, 1))
|
|
149
158
|
let overlayRenderSize = CGSize(
|
|
150
159
|
width: max(2, floor(renderSize.width * overlayScale)),
|
|
151
160
|
height: max(2, floor(renderSize.height * overlayScale))
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import AVFoundation
|
|
2
|
+
import CoreGraphics
|
|
3
|
+
|
|
4
|
+
/// Used by AVPlayer's video composition and export's layer instruction. This is the single source
|
|
5
|
+
/// of truth `videoMediaBounds()`/`videoNaturalSize` (in `PhotoVideoEditorViewController`) both derive
|
|
6
|
+
/// their size from, so overlay-relative coordinates stay consistent between preview and the real
|
|
7
|
+
/// composed frame — do not introduce a second, independently-computed size for either of those.
|
|
8
|
+
///
|
|
9
|
+
/// Known gap (not currently reachable from the UI — the video tool rail only exposes Crop/Text/
|
|
10
|
+
/// Stickers, not a whole-video "Rotate" tool): `VideoTransformState.rotationDegrees` (the top-level
|
|
11
|
+
/// rotate field, distinct from this function's `state.rotationDegrees`, which is the CROP sub-state's
|
|
12
|
+
/// own rotation) is applied to the player view as a cosmetic `.transform` only in
|
|
13
|
+
/// `applyVideoPreviewTransform` and never passed into this function, while `VideoExporter` bakes it
|
|
14
|
+
/// into the real output frame (swapping width/height for 90/270°). If that tool is ever wired back up,
|
|
15
|
+
/// `plan()` must also account for it, or preview and export will disagree.
|
|
16
|
+
enum VideoCropGeometry {
|
|
17
|
+
static func plan(size: CGSize, state: PhotoTransformState, includeCrop: Bool) -> (transform:CGAffineTransform,size:CGSize) {
|
|
18
|
+
let quarter = CGAffineTransform(rotationAngle:CGFloat(state.rotationDegrees) * .pi/180)
|
|
19
|
+
let oriented = CGRect(origin:.zero,size:size).applying(quarter)
|
|
20
|
+
let w = abs(oriented.width).rounded(), h = abs(oriented.height).rounded()
|
|
21
|
+
var transform = quarter.concatenating(CGAffineTransform(translationX:-oriented.minX,y:-oriented.minY))
|
|
22
|
+
if state.straightenDegrees != 0 {
|
|
23
|
+
let scale = CropGeometry.fillScale(width:w,height:h,degrees:state.straightenDegrees)
|
|
24
|
+
transform = transform.concatenating(CGAffineTransform(translationX:-w/2,y:-h/2))
|
|
25
|
+
.concatenating(CGAffineTransform(rotationAngle:state.straightenDegrees * .pi/180))
|
|
26
|
+
.concatenating(CGAffineTransform(scaleX:scale,y:scale))
|
|
27
|
+
.concatenating(CGAffineTransform(translationX:w/2,y:h/2))
|
|
28
|
+
}
|
|
29
|
+
guard includeCrop else { return (transform,CGSize(width:w,height:h)) }
|
|
30
|
+
let left = (state.cropLeft*w).rounded(), top = (state.cropTop*h).rounded()
|
|
31
|
+
let width = max(2,(state.cropRight*w).rounded()-left), height = max(2,(state.cropBottom*h).rounded()-top)
|
|
32
|
+
transform = transform.concatenating(CGAffineTransform(translationX:-left,y:-top))
|
|
33
|
+
return (transform,CGSize(width:width,height:height))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static func previewComposition(asset: AVAsset, state: PhotoTransformState, includeCrop: Bool) -> AVVideoComposition? {
|
|
37
|
+
guard let track = asset.tracks(withMediaType:.video).first else { return nil }
|
|
38
|
+
let upright = track.naturalSize.applying(track.preferredTransform)
|
|
39
|
+
let plan = plan(size:CGSize(width:abs(upright.width),height:abs(upright.height)),state:state,includeCrop:includeCrop)
|
|
40
|
+
let composition = AVMutableVideoComposition()
|
|
41
|
+
composition.renderSize = plan.size; composition.frameDuration = CMTime(value:1,timescale:30)
|
|
42
|
+
let instruction = AVMutableVideoCompositionInstruction(); instruction.timeRange = CMTimeRange(start:.zero,duration:asset.duration)
|
|
43
|
+
let layer = AVMutableVideoCompositionLayerInstruction(assetTrack:track)
|
|
44
|
+
layer.setTransform(track.preferredTransform.concatenating(plan.transform),at:.zero)
|
|
45
|
+
instruction.layerInstructions = [layer]; composition.instructions = [instruction]
|
|
46
|
+
return composition
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -8,6 +8,7 @@ import CoreGraphics
|
|
|
8
8
|
/// `AVMutableComposition` concatenating every clip — see `VideoEditSession`).
|
|
9
9
|
struct VideoTransformState: Equatable {
|
|
10
10
|
var rotationDegrees: Int = 0
|
|
11
|
+
var crop = PhotoTransformState()
|
|
11
12
|
var aspectRatio: CGFloat?
|
|
12
13
|
var coverFrameMs: Int64 = 0
|
|
13
14
|
/// Preview-only for now: cycled via the Speed tool. Export does not yet honor this — see docs/video-editor.md.
|
package/lib/module/index.js
CHANGED
|
@@ -16,8 +16,8 @@ const defaultFeatures = {
|
|
|
16
16
|
filters: true,
|
|
17
17
|
text: true,
|
|
18
18
|
stickers: true,
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
draw: true,
|
|
20
|
+
overlays: true
|
|
21
21
|
};
|
|
22
22
|
function invalid(message) {
|
|
23
23
|
throw new PhotoVideoEditorError('E_INVALID_OPTIONS', message);
|
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","
|
|
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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","ignoreList":[]}
|
|
@@ -23,8 +23,8 @@ export interface EditorFeatures {
|
|
|
23
23
|
/** Enables filters. */ filters?: boolean;
|
|
24
24
|
/** Enables text overlays. */ text?: boolean;
|
|
25
25
|
/** Enables stickers (built-in, consumer-supplied, and user-uploaded). */ stickers?: boolean;
|
|
26
|
+
/** Enables freehand drawing with configurable brush colors and widths. */ draw?: boolean;
|
|
26
27
|
/** Enables the user-uploaded image overlay tool. */ overlays?: boolean;
|
|
27
|
-
/** Enables browsing/downloading free stickers from the internet (OpenMoji). Disable to prevent any network calls. */ onlineStickers?: boolean;
|
|
28
28
|
}
|
|
29
29
|
/** Future native editor appearance options. */
|
|
30
30
|
export interface EditorTheme {
|
|
@@ -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
|
-
/**
|
|
56
|
+
/** Sticker image URI. Supports HTTPS URLs and local device file/content URIs. */ uri: string;
|
|
57
57
|
}
|
|
58
58
|
/** Options accepted by {@link openEditor}. */
|
|
59
59
|
export interface EditorOptions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;AAC1C,uCAAuC;AACvC,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AACnE,wCAAwC;AACxC,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AACxD,wCAAwC;AACxC,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,KAAK,CAAC;AAC9C,sDAAsD;AACtD,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,WAAW,GACX,SAAS,GACT,WAAW,GACX,WAAW,GACX,WAAW,GACX,QAAQ,CAAC;AAEb,2EAA2E;AAC3E,MAAM,WAAW,YAAY;IAC3B,8BAA8B,CAAC,GAAG,EAAE,MAAM,CAAC;IAC3C,kBAAkB,CAAC,IAAI,EAAE,SAAS,CAAC;IACnC,+BAA+B,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClD,iCAAiC,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrD;AACD,2EAA2E;AAC3E,MAAM,WAAW,cAAc;IAC7B,wBAAwB,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxC,wBAAwB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1C,8BAA8B,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAC9C,uBAAuB,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC1C,6BAA6B,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAC7C,yEAAyE,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC7F,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-photo-video-editor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Native photo and video editor for React Native, powered by Kotlin and Swift.",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"module": "./lib/module/index.js",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"CONTRIBUTING.md",
|
|
22
22
|
"CHANGELOG.md",
|
|
23
23
|
"LICENSE",
|
|
24
|
+
"THIRD_PARTY_NOTICES.md",
|
|
24
25
|
"android",
|
|
25
26
|
"ios",
|
|
26
27
|
"cpp",
|
package/src/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -31,8 +31,8 @@ export interface EditorFeatures {
|
|
|
31
31
|
/** Enables filters. */ filters?: boolean;
|
|
32
32
|
/** Enables text overlays. */ text?: boolean;
|
|
33
33
|
/** Enables stickers (built-in, consumer-supplied, and user-uploaded). */ stickers?: boolean;
|
|
34
|
+
/** Enables freehand drawing with configurable brush colors and widths. */ draw?: boolean;
|
|
34
35
|
/** Enables the user-uploaded image overlay tool. */ overlays?: boolean;
|
|
35
|
-
/** Enables browsing/downloading free stickers from the internet (OpenMoji). Disable to prevent any network calls. */ onlineStickers?: boolean;
|
|
36
36
|
}
|
|
37
37
|
/** Future native editor appearance options. */
|
|
38
38
|
export interface EditorTheme {
|
|
@@ -61,7 +61,7 @@ export interface FontAsset {
|
|
|
61
61
|
/** A consumer-provided sticker image, selectable from the Stickers tool. */
|
|
62
62
|
export interface StickerAsset {
|
|
63
63
|
/** Stable identifier, echoed back on layers created from this asset. */ id: string;
|
|
64
|
-
/**
|
|
64
|
+
/** Sticker image URI. Supports HTTPS URLs and local device file/content URIs. */ uri: string;
|
|
65
65
|
}
|
|
66
66
|
/** Options accepted by {@link openEditor}. */
|
|
67
67
|
export interface EditorOptions {
|