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.
Files changed (233) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/PhotoVideoEditor.podspec +1 -0
  3. package/README.md +10 -6
  4. package/THIRD_PARTY_NOTICES.md +20 -0
  5. package/android/build.gradle +1 -1
  6. package/android/src/main/AndroidManifest.xml +1 -1
  7. package/android/src/main/assets/Stickers/AUTUMN_1.png +0 -0
  8. package/android/src/main/assets/Stickers/AUTUMN_10.png +0 -0
  9. package/android/src/main/assets/Stickers/AUTUMN_11.png +0 -0
  10. package/android/src/main/assets/Stickers/AUTUMN_12.png +0 -0
  11. package/android/src/main/assets/Stickers/AUTUMN_13.png +0 -0
  12. package/android/src/main/assets/Stickers/AUTUMN_14.png +0 -0
  13. package/android/src/main/assets/Stickers/AUTUMN_15.png +0 -0
  14. package/android/src/main/assets/Stickers/AUTUMN_16.png +0 -0
  15. package/android/src/main/assets/Stickers/AUTUMN_17.png +0 -0
  16. package/android/src/main/assets/Stickers/AUTUMN_18.png +0 -0
  17. package/android/src/main/assets/Stickers/AUTUMN_2.png +0 -0
  18. package/android/src/main/assets/Stickers/AUTUMN_3.png +0 -0
  19. package/android/src/main/assets/Stickers/AUTUMN_4.png +0 -0
  20. package/android/src/main/assets/Stickers/AUTUMN_5.png +0 -0
  21. package/android/src/main/assets/Stickers/AUTUMN_6.png +0 -0
  22. package/android/src/main/assets/Stickers/AUTUMN_7.png +0 -0
  23. package/android/src/main/assets/Stickers/AUTUMN_8.png +0 -0
  24. package/android/src/main/assets/Stickers/AUTUMN_9.png +0 -0
  25. package/android/src/main/assets/Stickers/LOL_1.png +0 -0
  26. package/android/src/main/assets/Stickers/LOL_10.png +0 -0
  27. package/android/src/main/assets/Stickers/LOL_11.png +0 -0
  28. package/android/src/main/assets/Stickers/LOL_12.png +0 -0
  29. package/android/src/main/assets/Stickers/LOL_13.png +0 -0
  30. package/android/src/main/assets/Stickers/LOL_14.png +0 -0
  31. package/android/src/main/assets/Stickers/LOL_15.png +0 -0
  32. package/android/src/main/assets/Stickers/LOL_16.png +0 -0
  33. package/android/src/main/assets/Stickers/LOL_17.png +0 -0
  34. package/android/src/main/assets/Stickers/LOL_18.png +0 -0
  35. package/android/src/main/assets/Stickers/LOL_19.png +0 -0
  36. package/android/src/main/assets/Stickers/LOL_2.png +0 -0
  37. package/android/src/main/assets/Stickers/LOL_3.png +0 -0
  38. package/android/src/main/assets/Stickers/LOL_4.png +0 -0
  39. package/android/src/main/assets/Stickers/LOL_5.png +0 -0
  40. package/android/src/main/assets/Stickers/LOL_6.png +0 -0
  41. package/android/src/main/assets/Stickers/LOL_7.png +0 -0
  42. package/android/src/main/assets/Stickers/LOL_8.png +0 -0
  43. package/android/src/main/assets/Stickers/LOL_9.png +0 -0
  44. package/android/src/main/assets/Stickers/SPRING_1.png +0 -0
  45. package/android/src/main/assets/Stickers/SPRING_2.png +0 -0
  46. package/android/src/main/assets/Stickers/SPRING_3.png +0 -0
  47. package/android/src/main/assets/Stickers/SPRING_4.png +0 -0
  48. package/android/src/main/assets/Stickers/SPRING_5.png +0 -0
  49. package/android/src/main/assets/Stickers/SPRING_6.png +0 -0
  50. package/android/src/main/assets/Stickers/SUMMER_1.png +0 -0
  51. package/android/src/main/assets/Stickers/SUMMER_10.png +0 -0
  52. package/android/src/main/assets/Stickers/SUMMER_11.png +0 -0
  53. package/android/src/main/assets/Stickers/SUMMER_12.png +0 -0
  54. package/android/src/main/assets/Stickers/SUMMER_13.png +0 -0
  55. package/android/src/main/assets/Stickers/SUMMER_14.png +0 -0
  56. package/android/src/main/assets/Stickers/SUMMER_15.png +0 -0
  57. package/android/src/main/assets/Stickers/SUMMER_16.png +0 -0
  58. package/android/src/main/assets/Stickers/SUMMER_17.png +0 -0
  59. package/android/src/main/assets/Stickers/SUMMER_18.png +0 -0
  60. package/android/src/main/assets/Stickers/SUMMER_19.png +0 -0
  61. package/android/src/main/assets/Stickers/SUMMER_2.png +0 -0
  62. package/android/src/main/assets/Stickers/SUMMER_20.png +0 -0
  63. package/android/src/main/assets/Stickers/SUMMER_21.png +0 -0
  64. package/android/src/main/assets/Stickers/SUMMER_3.png +0 -0
  65. package/android/src/main/assets/Stickers/SUMMER_4.png +0 -0
  66. package/android/src/main/assets/Stickers/SUMMER_5.png +0 -0
  67. package/android/src/main/assets/Stickers/SUMMER_6.png +0 -0
  68. package/android/src/main/assets/Stickers/SUMMER_7.png +0 -0
  69. package/android/src/main/assets/Stickers/SUMMER_8.png +0 -0
  70. package/android/src/main/assets/Stickers/SUMMER_9.png +0 -0
  71. package/android/src/main/assets/Stickers/TEXT_1.png +0 -0
  72. package/android/src/main/assets/Stickers/TEXT_12.png +0 -0
  73. package/android/src/main/assets/Stickers/TEXT_13.png +0 -0
  74. package/android/src/main/assets/Stickers/TEXT_14.png +0 -0
  75. package/android/src/main/assets/Stickers/TEXT_15.png +0 -0
  76. package/android/src/main/assets/Stickers/TEXT_16.png +0 -0
  77. package/android/src/main/assets/Stickers/TEXT_2.png +0 -0
  78. package/android/src/main/assets/Stickers/TEXT_25.png +0 -0
  79. package/android/src/main/assets/Stickers/TEXT_26.png +0 -0
  80. package/android/src/main/assets/Stickers/TEXT_27.png +0 -0
  81. package/android/src/main/assets/Stickers/TEXT_28.png +0 -0
  82. package/android/src/main/assets/Stickers/TEXT_29.png +0 -0
  83. package/android/src/main/assets/Stickers/TEXT_3.png +0 -0
  84. package/android/src/main/assets/Stickers/TEXT_30.png +0 -0
  85. package/android/src/main/assets/Stickers/TEXT_31.png +0 -0
  86. package/android/src/main/assets/Stickers/TEXT_32.png +0 -0
  87. package/android/src/main/assets/Stickers/TEXT_7.png +0 -0
  88. package/android/src/main/assets/Stickers/TEXT_8.png +0 -0
  89. package/android/src/main/assets/Stickers/TEXT_9.png +0 -0
  90. package/android/src/main/java/com/photovideoeditor/editor/PhotoVideoEditorActivity.kt +901 -651
  91. package/android/src/main/java/com/photovideoeditor/photo/export/PhotoExporter.kt +8 -23
  92. package/android/src/main/java/com/photovideoeditor/photo/render/CropGeometry.kt +71 -0
  93. package/android/src/main/java/com/photovideoeditor/photo/render/OverlayGeometry.kt +36 -0
  94. package/android/src/main/java/com/photovideoeditor/photo/render/PhotoEditSession.kt +5 -7
  95. package/android/src/main/java/com/photovideoeditor/photo/render/PhotoFilterPresets.kt +71 -41
  96. package/android/src/main/java/com/photovideoeditor/photo/render/PhotoLayer.kt +5 -1
  97. package/android/src/main/java/com/photovideoeditor/photo/render/PhotoLayerRenderer.kt +24 -1
  98. package/android/src/main/java/com/photovideoeditor/photo/render/PhotoTransformRenderer.kt +33 -0
  99. package/android/src/main/java/com/photovideoeditor/photo/render/PhotoTransformState.kt +6 -1
  100. package/android/src/main/java/com/photovideoeditor/photo/ui/CropOverlayView.kt +112 -122
  101. package/android/src/main/java/com/photovideoeditor/photo/ui/DrawOverlayView.kt +66 -0
  102. package/android/src/main/java/com/photovideoeditor/photo/ui/LayerOverlayView.kt +63 -14
  103. package/android/src/main/java/com/photovideoeditor/photo/ui/ZoomableImageView.kt +10 -0
  104. package/android/src/main/java/com/photovideoeditor/stickers/OnlineStickerSheet.kt +135 -104
  105. package/android/src/main/java/com/photovideoeditor/ui/components/CropPanel.kt +122 -0
  106. package/android/src/main/java/com/photovideoeditor/ui/components/EditorPanel.kt +250 -0
  107. package/android/src/main/java/com/photovideoeditor/ui/components/EditorToolSlider.kt +103 -0
  108. package/android/src/main/java/com/photovideoeditor/video/export/VideoExporter.kt +38 -11
  109. package/android/src/main/java/com/photovideoeditor/video/render/VideoCropEffects.kt +35 -0
  110. package/android/src/main/java/com/photovideoeditor/video/render/VideoEditSession.kt +10 -0
  111. package/android/src/main/java/com/photovideoeditor/video/render/VideoTransformState.kt +1 -0
  112. package/android/src/main/res/drawable/ic_draw.xml +6 -0
  113. package/android/src/main/res/drawable/ic_pause.xml +7 -0
  114. package/android/src/main/res/drawable/ic_play.xml +7 -0
  115. package/docs/api.md +1 -1
  116. package/docs/editor-ui-validation.md +22 -0
  117. package/docs/photo-editor.md +5 -6
  118. package/ios/Editor/PhotoVideoEditorViewController.swift +918 -494
  119. package/ios/Editor/UI/CropPanel.swift +97 -0
  120. package/ios/Editor/UI/EditorPanel.swift +334 -0
  121. package/ios/Editor/UI/EditorToolSlider.swift +22 -0
  122. package/ios/Editor/UI/TextEditorSheet.swift +84 -0
  123. package/ios/Photo/Export/PhotoExporter.swift +3 -3
  124. package/ios/Photo/Rendering/CropGeometry.swift +48 -0
  125. package/ios/Photo/Rendering/PhotoEditSession.swift +21 -10
  126. package/ios/Photo/Rendering/PhotoFilterPresets.swift +53 -23
  127. package/ios/Photo/Rendering/PhotoLayer.swift +14 -2
  128. package/ios/Photo/Rendering/PhotoLayerRenderer.swift +16 -1
  129. package/ios/Photo/Rendering/PhotoTransformState.swift +5 -0
  130. package/ios/Photo/UI/CropOverlayView.swift +98 -134
  131. package/ios/Photo/UI/DrawOverlayView.swift +48 -0
  132. package/ios/Photo/UI/FilterThumbnailLoader.swift +62 -0
  133. package/ios/Photo/UI/LayerOverlayView.swift +36 -11
  134. package/ios/Photo/UI/ZoomableImageView.swift +19 -1
  135. package/ios/Resources/Stickers/AUTUMN_1.png +0 -0
  136. package/ios/Resources/Stickers/AUTUMN_10.png +0 -0
  137. package/ios/Resources/Stickers/AUTUMN_11.png +0 -0
  138. package/ios/Resources/Stickers/AUTUMN_12.png +0 -0
  139. package/ios/Resources/Stickers/AUTUMN_13.png +0 -0
  140. package/ios/Resources/Stickers/AUTUMN_14.png +0 -0
  141. package/ios/Resources/Stickers/AUTUMN_15.png +0 -0
  142. package/ios/Resources/Stickers/AUTUMN_16.png +0 -0
  143. package/ios/Resources/Stickers/AUTUMN_17.png +0 -0
  144. package/ios/Resources/Stickers/AUTUMN_18.png +0 -0
  145. package/ios/Resources/Stickers/AUTUMN_2.png +0 -0
  146. package/ios/Resources/Stickers/AUTUMN_3.png +0 -0
  147. package/ios/Resources/Stickers/AUTUMN_4.png +0 -0
  148. package/ios/Resources/Stickers/AUTUMN_5.png +0 -0
  149. package/ios/Resources/Stickers/AUTUMN_6.png +0 -0
  150. package/ios/Resources/Stickers/AUTUMN_7.png +0 -0
  151. package/ios/Resources/Stickers/AUTUMN_8.png +0 -0
  152. package/ios/Resources/Stickers/AUTUMN_9.png +0 -0
  153. package/ios/Resources/Stickers/LOL_1.png +0 -0
  154. package/ios/Resources/Stickers/LOL_10.png +0 -0
  155. package/ios/Resources/Stickers/LOL_11.png +0 -0
  156. package/ios/Resources/Stickers/LOL_12.png +0 -0
  157. package/ios/Resources/Stickers/LOL_13.png +0 -0
  158. package/ios/Resources/Stickers/LOL_14.png +0 -0
  159. package/ios/Resources/Stickers/LOL_15.png +0 -0
  160. package/ios/Resources/Stickers/LOL_16.png +0 -0
  161. package/ios/Resources/Stickers/LOL_17.png +0 -0
  162. package/ios/Resources/Stickers/LOL_18.png +0 -0
  163. package/ios/Resources/Stickers/LOL_19.png +0 -0
  164. package/ios/Resources/Stickers/LOL_2.png +0 -0
  165. package/ios/Resources/Stickers/LOL_3.png +0 -0
  166. package/ios/Resources/Stickers/LOL_4.png +0 -0
  167. package/ios/Resources/Stickers/LOL_5.png +0 -0
  168. package/ios/Resources/Stickers/LOL_6.png +0 -0
  169. package/ios/Resources/Stickers/LOL_7.png +0 -0
  170. package/ios/Resources/Stickers/LOL_8.png +0 -0
  171. package/ios/Resources/Stickers/LOL_9.png +0 -0
  172. package/ios/Resources/Stickers/SPRING_1.png +0 -0
  173. package/ios/Resources/Stickers/SPRING_2.png +0 -0
  174. package/ios/Resources/Stickers/SPRING_3.png +0 -0
  175. package/ios/Resources/Stickers/SPRING_4.png +0 -0
  176. package/ios/Resources/Stickers/SPRING_5.png +0 -0
  177. package/ios/Resources/Stickers/SPRING_6.png +0 -0
  178. package/ios/Resources/Stickers/SUMMER_1.png +0 -0
  179. package/ios/Resources/Stickers/SUMMER_10.png +0 -0
  180. package/ios/Resources/Stickers/SUMMER_11.png +0 -0
  181. package/ios/Resources/Stickers/SUMMER_12.png +0 -0
  182. package/ios/Resources/Stickers/SUMMER_13.png +0 -0
  183. package/ios/Resources/Stickers/SUMMER_14.png +0 -0
  184. package/ios/Resources/Stickers/SUMMER_15.png +0 -0
  185. package/ios/Resources/Stickers/SUMMER_16.png +0 -0
  186. package/ios/Resources/Stickers/SUMMER_17.png +0 -0
  187. package/ios/Resources/Stickers/SUMMER_18.png +0 -0
  188. package/ios/Resources/Stickers/SUMMER_19.png +0 -0
  189. package/ios/Resources/Stickers/SUMMER_2.png +0 -0
  190. package/ios/Resources/Stickers/SUMMER_20.png +0 -0
  191. package/ios/Resources/Stickers/SUMMER_21.png +0 -0
  192. package/ios/Resources/Stickers/SUMMER_3.png +0 -0
  193. package/ios/Resources/Stickers/SUMMER_4.png +0 -0
  194. package/ios/Resources/Stickers/SUMMER_5.png +0 -0
  195. package/ios/Resources/Stickers/SUMMER_6.png +0 -0
  196. package/ios/Resources/Stickers/SUMMER_7.png +0 -0
  197. package/ios/Resources/Stickers/SUMMER_8.png +0 -0
  198. package/ios/Resources/Stickers/SUMMER_9.png +0 -0
  199. package/ios/Resources/Stickers/TEXT_1.png +0 -0
  200. package/ios/Resources/Stickers/TEXT_12.png +0 -0
  201. package/ios/Resources/Stickers/TEXT_13.png +0 -0
  202. package/ios/Resources/Stickers/TEXT_14.png +0 -0
  203. package/ios/Resources/Stickers/TEXT_15.png +0 -0
  204. package/ios/Resources/Stickers/TEXT_16.png +0 -0
  205. package/ios/Resources/Stickers/TEXT_2.png +0 -0
  206. package/ios/Resources/Stickers/TEXT_25.png +0 -0
  207. package/ios/Resources/Stickers/TEXT_26.png +0 -0
  208. package/ios/Resources/Stickers/TEXT_27.png +0 -0
  209. package/ios/Resources/Stickers/TEXT_28.png +0 -0
  210. package/ios/Resources/Stickers/TEXT_29.png +0 -0
  211. package/ios/Resources/Stickers/TEXT_3.png +0 -0
  212. package/ios/Resources/Stickers/TEXT_30.png +0 -0
  213. package/ios/Resources/Stickers/TEXT_31.png +0 -0
  214. package/ios/Resources/Stickers/TEXT_32.png +0 -0
  215. package/ios/Resources/Stickers/TEXT_7.png +0 -0
  216. package/ios/Resources/Stickers/TEXT_8.png +0 -0
  217. package/ios/Resources/Stickers/TEXT_9.png +0 -0
  218. package/ios/Resources/Stickers/a_pety_icon.png +0 -0
  219. package/ios/Stickers/OnlineStickerSheetViewController.swift +102 -159
  220. package/ios/Video/Export/VideoExporter.swift +10 -1
  221. package/ios/Video/Render/VideoCropGeometry.swift +48 -0
  222. package/ios/Video/Render/VideoTransformState.swift +1 -0
  223. package/lib/module/index.js +2 -2
  224. package/lib/module/index.js.map +1 -1
  225. package/lib/typescript/src/types.d.ts +2 -2
  226. package/lib/typescript/src/types.d.ts.map +1 -1
  227. package/package.json +2 -1
  228. package/src/index.ts +1 -1
  229. package/src/types.ts +2 -2
  230. package/android/src/main/java/com/photovideoeditor/stickers/OpenMojiCatalog.kt +0 -172
  231. package/android/src/main/java/com/photovideoeditor/stickers/RemoteImageLoader.kt +0 -110
  232. package/ios/Stickers/OpenMojiCatalog.swift +0 -188
  233. package/ios/Stickers/RemoteImageLoader.swift +0 -127
@@ -8,7 +8,9 @@ final class LayerOverlayView: UIView {
8
8
  var maxScale: CGFloat = 8
9
9
  var layers: [PhotoLayer] = [] { didSet { setNeedsDisplay() } }
10
10
  var selectedLayerID: String? { didSet { setNeedsDisplay() } }
11
+ var onLayerDelete: ((String) -> Void)?
11
12
  var onLayerTapped: ((String?) -> Void)?
13
+ var onLayerDoubleTapped: ((String) -> Void)?
12
14
  var onLayerTransformChanged: ((_ id: String, _ x: CGFloat, _ y: CGFloat, _ scale: CGFloat, _ rotationDegrees: CGFloat) -> Void)?
13
15
  var onLayerTransformEnded: ((String) -> Void)?
14
16
 
@@ -41,19 +43,24 @@ final class LayerOverlayView: UIView {
41
43
  context.translateBy(x: center.x, y: center.y)
42
44
  context.rotate(by: layer.rotationDegrees * .pi / 180)
43
45
  context.setStrokeColor(UIColor.white.cgColor)
44
- context.setLineWidth(2)
45
- context.setLineDash(phase: 0, lengths: [8, 5])
46
+ context.setLineWidth(1)
46
47
  context.stroke(CGRect(x: -half.width, y: -half.height, width: half.width * 2, height: half.height * 2))
47
48
  context.setLineDash(phase: 0, lengths: [])
48
49
  context.setFillColor(UIColor.white.cgColor)
49
- context.fillEllipse(in: CGRect(x: half.width - handleRadius, y: half.height - handleRadius, width: handleRadius * 2, height: handleRadius * 2))
50
- context.setStrokeColor(UIColor(red: 87/255, green: 55/255, blue: 245/255, alpha: 1).cgColor)
51
- context.setLineWidth(2)
52
- let glyph = handleRadius * 0.52
53
- context.move(to: CGPoint(x: half.width - glyph, y: half.height + glyph)); context.addLine(to: CGPoint(x: half.width + glyph, y: half.height - glyph)); context.strokePath()
50
+ for point in [CGPoint(x: -half.width, y: -half.height), CGPoint(x: half.width, y: -half.height), CGPoint(x: -half.width, y: half.height), CGPoint(x: half.width, y: half.height)] {
51
+ context.fillEllipse(in: CGRect(x: point.x - 4, y: point.y - 4, width: 8, height: 8))
52
+ }
53
+ drawControl(context, point: CGPoint(x: -half.width, y: -half.height), symbol: "xmark", tint: .systemRed)
54
+ drawControl(context, point: CGPoint(x: half.width, y: half.height), symbol: "arrow.up.left.and.arrow.down.right", tint: DesignTokens.primary)
54
55
  context.restoreGState()
55
56
  }
56
57
 
58
+ private func drawControl(_ context: CGContext, point: CGPoint, symbol: String, tint: UIColor) {
59
+ context.setFillColor(UIColor.white.cgColor)
60
+ context.fillEllipse(in: CGRect(x: point.x - handleRadius, y: point.y - handleRadius, width: handleRadius * 2, height: handleRadius * 2))
61
+ UIImage(systemName: symbol)?.withTintColor(tint, renderingMode: .alwaysOriginal).draw(in: CGRect(x: point.x - 7, y: point.y - 7, width: 14, height: 14))
62
+ }
63
+
57
64
  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
58
65
  guard imageBounds.width > 0 else { return }
59
66
  for touch in touches {
@@ -70,6 +77,11 @@ final class LayerOverlayView: UIView {
70
77
  return
71
78
  }
72
79
  let selected = selectedLayer()
80
+ if let selected, !selected.locked {
81
+ let center = center(of: selected); let handle = handlePoint(selected)
82
+ let deletePoint = CGPoint(x: center.x * 2 - handle.x, y: center.y * 2 - handle.y)
83
+ if distance(deletePoint, point) <= handleTouchRadius { onLayerDelete?(selected.id); resetGesture(); return }
84
+ }
73
85
  let handleHit = selected.flatMap { !$0.locked && isHandleHit($0, point) ? $0 : nil }
74
86
  let hit = handleHit ?? hitTest(point)
75
87
  onLayerTapped?(hit?.id)
@@ -107,13 +119,22 @@ final class LayerOverlayView: UIView {
107
119
  }
108
120
  }
109
121
 
110
- override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { endTouches(touches) }
122
+ override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
123
+ if let touch = touches.first, touch.tapCount >= 2, let id = gestureID { onLayerDoubleTapped?(id) }
124
+ endTouches(touches)
125
+ }
111
126
  override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) { endTouches(touches) }
112
127
 
113
128
  private func endTouches(_ touches: Set<UITouch>) {
114
129
  let endedPrimary = primaryTouch.map { touches.contains($0) } ?? false
115
130
  let endedSecondary = secondaryTouch.map { touches.contains($0) } ?? false
116
- if endedPrimary || endedSecondary { finishGesture() }
131
+ if endedPrimary || endedSecondary {
132
+ let remaining = endedPrimary ? secondaryTouch : primaryTouch
133
+ if let remaining, !touches.contains(remaining), let layer = currentGestureLayer() {
134
+ primaryTouch = remaining; secondaryTouch = nil
135
+ startLayer = layer; startPoint = remaining.location(in: self); gesture = .drag
136
+ } else { finishGesture() }
137
+ }
117
138
  }
118
139
  private func emit(_ layer: PhotoLayer, x: CGFloat, y: CGFloat, scale: CGFloat, rotation: CGFloat) {
119
140
  onLayerTransformChanged?(layer.id, x, y, scale, rotation)
@@ -141,13 +162,17 @@ final class LayerOverlayView: UIView {
141
162
  case .sticker:
142
163
  // Mirrors `PhotoLayerRenderer.drawSticker`'s `shortSide * 0.18` box (half-extent = 0.09).
143
164
  let half = shortSide * 0.09
144
- base = CGSize(width: half, height: half)
165
+ base = CGSize(width: half + 4, height: half / max(layer.overlayAspectRatio ?? 1, 0.001) + 4)
145
166
  case .overlay:
146
167
  // Mirrors `PhotoLayerRenderer.drawOverlay`'s box: full canvas width, height from aspect ratio.
147
168
  let aspectRatio = layer.overlayAspectRatio.flatMap { $0 > 0 ? $0 : nil } ?? 1
148
169
  base = CGSize(width: imageBounds.width / 2, height: imageBounds.width / (2 * aspectRatio))
170
+ case .drawing:
171
+ let maxX = layer.drawPoints.map { abs($0.0) }.max() ?? 0.08
172
+ let maxY = layer.drawPoints.map { abs($0.1) }.max() ?? 0.08
173
+ base = CGSize(width: maxX * imageBounds.width + handleRadius, height: maxY * imageBounds.height + handleRadius)
149
174
  }
150
- return CGSize(width: max(handleRadius, base.width * layer.scale), height: max(handleRadius, base.height * layer.scale))
175
+ return CGSize(width: max(24, base.width * layer.scale), height: max(24, base.height * layer.scale))
151
176
  }
152
177
  private func handlePoint(_ layer: PhotoLayer) -> CGPoint {
153
178
  let center = center(of: layer); let half = selectionHalfExtents(layer); let radians = layer.rotationDegrees * .pi / 180
@@ -1,4 +1,5 @@
1
1
  import UIKit
2
+ import AVFoundation
2
3
 
3
4
  /// UIImageView supporting pinch-to-zoom and single-finger pan for the photo
4
5
  /// preview, reporting the current on-screen image rect via `onBoundsChanged`
@@ -14,6 +15,7 @@ final class ZoomableImageView: UIImageView {
14
15
 
15
16
  private let pinchRecognizer = UIPinchGestureRecognizer()
16
17
  private let panRecognizer = UIPanGestureRecognizer()
18
+ private var lastLayoutSize: CGSize = .zero
17
19
  private var minScale: CGFloat = 1
18
20
  private var maxScale: CGFloat = 4
19
21
 
@@ -37,12 +39,28 @@ final class ZoomableImageView: UIImageView {
37
39
  reportBounds()
38
40
  }
39
41
 
42
+ func setRenderedBounds(_ target: CGRect) {
43
+ guard let image, bounds.width > 0, bounds.height > 0 else { return }
44
+ let fitted = AVMakeRect(aspectRatio: image.size, insideRect: bounds)
45
+ let scale = target.width / max(fitted.width, 0.001)
46
+ transform = CGAffineTransform(translationX: target.midX - center.x, y: target.midY - center.y).scaledBy(x: scale, y: scale)
47
+ }
48
+
40
49
  func currentImageBounds() -> CGRect {
41
50
  guard let image = image, bounds.width > 0, bounds.height > 0, image.size.width > 0, image.size.height > 0 else { return bounds }
42
51
  let scale = min(bounds.width / image.size.width, bounds.height / image.size.height)
43
52
  let fittedSize = CGSize(width: image.size.width * scale, height: image.size.height * scale)
44
53
  let origin = CGPoint(x: (bounds.width - fittedSize.width) / 2, y: (bounds.height - fittedSize.height) / 2)
45
- return CGRect(origin: origin, size: fittedSize).applying(transform)
54
+ let rect = CGRect(origin: origin, size: fittedSize)
55
+ return superview.map { convert(rect, to: $0) } ?? rect
56
+ }
57
+
58
+ override func layoutSubviews() {
59
+ super.layoutSubviews()
60
+ if bounds.size != lastLayoutSize {
61
+ lastLayoutSize = bounds.size
62
+ resetToFit()
63
+ }
46
64
  }
47
65
 
48
66
  private func reportBounds() {
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file