@dynlabs/react-native-image-to-webp 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 (38) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +247 -0
  3. package/ReactNativeImageToWebp.podspec +35 -0
  4. package/android/build.gradle +85 -0
  5. package/android/src/main/AndroidManifest.xml +2 -0
  6. package/android/src/main/cpp/CMakeLists.txt +67 -0
  7. package/android/src/main/cpp/ImageToWebPJNI.cpp +73 -0
  8. package/android/src/main/java/com/dynlabs/reactnativeimagetowebp/ReactNativeImageToWebpModule.kt +258 -0
  9. package/android/src/main/java/com/dynlabs/reactnativeimagetowebp/ReactNativeImageToWebpPackage.kt +33 -0
  10. package/cpp/ImageToWebP.cpp +132 -0
  11. package/cpp/ImageToWebP.h +41 -0
  12. package/cpp/README.md +21 -0
  13. package/cpp/SETUP.md +71 -0
  14. package/ios/ReactNativeImageToWebp.h +5 -0
  15. package/ios/ReactNativeImageToWebp.mm +342 -0
  16. package/lib/module/NativeReactNativeImageToWebp.js +5 -0
  17. package/lib/module/NativeReactNativeImageToWebp.js.map +1 -0
  18. package/lib/module/index.js +78 -0
  19. package/lib/module/index.js.map +1 -0
  20. package/lib/module/package.json +1 -0
  21. package/lib/module/presets.js +64 -0
  22. package/lib/module/presets.js.map +1 -0
  23. package/lib/module/validation.js +36 -0
  24. package/lib/module/validation.js.map +1 -0
  25. package/lib/typescript/package.json +1 -0
  26. package/lib/typescript/src/NativeReactNativeImageToWebp.d.ts +24 -0
  27. package/lib/typescript/src/NativeReactNativeImageToWebp.d.ts.map +1 -0
  28. package/lib/typescript/src/index.d.ts +35 -0
  29. package/lib/typescript/src/index.d.ts.map +1 -0
  30. package/lib/typescript/src/presets.d.ts +3 -0
  31. package/lib/typescript/src/presets.d.ts.map +1 -0
  32. package/lib/typescript/src/validation.d.ts +9 -0
  33. package/lib/typescript/src/validation.d.ts.map +1 -0
  34. package/package.json +139 -0
  35. package/src/NativeReactNativeImageToWebp.ts +32 -0
  36. package/src/index.tsx +109 -0
  37. package/src/presets.ts +80 -0
  38. package/src/validation.ts +55 -0
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,247 @@
1
+ # @dynlabs/react-native-image-to-webp
2
+
3
+ A production-ready React Native library for converting images to WebP format with presets, resizing, and high-performance native implementations.
4
+
5
+ ## Features
6
+
7
+ - 🚀 **TurboModule** (New Architecture only) for optimal performance
8
+ - 🎨 **5 Presets**: balanced, small, fast, lossless, document
9
+ - 📐 **Smart Resizing**: Preserve aspect ratio with `maxLongEdge`
10
+ - 🔒 **Privacy-First**: Strips EXIF metadata by default
11
+ - ⚡ **High Performance**: Native decoders + libwebp encoding, all off main thread
12
+ - 📱 **iOS & Android**: Platform-optimized implementations
13
+ - 🛡️ **Type-Safe**: Full TypeScript support with strict types
14
+
15
+ ## Requirements
16
+
17
+ - React Native >= 0.68
18
+ - **New Architecture (TurboModules) enabled**
19
+ - iOS 11.0+ / Android API 24+
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install @dynlabs/react-native-image-to-webp
25
+ # or
26
+ yarn add @dynlabs/react-native-image-to-webp
27
+ ```
28
+
29
+ ### iOS
30
+
31
+ ```bash
32
+ cd ios && pod install && cd ..
33
+ ```
34
+
35
+ ### Android
36
+
37
+ No additional setup required. The library uses CMake for native builds.
38
+
39
+ ## Quick Start
40
+
41
+ ```typescript
42
+ import { convertImageToWebP } from '@dynlabs/react-native-image-to-webp';
43
+
44
+ const result = await convertImageToWebP({
45
+ inputPath: '/path/to/image.jpg',
46
+ preset: 'balanced',
47
+ maxLongEdge: 2048,
48
+ });
49
+
50
+ console.log(`Output: ${result.outputPath}`);
51
+ console.log(`Size: ${result.sizeBytes} bytes`);
52
+ console.log(`Dimensions: ${result.width}×${result.height}`);
53
+ ```
54
+
55
+ ## API
56
+
57
+ ### `convertImageToWebP(options: ConvertOptions): Promise<ConvertResult>`
58
+
59
+ #### Options
60
+
61
+ | Parameter | Type | Required | Default | Description |
62
+ | --------------- | --------------- | -------- | -------------- | ----------------------------------------- |
63
+ | `inputPath` | `string` | ✅ | - | Path to input image file |
64
+ | `outputPath` | `string` | ❌ | Auto-derived | Output WebP file path |
65
+ | `preset` | `ConvertPreset` | ❌ | `'balanced'` | Preset configuration |
66
+ | `maxLongEdge` | `number` | ❌ | - | Max dimension (preserves aspect ratio) |
67
+ | `quality` | `number` | ❌ | Preset default | Quality 0-100 (overrides preset) |
68
+ | `method` | `number` | ❌ | Preset default | Compression method 0-6 (overrides preset) |
69
+ | `lossless` | `boolean` | ❌ | Preset default | Use lossless encoding (overrides preset) |
70
+ | `stripMetadata` | `boolean` | ❌ | `true` | Strip EXIF metadata |
71
+
72
+ #### Result
73
+
74
+ ```typescript
75
+ {
76
+ outputPath: string; // Path to created WebP file
77
+ width: number; // Image width in pixels
78
+ height: number; // Image height in pixels
79
+ sizeBytes: number; // Output file size in bytes
80
+ }
81
+ ```
82
+
83
+ ## Presets
84
+
85
+ | Preset | Quality | Method | Use Case |
86
+ | ---------- | ------- | ------ | ------------------------------------------------------- |
87
+ | `balanced` | 80 | 3 | **Default**. General-purpose, good quality/size balance |
88
+ | `small` | 74 | 5 | Optimized for smaller file sizes |
89
+ | `fast` | 78 | 1 | Faster encoding, slightly larger files |
90
+ | `lossless` | - | 4 | Perfect quality, larger files |
91
+ | `document` | 82 | 4 | Documents, images with text/transparency |
92
+
93
+ ### Examples
94
+
95
+ ```typescript
96
+ // Balanced (default)
97
+ await convertImageToWebP({
98
+ inputPath: '/path/to/image.jpg',
99
+ preset: 'balanced',
100
+ });
101
+
102
+ // Small file size
103
+ await convertImageToWebP({
104
+ inputPath: '/path/to/image.jpg',
105
+ preset: 'small',
106
+ maxLongEdge: 1024,
107
+ });
108
+
109
+ // Lossless
110
+ await convertImageToWebP({
111
+ inputPath: '/path/to/image.jpg',
112
+ preset: 'lossless',
113
+ });
114
+
115
+ // Custom quality
116
+ await convertImageToWebP({
117
+ inputPath: '/path/to/image.jpg',
118
+ quality: 90,
119
+ method: 4,
120
+ maxLongEdge: 2048,
121
+ });
122
+ ```
123
+
124
+ ## Resizing
125
+
126
+ Use `maxLongEdge` to resize images while preserving aspect ratio:
127
+
128
+ ```typescript
129
+ // Resize so longest edge is max 2048px
130
+ await convertImageToWebP({
131
+ inputPath: '/path/to/large-image.jpg',
132
+ maxLongEdge: 2048, // If image is 4000×3000, becomes 2048×1536
133
+ });
134
+ ```
135
+
136
+ **Recommendation**: Always use `maxLongEdge` for better performance and smaller files. Common values:
137
+
138
+ - **Thumbnails**: 512
139
+ - **Mobile display**: 1024
140
+ - **Retina display**: 2048 (recommended default)
141
+ - **High-res**: 3072
142
+
143
+ ## Error Handling
144
+
145
+ ```typescript
146
+ import {
147
+ convertImageToWebP,
148
+ ImageToWebPError,
149
+ ERROR_CODES,
150
+ } from '@dynlabs/react-native-image-to-webp';
151
+
152
+ try {
153
+ const result = await convertImageToWebP({ inputPath: '/path/to/image.jpg' });
154
+ } catch (error) {
155
+ if (error instanceof ImageToWebPError) {
156
+ switch (error.code) {
157
+ case ERROR_CODES.FILE_NOT_FOUND:
158
+ console.error('File not found');
159
+ break;
160
+ case ERROR_CODES.DECODE_FAILED:
161
+ console.error('Failed to decode image');
162
+ break;
163
+ case ERROR_CODES.ENCODE_FAILED:
164
+ console.error('Failed to encode WebP');
165
+ break;
166
+ // ... other error codes
167
+ }
168
+ }
169
+ }
170
+ ```
171
+
172
+ ### Error Codes
173
+
174
+ - `INVALID_INPUT`: Invalid parameters
175
+ - `FILE_NOT_FOUND`: Input file doesn't exist
176
+ - `DECODE_FAILED`: Failed to decode input image
177
+ - `ENCODE_FAILED`: Failed to encode WebP
178
+ - `IO_ERROR`: File I/O error
179
+ - `UNSUPPORTED_FORMAT`: Unsupported image format
180
+
181
+ ## Supported Formats
182
+
183
+ ### iOS
184
+
185
+ - JPEG, PNG, HEIC/HEIF, TIFF, GIF (first frame), WebP
186
+
187
+ ### Android
188
+
189
+ - JPEG, PNG, WebP, HEIF (API 28+), GIF (first frame)
190
+
191
+ ## Performance
192
+
193
+ - All processing runs **off the main thread** (background queue/executor)
194
+ - Uses platform-native decoders (ImageIO on iOS, ImageDecoder/BitmapFactory on Android)
195
+ - Resize before encoding for best performance
196
+ - Typical encoding time: 0.5-2s for 2000×1500 images (varies by device/preset)
197
+
198
+ See [PERFORMANCE.md](docs/PERFORMANCE.md) for detailed performance guidance.
199
+
200
+ ## Architecture
201
+
202
+ This library uses React Native's **New Architecture (TurboModules)**:
203
+
204
+ - Type-safe Codegen interfaces
205
+ - Native implementations for iOS (ObjC++) and Android (Kotlin + JNI)
206
+ - Shared C++ code using libwebp for encoding
207
+ - Background threading for non-blocking operations
208
+
209
+ See [ARCHITECTURE.md](docs/ARCHITECTURE.md) for details.
210
+
211
+ ## Documentation
212
+
213
+ - [API Reference](docs/API.md) - Complete API documentation
214
+ - [Architecture](docs/ARCHITECTURE.md) - Technical architecture details
215
+ - [Performance Guide](docs/PERFORMANCE.md) - Performance optimization tips
216
+ - [Contributing](docs/CONTRIBUTING.md) - Development setup and guidelines
217
+ - [Release Process](docs/RELEASE.md) - Versioning and release workflow
218
+ - [Security](docs/SECURITY.md) - Security policy and considerations
219
+
220
+ ## Example App
221
+
222
+ See the [example app](example/) for a complete usage example.
223
+
224
+ ```bash
225
+ # Run example on iOS
226
+ yarn example:ios
227
+
228
+ # Run example on Android
229
+ yarn example:android
230
+ ```
231
+
232
+ ## Contributing
233
+
234
+ See [CONTRIBUTING.md](docs/CONTRIBUTING.md) for development setup and guidelines.
235
+
236
+ ## License
237
+
238
+ MIT
239
+
240
+ ## Related
241
+
242
+ - [libwebp](https://github.com/webmproject/libwebp) - WebP encoding library
243
+ - [React Native New Architecture](https://reactnative.dev/docs/the-new-architecture/landing-page) - TurboModules documentation
244
+
245
+ ---
246
+
247
+ Made with ❤️ for the React Native community
@@ -0,0 +1,35 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "ReactNativeImageToWebp"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => min_ios_version_supported }
14
+ s.source = { :git => ".git", :tag => "#{s.version}" }
15
+
16
+ s.source_files = "ios/**/*.{h,m,mm,swift,cpp}", "cpp/**/*.{h,cpp}"
17
+ s.private_header_files = "ios/**/*.h", "cpp/**/*.h"
18
+
19
+ # Include libwebp sources if vendored
20
+ libwebp_path = File.join(__dir__, "cpp/vendor/libwebp/src")
21
+ if Dir.exist?(libwebp_path)
22
+ s.source_files += "cpp/vendor/libwebp/src/**/*.{c,h}"
23
+ s.public_header_files = "cpp/vendor/libwebp/src/webp/*.h"
24
+ s.compiler_flags = "-O3 -DNDEBUG -DWEBP_AVAILABLE"
25
+ else
26
+ Pod::UI.warn "libwebp not found at #{libwebp_path}. Please vendor libwebp sources."
27
+ s.compiler_flags = "-O3 -DNDEBUG"
28
+ end
29
+ s.pod_target_xcconfig = {
30
+ "HEADER_SEARCH_PATHS" => "$(inherited) ${PODS_ROOT}/Headers/Private/React-Core",
31
+ "OTHER_CPLUSPLUSFLAGS" => "$(OTHER_CFLAGS) -std=c++17"
32
+ }
33
+
34
+ install_modules_dependencies(s)
35
+ end
@@ -0,0 +1,85 @@
1
+ buildscript {
2
+ ext.ReactNativeImageToWebp = [
3
+ kotlinVersion: "2.0.21",
4
+ minSdkVersion: 24,
5
+ compileSdkVersion: 36,
6
+ targetSdkVersion: 36
7
+ ]
8
+
9
+ ext.getExtOrDefault = { prop ->
10
+ if (rootProject.ext.has(prop)) {
11
+ return rootProject.ext.get(prop)
12
+ }
13
+
14
+ return ReactNativeImageToWebp[prop]
15
+ }
16
+
17
+ repositories {
18
+ google()
19
+ mavenCentral()
20
+ }
21
+
22
+ dependencies {
23
+ classpath "com.android.tools.build:gradle:8.7.2"
24
+ // noinspection DifferentKotlinGradleVersion
25
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
26
+ }
27
+ }
28
+
29
+
30
+ apply plugin: "com.android.library"
31
+ apply plugin: "kotlin-android"
32
+
33
+ apply plugin: "com.facebook.react"
34
+
35
+ android {
36
+ namespace "com.dynlabs.reactnativeimagetowebp"
37
+
38
+ compileSdkVersion getExtOrDefault("compileSdkVersion")
39
+
40
+ defaultConfig {
41
+ minSdkVersion getExtOrDefault("minSdkVersion")
42
+ targetSdkVersion getExtOrDefault("targetSdkVersion")
43
+
44
+ externalNativeBuild {
45
+ cmake {
46
+ cppFlags "-std=c++17", "-O3", "-DNDEBUG"
47
+ arguments "-DANDROID_STL=c++_shared"
48
+ }
49
+ }
50
+
51
+ ndk {
52
+ abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
53
+ }
54
+ }
55
+
56
+ externalNativeBuild {
57
+ cmake {
58
+ path "src/main/cpp/CMakeLists.txt"
59
+ version "3.22.1"
60
+ }
61
+ }
62
+
63
+ buildFeatures {
64
+ buildConfig true
65
+ }
66
+
67
+ buildTypes {
68
+ release {
69
+ minifyEnabled false
70
+ }
71
+ }
72
+
73
+ lint {
74
+ disable "GradleCompatible"
75
+ }
76
+
77
+ compileOptions {
78
+ sourceCompatibility JavaVersion.VERSION_1_8
79
+ targetCompatibility JavaVersion.VERSION_1_8
80
+ }
81
+ }
82
+
83
+ dependencies {
84
+ implementation "com.facebook.react:react-android"
85
+ }
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,67 @@
1
+ cmake_minimum_required(VERSION 3.22.1)
2
+
3
+ project("react-native-image-to-webp")
4
+
5
+ # Set C++ standard
6
+ set(CMAKE_CXX_STANDARD 17)
7
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
8
+
9
+ # Release flags
10
+ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -DNDEBUG")
11
+ set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3 -DNDEBUG")
12
+
13
+ # Include directories
14
+ include_directories(
15
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../cpp
16
+ )
17
+
18
+ # Check if libwebp is vendored
19
+ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../../cpp/vendor/libwebp/src")
20
+ # libwebp is vendored, include it
21
+ include_directories(
22
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../cpp/vendor/libwebp/src
23
+ )
24
+
25
+ # Add libwebp source files
26
+ file(GLOB_RECURSE WEBP_SOURCES
27
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../../cpp/vendor/libwebp/src/enc/*.c"
28
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../../cpp/vendor/libwebp/src/dec/*.c"
29
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../../cpp/vendor/libwebp/src/dsp/*.c"
30
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../../cpp/vendor/libwebp/src/utils/*.c"
31
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../../cpp/vendor/libwebp/src/webp/*.c"
32
+ )
33
+
34
+ # Define WEBP_AVAILABLE preprocessor macro
35
+ add_definitions(-DWEBP_AVAILABLE)
36
+
37
+ message(STATUS "libwebp found, including sources")
38
+ else()
39
+ message(WARNING "libwebp not found. Please vendor libwebp to cpp/vendor/libwebp/")
40
+ set(WEBP_SOURCES "")
41
+ endif()
42
+
43
+ # Source files
44
+ set(SOURCES
45
+ ImageToWebPJNI.cpp
46
+ ../../../cpp/ImageToWebP.cpp
47
+ ${WEBP_SOURCES}
48
+ )
49
+
50
+ # Create shared library
51
+ add_library(
52
+ react-native-image-to-webp
53
+ SHARED
54
+ ${SOURCES}
55
+ )
56
+
57
+ # Link libraries
58
+ find_library(
59
+ log-lib
60
+ log
61
+ )
62
+
63
+ target_link_libraries(
64
+ react-native-image-to-webp
65
+ ${log-lib}
66
+ # libwebp static library would be linked here if built separately
67
+ )
@@ -0,0 +1,73 @@
1
+ #include <jni.h>
2
+ #include <string>
3
+ #include <fstream>
4
+ #include "ImageToWebP.h"
5
+
6
+ extern "C" {
7
+
8
+ JNIEXPORT jboolean JNICALL
9
+ Java_com_dynlabs_reactnativeimagetowebp_ReactNativeImageToWebpModule_nativeEncodeWebP(
10
+ JNIEnv *env,
11
+ jobject /* this */,
12
+ jbyteArray rgbaData,
13
+ jint width,
14
+ jint height,
15
+ jint quality,
16
+ jint method,
17
+ jboolean lossless,
18
+ jstring outputPath) {
19
+
20
+ // Get RGBA data
21
+ jbyte *data = env->GetByteArrayElements(rgbaData, NULL);
22
+ if (!data) {
23
+ return JNI_FALSE;
24
+ }
25
+
26
+ jsize dataLength = env->GetArrayLength(rgbaData);
27
+ if (dataLength != width * height * 4) {
28
+ env->ReleaseByteArrayElements(rgbaData, data, JNI_ABORT);
29
+ return JNI_FALSE;
30
+ }
31
+
32
+ // Convert output path
33
+ const char *pathStr = env->GetStringUTFChars(outputPath, NULL);
34
+ if (!pathStr) {
35
+ env->ReleaseByteArrayElements(rgbaData, data, JNI_ABORT);
36
+ return JNI_FALSE;
37
+ }
38
+
39
+ std::string outputPathStr(pathStr);
40
+
41
+ // Prepare encoding options
42
+ WebPEncodeOptions options;
43
+ options.quality = quality;
44
+ options.method = method;
45
+ options.lossless = (lossless == JNI_TRUE);
46
+ options.stripMetadata = true;
47
+ options.threadLevel = 1;
48
+
49
+ // Encode
50
+ const uint8_t *rgba = reinterpret_cast<const uint8_t *>(data);
51
+ WebPEncodeResult result = encodeWebP(
52
+ rgba,
53
+ static_cast<uint32_t>(width),
54
+ static_cast<uint32_t>(height),
55
+ options,
56
+ outputPathStr);
57
+
58
+ // Cleanup
59
+ env->ReleaseStringUTFChars(outputPath, pathStr);
60
+ env->ReleaseByteArrayElements(rgbaData, data, JNI_ABORT);
61
+
62
+ return result.success ? JNI_TRUE : JNI_FALSE;
63
+ }
64
+
65
+ JNIEXPORT jstring JNICALL
66
+ Java_com_dynlabs_reactnativeimagetowebp_ReactNativeImageToWebpModule_nativeGetLastError(
67
+ JNIEnv *env,
68
+ jobject /* this */) {
69
+ // TODO: Store last error in thread-local storage
70
+ return env->NewStringUTF("Encoding failed");
71
+ }
72
+
73
+ } // extern "C"