react-native-image-editor-skia 0.1.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 (299) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +202 -0
  3. package/lib/commonjs/ImageEditor.js +141 -0
  4. package/lib/commonjs/ImageEditor.js.map +1 -0
  5. package/lib/commonjs/annotations/AnnotationView.js +42 -0
  6. package/lib/commonjs/annotations/AnnotationView.js.map +1 -0
  7. package/lib/commonjs/annotations/ArrowAnnotation.js +29 -0
  8. package/lib/commonjs/annotations/ArrowAnnotation.js.map +1 -0
  9. package/lib/commonjs/annotations/CircleAnnotation.js +31 -0
  10. package/lib/commonjs/annotations/CircleAnnotation.js.map +1 -0
  11. package/lib/commonjs/annotations/FreehandAnnotation.js +29 -0
  12. package/lib/commonjs/annotations/FreehandAnnotation.js.map +1 -0
  13. package/lib/commonjs/annotations/MarkerAnnotation.js +27 -0
  14. package/lib/commonjs/annotations/MarkerAnnotation.js.map +1 -0
  15. package/lib/commonjs/annotations/RotatedGroup.js +34 -0
  16. package/lib/commonjs/annotations/RotatedGroup.js.map +1 -0
  17. package/lib/commonjs/annotations/TextAnnotation.js +40 -0
  18. package/lib/commonjs/annotations/TextAnnotation.js.map +1 -0
  19. package/lib/commonjs/annotations/geometry.js +73 -0
  20. package/lib/commonjs/annotations/geometry.js.map +1 -0
  21. package/lib/commonjs/annotations/geometryPure.js +104 -0
  22. package/lib/commonjs/annotations/geometryPure.js.map +1 -0
  23. package/lib/commonjs/canvas/AnnotationLayer.js +58 -0
  24. package/lib/commonjs/canvas/AnnotationLayer.js.map +1 -0
  25. package/lib/commonjs/canvas/BaseImageLayer.js +27 -0
  26. package/lib/commonjs/canvas/BaseImageLayer.js.map +1 -0
  27. package/lib/commonjs/canvas/CropOverlay.js +135 -0
  28. package/lib/commonjs/canvas/CropOverlay.js.map +1 -0
  29. package/lib/commonjs/canvas/EditorCanvas.js +91 -0
  30. package/lib/commonjs/canvas/EditorCanvas.js.map +1 -0
  31. package/lib/commonjs/canvas/InFlightLayer.js +152 -0
  32. package/lib/commonjs/canvas/InFlightLayer.js.map +1 -0
  33. package/lib/commonjs/canvas/SelectionOverlay.js +90 -0
  34. package/lib/commonjs/canvas/SelectionOverlay.js.map +1 -0
  35. package/lib/commonjs/constants.js +56 -0
  36. package/lib/commonjs/constants.js.map +1 -0
  37. package/lib/commonjs/context/EditorContext.js +132 -0
  38. package/lib/commonjs/context/EditorContext.js.map +1 -0
  39. package/lib/commonjs/export/drawScene.js +97 -0
  40. package/lib/commonjs/export/drawScene.js.map +1 -0
  41. package/lib/commonjs/export/exportImage.js +92 -0
  42. package/lib/commonjs/export/exportImage.js.map +1 -0
  43. package/lib/commonjs/gestures/applyTransform.js +79 -0
  44. package/lib/commonjs/gestures/applyTransform.js.map +1 -0
  45. package/lib/commonjs/gestures/createAnnotation.js +73 -0
  46. package/lib/commonjs/gestures/createAnnotation.js.map +1 -0
  47. package/lib/commonjs/gestures/handles.js +53 -0
  48. package/lib/commonjs/gestures/handles.js.map +1 -0
  49. package/lib/commonjs/gestures/hitTest.js +72 -0
  50. package/lib/commonjs/gestures/hitTest.js.map +1 -0
  51. package/lib/commonjs/gestures/useCropGesture.js +149 -0
  52. package/lib/commonjs/gestures/useCropGesture.js.map +1 -0
  53. package/lib/commonjs/gestures/useEditorGestures.js +289 -0
  54. package/lib/commonjs/gestures/useEditorGestures.js.map +1 -0
  55. package/lib/commonjs/image/disposeRegistry.js +63 -0
  56. package/lib/commonjs/image/disposeRegistry.js.map +1 -0
  57. package/lib/commonjs/image/useLoadedImage.js +121 -0
  58. package/lib/commonjs/image/useLoadedImage.js.map +1 -0
  59. package/lib/commonjs/index.js +52 -0
  60. package/lib/commonjs/index.js.map +1 -0
  61. package/lib/commonjs/package.json +1 -0
  62. package/lib/commonjs/state/history.js +85 -0
  63. package/lib/commonjs/state/history.js.map +1 -0
  64. package/lib/commonjs/state/selectors.js +19 -0
  65. package/lib/commonjs/state/selectors.js.map +1 -0
  66. package/lib/commonjs/state/useEditorReducer.js +83 -0
  67. package/lib/commonjs/state/useEditorReducer.js.map +1 -0
  68. package/lib/commonjs/toolbar/ColorPicker.js +84 -0
  69. package/lib/commonjs/toolbar/ColorPicker.js.map +1 -0
  70. package/lib/commonjs/toolbar/CropControls.js +65 -0
  71. package/lib/commonjs/toolbar/CropControls.js.map +1 -0
  72. package/lib/commonjs/toolbar/RotationSlider.js +73 -0
  73. package/lib/commonjs/toolbar/RotationSlider.js.map +1 -0
  74. package/lib/commonjs/toolbar/TextInputOverlay.js +108 -0
  75. package/lib/commonjs/toolbar/TextInputOverlay.js.map +1 -0
  76. package/lib/commonjs/toolbar/ToolButton.js +56 -0
  77. package/lib/commonjs/toolbar/ToolButton.js.map +1 -0
  78. package/lib/commonjs/toolbar/Toolbar.js +137 -0
  79. package/lib/commonjs/toolbar/Toolbar.js.map +1 -0
  80. package/lib/commonjs/types.js +47 -0
  81. package/lib/commonjs/types.js.map +1 -0
  82. package/lib/commonjs/utils/color.js +37 -0
  83. package/lib/commonjs/utils/color.js.map +1 -0
  84. package/lib/commonjs/utils/id.js +14 -0
  85. package/lib/commonjs/utils/id.js.map +1 -0
  86. package/lib/commonjs/utils/math.js +277 -0
  87. package/lib/commonjs/utils/math.js.map +1 -0
  88. package/lib/module/ImageEditor.js +138 -0
  89. package/lib/module/ImageEditor.js.map +1 -0
  90. package/lib/module/annotations/AnnotationView.js +39 -0
  91. package/lib/module/annotations/AnnotationView.js.map +1 -0
  92. package/lib/module/annotations/ArrowAnnotation.js +26 -0
  93. package/lib/module/annotations/ArrowAnnotation.js.map +1 -0
  94. package/lib/module/annotations/CircleAnnotation.js +27 -0
  95. package/lib/module/annotations/CircleAnnotation.js.map +1 -0
  96. package/lib/module/annotations/FreehandAnnotation.js +25 -0
  97. package/lib/module/annotations/FreehandAnnotation.js.map +1 -0
  98. package/lib/module/annotations/MarkerAnnotation.js +23 -0
  99. package/lib/module/annotations/MarkerAnnotation.js.map +1 -0
  100. package/lib/module/annotations/RotatedGroup.js +29 -0
  101. package/lib/module/annotations/RotatedGroup.js.map +1 -0
  102. package/lib/module/annotations/TextAnnotation.js +37 -0
  103. package/lib/module/annotations/TextAnnotation.js.map +1 -0
  104. package/lib/module/annotations/geometry.js +56 -0
  105. package/lib/module/annotations/geometry.js.map +1 -0
  106. package/lib/module/annotations/geometryPure.js +100 -0
  107. package/lib/module/annotations/geometryPure.js.map +1 -0
  108. package/lib/module/canvas/AnnotationLayer.js +55 -0
  109. package/lib/module/canvas/AnnotationLayer.js.map +1 -0
  110. package/lib/module/canvas/BaseImageLayer.js +23 -0
  111. package/lib/module/canvas/BaseImageLayer.js.map +1 -0
  112. package/lib/module/canvas/CropOverlay.js +131 -0
  113. package/lib/module/canvas/CropOverlay.js.map +1 -0
  114. package/lib/module/canvas/EditorCanvas.js +88 -0
  115. package/lib/module/canvas/EditorCanvas.js.map +1 -0
  116. package/lib/module/canvas/InFlightLayer.js +149 -0
  117. package/lib/module/canvas/InFlightLayer.js.map +1 -0
  118. package/lib/module/canvas/SelectionOverlay.js +85 -0
  119. package/lib/module/canvas/SelectionOverlay.js.map +1 -0
  120. package/lib/module/constants.js +52 -0
  121. package/lib/module/constants.js.map +1 -0
  122. package/lib/module/context/EditorContext.js +126 -0
  123. package/lib/module/context/EditorContext.js.map +1 -0
  124. package/lib/module/export/drawScene.js +93 -0
  125. package/lib/module/export/drawScene.js.map +1 -0
  126. package/lib/module/export/exportImage.js +88 -0
  127. package/lib/module/export/exportImage.js.map +1 -0
  128. package/lib/module/gestures/applyTransform.js +75 -0
  129. package/lib/module/gestures/applyTransform.js.map +1 -0
  130. package/lib/module/gestures/createAnnotation.js +65 -0
  131. package/lib/module/gestures/createAnnotation.js.map +1 -0
  132. package/lib/module/gestures/handles.js +49 -0
  133. package/lib/module/gestures/handles.js.map +1 -0
  134. package/lib/module/gestures/hitTest.js +69 -0
  135. package/lib/module/gestures/hitTest.js.map +1 -0
  136. package/lib/module/gestures/useCropGesture.js +145 -0
  137. package/lib/module/gestures/useCropGesture.js.map +1 -0
  138. package/lib/module/gestures/useEditorGestures.js +285 -0
  139. package/lib/module/gestures/useEditorGestures.js.map +1 -0
  140. package/lib/module/image/disposeRegistry.js +57 -0
  141. package/lib/module/image/disposeRegistry.js.map +1 -0
  142. package/lib/module/image/useLoadedImage.js +117 -0
  143. package/lib/module/image/useLoadedImage.js.map +1 -0
  144. package/lib/module/index.js +8 -0
  145. package/lib/module/index.js.map +1 -0
  146. package/lib/module/package.json +1 -0
  147. package/lib/module/state/history.js +76 -0
  148. package/lib/module/state/history.js.map +1 -0
  149. package/lib/module/state/selectors.js +14 -0
  150. package/lib/module/state/selectors.js.map +1 -0
  151. package/lib/module/state/useEditorReducer.js +79 -0
  152. package/lib/module/state/useEditorReducer.js.map +1 -0
  153. package/lib/module/toolbar/ColorPicker.js +80 -0
  154. package/lib/module/toolbar/ColorPicker.js.map +1 -0
  155. package/lib/module/toolbar/CropControls.js +62 -0
  156. package/lib/module/toolbar/CropControls.js.map +1 -0
  157. package/lib/module/toolbar/RotationSlider.js +69 -0
  158. package/lib/module/toolbar/RotationSlider.js.map +1 -0
  159. package/lib/module/toolbar/TextInputOverlay.js +105 -0
  160. package/lib/module/toolbar/TextInputOverlay.js.map +1 -0
  161. package/lib/module/toolbar/ToolButton.js +52 -0
  162. package/lib/module/toolbar/ToolButton.js.map +1 -0
  163. package/lib/module/toolbar/Toolbar.js +133 -0
  164. package/lib/module/toolbar/Toolbar.js.map +1 -0
  165. package/lib/module/types.js +43 -0
  166. package/lib/module/types.js.map +1 -0
  167. package/lib/module/utils/color.js +33 -0
  168. package/lib/module/utils/color.js.map +1 -0
  169. package/lib/module/utils/id.js +10 -0
  170. package/lib/module/utils/id.js.map +1 -0
  171. package/lib/module/utils/math.js +258 -0
  172. package/lib/module/utils/math.js.map +1 -0
  173. package/lib/typescript/src/ImageEditor.d.ts +9 -0
  174. package/lib/typescript/src/ImageEditor.d.ts.map +1 -0
  175. package/lib/typescript/src/annotations/AnnotationView.d.ts +6 -0
  176. package/lib/typescript/src/annotations/AnnotationView.d.ts.map +1 -0
  177. package/lib/typescript/src/annotations/ArrowAnnotation.d.ts +5 -0
  178. package/lib/typescript/src/annotations/ArrowAnnotation.d.ts.map +1 -0
  179. package/lib/typescript/src/annotations/CircleAnnotation.d.ts +5 -0
  180. package/lib/typescript/src/annotations/CircleAnnotation.d.ts.map +1 -0
  181. package/lib/typescript/src/annotations/FreehandAnnotation.d.ts +5 -0
  182. package/lib/typescript/src/annotations/FreehandAnnotation.d.ts.map +1 -0
  183. package/lib/typescript/src/annotations/MarkerAnnotation.d.ts +5 -0
  184. package/lib/typescript/src/annotations/MarkerAnnotation.d.ts.map +1 -0
  185. package/lib/typescript/src/annotations/RotatedGroup.d.ts +13 -0
  186. package/lib/typescript/src/annotations/RotatedGroup.d.ts.map +1 -0
  187. package/lib/typescript/src/annotations/TextAnnotation.d.ts +10 -0
  188. package/lib/typescript/src/annotations/TextAnnotation.d.ts.map +1 -0
  189. package/lib/typescript/src/annotations/geometry.d.ts +11 -0
  190. package/lib/typescript/src/annotations/geometry.d.ts.map +1 -0
  191. package/lib/typescript/src/annotations/geometryPure.d.ts +14 -0
  192. package/lib/typescript/src/annotations/geometryPure.d.ts.map +1 -0
  193. package/lib/typescript/src/canvas/AnnotationLayer.d.ts +11 -0
  194. package/lib/typescript/src/canvas/AnnotationLayer.d.ts.map +1 -0
  195. package/lib/typescript/src/canvas/BaseImageLayer.d.ts +12 -0
  196. package/lib/typescript/src/canvas/BaseImageLayer.d.ts.map +1 -0
  197. package/lib/typescript/src/canvas/CropOverlay.d.ts +10 -0
  198. package/lib/typescript/src/canvas/CropOverlay.d.ts.map +1 -0
  199. package/lib/typescript/src/canvas/EditorCanvas.d.ts +18 -0
  200. package/lib/typescript/src/canvas/EditorCanvas.d.ts.map +1 -0
  201. package/lib/typescript/src/canvas/InFlightLayer.d.ts +12 -0
  202. package/lib/typescript/src/canvas/InFlightLayer.d.ts.map +1 -0
  203. package/lib/typescript/src/canvas/SelectionOverlay.d.ts +11 -0
  204. package/lib/typescript/src/canvas/SelectionOverlay.d.ts.map +1 -0
  205. package/lib/typescript/src/constants.d.ts +25 -0
  206. package/lib/typescript/src/constants.d.ts.map +1 -0
  207. package/lib/typescript/src/context/EditorContext.d.ts +66 -0
  208. package/lib/typescript/src/context/EditorContext.d.ts.map +1 -0
  209. package/lib/typescript/src/export/drawScene.d.ts +10 -0
  210. package/lib/typescript/src/export/drawScene.d.ts.map +1 -0
  211. package/lib/typescript/src/export/exportImage.d.ts +23 -0
  212. package/lib/typescript/src/export/exportImage.d.ts.map +1 -0
  213. package/lib/typescript/src/gestures/applyTransform.d.ts +17 -0
  214. package/lib/typescript/src/gestures/applyTransform.d.ts.map +1 -0
  215. package/lib/typescript/src/gestures/createAnnotation.d.ts +11 -0
  216. package/lib/typescript/src/gestures/createAnnotation.d.ts.map +1 -0
  217. package/lib/typescript/src/gestures/handles.d.ts +17 -0
  218. package/lib/typescript/src/gestures/handles.d.ts.map +1 -0
  219. package/lib/typescript/src/gestures/hitTest.d.ts +9 -0
  220. package/lib/typescript/src/gestures/hitTest.d.ts.map +1 -0
  221. package/lib/typescript/src/gestures/useCropGesture.d.ts +7 -0
  222. package/lib/typescript/src/gestures/useCropGesture.d.ts.map +1 -0
  223. package/lib/typescript/src/gestures/useEditorGestures.d.ts +8 -0
  224. package/lib/typescript/src/gestures/useEditorGestures.d.ts.map +1 -0
  225. package/lib/typescript/src/image/disposeRegistry.d.ts +25 -0
  226. package/lib/typescript/src/image/disposeRegistry.d.ts.map +1 -0
  227. package/lib/typescript/src/image/useLoadedImage.d.ts +23 -0
  228. package/lib/typescript/src/image/useLoadedImage.d.ts.map +1 -0
  229. package/lib/typescript/src/index.d.ts +6 -0
  230. package/lib/typescript/src/index.d.ts.map +1 -0
  231. package/lib/typescript/src/state/history.d.ts +23 -0
  232. package/lib/typescript/src/state/history.d.ts.map +1 -0
  233. package/lib/typescript/src/state/selectors.d.ts +5 -0
  234. package/lib/typescript/src/state/selectors.d.ts.map +1 -0
  235. package/lib/typescript/src/state/useEditorReducer.d.ts +32 -0
  236. package/lib/typescript/src/state/useEditorReducer.d.ts.map +1 -0
  237. package/lib/typescript/src/toolbar/ColorPicker.d.ts +7 -0
  238. package/lib/typescript/src/toolbar/ColorPicker.d.ts.map +1 -0
  239. package/lib/typescript/src/toolbar/CropControls.d.ts +3 -0
  240. package/lib/typescript/src/toolbar/CropControls.d.ts.map +1 -0
  241. package/lib/typescript/src/toolbar/RotationSlider.d.ts +8 -0
  242. package/lib/typescript/src/toolbar/RotationSlider.d.ts.map +1 -0
  243. package/lib/typescript/src/toolbar/TextInputOverlay.d.ts +9 -0
  244. package/lib/typescript/src/toolbar/TextInputOverlay.d.ts.map +1 -0
  245. package/lib/typescript/src/toolbar/ToolButton.d.ts +7 -0
  246. package/lib/typescript/src/toolbar/ToolButton.d.ts.map +1 -0
  247. package/lib/typescript/src/toolbar/Toolbar.d.ts +4 -0
  248. package/lib/typescript/src/toolbar/Toolbar.d.ts.map +1 -0
  249. package/lib/typescript/src/types.d.ts +170 -0
  250. package/lib/typescript/src/types.d.ts.map +1 -0
  251. package/lib/typescript/src/utils/color.d.ts +8 -0
  252. package/lib/typescript/src/utils/color.d.ts.map +1 -0
  253. package/lib/typescript/src/utils/id.d.ts +3 -0
  254. package/lib/typescript/src/utils/id.d.ts.map +1 -0
  255. package/lib/typescript/src/utils/math.d.ts +68 -0
  256. package/lib/typescript/src/utils/math.d.ts.map +1 -0
  257. package/package.json +90 -0
  258. package/src/ImageEditor.tsx +133 -0
  259. package/src/annotations/AnnotationView.tsx +24 -0
  260. package/src/annotations/ArrowAnnotation.tsx +26 -0
  261. package/src/annotations/CircleAnnotation.tsx +22 -0
  262. package/src/annotations/FreehandAnnotation.tsx +22 -0
  263. package/src/annotations/MarkerAnnotation.tsx +20 -0
  264. package/src/annotations/RotatedGroup.tsx +28 -0
  265. package/src/annotations/TextAnnotation.tsx +42 -0
  266. package/src/annotations/geometry.ts +62 -0
  267. package/src/annotations/geometryPure.ts +73 -0
  268. package/src/canvas/AnnotationLayer.tsx +43 -0
  269. package/src/canvas/BaseImageLayer.tsx +28 -0
  270. package/src/canvas/CropOverlay.tsx +92 -0
  271. package/src/canvas/EditorCanvas.tsx +70 -0
  272. package/src/canvas/InFlightLayer.tsx +140 -0
  273. package/src/canvas/SelectionOverlay.tsx +92 -0
  274. package/src/constants.ts +46 -0
  275. package/src/context/EditorContext.tsx +229 -0
  276. package/src/export/drawScene.ts +120 -0
  277. package/src/export/exportImage.ts +111 -0
  278. package/src/gestures/applyTransform.ts +76 -0
  279. package/src/gestures/createAnnotation.ts +92 -0
  280. package/src/gestures/handles.ts +50 -0
  281. package/src/gestures/hitTest.ts +79 -0
  282. package/src/gestures/useCropGesture.ts +123 -0
  283. package/src/gestures/useEditorGestures.ts +308 -0
  284. package/src/image/disposeRegistry.ts +59 -0
  285. package/src/image/useLoadedImage.ts +131 -0
  286. package/src/index.ts +32 -0
  287. package/src/state/history.ts +71 -0
  288. package/src/state/selectors.ts +16 -0
  289. package/src/state/useEditorReducer.ts +93 -0
  290. package/src/toolbar/ColorPicker.tsx +72 -0
  291. package/src/toolbar/CropControls.tsx +46 -0
  292. package/src/toolbar/RotationSlider.tsx +56 -0
  293. package/src/toolbar/TextInputOverlay.tsx +104 -0
  294. package/src/toolbar/ToolButton.tsx +46 -0
  295. package/src/toolbar/Toolbar.tsx +110 -0
  296. package/src/types.ts +203 -0
  297. package/src/utils/color.ts +34 -0
  298. package/src/utils/id.ts +7 -0
  299. package/src/utils/math.ts +222 -0
package/src/types.ts ADDED
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Public + internal types for the Skia image editor.
3
+ *
4
+ * COORDINATE-SPACE CONTRACT (read this first):
5
+ * Every annotation's geometry is stored in **image space** — the base image's
6
+ * full-resolution pixel coordinate system (0..imageWidth, 0..imageHeight).
7
+ * It is NEVER stored in screen space. A single `imageToScreen` affine matrix
8
+ * maps image space onto the on-screen canvas; its inverse maps touches back.
9
+ * Because of this, export can render the exact same numbers off-screen at native
10
+ * resolution with no rescaling. See `utils/math.ts`.
11
+ */
12
+
13
+ export interface Vec2 {
14
+ x: number;
15
+ y: number;
16
+ }
17
+
18
+ export interface Rect {
19
+ x: number;
20
+ y: number;
21
+ width: number;
22
+ height: number;
23
+ }
24
+
25
+ export type ToolType =
26
+ | 'select'
27
+ | 'circle'
28
+ | 'arrow'
29
+ | 'marker'
30
+ | 'freehand'
31
+ | 'text'
32
+ | 'crop';
33
+
34
+ export type AnnotationType = 'circle' | 'arrow' | 'marker' | 'freehand' | 'text';
35
+
36
+ /**
37
+ * Colors are stored as CSS-style strings ("#RRGGBB" / "#RRGGBBAA" / named).
38
+ * Skia accepts these directly, and they are trivially serializable for history.
39
+ */
40
+ export type ColorString = string;
41
+
42
+ interface BaseAnnotation {
43
+ id: string;
44
+ type: AnnotationType;
45
+ /** Rotation in radians about the annotation's own center. */
46
+ rotation: number;
47
+ /** Paint order; higher = drawn on top / hit-tested first. */
48
+ z: number;
49
+ }
50
+
51
+ export interface CircleAnnotation extends BaseAnnotation {
52
+ type: 'circle';
53
+ center: Vec2;
54
+ radius: number;
55
+ strokeColor: ColorString;
56
+ strokeWidth: number;
57
+ /** Optional fill; undefined = outline only. */
58
+ fill?: ColorString;
59
+ }
60
+
61
+ export interface ArrowAnnotation extends BaseAnnotation {
62
+ type: 'arrow';
63
+ start: Vec2;
64
+ end: Vec2;
65
+ /** Length of the arrowhead barbs, in image pixels. */
66
+ headSize: number;
67
+ strokeColor: ColorString;
68
+ strokeWidth: number;
69
+ }
70
+
71
+ export interface MarkerAnnotation extends BaseAnnotation {
72
+ type: 'marker';
73
+ /** Highlight rectangle, in image space. */
74
+ rect: Rect;
75
+ color: ColorString;
76
+ /** 0..1 — highlighters are semi-transparent. */
77
+ opacity: number;
78
+ }
79
+
80
+ export interface FreehandAnnotation extends BaseAnnotation {
81
+ type: 'freehand';
82
+ points: Vec2[];
83
+ strokeColor: ColorString;
84
+ strokeWidth: number;
85
+ }
86
+
87
+ export interface TextAnnotation extends BaseAnnotation {
88
+ type: 'text';
89
+ /** Top-left origin of the text box, in image space. */
90
+ origin: Vec2;
91
+ text: string;
92
+ color: ColorString;
93
+ fontSize: number;
94
+ /** Wrap width of the text box, in image pixels. */
95
+ width: number;
96
+ }
97
+
98
+ export type Annotation =
99
+ | CircleAnnotation
100
+ | ArrowAnnotation
101
+ | MarkerAnnotation
102
+ | FreehandAnnotation
103
+ | TextAnnotation;
104
+
105
+ /**
106
+ * Non-destructive image transforms. Nothing is baked into pixels until export,
107
+ * so all of these are undoable.
108
+ */
109
+ export interface SceneTransform {
110
+ /** Free-angle rotation of the whole image + its annotations, in radians. */
111
+ rotation: number;
112
+ /** Uniform resize factor applied at export. */
113
+ scale: number;
114
+ /** Crop region in image space, or null for the full image. */
115
+ cropRect: Rect | null;
116
+ }
117
+
118
+ export const IDENTITY_SCENE: SceneTransform = {
119
+ rotation: 0,
120
+ scale: 1,
121
+ cropRect: null,
122
+ };
123
+
124
+ /** The serializable editor document — everything undo/redo tracks. */
125
+ export interface EditorDocument {
126
+ annotations: Annotation[];
127
+ scene: SceneTransform;
128
+ }
129
+
130
+ export type OutputFormat = 'png' | 'jpeg';
131
+
132
+ /**
133
+ * Callback that persists an encoded image to disk. The library has no
134
+ * filesystem access of its own, so the consumer supplies this using whatever fs
135
+ * module they already have (react-native-fs, expo-file-system, etc.). It
136
+ * receives the destination path and the RAW base64 payload (no data-URI prefix).
137
+ */
138
+ export type WriteFileFn = (path: string, base64: string) => Promise<void>;
139
+
140
+ export interface ExportOptions {
141
+ format?: OutputFormat;
142
+ /** JPEG quality 0..100 (ignored for PNG). Default 100. */
143
+ quality?: number;
144
+ /**
145
+ * Clamp the longest output edge to this many pixels (keeps aspect ratio).
146
+ * Guards against OOM when snapshotting very large images. Default: no clamp.
147
+ */
148
+ maxExportSize?: number;
149
+ /**
150
+ * `'base64'` (default) resolves to a base64 string. `'file'` writes the image
151
+ * to `filePath` using `writeFile` and resolves to that path.
152
+ */
153
+ output?: 'base64' | 'file';
154
+ /** Destination path/URI when `output: 'file'`. */
155
+ filePath?: string;
156
+ /** Required when `output: 'file'` — writes the base64 payload to disk. */
157
+ writeFile?: WriteFileFn;
158
+ /**
159
+ * For `output: 'base64'` only. When true (default), returns a
160
+ * `data:image/...;base64,...` URI. When false, returns raw base64 (no prefix).
161
+ */
162
+ dataUri?: boolean;
163
+ }
164
+
165
+ /** Imperative handle exposed via `ref`. */
166
+ export interface ImageEditorRef {
167
+ /**
168
+ * Render the current scene off-screen at full resolution. Resolves to base64
169
+ * (default) or, with `output: 'file'`, to the written file path.
170
+ */
171
+ export: (options?: ExportOptions) => Promise<string>;
172
+ undo: () => void;
173
+ redo: () => void;
174
+ canUndo: () => boolean;
175
+ canRedo: () => boolean;
176
+ /** Remove all annotations and reset scene transforms (keeps the base image). */
177
+ reset: () => void;
178
+ }
179
+
180
+ /** Source image: a base64 string (raw or data-URI) or a file/remote URI. */
181
+ export type ImageSource =
182
+ | { base64: string }
183
+ | { uri: string };
184
+
185
+ export interface ImageEditorProps {
186
+ /** Base image to edit. */
187
+ source: ImageSource;
188
+ /** Fired when the user taps the built-in export/done control. */
189
+ onExport?: (base64: string) => void;
190
+ /** Default export options for the built-in export control. */
191
+ exportOptions?: ExportOptions;
192
+ /** Initial stroke color for new shapes. Default "#FF3B30". */
193
+ initialStrokeColor?: ColorString;
194
+ /** Initial text color. Default "#FFFFFF". */
195
+ initialTextColor?: ColorString;
196
+ /** Palette shown in the color picker. */
197
+ palette?: ColorString[];
198
+ /** Hide the built-in toolbar (drive tools yourself via ref/context). */
199
+ hideToolbar?: boolean;
200
+ /** Called whenever the loaded image fails to decode. */
201
+ onError?: (error: Error) => void;
202
+ style?: import('react-native').StyleProp<import('react-native').ViewStyle>;
203
+ }
@@ -0,0 +1,34 @@
1
+ import type { ColorString } from '../types';
2
+
3
+ /**
4
+ * Apply an opacity (0..1) to a hex color string, returning an 8-digit hex
5
+ * ("#RRGGBBAA"). Used by the marker/highlighter which draws semi-transparently.
6
+ * Non-hex inputs (named colors, rgba(...)) are returned unchanged.
7
+ */
8
+ export function withOpacity(color: ColorString, opacity: number): ColorString {
9
+ const clamped = Math.max(0, Math.min(1, opacity));
10
+ const hex = color.trim();
11
+ if (!hex.startsWith('#')) {
12
+ return hex;
13
+ }
14
+ let r: string;
15
+ let g: string;
16
+ let b: string;
17
+ if (hex.length === 4) {
18
+ // #RGB
19
+ r = hex[1]! + hex[1]!;
20
+ g = hex[2]! + hex[2]!;
21
+ b = hex[3]! + hex[3]!;
22
+ } else if (hex.length === 7 || hex.length === 9) {
23
+ // #RRGGBB or #RRGGBBAA (ignore existing alpha)
24
+ r = hex.slice(1, 3);
25
+ g = hex.slice(3, 5);
26
+ b = hex.slice(5, 7);
27
+ } else {
28
+ return hex;
29
+ }
30
+ const a = Math.round(clamped * 255)
31
+ .toString(16)
32
+ .padStart(2, '0');
33
+ return `#${r}${g}${b}${a}`;
34
+ }
@@ -0,0 +1,7 @@
1
+ let counter = 0;
2
+
3
+ /** Small unique id generator (JS thread only — avoids a nanoid dependency). */
4
+ export function genId(prefix = 'a'): string {
5
+ counter += 1;
6
+ return `${prefix}_${Date.now().toString(36)}_${counter.toString(36)}`;
7
+ }
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Pure 2D affine-matrix math. Every function is marked `worklet` so it can run
3
+ * on the UI thread inside gesture handlers as well as on the JS thread.
4
+ *
5
+ * A matrix `Mat` maps a point p to p' via:
6
+ * x' = a*x + c*y + e
7
+ * y' = b*x + d*y + f
8
+ * (the canonical CSS/canvas affine convention).
9
+ */
10
+
11
+ import type { Rect, SceneTransform, Vec2 } from '../types';
12
+
13
+ export interface Mat {
14
+ a: number;
15
+ b: number;
16
+ c: number;
17
+ d: number;
18
+ e: number;
19
+ f: number;
20
+ }
21
+
22
+ export function identity(): Mat {
23
+ 'worklet';
24
+ return { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 };
25
+ }
26
+
27
+ /** Returns m1 ∘ m2 (applies m2 first, then m1). */
28
+ export function multiply(m1: Mat, m2: Mat): Mat {
29
+ 'worklet';
30
+ return {
31
+ a: m1.a * m2.a + m1.c * m2.b,
32
+ b: m1.b * m2.a + m1.d * m2.b,
33
+ c: m1.a * m2.c + m1.c * m2.d,
34
+ d: m1.b * m2.c + m1.d * m2.d,
35
+ e: m1.a * m2.e + m1.c * m2.f + m1.e,
36
+ f: m1.b * m2.e + m1.d * m2.f + m1.f,
37
+ };
38
+ }
39
+
40
+ export function translation(tx: number, ty: number): Mat {
41
+ 'worklet';
42
+ return { a: 1, b: 0, c: 0, d: 1, e: tx, f: ty };
43
+ }
44
+
45
+ export function scaling(sx: number, sy: number): Mat {
46
+ 'worklet';
47
+ return { a: sx, b: 0, c: 0, d: sy, e: 0, f: 0 };
48
+ }
49
+
50
+ export function rotation(theta: number): Mat {
51
+ 'worklet';
52
+ const cos = Math.cos(theta);
53
+ const sin = Math.sin(theta);
54
+ return { a: cos, b: sin, c: -sin, d: cos, e: 0, f: 0 };
55
+ }
56
+
57
+ export function invert(m: Mat): Mat {
58
+ 'worklet';
59
+ const det = m.a * m.d - m.b * m.c;
60
+ if (det === 0) {
61
+ return identity();
62
+ }
63
+ const inv = 1 / det;
64
+ return {
65
+ a: m.d * inv,
66
+ b: -m.b * inv,
67
+ c: -m.c * inv,
68
+ d: m.a * inv,
69
+ e: (m.c * m.f - m.d * m.e) * inv,
70
+ f: (m.b * m.e - m.a * m.f) * inv,
71
+ };
72
+ }
73
+
74
+ export function applyToPoint(m: Mat, p: Vec2): Vec2 {
75
+ 'worklet';
76
+ return {
77
+ x: m.a * p.x + m.c * p.y + m.e,
78
+ y: m.b * p.x + m.d * p.y + m.f,
79
+ };
80
+ }
81
+
82
+ /** Rotate point `p` by `theta` radians about `center`. */
83
+ export function rotatePoint(p: Vec2, center: Vec2, theta: number): Vec2 {
84
+ 'worklet';
85
+ const cos = Math.cos(theta);
86
+ const sin = Math.sin(theta);
87
+ const dx = p.x - center.x;
88
+ const dy = p.y - center.y;
89
+ return {
90
+ x: center.x + dx * cos - dy * sin,
91
+ y: center.y + dx * sin + dy * cos,
92
+ };
93
+ }
94
+
95
+ export interface Layout {
96
+ width: number;
97
+ height: number;
98
+ }
99
+
100
+ export interface ImageSize {
101
+ width: number;
102
+ height: number;
103
+ }
104
+
105
+ /**
106
+ * The base fit scale used to "contain" the image inside the on-screen layout,
107
+ * before any user scene scale is applied.
108
+ */
109
+ export function fitScale(image: ImageSize, layout: Layout): number {
110
+ 'worklet';
111
+ if (image.width <= 0 || image.height <= 0) {
112
+ return 1;
113
+ }
114
+ return Math.min(layout.width / image.width, layout.height / image.height);
115
+ }
116
+
117
+ /**
118
+ * Compose the image→screen matrix:
119
+ * M = T(center) · R(rotation) · S(fit·userScale) · T(-imageCenter)
120
+ *
121
+ * The on-screen Skia `<Group>` uses `sceneTransforms2d` (below), built from the
122
+ * SAME parameters, so the preview and this matrix stay in lock-step. Gestures
123
+ * use `invert(M)` to map screen touches back into image space.
124
+ */
125
+ export function imageToScreenMatrix(
126
+ scene: SceneTransform,
127
+ image: ImageSize,
128
+ layout: Layout
129
+ ): Mat {
130
+ 'worklet';
131
+ const s = fitScale(image, layout) * scene.scale;
132
+ const cx = layout.width / 2;
133
+ const cy = layout.height / 2;
134
+ let m = translation(cx, cy);
135
+ m = multiply(m, rotation(scene.rotation));
136
+ m = multiply(m, scaling(s, s));
137
+ m = multiply(m, translation(-image.width / 2, -image.height / 2));
138
+ return m;
139
+ }
140
+
141
+ /**
142
+ * Skia `Transforms2d` array for the on-screen scene `<Group>`. Applied in order,
143
+ * this reproduces `imageToScreenMatrix` exactly.
144
+ */
145
+ export function sceneTransforms2d(
146
+ scene: SceneTransform,
147
+ image: ImageSize,
148
+ layout: Layout
149
+ ): { translateX: number }[] | object[] {
150
+ 'worklet';
151
+ const s = fitScale(image, layout) * scene.scale;
152
+ return [
153
+ { translateX: layout.width / 2 },
154
+ { translateY: layout.height / 2 },
155
+ { rotate: scene.rotation },
156
+ { scaleX: s },
157
+ { scaleY: s },
158
+ { translateX: -image.width / 2 },
159
+ { translateY: -image.height / 2 },
160
+ ];
161
+ }
162
+
163
+ export function clampSizeToMax(
164
+ width: number,
165
+ height: number,
166
+ maxSize?: number
167
+ ): { width: number; height: number } {
168
+ 'worklet';
169
+ if (!maxSize || maxSize <= 0) {
170
+ return { width, height };
171
+ }
172
+ const longest = Math.max(width, height);
173
+ if (longest <= maxSize) {
174
+ return { width, height };
175
+ }
176
+ const k = maxSize / longest;
177
+ return { width: Math.round(width * k), height: Math.round(height * k) };
178
+ }
179
+
180
+ /** Axis-aligned bounding box of a set of points. */
181
+ export function boundsOfPoints(points: Vec2[]): Rect {
182
+ 'worklet';
183
+ if (points.length === 0) {
184
+ return { x: 0, y: 0, width: 0, height: 0 };
185
+ }
186
+ let minX = points[0]!.x;
187
+ let minY = points[0]!.y;
188
+ let maxX = points[0]!.x;
189
+ let maxY = points[0]!.y;
190
+ for (let i = 1; i < points.length; i++) {
191
+ const p = points[i]!;
192
+ if (p.x < minX) minX = p.x;
193
+ if (p.y < minY) minY = p.y;
194
+ if (p.x > maxX) maxX = p.x;
195
+ if (p.y > maxY) maxY = p.y;
196
+ }
197
+ return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
198
+ }
199
+
200
+ export function rectCenter(r: Rect): Vec2 {
201
+ 'worklet';
202
+ return { x: r.x + r.width / 2, y: r.y + r.height / 2 };
203
+ }
204
+
205
+ export function distance(a: Vec2, b: Vec2): number {
206
+ 'worklet';
207
+ return Math.hypot(a.x - b.x, a.y - b.y);
208
+ }
209
+
210
+ /** Shortest distance from point `p` to segment `a`→`b`. */
211
+ export function distanceToSegment(p: Vec2, a: Vec2, b: Vec2): number {
212
+ 'worklet';
213
+ const dx = b.x - a.x;
214
+ const dy = b.y - a.y;
215
+ const lenSq = dx * dx + dy * dy;
216
+ if (lenSq === 0) {
217
+ return distance(p, a);
218
+ }
219
+ let t = ((p.x - a.x) * dx + (p.y - a.y) * dy) / lenSq;
220
+ t = Math.max(0, Math.min(1, t));
221
+ return distance(p, { x: a.x + t * dx, y: a.y + t * dy });
222
+ }