@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.
- package/LICENSE +20 -0
- package/README.md +247 -0
- package/ReactNativeImageToWebp.podspec +35 -0
- package/android/build.gradle +85 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/CMakeLists.txt +67 -0
- package/android/src/main/cpp/ImageToWebPJNI.cpp +73 -0
- package/android/src/main/java/com/dynlabs/reactnativeimagetowebp/ReactNativeImageToWebpModule.kt +258 -0
- package/android/src/main/java/com/dynlabs/reactnativeimagetowebp/ReactNativeImageToWebpPackage.kt +33 -0
- package/cpp/ImageToWebP.cpp +132 -0
- package/cpp/ImageToWebP.h +41 -0
- package/cpp/README.md +21 -0
- package/cpp/SETUP.md +71 -0
- package/ios/ReactNativeImageToWebp.h +5 -0
- package/ios/ReactNativeImageToWebp.mm +342 -0
- package/lib/module/NativeReactNativeImageToWebp.js +5 -0
- package/lib/module/NativeReactNativeImageToWebp.js.map +1 -0
- package/lib/module/index.js +78 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/presets.js +64 -0
- package/lib/module/presets.js.map +1 -0
- package/lib/module/validation.js +36 -0
- package/lib/module/validation.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativeReactNativeImageToWebp.d.ts +24 -0
- package/lib/typescript/src/NativeReactNativeImageToWebp.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +35 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/presets.d.ts +3 -0
- package/lib/typescript/src/presets.d.ts.map +1 -0
- package/lib/typescript/src/validation.d.ts +9 -0
- package/lib/typescript/src/validation.d.ts.map +1 -0
- package/package.json +139 -0
- package/src/NativeReactNativeImageToWebp.ts +32 -0
- package/src/index.tsx +109 -0
- package/src/presets.ts +80 -0
- 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,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"
|