react-native-libyuv-resizer 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 (50) hide show
  1. package/LICENSE +20 -0
  2. package/LibyuvResizer.podspec +20 -0
  3. package/README.md +188 -0
  4. package/android/CMakeLists.txt +30 -0
  5. package/android/build.gradle +100 -0
  6. package/android/src/androidTest/java/com/libyuvresizer/ExifCopierTest.kt +131 -0
  7. package/android/src/androidTest/java/com/libyuvresizer/FakePromise.kt +71 -0
  8. package/android/src/androidTest/java/com/libyuvresizer/FakeReactContext.kt +55 -0
  9. package/android/src/androidTest/java/com/libyuvresizer/LibyuvResizerModuleErrorTest.kt +135 -0
  10. package/android/src/androidTest/java/com/libyuvresizer/LibyuvResizerModuleExifTest.kt +140 -0
  11. package/android/src/androidTest/java/com/libyuvresizer/LibyuvResizerModuleFilterModeTest.kt +85 -0
  12. package/android/src/androidTest/java/com/libyuvresizer/LibyuvResizerModuleFormatTest.kt +146 -0
  13. package/android/src/androidTest/java/com/libyuvresizer/LibyuvResizerModuleIntegrationTest.kt +157 -0
  14. package/android/src/androidTest/java/com/libyuvresizer/LibyuvResizerModuleOutputPathTest.kt +96 -0
  15. package/android/src/androidTest/java/com/libyuvresizer/LibyuvResizerModuleRotationTest.kt +120 -0
  16. package/android/src/androidTest/java/com/libyuvresizer/TestFixtures.kt +48 -0
  17. package/android/src/main/AndroidManifest.xml +2 -0
  18. package/android/src/main/cpp/LibyuvResizerModule.cpp +137 -0
  19. package/android/src/main/java/com/libyuvresizer/DimensionCalculator.kt +52 -0
  20. package/android/src/main/java/com/libyuvresizer/ExifCopier.kt +133 -0
  21. package/android/src/main/java/com/libyuvresizer/LibyuvResizerModule.kt +179 -0
  22. package/android/src/main/java/com/libyuvresizer/LibyuvResizerPackage.kt +30 -0
  23. package/android/src/main/java/com/libyuvresizer/ResizeValidator.kt +71 -0
  24. package/android/src/test/java/com/libyuvresizer/DimensionCalculatorTest.kt +181 -0
  25. package/android/src/test/java/com/libyuvresizer/ResizeValidatorTest.kt +203 -0
  26. package/ios/LibyuvResizer.h +6 -0
  27. package/ios/LibyuvResizer.mm +31 -0
  28. package/lib/module/NativeLibyuvResizer.js +28 -0
  29. package/lib/module/NativeLibyuvResizer.js.map +1 -0
  30. package/lib/module/index.js +20 -0
  31. package/lib/module/index.js.map +1 -0
  32. package/lib/module/package.json +1 -0
  33. package/lib/module/resizer.js +15 -0
  34. package/lib/module/resizer.js.map +1 -0
  35. package/lib/module/resizer.native.js +110 -0
  36. package/lib/module/resizer.native.js.map +1 -0
  37. package/lib/typescript/package.json +1 -0
  38. package/lib/typescript/src/NativeLibyuvResizer.d.ts +52 -0
  39. package/lib/typescript/src/NativeLibyuvResizer.d.ts.map +1 -0
  40. package/lib/typescript/src/index.d.ts +19 -0
  41. package/lib/typescript/src/index.d.ts.map +1 -0
  42. package/lib/typescript/src/resizer.d.ts +13 -0
  43. package/lib/typescript/src/resizer.d.ts.map +1 -0
  44. package/lib/typescript/src/resizer.native.d.ts +119 -0
  45. package/lib/typescript/src/resizer.native.d.ts.map +1 -0
  46. package/package.json +184 -0
  47. package/src/NativeLibyuvResizer.ts +81 -0
  48. package/src/index.tsx +23 -0
  49. package/src/resizer.native.tsx +175 -0
  50. package/src/resizer.tsx +31 -0
@@ -0,0 +1,31 @@
1
+ #import "LibyuvResizer.h"
2
+
3
+ @implementation LibyuvResizer
4
+
5
+ RCT_EXPORT_MODULE(LibyuvResizer)
6
+
7
+ RCT_EXPORT_METHOD(resize:(NSString *)filePath
8
+ targetWidth:(double)targetWidth
9
+ targetHeight:(double)targetHeight
10
+ quality:(double)quality
11
+ rotation:(double)rotation
12
+ mode:(NSString *)mode
13
+ outputPath:(NSString *)outputPath
14
+ filterMode:(NSString *)filterMode
15
+ keepMeta:(BOOL)keepMeta
16
+ format:(NSString *)format
17
+ resolve:(RCTPromiseResolveBlock)resolve
18
+ reject:(RCTPromiseRejectBlock)reject)
19
+ {
20
+ (void)keepMeta;
21
+ (void)format;
22
+ reject(@"E_NOT_IMPLEMENTED", @"resize is not yet implemented on iOS", nil);
23
+ }
24
+
25
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
26
+ (const facebook::react::ObjCTurboModule::InitParams &)params
27
+ {
28
+ return std::make_shared<facebook::react::NativeLibyuvResizerSpecJSI>(params);
29
+ }
30
+
31
+ @end
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+
3
+ import { NativeModules, TurboModuleRegistry } from 'react-native';
4
+
5
+ /**
6
+ * Metadata returned by the native `resize()` bridge call.
7
+ *
8
+ * All numeric fields reflect the **output** file / bitmap — not the source.
9
+ */
10
+
11
+ /**
12
+ * Turbo Module contract for the native `LibyuvResizer` implementation.
13
+ *
14
+ * **Do not call this interface directly.** Use the public {@link resize}
15
+ * function from `./resizer` (or the package root) instead — it validates
16
+ * arguments and applies sensible defaults before forwarding to this bridge.
17
+ *
18
+ * This interface is consumed by React Native codegen to auto-generate the
19
+ * JSI glue code (`LibyuvResizerSpec`).
20
+ */
21
+
22
+ const isTurboModuleEnabled = globalThis.__turboModuleProxy != null;
23
+ const LibyuvResizerModule = isTurboModuleEnabled ? TurboModuleRegistry.getEnforcing('LibyuvResizer') : NativeModules.LibyuvResizer;
24
+ if (!LibyuvResizerModule) {
25
+ throw new Error('react-native-libyuv-resizer: native module not found. Did you forget to link the library?');
26
+ }
27
+ export default LibyuvResizerModule;
28
+ //# sourceMappingURL=NativeLibyuvResizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["NativeModules","TurboModuleRegistry","isTurboModuleEnabled","globalThis","__turboModuleProxy","LibyuvResizerModule","getEnforcing","LibyuvResizer","Error"],"sourceRoot":"..\\..\\src","sources":["NativeLibyuvResizer.ts"],"mappings":";;AAAA,SACEA,aAAa,EACbC,mBAAmB,QAEd,cAAc;;AAErB;AACA;AACA;AACA;AACA;;AAgBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAgCA,MAAMC,oBAAoB,GACvBC,UAAU,CAA6BC,kBAAkB,IAAI,IAAI;AAEpE,MAAMC,mBAAyB,GAAGH,oBAAoB,GAClDD,mBAAmB,CAACK,YAAY,CAAO,eAAe,CAAC,GACtDN,aAAa,CAACO,aAAsB;AAEzC,IAAI,CAACF,mBAAmB,EAAE;EACxB,MAAM,IAAIG,KAAK,CACb,2FACF,CAAC;AACH;AAEA,eAAeH,mBAAmB","ignoreList":[]}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * `react-native-libyuv-resizer` — high-performance image resizer for React
5
+ * Native backed by libyuv on Android.
6
+ *
7
+ * **Entry point.** Import {@link resize} and the supporting types from here.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * import { resize } from 'react-native-libyuv-resizer';
12
+ *
13
+ * const result = await resize('/path/to/photo.jpg', 1280, 720, 85);
14
+ * console.log(result.path, result.width, result.height);
15
+ * ```
16
+ *
17
+ * @packageDocumentation
18
+ */
19
+ export { resize } from './resizer';
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["resize"],"sourceRoot":"..\\..\\src","sources":["index.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASA,MAAM,QAAQ,WAAW","ignoreList":[]}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Web / non-native fallback for {@link resize}.
5
+ *
6
+ * This module is selected by Metro on platforms other than Android/iOS.
7
+ * It always rejects because `react-native-libyuv-resizer` requires a native
8
+ * runtime. Import the real implementation via the `.native` platform extension.
9
+ *
10
+ * @throws {Error} Always — native platform required.
11
+ */
12
+ export function resize(_filePath, _targetWidth, _targetHeight, _quality, _options) {
13
+ return Promise.reject(new Error("'react-native-libyuv-resizer' is only supported on native platforms."));
14
+ }
15
+ //# sourceMappingURL=resizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["resize","_filePath","_targetWidth","_targetHeight","_quality","_options","Promise","reject","Error"],"sourceRoot":"..\\..\\src","sources":["resizer.tsx"],"mappings":";;AASA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASA,MAAMA,CACpBC,SAAiB,EACjBC,YAAoB,EACpBC,aAAqB,EACrBC,QAAgB,EAChBC,QAAwB,EACD;EACvB,OAAOC,OAAO,CAACC,MAAM,CACnB,IAAIC,KAAK,CACP,sEACF,CACF,CAAC;AACH","ignoreList":[]}
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+
3
+ import LibyuvResizer from "./NativeLibyuvResizer.js";
4
+
5
+ /**
6
+ * Valid rotation values in degrees.
7
+ * Negative angles are normalised to their positive equivalents before being
8
+ * sent to the native layer (`-90` → `270`, etc.).
9
+ */
10
+
11
+ /**
12
+ * Controls how the source image is fitted into the target bounding box.
13
+ *
14
+ * - `'contain'` — scales uniformly so the entire image fits within the box,
15
+ * preserving aspect ratio. Empty space is left uncropped.
16
+ * - `'cover'` — scales uniformly so the image fills the box, preserving
17
+ * aspect ratio. Excess pixels are cropped.
18
+ * - `'stretch'` — scales to exactly `targetWidth × targetHeight`, ignoring
19
+ * aspect ratio.
20
+ */
21
+
22
+ /**
23
+ * Scaling filter applied during the resize operation.
24
+ *
25
+ * Higher quality filters are slower. Choose based on your latency vs quality
26
+ * requirements.
27
+ *
28
+ * - `'none'` — nearest-neighbour; fastest, lowest quality.
29
+ * - `'linear'` — linear interpolation.
30
+ * - `'bilinear'` — bilinear interpolation.
31
+ * - `'box'` — box filter; best quality for downscaling *(default)*.
32
+ */
33
+
34
+ /**
35
+ * Output image format.
36
+ *
37
+ * When omitted, format is derived from `quality`: `quality === 100` → `'png'`, else `'jpeg'`.
38
+ * When specified, this value takes precedence over the quality-based heuristic.
39
+ *
40
+ * - `'jpeg'` — lossy JPEG.
41
+ * - `'png'` — lossless PNG. `quality` is ignored.
42
+ * - `'webp'` — lossy WebP. `quality` controls compression level *(Android only; iOS produces JPEG)*.
43
+ */
44
+
45
+ /** Options accepted by {@link resize}. */
46
+
47
+ const VALID_MODES = ['contain', 'cover', 'stretch'];
48
+ const VALID_FILTER_MODES = ['none', 'linear', 'bilinear', 'box'];
49
+ const VALID_FORMATS = ['jpeg', 'png', 'webp'];
50
+
51
+ /** Normalises negative or out-of-range angles to 0 | 90 | 180 | 270. */
52
+ function toCanonicalAngle(angle) {
53
+ return (angle % 360 + 360) % 360;
54
+ }
55
+
56
+ /**
57
+ * Resizes an image using the libyuv native backend (Android).
58
+ *
59
+ * The source image is read from `filePath`, resized to the requested
60
+ * dimensions, and saved as a JPEG. The absolute path of the output file is
61
+ * returned on success.
62
+ *
63
+ * @param filePath - Absolute path to the source image.
64
+ * @param targetWidth - Output width in pixels (must be > 0).
65
+ * @param targetHeight - Output height in pixels (must be > 0).
66
+ * @param quality - JPEG encoding quality from `1` (lowest) to `100` (highest).
67
+ * Use `100` to produce PNG output instead of JPEG.
68
+ * @param options - Optional resize behaviour overrides.
69
+ * @returns A `Promise` that resolves to a {@link ResizeResult} with the output
70
+ * file path, URI, size, name, and dimensions.
71
+ * @throws {TypeError} When `options.mode` or `options.filterMode` is not one
72
+ * of the accepted string literals.
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * // Basic resize
77
+ * const result = await resize('/path/to/photo.jpg', 1280, 720, 85);
78
+ * console.log(result.path, result.width, result.height);
79
+ *
80
+ * // With options
81
+ * const result = await resize('/path/to/photo.jpg', 800, 600, 80, {
82
+ * rotation: 90,
83
+ * mode: 'cover',
84
+ * filterMode: 'bilinear',
85
+ * outputPath: '/path/to/output-dir',
86
+ * });
87
+ *
88
+ * // Preserve EXIF (GPS, camera, date) — Android only
89
+ * const result = await resize('/path/to/photo.jpg', 800, 600, 80, {
90
+ * keepMeta: true,
91
+ * });
92
+ * ```
93
+ */
94
+ export function resize(filePath, targetWidth, targetHeight, quality, options) {
95
+ const rotation = options?.rotation != null ? toCanonicalAngle(options.rotation) : 0;
96
+ const mode = options?.mode ?? 'contain';
97
+ if (!VALID_MODES.includes(mode)) {
98
+ return Promise.reject(new TypeError(`Invalid resize mode: '${mode}'`));
99
+ }
100
+ const filterMode = options?.filterMode ?? 'box';
101
+ if (!VALID_FILTER_MODES.includes(filterMode)) {
102
+ return Promise.reject(new TypeError(`Invalid filter mode: '${filterMode}'`));
103
+ }
104
+ const format = options?.format ?? (quality === 100 ? 'png' : 'jpeg');
105
+ if (!VALID_FORMATS.includes(format)) {
106
+ return Promise.reject(new TypeError(`Invalid format: '${format}'`));
107
+ }
108
+ return LibyuvResizer.resize(filePath, targetWidth, targetHeight, quality, rotation, mode, options?.outputPath ?? '', filterMode, options?.keepMeta ?? false, format);
109
+ }
110
+ //# sourceMappingURL=resizer.native.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["LibyuvResizer","VALID_MODES","VALID_FILTER_MODES","VALID_FORMATS","toCanonicalAngle","angle","resize","filePath","targetWidth","targetHeight","quality","options","rotation","mode","includes","Promise","reject","TypeError","filterMode","format","outputPath","keepMeta"],"sourceRoot":"..\\..\\src","sources":["resizer.native.tsx"],"mappings":";;AAAA,OAAOA,aAAa,MAA6B,0BAAuB;;AAIxE;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;;AA2CA,MAAMC,WAAyB,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,CAAC;AACjE,MAAMC,kBAAgC,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,CAAC;AAC9E,MAAMC,aAA6B,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC;;AAE7D;AACA,SAASC,gBAAgBA,CAACC,KAAoB,EAAsB;EAClE,OAAQ,CAAEA,KAAK,GAAG,GAAG,GAAI,GAAG,IAAI,GAAG;AACrC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,MAAMA,CACpBC,QAAgB,EAChBC,WAAmB,EACnBC,YAAoB,EACpBC,OAAe,EACfC,OAAuB,EACA;EACvB,MAAMC,QAAQ,GACZD,OAAO,EAAEC,QAAQ,IAAI,IAAI,GAAGR,gBAAgB,CAACO,OAAO,CAACC,QAAQ,CAAC,GAAG,CAAC;EACpE,MAAMC,IAAgB,GAAGF,OAAO,EAAEE,IAAI,IAAI,SAAS;EACnD,IAAI,CAACZ,WAAW,CAACa,QAAQ,CAACD,IAAI,CAAC,EAAE;IAC/B,OAAOE,OAAO,CAACC,MAAM,CAAC,IAAIC,SAAS,CAAC,yBAAyBJ,IAAI,GAAG,CAAC,CAAC;EACxE;EACA,MAAMK,UAAsB,GAAGP,OAAO,EAAEO,UAAU,IAAI,KAAK;EAC3D,IAAI,CAAChB,kBAAkB,CAACY,QAAQ,CAACI,UAAU,CAAC,EAAE;IAC5C,OAAOH,OAAO,CAACC,MAAM,CACnB,IAAIC,SAAS,CAAC,yBAAyBC,UAAU,GAAG,CACtD,CAAC;EACH;EACA,MAAMC,MAAoB,GACxBR,OAAO,EAAEQ,MAAM,KAAKT,OAAO,KAAK,GAAG,GAAG,KAAK,GAAG,MAAM,CAAC;EACvD,IAAI,CAACP,aAAa,CAACW,QAAQ,CAACK,MAAM,CAAC,EAAE;IACnC,OAAOJ,OAAO,CAACC,MAAM,CAAC,IAAIC,SAAS,CAAC,oBAAoBE,MAAM,GAAG,CAAC,CAAC;EACrE;EACA,OAAOnB,aAAa,CAACM,MAAM,CACzBC,QAAQ,EACRC,WAAW,EACXC,YAAY,EACZC,OAAO,EACPE,QAAQ,EACRC,IAAI,EACJF,OAAO,EAAES,UAAU,IAAI,EAAE,EACzBF,UAAU,EACVP,OAAO,EAAEU,QAAQ,IAAI,KAAK,EAC1BF,MACF,CAAC;AACH","ignoreList":[]}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,52 @@
1
+ import { type TurboModule } from 'react-native';
2
+ /**
3
+ * Metadata returned by the native `resize()` bridge call.
4
+ *
5
+ * All numeric fields reflect the **output** file / bitmap — not the source.
6
+ */
7
+ export type ResizeResult = {
8
+ /** Absolute path of the resized image file. */
9
+ path: string;
10
+ /** `file://` URI of the resized image file. */
11
+ uri: string;
12
+ /** File size in bytes. */
13
+ size: number;
14
+ /** File name (e.g. `"a3f2…1c.jpg"`). */
15
+ name: string;
16
+ /** Output bitmap width in pixels. */
17
+ width: number;
18
+ /** Output bitmap height in pixels. */
19
+ height: number;
20
+ };
21
+ /**
22
+ * Turbo Module contract for the native `LibyuvResizer` implementation.
23
+ *
24
+ * **Do not call this interface directly.** Use the public {@link resize}
25
+ * function from `./resizer` (or the package root) instead — it validates
26
+ * arguments and applies sensible defaults before forwarding to this bridge.
27
+ *
28
+ * This interface is consumed by React Native codegen to auto-generate the
29
+ * JSI glue code (`LibyuvResizerSpec`).
30
+ */
31
+ export interface Spec extends TurboModule {
32
+ /**
33
+ * Low-level bridge method. Parameter order mirrors the public `resize()`
34
+ * signature with defaults already resolved.
35
+ *
36
+ * @param filePath - Absolute path to the source image.
37
+ * @param targetWidth - Output width in pixels.
38
+ * @param targetHeight - Output height in pixels.
39
+ * @param quality - JPEG/WebP quality `0–100`.
40
+ * @param rotation - Canonical rotation in degrees (`0 | 90 | 180 | 270`).
41
+ * @param mode - Resize mode string (`'contain' | 'cover' | 'stretch'`).
42
+ * @param outputPath - Absolute output path, or empty string for auto.
43
+ * @param filterMode - Scaling filter (`'none' | 'linear' | 'bilinear' | 'box'`).
44
+ * @param keepMeta - Copy EXIF tags from source to output (JPEG only, Android only).
45
+ * @param format - Output format (`'jpeg' | 'png' | 'webp'`).
46
+ * @returns Metadata about the resized image.
47
+ */
48
+ resize(filePath: string, targetWidth: number, targetHeight: number, quality: number, rotation: number, mode: string, outputPath: string, filterMode: string, keepMeta: boolean, format: string): Promise<ResizeResult>;
49
+ }
50
+ declare const LibyuvResizerModule: Spec;
51
+ export default LibyuvResizerModule;
52
+ //# sourceMappingURL=NativeLibyuvResizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeLibyuvResizer.d.ts","sourceRoot":"","sources":["../../../src/NativeLibyuvResizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,cAAc,CAAC;AAEtB;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CACJ,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,OAAO,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,YAAY,CAAC,CAAC;CAC1B;AAKD,QAAA,MAAM,mBAAmB,EAAE,IAEc,CAAC;AAQ1C,eAAe,mBAAmB,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `react-native-libyuv-resizer` — high-performance image resizer for React
3
+ * Native backed by libyuv on Android.
4
+ *
5
+ * **Entry point.** Import {@link resize} and the supporting types from here.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { resize } from 'react-native-libyuv-resizer';
10
+ *
11
+ * const result = await resize('/path/to/photo.jpg', 1280, 720, 85);
12
+ * console.log(result.path, result.width, result.height);
13
+ * ```
14
+ *
15
+ * @packageDocumentation
16
+ */
17
+ export { resize } from './resizer';
18
+ export type { RotationAngle, ResizeMode, ResizeOptions, ResizeResult, } from './resizer';
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,YAAY,EACV,aAAa,EACb,UAAU,EACV,aAAa,EACb,YAAY,GACb,MAAM,WAAW,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { ResizeOptions, ResizeResult } from './resizer.native';
2
+ export type { RotationAngle, ResizeMode, ResizeOptions, ResizeResult, } from './resizer.native';
3
+ /**
4
+ * Web / non-native fallback for {@link resize}.
5
+ *
6
+ * This module is selected by Metro on platforms other than Android/iOS.
7
+ * It always rejects because `react-native-libyuv-resizer` requires a native
8
+ * runtime. Import the real implementation via the `.native` platform extension.
9
+ *
10
+ * @throws {Error} Always — native platform required.
11
+ */
12
+ export declare function resize(_filePath: string, _targetWidth: number, _targetHeight: number, _quality: number, _options?: ResizeOptions): Promise<ResizeResult>;
13
+ //# sourceMappingURL=resizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resizer.d.ts","sourceRoot":"","sources":["../../../src/resizer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEpE,YAAY,EACV,aAAa,EACb,UAAU,EACV,aAAa,EACb,YAAY,GACb,MAAM,kBAAkB,CAAC;AAE1B;;;;;;;;GAQG;AACH,wBAAgB,MAAM,CACpB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,aAAa,GACvB,OAAO,CAAC,YAAY,CAAC,CAMvB"}
@@ -0,0 +1,119 @@
1
+ import { type ResizeResult } from './NativeLibyuvResizer';
2
+ export type { ResizeResult };
3
+ /**
4
+ * Valid rotation values in degrees.
5
+ * Negative angles are normalised to their positive equivalents before being
6
+ * sent to the native layer (`-90` → `270`, etc.).
7
+ */
8
+ export type RotationAngle = 0 | 90 | 180 | 270 | -90 | -180 | -270;
9
+ /**
10
+ * Controls how the source image is fitted into the target bounding box.
11
+ *
12
+ * - `'contain'` — scales uniformly so the entire image fits within the box,
13
+ * preserving aspect ratio. Empty space is left uncropped.
14
+ * - `'cover'` — scales uniformly so the image fills the box, preserving
15
+ * aspect ratio. Excess pixels are cropped.
16
+ * - `'stretch'` — scales to exactly `targetWidth × targetHeight`, ignoring
17
+ * aspect ratio.
18
+ */
19
+ export type ResizeMode = 'contain' | 'cover' | 'stretch';
20
+ /**
21
+ * Scaling filter applied during the resize operation.
22
+ *
23
+ * Higher quality filters are slower. Choose based on your latency vs quality
24
+ * requirements.
25
+ *
26
+ * - `'none'` — nearest-neighbour; fastest, lowest quality.
27
+ * - `'linear'` — linear interpolation.
28
+ * - `'bilinear'` — bilinear interpolation.
29
+ * - `'box'` — box filter; best quality for downscaling *(default)*.
30
+ */
31
+ export type FilterMode = 'none' | 'linear' | 'bilinear' | 'box';
32
+ /**
33
+ * Output image format.
34
+ *
35
+ * When omitted, format is derived from `quality`: `quality === 100` → `'png'`, else `'jpeg'`.
36
+ * When specified, this value takes precedence over the quality-based heuristic.
37
+ *
38
+ * - `'jpeg'` — lossy JPEG.
39
+ * - `'png'` — lossless PNG. `quality` is ignored.
40
+ * - `'webp'` — lossy WebP. `quality` controls compression level *(Android only; iOS produces JPEG)*.
41
+ */
42
+ export type OutputFormat = 'jpeg' | 'png' | 'webp';
43
+ /** Options accepted by {@link resize}. */
44
+ export interface ResizeOptions {
45
+ /**
46
+ * Clockwise rotation applied to the image **before** resizing.
47
+ * @default 0
48
+ */
49
+ rotation?: RotationAngle;
50
+ /**
51
+ * How the image is fitted into the target bounding box.
52
+ * @default 'contain'
53
+ */
54
+ mode?: ResizeMode;
55
+ /**
56
+ * Scaling filter used during the resize operation.
57
+ * @default 'box'
58
+ */
59
+ filterMode?: FilterMode;
60
+ /**
61
+ * Absolute path for the output file.
62
+ * When omitted the native layer generates a path in the app's cache
63
+ * directory automatically.
64
+ */
65
+ outputPath?: string;
66
+ /**
67
+ * When `true`, copies all EXIF tags from the source image to the output
68
+ * JPEG. Has no effect on PNG or WebP output, or on iOS (no-op).
69
+ * @default false
70
+ * @platform android
71
+ */
72
+ keepMeta?: boolean;
73
+ /**
74
+ * Output image format. When omitted, `quality === 100` produces PNG; otherwise JPEG.
75
+ * Specifying `format` takes precedence over the quality-based heuristic.
76
+ * @default derived from quality
77
+ */
78
+ format?: OutputFormat;
79
+ }
80
+ /**
81
+ * Resizes an image using the libyuv native backend (Android).
82
+ *
83
+ * The source image is read from `filePath`, resized to the requested
84
+ * dimensions, and saved as a JPEG. The absolute path of the output file is
85
+ * returned on success.
86
+ *
87
+ * @param filePath - Absolute path to the source image.
88
+ * @param targetWidth - Output width in pixels (must be > 0).
89
+ * @param targetHeight - Output height in pixels (must be > 0).
90
+ * @param quality - JPEG encoding quality from `1` (lowest) to `100` (highest).
91
+ * Use `100` to produce PNG output instead of JPEG.
92
+ * @param options - Optional resize behaviour overrides.
93
+ * @returns A `Promise` that resolves to a {@link ResizeResult} with the output
94
+ * file path, URI, size, name, and dimensions.
95
+ * @throws {TypeError} When `options.mode` or `options.filterMode` is not one
96
+ * of the accepted string literals.
97
+ *
98
+ * @example
99
+ * ```ts
100
+ * // Basic resize
101
+ * const result = await resize('/path/to/photo.jpg', 1280, 720, 85);
102
+ * console.log(result.path, result.width, result.height);
103
+ *
104
+ * // With options
105
+ * const result = await resize('/path/to/photo.jpg', 800, 600, 80, {
106
+ * rotation: 90,
107
+ * mode: 'cover',
108
+ * filterMode: 'bilinear',
109
+ * outputPath: '/path/to/output-dir',
110
+ * });
111
+ *
112
+ * // Preserve EXIF (GPS, camera, date) — Android only
113
+ * const result = await resize('/path/to/photo.jpg', 800, 600, 80, {
114
+ * keepMeta: true,
115
+ * });
116
+ * ```
117
+ */
118
+ export declare function resize(filePath: string, targetWidth: number, targetHeight: number, quality: number, options?: ResizeOptions): Promise<ResizeResult>;
119
+ //# sourceMappingURL=resizer.native.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resizer.native.d.ts","sourceRoot":"","sources":["../../../src/resizer.native.tsx"],"names":[],"mappings":"AAAA,OAAsB,EAAE,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAEzE,YAAY,EAAE,YAAY,EAAE,CAAC;AAE7B;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC;AAEnE;;;;;;;;;GASG;AACH,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AAEzD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,KAAK,CAAC;AAEhE;;;;;;;;;GASG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AAEnD,0CAA0C;AAC1C,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;IAEzB;;;OAGG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC;IAElB;;;OAGG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IAExB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;OAIG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAWD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,MAAM,CACpB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,aAAa,GACtB,OAAO,CAAC,YAAY,CAAC,CA8BvB"}
package/package.json ADDED
@@ -0,0 +1,184 @@
1
+ {
2
+ "name": "react-native-libyuv-resizer",
3
+ "version": "0.2.0",
4
+ "description": "High-performance image resizer for React Native using libyuv (Android, New Architecture)",
5
+ "main": "./lib/module/index.js",
6
+ "types": "./lib/typescript/src/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "source": "./src/index.tsx",
10
+ "types": "./lib/typescript/src/index.d.ts",
11
+ "default": "./lib/module/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "src",
17
+ "lib",
18
+ "android",
19
+ "ios",
20
+ "cpp",
21
+ "*.podspec",
22
+ "react-native.config.js",
23
+ "!ios/build",
24
+ "!android/build",
25
+ "!android/gradle",
26
+ "!android/gradlew",
27
+ "!android/gradlew.bat",
28
+ "!android/local.properties",
29
+ "!**/__tests__",
30
+ "!**/__fixtures__",
31
+ "!**/__mocks__",
32
+ "!**/.*"
33
+ ],
34
+ "scripts": {
35
+ "example": "yarn workspace react-native-libyuv-resizer-example",
36
+ "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
37
+ "prepare": "bob build",
38
+ "typecheck": "tsc",
39
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
40
+ "test": "jest",
41
+ "release": "release-it --only-version"
42
+ },
43
+ "keywords": [
44
+ "react-native",
45
+ "android",
46
+ "image",
47
+ "resize",
48
+ "libyuv",
49
+ "turbo-module",
50
+ "new-architecture",
51
+ "performance"
52
+ ],
53
+ "repository": {
54
+ "type": "git",
55
+ "url": "git+https://github.com/anderson-souza/react-native-libyuv-resizer.git"
56
+ },
57
+ "author": "Anderson Souza <andersonpds14@gmail.com> (https://github.com/anderson-souza)",
58
+ "license": "MIT",
59
+ "bugs": {
60
+ "url": "https://github.com/anderson-souza/react-native-libyuv-resizer/issues"
61
+ },
62
+ "homepage": "https://github.com/anderson-souza/react-native-libyuv-resizer#readme",
63
+ "publishConfig": {
64
+ "registry": "https://registry.npmjs.org/"
65
+ },
66
+ "devDependencies": {
67
+ "@commitlint/config-conventional": "^20.5.0",
68
+ "@eslint/compat": "^2.0.3",
69
+ "@eslint/eslintrc": "^3.3.5",
70
+ "@eslint/js": "^10.0.1",
71
+ "@jest/globals": "^30.0.0",
72
+ "@react-native/babel-preset": "0.85.0",
73
+ "@react-native/eslint-config": "0.85.0",
74
+ "@react-native/jest-preset": "0.85.0",
75
+ "@release-it/conventional-changelog": "^10.0.6",
76
+ "@types/react": "^19.2.0",
77
+ "commitlint": "^20.5.0",
78
+ "del-cli": "^7.0.0",
79
+ "eslint": "^9.39.4",
80
+ "eslint-config-prettier": "^10.1.8",
81
+ "eslint-plugin-ft-flow": "^3.0.11",
82
+ "eslint-plugin-prettier": "^5.5.5",
83
+ "jest": "^30.3.0",
84
+ "lefthook": "^2.1.4",
85
+ "prettier": "^3.8.1",
86
+ "react": "19.2.3",
87
+ "react-native": "0.85.0",
88
+ "react-native-builder-bob": "^0.41.0",
89
+ "release-it": "^19.2.4",
90
+ "turbo": "^2.8.21",
91
+ "typescript": "^6.0.2"
92
+ },
93
+ "peerDependencies": {
94
+ "react": "*",
95
+ "react-native": "*"
96
+ },
97
+ "workspaces": [
98
+ "example"
99
+ ],
100
+ "packageManager": "yarn@4.11.0",
101
+ "react-native-builder-bob": {
102
+ "source": "src",
103
+ "output": "lib",
104
+ "targets": [
105
+ [
106
+ "module",
107
+ {
108
+ "esm": true
109
+ }
110
+ ],
111
+ [
112
+ "typescript",
113
+ {
114
+ "project": "tsconfig.build.json"
115
+ }
116
+ ]
117
+ ]
118
+ },
119
+ "codegenConfig": {
120
+ "name": "LibyuvResizerSpec",
121
+ "type": "modules",
122
+ "jsSrcsDir": "src",
123
+ "android": {
124
+ "javaPackageName": "com.libyuvresizer"
125
+ }
126
+ },
127
+ "prettier": {
128
+ "quoteProps": "consistent",
129
+ "singleQuote": true,
130
+ "tabWidth": 2,
131
+ "trailingComma": "es5",
132
+ "useTabs": false
133
+ },
134
+ "jest": {
135
+ "preset": "@react-native/jest-preset",
136
+ "modulePathIgnorePatterns": [
137
+ "<rootDir>/example/node_modules",
138
+ "<rootDir>/lib/"
139
+ ],
140
+ "coverageThreshold": {
141
+ "global": {
142
+ "statements": 85,
143
+ "branches": 85,
144
+ "functions": 85,
145
+ "lines": 85
146
+ }
147
+ }
148
+ },
149
+ "commitlint": {
150
+ "extends": [
151
+ "@commitlint/config-conventional"
152
+ ]
153
+ },
154
+ "release-it": {
155
+ "git": {
156
+ "commitMessage": "chore: release ${version}",
157
+ "tagName": "v${version}"
158
+ },
159
+ "npm": {
160
+ "publish": true
161
+ },
162
+ "github": {
163
+ "release": true
164
+ },
165
+ "plugins": {
166
+ "@release-it/conventional-changelog": {
167
+ "preset": {
168
+ "name": "angular"
169
+ }
170
+ }
171
+ }
172
+ },
173
+ "create-react-native-library": {
174
+ "type": "turbo-module",
175
+ "languages": "kotlin-objc",
176
+ "tools": [
177
+ "eslint",
178
+ "jest",
179
+ "lefthook",
180
+ "release-it"
181
+ ],
182
+ "version": "0.62.0"
183
+ }
184
+ }