react-native-photo-video-editor 0.1.1 → 0.2.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/android/src/main/java/com/photovideoeditor/editor/PhotoVideoEditorActivity.kt +760 -590
  3. package/android/src/main/java/com/photovideoeditor/photo/export/PhotoExporter.kt +8 -23
  4. package/android/src/main/java/com/photovideoeditor/photo/render/CropGeometry.kt +71 -0
  5. package/android/src/main/java/com/photovideoeditor/photo/render/OverlayGeometry.kt +36 -0
  6. package/android/src/main/java/com/photovideoeditor/photo/render/PhotoEditSession.kt +5 -7
  7. package/android/src/main/java/com/photovideoeditor/photo/render/PhotoFilterPresets.kt +71 -41
  8. package/android/src/main/java/com/photovideoeditor/photo/render/PhotoLayerRenderer.kt +3 -1
  9. package/android/src/main/java/com/photovideoeditor/photo/render/PhotoTransformRenderer.kt +33 -0
  10. package/android/src/main/java/com/photovideoeditor/photo/render/PhotoTransformState.kt +6 -1
  11. package/android/src/main/java/com/photovideoeditor/photo/ui/CropOverlayView.kt +109 -151
  12. package/android/src/main/java/com/photovideoeditor/photo/ui/LayerOverlayView.kt +42 -13
  13. package/android/src/main/java/com/photovideoeditor/photo/ui/ZoomableImageView.kt +10 -0
  14. package/android/src/main/java/com/photovideoeditor/ui/components/CropPanel.kt +122 -0
  15. package/android/src/main/java/com/photovideoeditor/ui/components/EditorPanel.kt +250 -0
  16. package/android/src/main/java/com/photovideoeditor/ui/components/EditorToolSlider.kt +103 -0
  17. package/android/src/main/java/com/photovideoeditor/video/export/VideoExporter.kt +38 -11
  18. package/android/src/main/java/com/photovideoeditor/video/render/VideoCropEffects.kt +35 -0
  19. package/android/src/main/java/com/photovideoeditor/video/render/VideoEditSession.kt +10 -0
  20. package/android/src/main/java/com/photovideoeditor/video/render/VideoTransformState.kt +1 -0
  21. package/docs/editor-ui-validation.md +22 -0
  22. package/ios/Editor/PhotoVideoEditorViewController.swift +778 -443
  23. package/ios/Editor/UI/CropPanel.swift +97 -0
  24. package/ios/Editor/UI/EditorPanel.swift +334 -0
  25. package/ios/Editor/UI/EditorToolSlider.swift +22 -0
  26. package/ios/Editor/UI/TextEditorSheet.swift +84 -0
  27. package/ios/Photo/Export/PhotoExporter.swift +3 -3
  28. package/ios/Photo/Rendering/CropGeometry.swift +48 -0
  29. package/ios/Photo/Rendering/PhotoEditSession.swift +21 -10
  30. package/ios/Photo/Rendering/PhotoFilterPresets.swift +53 -23
  31. package/ios/Photo/Rendering/PhotoLayer.swift +9 -1
  32. package/ios/Photo/Rendering/PhotoLayerRenderer.swift +2 -1
  33. package/ios/Photo/Rendering/PhotoTransformState.swift +5 -0
  34. package/ios/Photo/UI/CropOverlayView.swift +96 -169
  35. package/ios/Photo/UI/FilterThumbnailLoader.swift +62 -0
  36. package/ios/Photo/UI/LayerOverlayView.swift +27 -10
  37. package/ios/Photo/UI/ZoomableImageView.swift +19 -1
  38. package/ios/Video/Export/VideoExporter.swift +10 -1
  39. package/ios/Video/Render/VideoCropGeometry.swift +48 -0
  40. package/ios/Video/Render/VideoTransformState.swift +1 -0
  41. package/package.json +1 -1
@@ -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
- let overlayScale = min(1, 1280 / max(longestEdge, 1))
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-photo-video-editor",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
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",