├── example
├── .watchmanconfig
├── .bundle
│ └── config
├── app.json
├── babel.config.js
├── android
│ ├── app
│ │ ├── debug.keystore
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── res
│ │ │ │ │ ├── values
│ │ │ │ │ │ ├── strings.xml
│ │ │ │ │ │ └── styles.xml
│ │ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ │ └── drawable
│ │ │ │ │ │ └── rn_edit_text_material.xml
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ └── java
│ │ │ │ │ └── com
│ │ │ │ │ └── nitroimageexample
│ │ │ │ │ ├── MainActivity.kt
│ │ │ │ │ └── MainApplication.kt
│ │ │ └── debug
│ │ │ │ └── AndroidManifest.xml
│ │ └── proguard-rules.pro
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── settings.gradle
│ ├── build.gradle
│ └── gradle.properties
├── ios
│ ├── NitroImageExample
│ │ ├── Images.xcassets
│ │ │ ├── Contents.json
│ │ │ └── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ ├── PrivacyInfo.xcprivacy
│ │ ├── AppDelegate.swift
│ │ └── Info.plist
│ ├── NitroImageExample.xcworkspace
│ │ └── contents.xcworkspacedata
│ ├── .xcode.env
│ └── Podfile
├── tsconfig.json
├── index.js
├── src
│ ├── createImageURLs.ts
│ ├── App.tsx
│ ├── FastImageTab.tsx
│ ├── NitroImageTab.tsx
│ └── EmptyTab.tsx
├── Gemfile
├── metro.config.js
├── .gitignore
└── package.json
├── bunfig.toml
├── packages
├── react-native-nitro-image
│ ├── nitrogen
│ │ └── generated
│ │ │ ├── .gitattributes
│ │ │ ├── shared
│ │ │ ├── json
│ │ │ │ └── NitroImageViewConfig.json
│ │ │ └── c++
│ │ │ │ ├── HybridImageLoaderSpec.cpp
│ │ │ │ ├── HybridImageUtilsSpec.cpp
│ │ │ │ ├── HybridNitroImageViewSpec.cpp
│ │ │ │ ├── HybridImageLoaderFactorySpec.cpp
│ │ │ │ ├── HybridImageUtilsSpec.hpp
│ │ │ │ ├── HybridImageFactorySpec.cpp
│ │ │ │ └── HybridImageSpec.cpp
│ │ │ ├── ios
│ │ │ ├── c++
│ │ │ │ ├── HybridImageSpecSwift.cpp
│ │ │ │ ├── HybridImageLoaderSpecSwift.cpp
│ │ │ │ ├── HybridImageUtilsSpecSwift.cpp
│ │ │ │ ├── HybridImageFactorySpecSwift.cpp
│ │ │ │ ├── HybridNitroImageViewSpecSwift.cpp
│ │ │ │ └── HybridImageLoaderFactorySpecSwift.cpp
│ │ │ ├── swift
│ │ │ │ ├── Variant__any_HybridImageSpec___any_HybridImageLoaderSpec_.swift
│ │ │ │ ├── ImageFormat.swift
│ │ │ │ ├── ResizeMode.swift
│ │ │ │ ├── Func_void.swift
│ │ │ │ ├── Func_void_std__string.swift
│ │ │ │ ├── Func_void_RawPixelData.swift
│ │ │ │ ├── RawPixelData.swift
│ │ │ │ ├── Func_void_EncodedImageData.swift
│ │ │ │ ├── Func_void_std__exception_ptr.swift
│ │ │ │ ├── EncodedImageData.swift
│ │ │ │ ├── Func_void_std__shared_ptr_ArrayBuffer_.swift
│ │ │ │ ├── HybridImageLoaderSpec.swift
│ │ │ │ ├── HybridImageUtilsSpec.swift
│ │ │ │ ├── HybridNitroImageViewSpec.swift
│ │ │ │ ├── Color.swift
│ │ │ │ ├── PixelFormat.swift
│ │ │ │ ├── Func_void_std__shared_ptr_HybridImageSpec_.swift
│ │ │ │ └── HybridImageLoaderFactorySpec.swift
│ │ │ ├── NitroImageAutolinking.mm
│ │ │ └── NitroImage+autolinking.rb
│ │ │ └── android
│ │ │ ├── kotlin
│ │ │ └── com
│ │ │ │ └── margelo
│ │ │ │ └── nitro
│ │ │ │ └── image
│ │ │ │ ├── ImageFormat.kt
│ │ │ │ ├── ResizeMode.kt
│ │ │ │ ├── PixelFormat.kt
│ │ │ │ ├── views
│ │ │ │ ├── HybridNitroImageViewStateUpdater.kt
│ │ │ │ └── HybridNitroImageViewManager.kt
│ │ │ │ ├── Color.kt
│ │ │ │ ├── RawPixelData.kt
│ │ │ │ ├── NitroImageOnLoad.kt
│ │ │ │ ├── EncodedImageData.kt
│ │ │ │ ├── HybridImageLoaderSpec.kt
│ │ │ │ ├── HybridNitroImageViewSpec.kt
│ │ │ │ ├── HybridImageUtilsSpec.kt
│ │ │ │ ├── Variant_HybridImageSpec_HybridImageLoaderSpec.kt
│ │ │ │ └── HybridImageLoaderFactorySpec.kt
│ │ │ ├── NitroImageOnLoad.hpp
│ │ │ ├── NitroImage+autolinking.gradle
│ │ │ └── c++
│ │ │ ├── JVariant_HybridImageSpec_HybridImageLoaderSpec.cpp
│ │ │ ├── views
│ │ │ └── JHybridNitroImageViewStateUpdater.hpp
│ │ │ └── JImageFormat.hpp
│ ├── babel.config.js
│ ├── android
│ │ ├── src
│ │ │ └── main
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ ├── cpp
│ │ │ │ └── cpp-adapter.cpp
│ │ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── margelo
│ │ │ │ └── nitro
│ │ │ │ └── image
│ │ │ │ ├── ArrayBuffer+copyIfNotOwner.kt
│ │ │ │ ├── Bitmap+isGPU.kt
│ │ │ │ ├── Bitmap+toCpuAccessible.kt
│ │ │ │ ├── Color+toBitmapColor.kt
│ │ │ │ ├── Bitmap+toByteBuffer.kt
│ │ │ │ ├── Bitmap+toMutable.kt
│ │ │ │ ├── CustomImageView.kt
│ │ │ │ ├── HybridImageLoader.kt
│ │ │ │ ├── Bitmap+compressInMemory.kt
│ │ │ │ ├── ArrayBuffer+toByteArray.kt
│ │ │ │ ├── FastByteArrayOutputStream.kt
│ │ │ │ ├── Bitmap+saveToFile.kt
│ │ │ │ ├── HybridImageLoaderFactory.kt
│ │ │ │ ├── HybridImageUtils.kt
│ │ │ │ ├── NitroImagePackage.java
│ │ │ │ └── Bitmap+pixelFormat.kt
│ │ ├── gradle.properties
│ │ └── CMakeLists.txt
│ ├── ios
│ │ ├── Bridge.h
│ │ ├── HybridImage.swift
│ │ ├── Color+toUIColor.swift
│ │ ├── ImageFormat+toUTType.swift
│ │ ├── NativeImageView.swift
│ │ ├── UIImage+memorySize.swift
│ │ ├── UIImage+toEncodedImageData.swift
│ │ ├── UIImageOrientation+fromDegrees.swift
│ │ ├── HybridImageUtils.swift
│ │ ├── CGImage+getPixelFormat.swift
│ │ ├── HybridImageLoaderFactory.swift
│ │ ├── UIImage+getData.swift
│ │ └── HybridImageLoader.swift
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ ├── src
│ │ ├── Images.ts
│ │ ├── ImageLoaders.ts
│ │ ├── index.ts
│ │ ├── specs
│ │ │ ├── ImageLoaderFactory.nitro.ts
│ │ │ ├── ImageUtils.nitro.ts
│ │ │ └── ImageLoader.nitro.ts
│ │ ├── ImageUtils.ts
│ │ ├── NativeNitroImage.tsx
│ │ ├── useImageLoader.ts
│ │ ├── markHybridObject.ts
│ │ ├── OptionalWebLoader.ts
│ │ ├── NitroImage.tsx
│ │ ├── AsyncImageSource.ts
│ │ └── useImage.ts
│ ├── react-native.config.js
│ ├── nitro.json
│ └── NitroImage.podspec
└── react-native-nitro-web-image
│ ├── nitrogen
│ └── generated
│ │ ├── .gitattributes
│ │ ├── ios
│ │ ├── c++
│ │ │ └── HybridWebImageFactorySpecSwift.cpp
│ │ ├── NitroWebImageAutolinking.mm
│ │ ├── NitroWebImageAutolinking.swift
│ │ └── swift
│ │ │ ├── AsyncImagePriority.swift
│ │ │ ├── Func_void_std__exception_ptr.swift
│ │ │ └── HybridWebImageFactorySpec.swift
│ │ ├── android
│ │ ├── kotlin
│ │ │ └── com
│ │ │ │ └── margelo
│ │ │ │ └── nitro
│ │ │ │ └── web
│ │ │ │ └── image
│ │ │ │ ├── AsyncImagePriority.kt
│ │ │ │ ├── NitroWebImageOnLoad.kt
│ │ │ │ ├── AsyncImageLoadOptions.kt
│ │ │ │ └── HybridWebImageFactorySpec.kt
│ │ ├── NitroWebImage+autolinking.gradle
│ │ ├── NitroWebImageOnLoad.hpp
│ │ └── NitroWebImageOnLoad.cpp
│ │ └── shared
│ │ └── c++
│ │ └── HybridWebImageFactorySpec.cpp
│ ├── babel.config.js
│ ├── android
│ ├── src
│ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── cpp
│ │ │ └── cpp-adapter.cpp
│ │ │ └── java
│ │ │ └── com
│ │ │ └── margelo
│ │ │ └── nitro
│ │ │ └── web
│ │ │ └── image
│ │ │ ├── AsyncImagePriority+toCoroutineContext.kt
│ │ │ ├── HybridWebImageLoader.kt
│ │ │ ├── NitroWebImagePackage.java
│ │ │ ├── ImageLoader+loadImageAsync.kt
│ │ │ ├── HybridWebImageFactory.kt
│ │ │ └── ImageRequestBuilder+applyOptions.kt
│ ├── gradle.properties
│ └── CMakeLists.txt
│ ├── ios
│ ├── Bridge.h
│ ├── SDWebImageManager+loadImage.swift
│ ├── HybridWebImageFactory.swift
│ ├── AsyncImageLoadOptions+toSDWebImageOptions.swift
│ └── HybridWebImageLoader.swift
│ ├── src
│ └── index.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ ├── react-native.config.js
│ ├── nitro.json
│ └── NitroWebImage.podspec
├── img
├── banner-dark.png
└── banner-light.png
├── .github
├── FUNDING.yml
└── workflows
│ ├── lint.yml
│ ├── run-nitrogen.yml
│ ├── build-android-release.yml
│ └── build-android.yml
├── scripts
├── try-install-lockfiles.sh
└── release.sh
├── config
└── tsconfig.json
├── biome.json
├── .gitignore
└── LICENSE
/example/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/bunfig.toml:
--------------------------------------------------------------------------------
1 | [install]
2 | # Opt out from isolated installs
3 | linker = "hoisted"
4 |
--------------------------------------------------------------------------------
/example/.bundle/config:
--------------------------------------------------------------------------------
1 | BUNDLE_PATH: "vendor/bundle"
2 | BUNDLE_FORCE_RUBY_PLATFORM: 1
3 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/.gitattributes:
--------------------------------------------------------------------------------
1 | ** linguist-generated=true
2 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/nitrogen/generated/.gitattributes:
--------------------------------------------------------------------------------
1 | ** linguist-generated=true
2 |
--------------------------------------------------------------------------------
/example/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "NitroImageExample",
3 | "displayName": "NitroImageExample"
4 | }
5 |
--------------------------------------------------------------------------------
/img/banner-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrousavy/react-native-nitro-image/HEAD/img/banner-dark.png
--------------------------------------------------------------------------------
/example/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ["module:@react-native/babel-preset"],
3 | };
4 |
--------------------------------------------------------------------------------
/img/banner-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrousavy/react-native-nitro-image/HEAD/img/banner-light.png
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: mrousavy
4 | ko_fi: mrousavy
5 |
--------------------------------------------------------------------------------
/example/android/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrousavy/react-native-nitro-image/HEAD/example/android/app/debug.keystore
--------------------------------------------------------------------------------
/example/ios/NitroImageExample/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info": {
3 | "version": 1,
4 | "author": "xcode"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ["module:@react-native/babel-preset"],
3 | };
4 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | NitroImageExample
3 |
4 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ["module:@react-native/babel-preset"],
3 | };
4 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrousavy/react-native-nitro-image/HEAD/example/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/ios/Bridge.h:
--------------------------------------------------------------------------------
1 | //
2 | // Bridge.h
3 | // NitroImage
4 | //
5 | // Created by Marc Rousavy on 22.07.24.
6 | //
7 |
8 | #pragma once
9 |
--------------------------------------------------------------------------------
/example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@react-native/typescript-config",
3 | "include": ["**/*.ts", "**/*.tsx"],
4 | "exclude": ["**/node_modules", "**/Pods"]
5 | }
6 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/ios/Bridge.h:
--------------------------------------------------------------------------------
1 | //
2 | // Bridge.h
3 | // NitroWebImage
4 | //
5 | // Created by Marc Rousavy on 22.07.24.
6 | //
7 |
8 | #pragma once
9 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrousavy/react-native-nitro-image/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrousavy/react-native-nitro-image/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrousavy/react-native-nitro-image/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrousavy/react-native-nitro-image/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrousavy/react-native-nitro-image/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrousavy/react-native-nitro-image/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrousavy/react-native-nitro-image/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrousavy/react-native-nitro-image/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrousavy/react-native-nitro-image/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrousavy/react-native-nitro-image/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/android/gradle.properties:
--------------------------------------------------------------------------------
1 | NitroImage_kotlinVersion=2.0.21
2 | NitroImage_minSdkVersion=23
3 | NitroImage_targetSdkVersion=35
4 | NitroImage_compileSdkVersion=34
5 | NitroImage_ndkVersion=27.1.12297006
6 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/android/gradle.properties:
--------------------------------------------------------------------------------
1 | NitroImage_kotlinVersion=2.0.21
2 | NitroImage_minSdkVersion=23
3 | NitroImage_targetSdkVersion=35
4 | NitroImage_compileSdkVersion=34
5 | NitroImage_ndkVersion=27.1.12297006
6 |
--------------------------------------------------------------------------------
/example/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @format
3 | */
4 |
5 | import { AppRegistry } from "react-native";
6 | import { name as appName } from "./app.json";
7 | import App from "./src/App";
8 |
9 | AppRegistry.registerComponent(appName, () => App);
10 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/android/src/main/cpp/cpp-adapter.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "NitroImageOnLoad.hpp"
3 |
4 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
5 | return margelo::nitro::image::initialize(vm);
6 | }
7 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/android/src/main/cpp/cpp-adapter.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "NitroWebImageOnLoad.hpp"
3 |
4 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
5 | return margelo::nitro::web::image::initialize(vm);
6 | }
7 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/src/index.ts:
--------------------------------------------------------------------------------
1 | import { NitroModules } from "react-native-nitro-modules";
2 | import type { WebImageFactory } from "./specs/WebImageFactory.nitro";
3 |
4 | export const WebImages =
5 | NitroModules.createHybridObject("WebImageFactory");
6 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "@tsconfig/react-native/tsconfig.json",
4 | "../../config/tsconfig.json"
5 | ],
6 | "include": ["src"],
7 | "compilerOptions": {
8 | "rootDir": "src",
9 | "jsx": "react"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "@tsconfig/react-native/tsconfig.json",
4 | "../../config/tsconfig.json"
5 | ],
6 | "include": ["src"],
7 | "compilerOptions": {
8 | "rootDir": "src",
9 | "jsx": "react"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/example/ios/NitroImageExample.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/src/createImageURLs.ts:
--------------------------------------------------------------------------------
1 | const DEFAULT_COUNT = 1000;
2 |
3 | export function createImageURLs(
4 | count: number = DEFAULT_COUNT,
5 | size = 800,
6 | ): string[] {
7 | return [...Array(count).fill(undefined)].map((_, index) => {
8 | return `https://picsum.photos/seed/${index + 1}/${size}`;
9 | });
10 | }
11 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "@tsconfig/react-native/tsconfig.json",
4 | "../../config/tsconfig.json"
5 | ],
6 | "include": ["src"],
7 | "compilerOptions": {
8 | "rootDir": "src",
9 | "outDir": "lib",
10 | "jsx": "react"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/src/Images.ts:
--------------------------------------------------------------------------------
1 | import { NitroModules } from "react-native-nitro-modules";
2 | import type { ImageFactory } from "./specs/ImageFactory.nitro";
3 |
4 | /**
5 | * A factory for loading and creating `Image` instances.
6 | */
7 | export const Images =
8 | NitroModules.createHybridObject("ImageFactory");
9 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "@tsconfig/react-native/tsconfig.json",
4 | "../../config/tsconfig.json"
5 | ],
6 | "include": ["src"],
7 | "compilerOptions": {
8 | "rootDir": "src",
9 | "outDir": "lib",
10 | "jsx": "react"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/ArrayBuffer+copyIfNotOwner.kt:
--------------------------------------------------------------------------------
1 | package com.margelo.nitro.image
2 |
3 | import com.margelo.nitro.core.ArrayBuffer
4 |
5 | fun ArrayBuffer.copyIfNotOwner(): ArrayBuffer {
6 | if (!this.isOwner) {
7 | return ArrayBuffer.copy(this)
8 | }
9 | return this
10 | }
11 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/src/ImageLoaders.ts:
--------------------------------------------------------------------------------
1 | import { NitroModules } from "react-native-nitro-modules";
2 | import type { ImageLoaderFactory } from "./specs/ImageLoaderFactory.nitro";
3 |
4 | /**
5 | * A factory for creating `ImageLoader` instances.
6 | */
7 | export const ImageLoaders =
8 | NitroModules.createHybridObject("ImageLoaderFactory");
9 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/Bitmap+isGPU.kt:
--------------------------------------------------------------------------------
1 | package com.margelo.nitro.image
2 |
3 | import android.graphics.Bitmap
4 | import android.os.Build
5 |
6 | val Bitmap.isGPU: Boolean
7 | get() {
8 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
9 | this.config == Bitmap.Config.HARDWARE
10 | }
11 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/shared/json/NitroImageViewConfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "uiViewClassName": "NitroImageView",
3 | "supportsRawText": false,
4 | "bubblingEventTypes": {},
5 | "directEventTypes": {},
6 | "validAttributes": {
7 | "image": true,
8 | "resizeMode": true,
9 | "recyclingKey": true,
10 | "hybridRef": true
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement { includeBuild("../../node_modules/@react-native/gradle-plugin") }
2 | plugins { id("com.facebook.react.settings") }
3 | extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
4 | rootProject.name = 'NitroImageExample'
5 | include ':app'
6 | includeBuild('../../node_modules/@react-native/gradle-plugin')
7 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/ios/HybridImage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HybridImage.swift
3 | // react-native-nitro-image
4 | //
5 | // Created by Marc Rousavy on 10.06.25.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 | import NitroModules
11 |
12 | class HybridImage: HybridImageSpec, NativeImage {
13 | let uiImage: UIImage
14 |
15 | init(uiImage: UIImage) {
16 | self.uiImage = uiImage
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/c++/HybridImageSpecSwift.cpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridImageSpecSwift.cpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #include "HybridImageSpecSwift.hpp"
9 |
10 | namespace margelo::nitro::image {
11 | } // namespace margelo::nitro::image
12 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/c++/HybridImageLoaderSpecSwift.cpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridImageLoaderSpecSwift.cpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #include "HybridImageLoaderSpecSwift.hpp"
9 |
10 | namespace margelo::nitro::image {
11 | } // namespace margelo::nitro::image
12 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/c++/HybridImageUtilsSpecSwift.cpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridImageUtilsSpecSwift.cpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #include "HybridImageUtilsSpecSwift.hpp"
9 |
10 | namespace margelo::nitro::image {
11 | } // namespace margelo::nitro::image
12 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/Bitmap+toCpuAccessible.kt:
--------------------------------------------------------------------------------
1 | package com.margelo.nitro.image
2 |
3 | import android.graphics.Bitmap
4 |
5 | fun Bitmap.toCpuAccessible(): Bitmap {
6 | if (this.config == Bitmap.Config.HARDWARE) {
7 | // HARDWARE isn't CPU-accessible, so we convert to ARGB
8 | return this.copy(Bitmap.Config.ARGB_8888, true)
9 | }
10 | return this
11 | }
12 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/c++/HybridImageFactorySpecSwift.cpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridImageFactorySpecSwift.cpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #include "HybridImageFactorySpecSwift.hpp"
9 |
10 | namespace margelo::nitro::image {
11 | } // namespace margelo::nitro::image
12 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/c++/HybridNitroImageViewSpecSwift.cpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridNitroImageViewSpecSwift.cpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #include "HybridNitroImageViewSpecSwift.hpp"
9 |
10 | namespace margelo::nitro::image {
11 | } // namespace margelo::nitro::image
12 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/ios/Color+toUIColor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Color+toUIColor.swift
3 | // react-native-nitro-image
4 | //
5 | // Created by Marc Rousavy on 05.11.25.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | extension Color {
12 | func toUIColor() -> UIColor {
13 | return UIColor(red: r,
14 | green: g,
15 | blue: b,
16 | alpha: a ?? 1.0)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/scripts/try-install-lockfiles.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Do not exit on errors
4 | set +e
5 |
6 | cd "$(pwd)" || exit 0
7 |
8 | # Run commands and ignore any errors
9 | {
10 | # Install JS lockfiles
11 | bun i
12 | bun run build
13 |
14 | # Install example pods
15 | bun example bundle-install
16 | bun example pods
17 |
18 | # Add everything to git
19 | git add **/*.lock
20 | } || true
21 |
22 | # No errors - whatever.
23 | exit 0
24 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/c++/HybridImageLoaderFactorySpecSwift.cpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridImageLoaderFactorySpecSwift.cpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #include "HybridImageLoaderFactorySpecSwift.hpp"
9 |
10 | namespace margelo::nitro::image {
11 | } // namespace margelo::nitro::image
12 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/nitrogen/generated/ios/c++/HybridWebImageFactorySpecSwift.cpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridWebImageFactorySpecSwift.cpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #include "HybridWebImageFactorySpecSwift.hpp"
9 |
10 | namespace margelo::nitro::web::image {
11 | } // namespace margelo::nitro::web::image
12 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/Color+toBitmapColor.kt:
--------------------------------------------------------------------------------
1 | package com.margelo.nitro.image
2 |
3 | fun Color.toBitmapColor(): Int {
4 | if (a != null) {
5 | // We have an alpha Channel
6 | return android.graphics.Color.argb(a.toFloat(), r.toFloat(), g.toFloat(), b.toFloat())
7 | } else {
8 | // We don't have an alpha channel
9 | return android.graphics.Color.rgb(r.toFloat(), g.toFloat(), b.toFloat())
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/ios/ImageFormat+toUTType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageFormat+toUTType.swift
3 | // react-native-nitro-image
4 | //
5 | // Created by Marc Rousavy on 11.06.25.
6 | //
7 |
8 | import Foundation
9 | import UniformTypeIdentifiers
10 |
11 | extension ImageFormat {
12 | func toUTType() -> UTType {
13 | switch self {
14 | case .jpg:
15 | return .jpeg
16 | case .png:
17 | return .png
18 | case .heic:
19 | return .heic
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./createImageLoader";
2 | export * from "./Images";
3 | export * from "./ImageUtils";
4 | export * from "./loadImage";
5 | export { NativeNitroImage } from "./NativeNitroImage";
6 | export { NitroImage, type NitroImageProps } from "./NitroImage";
7 | export type { Image } from "./specs/Image.nitro";
8 | export type { ImageLoader } from "./specs/ImageLoader.nitro";
9 |
10 | export * from "./useImage";
11 | export * from "./useImageLoader";
12 |
--------------------------------------------------------------------------------
/example/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
--------------------------------------------------------------------------------
/scripts/release.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | echo "Starting the release process..."
6 | echo "Provided options: $@"
7 |
8 | echo "Publishing 'react-native-nitro-image' to NPM"
9 | cd packages/react-native-nitro-image
10 | bun release $@
11 |
12 | echo "Publishing 'react-native-nitro-web-image' to NPM"
13 | cd ../react-native-nitro-web-image
14 | bun release $@
15 |
16 | echo "Creating a Git bump commit and GitHub release"
17 | cd ../..
18 | bun run release-it $@
19 |
20 | echo "Successfully released NitroImage!"
21 |
--------------------------------------------------------------------------------
/example/ios/.xcode.env:
--------------------------------------------------------------------------------
1 | # This `.xcode.env` file is versioned and is used to source the environment
2 | # used when running script phases inside Xcode.
3 | # To customize your local environment, you can create an `.xcode.env.local`
4 | # file that is not versioned.
5 |
6 | # NODE_BINARY variable contains the PATH to the node executable.
7 | #
8 | # Customize the NODE_BINARY variable here.
9 | # For example, to use nvm with brew, add the following line
10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use
11 | export NODE_BINARY=$(command -v node)
12 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/react-native.config.js:
--------------------------------------------------------------------------------
1 | // https://github.com/react-native-community/cli/blob/main/docs/dependencies.md
2 |
3 | module.exports = {
4 | dependency: {
5 | platforms: {
6 | /**
7 | * @type {import('@react-native-community/cli-types').IOSDependencyParams}
8 | */
9 | ios: {},
10 | /**
11 | * @type {import('@react-native-community/cli-types').AndroidDependencyParams}
12 | */
13 | android: {},
14 | },
15 | },
16 | };
17 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/react-native.config.js:
--------------------------------------------------------------------------------
1 | // https://github.com/react-native-community/cli/blob/main/docs/dependencies.md
2 |
3 | module.exports = {
4 | dependency: {
5 | platforms: {
6 | /**
7 | * @type {import('@react-native-community/cli-types').IOSDependencyParams}
8 | */
9 | ios: {},
10 | /**
11 | * @type {import('@react-native-community/cli-types').AndroidDependencyParams}
12 | */
13 | android: {},
14 | },
15 | },
16 | };
17 |
--------------------------------------------------------------------------------
/example/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
4 | ruby ">= 2.6.10"
5 |
6 | # Exclude problematic versions of cocoapods and activesupport that causes build failures.
7 | gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1'
8 | gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0'
9 | gem 'xcodeproj', '< 1.26.0'
10 | gem 'concurrent-ruby', '< 1.3.4'
11 |
12 | # Ruby 3.4.0 has removed some libraries from the standard library.
13 | gem 'bigdecimal'
14 | gem 'logger'
15 | gem 'benchmark'
16 | gem 'mutex_m'
17 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/nitro.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://nitro.margelo.com/nitro.schema.json",
3 | "cxxNamespace": ["web", "image"],
4 | "ios": {
5 | "iosModuleName": "NitroWebImage"
6 | },
7 | "android": {
8 | "androidNamespace": ["web", "image"],
9 | "androidCxxLibName": "NitroWebImage"
10 | },
11 | "autolinking": {
12 | "WebImageFactory": {
13 | "swift": "HybridWebImageFactory",
14 | "kotlin": "HybridWebImageFactory"
15 | }
16 | },
17 | "ignorePaths": ["**/node_modules"]
18 | }
19 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/ios/NativeImageView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NativeImageView.swift
3 | // Pods
4 | //
5 | // Created by Marc Rousavy on 25.07.25.
6 | //
7 |
8 | import UIKit
9 |
10 | /**
11 | * A protocol that represents a native image view.
12 | * This can be used to downcast from `HybridImageViewSpec`
13 | * which gives you a concrete `UIImageView`.
14 | *
15 | * If you want to use other Image Views with Image Loaders,
16 | * make sure your native view class conforms to this protocol.
17 | */
18 | public protocol NativeImageView {
19 | var imageView: UIImageView { get }
20 | }
21 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: Lint with Biome
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | branches:
9 | - main
10 | workflow_dispatch:
11 |
12 | jobs:
13 | lint:
14 | name: Run Biome Linter
15 | runs-on: ubuntu-latest
16 | steps:
17 | - uses: actions/checkout@v4
18 |
19 | - name: Setup Bun
20 | uses: oven-sh/setup-bun@v2
21 |
22 | - name: Install dependencies
23 | run: bun install
24 |
25 | - name: Run Biome
26 | run: bun lint-ci
27 | env:
28 | CI: true
29 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/Bitmap+toByteBuffer.kt:
--------------------------------------------------------------------------------
1 | package com.margelo.nitro.image
2 |
3 | import android.graphics.Bitmap
4 | import java.nio.ByteBuffer
5 |
6 | fun Bitmap.toByteBuffer(): ByteBuffer {
7 | var bitmap = this
8 | if (isGPU) {
9 | // It's a GPU Bitmap - we need to copy it to CPU memory first.
10 | bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false)
11 | }
12 |
13 | val buffer = ByteBuffer.allocateDirect(bitmap.byteCount)
14 | bitmap.copyPixelsToBuffer(buffer)
15 | buffer.rewind()
16 | return buffer
17 | }
18 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/Bitmap+toMutable.kt:
--------------------------------------------------------------------------------
1 | package com.margelo.nitro.image
2 |
3 | import android.graphics.Bitmap
4 |
5 | fun Bitmap.toMutable(forceCopy: Boolean): Bitmap {
6 | if (isMutable && !forceCopy) {
7 | // It's already Mutable!
8 | return this
9 | }
10 | var config = this.config ?: throw Error("Failed to get Bitmap's format! $this")
11 | if (config == Bitmap.Config.HARDWARE) {
12 | // HARDWARE Bitmaps are not mutable, so we need to change to ARGB
13 | config = Bitmap.Config.ARGB_8888
14 | }
15 | return this.copy(config, true)
16 | }
17 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/CustomImageView.kt:
--------------------------------------------------------------------------------
1 | package com.margelo.nitro.image
2 |
3 | import android.content.Context
4 | import androidx.appcompat.widget.AppCompatImageView
5 |
6 | class CustomImageView(context: Context,
7 | private val visibilityChanged: (Boolean) -> Unit): AppCompatImageView(context) {
8 | override fun onAttachedToWindow() {
9 | super.onAttachedToWindow()
10 | visibilityChanged(true)
11 | }
12 |
13 | override fun onDetachedFromWindow() {
14 | super.onDetachedFromWindow()
15 | visibilityChanged(false)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/android/src/main/java/com/margelo/nitro/web/image/AsyncImagePriority+toCoroutineContext.kt:
--------------------------------------------------------------------------------
1 | package com.margelo.nitro.web.image
2 |
3 | import kotlinx.coroutines.Dispatchers
4 | import kotlinx.coroutines.currentCoroutineContext
5 | import kotlin.coroutines.CoroutineContext
6 |
7 |
8 | fun AsyncImagePriority.toCoroutineContext(): CoroutineContext? {
9 | // TODO: Does this look about right?
10 | return when (this) {
11 | AsyncImagePriority.LOW -> Dispatchers.IO.limitedParallelism(2)
12 | AsyncImagePriority.DEFAULT -> null
13 | AsyncImagePriority.HIGH -> Dispatchers.IO
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext {
3 | buildToolsVersion = "36.0.0"
4 | minSdkVersion = 26
5 | compileSdkVersion = 36
6 | targetSdkVersion = 36
7 | ndkVersion = "27.1.12297006"
8 | kotlinVersion = "2.1.20"
9 | }
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | dependencies {
15 | classpath("com.android.tools.build:gradle")
16 | classpath("com.facebook.react:react-native-gradle-plugin")
17 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
18 | }
19 | }
20 |
21 | apply plugin: "com.facebook.react.rootproject"
22 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/ImageFormat.kt:
--------------------------------------------------------------------------------
1 | ///
2 | /// ImageFormat.kt
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | package com.margelo.nitro.image
9 |
10 | import androidx.annotation.Keep
11 | import com.facebook.proguard.annotations.DoNotStrip
12 |
13 | /**
14 | * Represents the JavaScript enum/union "ImageFormat".
15 | */
16 | @DoNotStrip
17 | @Keep
18 | enum class ImageFormat(@DoNotStrip @Keep val value: Int) {
19 | JPG(0),
20 | PNG(1),
21 | HEIC(2);
22 | }
23 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/ResizeMode.kt:
--------------------------------------------------------------------------------
1 | ///
2 | /// ResizeMode.kt
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | package com.margelo.nitro.image
9 |
10 | import androidx.annotation.Keep
11 | import com.facebook.proguard.annotations.DoNotStrip
12 |
13 | /**
14 | * Represents the JavaScript enum/union "ResizeMode".
15 | */
16 | @DoNotStrip
17 | @Keep
18 | enum class ResizeMode(@DoNotStrip @Keep val value: Int) {
19 | COVER(0),
20 | CONTAIN(1),
21 | CENTER(2),
22 | STRETCH(3);
23 | }
24 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/nitrogen/generated/android/kotlin/com/margelo/nitro/web/image/AsyncImagePriority.kt:
--------------------------------------------------------------------------------
1 | ///
2 | /// AsyncImagePriority.kt
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | package com.margelo.nitro.web.image
9 |
10 | import androidx.annotation.Keep
11 | import com.facebook.proguard.annotations.DoNotStrip
12 |
13 | /**
14 | * Represents the JavaScript enum/union "AsyncImagePriority".
15 | */
16 | @DoNotStrip
17 | @Keep
18 | enum class AsyncImagePriority(@DoNotStrip @Keep val value: Int) {
19 | LOW(0),
20 | DEFAULT(1),
21 | HIGH(2);
22 | }
23 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/swift/Variant__any_HybridImageSpec___any_HybridImageLoaderSpec_.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// Variant__any_HybridImageSpec___any_HybridImageLoaderSpec_.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 |
9 |
10 | /**
11 | * An Swift enum with associated values representing a Variant/Union type.
12 | * JS type: `hybrid-object | hybrid-object`
13 | */
14 | @frozen
15 | public indirect enum Variant__any_HybridImageSpec___any_HybridImageLoaderSpec_ {
16 | case first((any HybridImageSpec))
17 | case second((any HybridImageLoaderSpec))
18 | }
19 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/src/specs/ImageLoaderFactory.nitro.ts:
--------------------------------------------------------------------------------
1 | import type { HybridObject } from "react-native-nitro-modules";
2 | import type { EncodedImageData, RawPixelData } from "./Image.nitro";
3 | import type { ImageLoader } from "./ImageLoader.nitro";
4 |
5 | export interface ImageLoaderFactory
6 | extends HybridObject<{ ios: "swift"; android: "kotlin" }> {
7 | createFileImageLoader(filePath: string): ImageLoader;
8 | createResourceImageLoader(name: string): ImageLoader;
9 | createSymbolImageLoader(symbolName: string): ImageLoader;
10 | createRawPixelDataImageLoader(data: RawPixelData): ImageLoader;
11 | createEncodedImageDataImageLoader(data: EncodedImageData): ImageLoader;
12 | }
13 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/ios/UIImage+memorySize.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+memorySize.swift
3 | // react-native-nitro-image
4 | //
5 | // Created by Marc Rousavy on 11.06.25.
6 | //
7 |
8 | import UIKit
9 |
10 | extension UIImage {
11 | private var rgbaMemorySize: Int {
12 | let pixelWidth = Int(size.width * scale)
13 | let pixelHeight = Int(size.height * scale)
14 | let bytesPerPixel = 4
15 |
16 | return pixelWidth * pixelHeight * bytesPerPixel
17 | }
18 |
19 | private var cgImageMemorySize: Int? {
20 | guard let cgImage = cgImage else { return nil }
21 | return cgImage.bytesPerRow * cgImage.height
22 | }
23 |
24 | var memorySize: Int {
25 | return cgImageMemorySize ?? rgbaMemorySize
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/PixelFormat.kt:
--------------------------------------------------------------------------------
1 | ///
2 | /// PixelFormat.kt
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | package com.margelo.nitro.image
9 |
10 | import androidx.annotation.Keep
11 | import com.facebook.proguard.annotations.DoNotStrip
12 |
13 | /**
14 | * Represents the JavaScript enum/union "PixelFormat".
15 | */
16 | @DoNotStrip
17 | @Keep
18 | enum class PixelFormat(@DoNotStrip @Keep val value: Int) {
19 | ARGB(0),
20 | BGRA(1),
21 | ABGR(2),
22 | RGBA(3),
23 | XRGB(4),
24 | BGRX(5),
25 | XBGR(6),
26 | RGBX(7),
27 | RGB(8),
28 | BGR(9),
29 | UNKNOWN(10);
30 | }
31 |
--------------------------------------------------------------------------------
/example/metro.config.js:
--------------------------------------------------------------------------------
1 | const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config");
2 | const path = require("node:path");
3 |
4 | const root = path.resolve(__dirname, "..");
5 |
6 | /**
7 | * Metro configuration
8 | * https://facebook.github.io/metro/docs/configuration
9 | *
10 | * @type {import('@react-native/metro-config').MetroConfig}
11 | */
12 | const config = {
13 | watchFolders: [root],
14 |
15 | transformer: {
16 | getTransformOptions: async () => ({
17 | transform: {
18 | experimentalImportSupport: false,
19 | inlineRequires: true,
20 | },
21 | }),
22 | },
23 | };
24 |
25 | module.exports = mergeConfig(getDefaultConfig(__dirname), config);
26 | console.log(module.exports);
27 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/src/ImageUtils.ts:
--------------------------------------------------------------------------------
1 | import { NitroModules } from "react-native-nitro-modules";
2 | import type { ImageUtils } from "./specs/ImageUtils.nitro";
3 |
4 | const utils = NitroModules.createHybridObject("ImageUtils");
5 |
6 | /**
7 | * Returns `true` when the host platform supports loading Images
8 | * in `HEIC` format.
9 | */
10 | export const supportsHeicLoading = utils.supportsHeicLoading;
11 | /**
12 | * Returns `true` when the host platform supports writing Images
13 | * in `HEIC` format.
14 | */
15 | export const supportsHeicWriting = utils.supportsHeicWriting;
16 |
17 | export const thumbHashToBase64String =
18 | utils.thumbHashToBase64String.bind(utils);
19 |
20 | export const thumbHashFromBase64String =
21 | utils.thumbhashFromBase64String.bind(utils);
22 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/src/NativeNitroImage.tsx:
--------------------------------------------------------------------------------
1 | import { getHostComponent } from "react-native-nitro-modules";
2 | import ViewConfig from "../nitrogen/generated/shared/json/NitroImageViewConfig.json";
3 | import type {
4 | NativeNitroImageViewMethods,
5 | NativeNitroImageViewProps,
6 | } from "./specs/ImageView.nitro";
7 |
8 | /**
9 | * The native renderable `` view.
10 | * @example
11 | * ```tsx
12 | * function App() {
13 | * const image = useImage('https://picsum.photos/seed/123/400')
14 | * return
15 | * }
16 | * ```
17 | */
18 | export const NativeNitroImage = getHostComponent<
19 | NativeNitroImageViewProps,
20 | NativeNitroImageViewMethods
21 | >("NitroImageView", () => ViewConfig);
22 |
--------------------------------------------------------------------------------
/example/src/App.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Sample React Native App
3 | * https://github.com/facebook/react-native
4 | *
5 | * @format
6 | */
7 |
8 | import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
9 | import { createStaticNavigation } from "@react-navigation/native";
10 | import { EmptyTab } from "./EmptyTab";
11 | import { FastImageTab } from "./FastImageTab";
12 | import { NitroImageTab } from "./NitroImageTab";
13 |
14 | const Tabs = createBottomTabNavigator({
15 | detachInactiveScreens: false,
16 | screens: {
17 | Empty: EmptyTab,
18 | FastImage: FastImageTab,
19 | NitroImage: NitroImageTab,
20 | },
21 | });
22 | const Navigation = createStaticNavigation(Tabs);
23 |
24 | function App(): React.JSX.Element {
25 | return ;
26 | }
27 |
28 | export default App;
29 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/ios/UIImage+toEncodedImageData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+toEncodedImageData.swift
3 | // react-native-nitro-image
4 | //
5 | // Created by Marc Rousavy on 22.10.25.
6 | //
7 |
8 | import UIKit
9 | import CoreGraphics
10 | import NitroModules
11 |
12 | extension UIImage {
13 | /**
14 | * Returns encoded Image data of this Image (JPG, PNG, ...)
15 | */
16 | func toEncodedImageData(format: ImageFormat, quality: Double = 1.0) throws -> EncodedImageData {
17 | let data = try getData(in: format, quality: quality)
18 | let arrayBuffer = try ArrayBuffer.copy(data: data)
19 | return EncodedImageData(buffer: arrayBuffer,
20 | width: self.size.width,
21 | height: self.size.height,
22 | imageFormat: format)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/android/NitroImageOnLoad.hpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// NitroImageOnLoad.hpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #include
9 | #include
10 |
11 | namespace margelo::nitro::image {
12 |
13 | /**
14 | * Initializes the native (C++) part of NitroImage, and autolinks all Hybrid Objects.
15 | * Call this in your `JNI_OnLoad` function (probably inside `cpp-adapter.cpp`).
16 | * Example:
17 | * ```cpp (cpp-adapter.cpp)
18 | * JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
19 | * return margelo::nitro::image::initialize(vm);
20 | * }
21 | * ```
22 | */
23 | int initialize(JavaVM* vm);
24 |
25 | } // namespace margelo::nitro::image
26 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/android/NitroImage+autolinking.gradle:
--------------------------------------------------------------------------------
1 | ///
2 | /// NitroImage+autolinking.gradle
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | /// This is a Gradle file that adds all files generated by Nitrogen
9 | /// to the current Gradle project.
10 | ///
11 | /// To use it, add this to your build.gradle:
12 | /// ```gradle
13 | /// apply from: '../nitrogen/generated/android/NitroImage+autolinking.gradle'
14 | /// ```
15 |
16 | logger.warn("[NitroModules] 🔥 NitroImage is boosted by nitro!")
17 |
18 | android {
19 | sourceSets {
20 | main {
21 | java.srcDirs += [
22 | // Nitrogen files
23 | "${project.projectDir}/../nitrogen/generated/android/kotlin"
24 | ]
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/nitrogen/generated/android/NitroWebImage+autolinking.gradle:
--------------------------------------------------------------------------------
1 | ///
2 | /// NitroWebImage+autolinking.gradle
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | /// This is a Gradle file that adds all files generated by Nitrogen
9 | /// to the current Gradle project.
10 | ///
11 | /// To use it, add this to your build.gradle:
12 | /// ```gradle
13 | /// apply from: '../nitrogen/generated/android/NitroWebImage+autolinking.gradle'
14 | /// ```
15 |
16 | logger.warn("[NitroModules] 🔥 NitroWebImage is boosted by nitro!")
17 |
18 | android {
19 | sourceSets {
20 | main {
21 | java.srcDirs += [
22 | // Nitrogen files
23 | "${project.projectDir}/../nitrogen/generated/android/kotlin"
24 | ]
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/nitrogen/generated/android/NitroWebImageOnLoad.hpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// NitroWebImageOnLoad.hpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #include
9 | #include
10 |
11 | namespace margelo::nitro::web::image {
12 |
13 | /**
14 | * Initializes the native (C++) part of NitroWebImage, and autolinks all Hybrid Objects.
15 | * Call this in your `JNI_OnLoad` function (probably inside `cpp-adapter.cpp`).
16 | * Example:
17 | * ```cpp (cpp-adapter.cpp)
18 | * JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
19 | * return margelo::nitro::web::image::initialize(vm);
20 | * }
21 | * ```
22 | */
23 | int initialize(JavaVM* vm);
24 |
25 | } // namespace margelo::nitro::web::image
26 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/views/HybridNitroImageViewStateUpdater.kt:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridNitroImageViewStateUpdater.kt
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | package com.margelo.nitro.image.views
9 |
10 | import com.facebook.react.uimanager.StateWrapper
11 | import com.margelo.nitro.image.*
12 |
13 | internal class HybridNitroImageViewStateUpdater {
14 | companion object {
15 | /**
16 | * Updates the props for [view] through C++.
17 | * The [state] prop is expected to contain [view]'s props as wrapped Fabric state.
18 | */
19 | @Suppress("KotlinJniMissingFunction")
20 | @JvmStatic
21 | external fun updateViewProps(view: HybridNitroImageViewSpec, state: StateWrapper)
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example/src/FastImageTab.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo } from "react";
2 | import { FlatList, StyleSheet, Text, View } from "react-native";
3 | import FastImage from "react-native-fast-image";
4 | import { createImageURLs } from "./createImageURLs";
5 |
6 | export function FastImageTab() {
7 | const imageURLs = useMemo(() => createImageURLs(), []);
8 |
9 | return (
10 |
11 | FastImage Tab
12 | (
17 |
18 | )}
19 | />
20 |
21 | );
22 | }
23 |
24 | const styles = StyleSheet.create({
25 | image: {
26 | width: "25%",
27 | aspectRatio: 1,
28 | },
29 | });
30 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/src/specs/ImageUtils.nitro.ts:
--------------------------------------------------------------------------------
1 | import type { HybridObject } from "react-native-nitro-modules";
2 |
3 | export interface ImageUtils
4 | extends HybridObject<{ ios: "swift"; android: "kotlin" }> {
5 | /**
6 | * Returns `true` when the host platform supports loading Images
7 | * in `HEIC` format.
8 | */
9 | readonly supportsHeicLoading: boolean;
10 | /**
11 | * Returns `true` when the host platform supports writing Images
12 | * in `HEIC` format.
13 | */
14 | readonly supportsHeicWriting: boolean;
15 |
16 | /**
17 | * Converts the given ThumbHash {@linkcode ArrayBuffer} to a `string`.
18 | */
19 | thumbHashToBase64String(thumbhash: ArrayBuffer): string;
20 | /**
21 | * Converts the given ThumbHash `string` to an {@linkcode ArrayBuffer}.
22 | */
23 | thumbhashFromBase64String(thumbhashBase64: string): ArrayBuffer;
24 | }
25 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/android/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | project(NitroImage)
2 | cmake_minimum_required(VERSION 3.9.0)
3 |
4 | set (PACKAGE_NAME NitroImage)
5 | set (CMAKE_VERBOSE_MAKEFILE ON)
6 | set (CMAKE_CXX_STANDARD 20)
7 |
8 | # Enable Raw Props parsing in react-native (for Nitro Views)
9 | add_compile_options(-DRN_SERIALIZABLE_STATE=1)
10 |
11 | # Define C++ library and add all sources
12 | add_library(${PACKAGE_NAME} SHARED
13 | src/main/cpp/cpp-adapter.cpp
14 | )
15 |
16 | # Add Nitrogen specs :)
17 | include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/NitroImage+autolinking.cmake)
18 |
19 | # Set up local includes
20 | include_directories(
21 | "src/main/cpp"
22 | "../cpp"
23 | )
24 |
25 | find_library(LOG_LIB log)
26 |
27 | # Link all libraries together
28 | target_link_libraries(
29 | ${PACKAGE_NAME}
30 | ${LOG_LIB}
31 | android # <-- Android core
32 | )
33 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/shared/c++/HybridImageLoaderSpec.cpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridImageLoaderSpec.cpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #include "HybridImageLoaderSpec.hpp"
9 |
10 | namespace margelo::nitro::image {
11 |
12 | void HybridImageLoaderSpec::loadHybridMethods() {
13 | // load base methods/properties
14 | HybridObject::loadHybridMethods();
15 | // load custom methods/properties
16 | registerHybrids(this, [](Prototype& prototype) {
17 | prototype.registerHybridMethod("loadImage", &HybridImageLoaderSpec::loadImage);
18 | prototype.registerHybridMethod("requestImage", &HybridImageLoaderSpec::requestImage);
19 | prototype.registerHybridMethod("dropImage", &HybridImageLoaderSpec::dropImage);
20 | });
21 | }
22 |
23 | } // namespace margelo::nitro::image
24 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitro.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://nitro.margelo.com/nitro.schema.json",
3 | "cxxNamespace": ["image"],
4 | "ios": {
5 | "iosModuleName": "NitroImage"
6 | },
7 | "android": {
8 | "androidNamespace": ["image"],
9 | "androidCxxLibName": "NitroImage"
10 | },
11 | "autolinking": {
12 | "ImageFactory": {
13 | "swift": "HybridImageFactory",
14 | "kotlin": "HybridImageFactory"
15 | },
16 | "ImageLoaderFactory": {
17 | "swift": "HybridImageLoaderFactory",
18 | "kotlin": "HybridImageLoaderFactory"
19 | },
20 | "ImageUtils": {
21 | "swift": "HybridImageUtils",
22 | "kotlin": "HybridImageUtils"
23 | },
24 | "NitroImageView": {
25 | "swift": "HybridImageView",
26 | "kotlin": "HybridImageView"
27 | }
28 | },
29 | "ignorePaths": ["**/node_modules"]
30 | }
31 |
--------------------------------------------------------------------------------
/example/ios/NitroImageExample/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "idiom": "iphone",
5 | "scale": "2x",
6 | "size": "20x20"
7 | },
8 | {
9 | "idiom": "iphone",
10 | "scale": "3x",
11 | "size": "20x20"
12 | },
13 | {
14 | "idiom": "iphone",
15 | "scale": "2x",
16 | "size": "29x29"
17 | },
18 | {
19 | "idiom": "iphone",
20 | "scale": "3x",
21 | "size": "29x29"
22 | },
23 | {
24 | "idiom": "iphone",
25 | "scale": "2x",
26 | "size": "40x40"
27 | },
28 | {
29 | "idiom": "iphone",
30 | "scale": "3x",
31 | "size": "40x40"
32 | },
33 | {
34 | "idiom": "iphone",
35 | "scale": "2x",
36 | "size": "60x60"
37 | },
38 | {
39 | "idiom": "iphone",
40 | "scale": "3x",
41 | "size": "60x60"
42 | },
43 | {
44 | "idiom": "ios-marketing",
45 | "scale": "1x",
46 | "size": "1024x1024"
47 | }
48 | ],
49 | "info": {
50 | "author": "xcode",
51 | "version": 1
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/android/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | project(NitroWebImage)
2 | cmake_minimum_required(VERSION 3.9.0)
3 |
4 | set (PACKAGE_NAME NitroWebImage)
5 | set (CMAKE_VERBOSE_MAKEFILE ON)
6 | set (CMAKE_CXX_STANDARD 20)
7 |
8 | # Define C++ library and add all sources
9 | add_library(${PACKAGE_NAME} SHARED
10 | src/main/cpp/cpp-adapter.cpp
11 | )
12 |
13 | # Add Nitrogen specs :)
14 | include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/NitroWebImage+autolinking.cmake)
15 |
16 | # Set up local includes
17 | include_directories(
18 | "src/main/cpp"
19 | "../cpp"
20 | )
21 |
22 | find_library(LOG_LIB log)
23 | find_package(react-native-nitro-image REQUIRED) # <-- for the HybridImage type
24 |
25 | # Link all libraries together
26 | target_link_libraries(
27 | ${PACKAGE_NAME}
28 | ${LOG_LIB}
29 | android # <-- Android core
30 | react-native-nitro-image::NitroImage # <-- NitroImage
31 | )
32 |
--------------------------------------------------------------------------------
/example/src/NitroImageTab.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo } from "react";
2 | import { FlatList, StyleSheet, Text, View } from "react-native";
3 | import { NitroImage } from "react-native-nitro-image";
4 | import { createImageURLs } from "./createImageURLs";
5 |
6 | export function NitroImageTab() {
7 | const imageURLs = useMemo(() => createImageURLs(), []);
8 |
9 | return (
10 |
11 | NitroImage Tab
12 | (
17 |
21 | )}
22 | />
23 |
24 | );
25 | }
26 |
27 | const styles= StyleSheet.create({
28 | image: {
29 | width: '25%',
30 | aspectRatio: 1
31 | }
32 | })
33 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/HybridImageLoader.kt:
--------------------------------------------------------------------------------
1 | package com.margelo.nitro.image
2 |
3 | import androidx.annotation.Keep
4 | import com.facebook.proguard.annotations.DoNotStrip
5 | import com.margelo.nitro.core.Promise
6 |
7 | @Keep
8 | @DoNotStrip
9 | class HybridImageLoader(private val loadImageFunc: () -> Promise): HybridImageLoaderSpec() {
10 | override fun loadImage(): Promise = loadImageFunc()
11 |
12 | override fun requestImage(forView: HybridNitroImageViewSpec) {
13 | val view = forView as? HybridImageView ?: return
14 |
15 | loadImage().then { maybeImage ->
16 | val image = maybeImage as? HybridImage ?: return@then
17 | view.imageView.setImageBitmap(image.bitmap)
18 | }
19 | }
20 |
21 | override fun dropImage(forView: HybridNitroImageViewSpec) {
22 | val view = forView as? HybridImageView ?: return
23 | view.imageView.setImageDrawable(null)
24 | }
25 | }
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/src/useImageLoader.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from "react";
2 | import { type AsyncImageSource, isHybridObject } from "./AsyncImageSource";
3 | import { createImageLoader } from "./createImageLoader";
4 | import { markHybridObject } from "./markHybridObject";
5 | import type { Image } from "./specs/Image.nitro";
6 | import type { ImageLoader } from "./specs/ImageLoader.nitro";
7 |
8 | export function useImageLoader(
9 | source: AsyncImageSource,
10 | ): Image | ImageLoader | undefined {
11 | // biome-ignore lint: The dependencies array is a bit hacky.
12 | return useMemo(() => {
13 | // 1. Create the Image/ImageLoader instance
14 | const loader = createImageLoader(source);
15 | // 2. Add `__source` as a property on the JS side so React diffs properly
16 | markHybridObject(loader, source);
17 | // 3. Return it
18 | return loader;
19 | }, [isHybridObject(source) ? source : JSON.stringify(source)]);
20 | }
21 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/nitrogen/generated/shared/c++/HybridWebImageFactorySpec.cpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridWebImageFactorySpec.cpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #include "HybridWebImageFactorySpec.hpp"
9 |
10 | namespace margelo::nitro::web::image {
11 |
12 | void HybridWebImageFactorySpec::loadHybridMethods() {
13 | // load base methods/properties
14 | HybridObject::loadHybridMethods();
15 | // load custom methods/properties
16 | registerHybrids(this, [](Prototype& prototype) {
17 | prototype.registerHybridMethod("createWebImageLoader", &HybridWebImageFactorySpec::createWebImageLoader);
18 | prototype.registerHybridMethod("loadFromURLAsync", &HybridWebImageFactorySpec::loadFromURLAsync);
19 | prototype.registerHybridMethod("preload", &HybridWebImageFactorySpec::preload);
20 | });
21 | }
22 |
23 | } // namespace margelo::nitro::web::image
24 |
--------------------------------------------------------------------------------
/config/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "exclude": [
3 | "**/node_modules",
4 | "**/lib",
5 | "**/.eslintrc.js",
6 | "**/.prettierrc.js",
7 | "**/jest.config.js",
8 | "**/babel.config.js",
9 | "**/metro.config.js",
10 | "**/tsconfig.json"
11 | ],
12 | "compilerOptions": {
13 | "composite": true,
14 | "allowUnreachableCode": false,
15 | "allowUnusedLabels": false,
16 | "esModuleInterop": true,
17 | "forceConsistentCasingInFileNames": true,
18 | "lib": ["ESNext"],
19 | "module": "ESNext",
20 | "moduleResolution": "Node",
21 | "noEmit": false,
22 | "noFallthroughCasesInSwitch": true,
23 | "noImplicitReturns": true,
24 | "noImplicitUseStrict": false,
25 | "noStrictGenericChecks": false,
26 | "noUncheckedIndexedAccess": true,
27 | "noUnusedLocals": true,
28 | "noUnusedParameters": true,
29 | "resolveJsonModule": true,
30 | "skipLibCheck": true,
31 | "strict": true,
32 | "target": "esnext",
33 | "verbatimModuleSyntax": true
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/ios/UIImageOrientation+fromDegrees.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImageOrientation+fromDegrees.swift
3 | // react-native-nitro-image
4 | //
5 | // Created by Marc Rousavy on 11.06.25.
6 | //
7 |
8 | import UIKit
9 | import NitroModules
10 |
11 | extension UIImage.Orientation {
12 | private func rotated90CW() -> UIImage.Orientation {
13 | switch self {
14 | case .up: return .right
15 | case .right: return .down
16 | case .down: return .left
17 | case .left: return .up
18 | case .upMirrored: return .rightMirrored
19 | case .rightMirrored: return .downMirrored
20 | case .downMirrored: return .leftMirrored
21 | case .leftMirrored: return .upMirrored
22 | @unknown default: return .right
23 | }
24 | }
25 | func rotated(byRightAngles k: Int) -> UIImage.Orientation {
26 | let t = ((k % 4) + 4) % 4
27 | var o = self
28 | for _ in 0.. min_ios_version_supported, :visionos => 1.0 }
14 | s.source = { :git => "https://github.com/mrousavy/nitro.git", :tag => "#{s.version}" }
15 |
16 | s.source_files = [
17 | # Implementation (Swift)
18 | "ios/**/*.{swift}",
19 | # Autolinking/Registration (Objective-C++)
20 | "ios/**/*.{m,mm}",
21 | # Implementation (C++ objects)
22 | "cpp/**/*.{hpp,cpp}",
23 | ]
24 |
25 | load 'nitrogen/generated/ios/NitroImage+autolinking.rb'
26 | add_nitrogen_files(s)
27 |
28 | s.dependency 'React-jsi'
29 | s.dependency 'React-callinvoker'
30 | install_modules_dependencies(s)
31 | end
32 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/Bitmap+compressInMemory.kt:
--------------------------------------------------------------------------------
1 | package com.margelo.nitro.image
2 |
3 | import android.graphics.Bitmap
4 | import java.nio.ByteBuffer
5 |
6 | fun Bitmap.compressInMemory(format: ImageFormat, quality: Int): ByteBuffer {
7 | if (quality < 0 || quality > 100) {
8 | throw Error("Image quality has to be between 0 and 100! (Received: $quality)")
9 | }
10 | val estimatedByteSize = when (format) {
11 | ImageFormat.JPG -> (width * height) / 2
12 | ImageFormat.PNG -> width * height
13 | ImageFormat.HEIC -> width * height
14 | }
15 |
16 | FastByteArrayOutputStream(estimatedByteSize).use { out ->
17 | val successful = this.compress(format.toBitmapFormat(), quality, out)
18 | if (!successful) {
19 | throw Error("Failed to compress the Bitmap into EncodedImageData! (Format: ${format.name}, " +
20 | "Quality: ${quality}, Written Bytes: ${out.count})")
21 | }
22 | return out.toByteBuffer()
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | **/.xcode.env.local
6 |
7 | # XDE
8 | .expo/
9 |
10 | # VSCode
11 | .vscode/
12 | jsconfig.json
13 |
14 | # Xcode
15 | #
16 | build/
17 | *.pbxuser
18 | !default.pbxuser
19 | *.mode1v3
20 | !default.mode1v3
21 | *.mode2v3
22 | !default.mode2v3
23 | *.perspectivev3
24 | !default.perspectivev3
25 | xcuserdata
26 | *.xccheckout
27 | *.moved-aside
28 | DerivedData
29 | *.hmap
30 | *.ipa
31 | *.xcuserstate
32 | project.xcworkspace
33 |
34 | # Android/IJ
35 | #
36 | .classpath
37 | .cxx
38 | .gradle
39 | .idea
40 | .project
41 | .settings
42 | local.properties
43 | android.iml
44 |
45 | # Cocoapods
46 | #
47 | example/ios/Pods
48 |
49 | # Ruby
50 | example/vendor/
51 |
52 | # node.js
53 | #
54 | node_modules/
55 | npm-debug.log
56 |
57 | # Bun
58 | package-lock.json
59 | **/*.bun
60 |
61 | # BUCK
62 | buck-out/
63 | \.buckd/
64 | android/app/libs
65 | android/keystores/debug.keystore
66 |
67 | # Expo
68 | .expo/
69 |
70 | # Turborepo
71 | .turbo/
72 |
73 | # generated by bob
74 | lib/
75 |
76 | # TypeScript
77 | tsconfig.tsbuildinfo
78 |
79 |
--------------------------------------------------------------------------------
/.github/workflows/run-nitrogen.yml:
--------------------------------------------------------------------------------
1 | name: Run Nitrogen
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | - '.github/workflows/run-nitrogen.yml'
9 | - 'src/specs/**'
10 | - '**/nitro.json'
11 | - '**/package.json'
12 | pull_request:
13 | paths:
14 | - '.github/workflows/run-nitrogen.yml'
15 | - 'src/specs/**'
16 | - '**/nitro.json'
17 | - '**/package.json'
18 |
19 | jobs:
20 | lint:
21 | name: Run Nitrogen
22 | runs-on: ubuntu-latest
23 | steps:
24 | - uses: actions/checkout@v4
25 | - uses: oven-sh/setup-bun@v2
26 |
27 | - name: Install npm dependencies (bun)
28 | run: bun install
29 |
30 | - name: Build the typescript types
31 | run: bun run build
32 |
33 | - name: Run nitrogen for NitroImage
34 | run: bun image specs
35 |
36 | - name: Run nitrogen for NitroWebImage
37 | run: bun web-image specs
38 |
39 | - name: Verify no files have changed after nitrogen
40 | run: git diff --exit-code HEAD -- . ':(exclude)bun.lockb'
41 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/Color.kt:
--------------------------------------------------------------------------------
1 | ///
2 | /// Color.kt
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | package com.margelo.nitro.image
9 |
10 | import androidx.annotation.Keep
11 | import com.facebook.proguard.annotations.DoNotStrip
12 |
13 |
14 | /**
15 | * Represents the JavaScript object/struct "Color".
16 | */
17 | @DoNotStrip
18 | @Keep
19 | data class Color(
20 | @DoNotStrip
21 | @Keep
22 | val r: Double,
23 | @DoNotStrip
24 | @Keep
25 | val g: Double,
26 | @DoNotStrip
27 | @Keep
28 | val b: Double,
29 | @DoNotStrip
30 | @Keep
31 | val a: Double?
32 | ) {
33 | private companion object {
34 | /**
35 | * Constructor called from C++
36 | */
37 | @DoNotStrip
38 | @Keep
39 | @Suppress("unused")
40 | @JvmStatic
41 | private fun fromCpp(r: Double, g: Double, b: Double, a: Double?): Color {
42 | return Color(r, g, b, a)
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/nitrogen/generated/ios/NitroWebImageAutolinking.mm:
--------------------------------------------------------------------------------
1 | ///
2 | /// NitroWebImageAutolinking.mm
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #import
9 | #import
10 | #import "NitroWebImage-Swift-Cxx-Umbrella.hpp"
11 | #import
12 |
13 | #include "HybridWebImageFactorySpecSwift.hpp"
14 |
15 | @interface NitroWebImageAutolinking : NSObject
16 | @end
17 |
18 | @implementation NitroWebImageAutolinking
19 |
20 | + (void) load {
21 | using namespace margelo::nitro;
22 | using namespace margelo::nitro::web::image;
23 |
24 | HybridObjectRegistry::registerHybridObjectConstructor(
25 | "WebImageFactory",
26 | []() -> std::shared_ptr {
27 | std::shared_ptr hybridObject = NitroWebImage::NitroWebImageAutolinking::createWebImageFactory();
28 | return hybridObject;
29 | }
30 | );
31 | }
32 |
33 | @end
34 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/ArrayBuffer+toByteArray.kt:
--------------------------------------------------------------------------------
1 | package com.margelo.nitro.image
2 |
3 | import com.margelo.nitro.core.ArrayBuffer
4 | import java.nio.ByteBuffer
5 |
6 | fun ArrayBuffer.toByteArray(): ByteArray {
7 | val buffer = this.getBuffer(false)
8 | if (buffer.hasArray()) {
9 | // It's a CPU-backed array - we can return this directly
10 | val array = buffer.array()
11 | if (array.size == this.size) {
12 | // The CPU-backed array is exactly the view we have in our ArrayBuffer.
13 | // Return as is!
14 | return array
15 | }
16 | // we had a CPU-backed array, but it's size differs from our ArrayBuffer size.
17 | // This might be because the ArrayBuffer has a smaller view of the data, so we need
18 | // to resort back to a good ol' copy.
19 | }
20 | // It's not a CPU-backed array (e.g. HardwareBuffer) - we need to copy to the CPU
21 | val copy = ByteBuffer.allocate(buffer.capacity())
22 | copy.put(buffer)
23 | return copy.array()
24 | }
25 |
--------------------------------------------------------------------------------
/example/src/EmptyTab.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { StyleSheet, TextInput, View } from "react-native";
3 | import { NitroImage } from "react-native-nitro-image";
4 |
5 | export function EmptyTab() {
6 | const [value, setValue] = useState('https://picsum.photos/seed/123/600')
7 |
8 | return (
9 |
10 |
11 |
12 |
13 | );
14 | }
15 |
16 | const styles = StyleSheet.create({
17 | container: {
18 | flex: 1,
19 | justifyContent: "center",
20 | alignItems: "center",
21 | },
22 | text: {
23 | fontSize: 18,
24 | fontWeight: "500",
25 | },
26 | textInput: {
27 | borderWidth: 1,
28 | borderRadius: 5,
29 | paddingHorizontal: 10,
30 | },
31 | image: {
32 | width: 350,
33 | height: 350,
34 | backgroundColor: 'grey',
35 | marginTop: 15
36 | }
37 | });
38 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/src/specs/ImageLoader.nitro.ts:
--------------------------------------------------------------------------------
1 | import type { HybridObject } from "react-native-nitro-modules";
2 | import type { Image } from "./Image.nitro";
3 | import type { NitroImageView } from "./ImageView.nitro";
4 |
5 | export interface ImageLoader
6 | extends HybridObject<{ ios: "swift"; android: "kotlin" }> {
7 | /**
8 | * Imperatively loads this `Image` using the underlying load implementation.
9 | */
10 | loadImage(): Promise;
11 |
12 | /**
13 | * Called by an Image View when it becomes visible.
14 | * The native implementation must set the `image` on the `imageView` by downcasting it to a concrete type.
15 | * @param forView The native view type (e.g. `HybridImageView`)
16 | */
17 | requestImage(forView: NitroImageView): void;
18 | /**
19 | * Called by an Image View when it becomes invisible.
20 | * The native implementation can remove the `image` on the `imageView` if needed to save memory.
21 | * @param forView The native view type (e.g. `HybridImageView`)
22 | */
23 | dropImage(forView: NitroImageView): void;
24 | }
25 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/FastByteArrayOutputStream.kt:
--------------------------------------------------------------------------------
1 | package com.margelo.nitro.image
2 |
3 | import java.io.OutputStream
4 | import java.nio.ByteBuffer
5 |
6 | class FastByteArrayOutputStream(initialSize: Int = 64 * 1024) : OutputStream() {
7 | var bytes = ByteArray(initialSize)
8 | private set
9 | var count = 0
10 | private set
11 |
12 | override fun write(b: Int) {
13 | val i = count + 1
14 | ensureCapacity(i)
15 | bytes[count] = b.toByte()
16 | count = i
17 | }
18 |
19 | override fun write(b: ByteArray, off: Int, len: Int) {
20 | val i = count + len
21 | ensureCapacity(i)
22 | System.arraycopy(b, off, bytes, count, len)
23 | count = i
24 | }
25 |
26 | private fun ensureCapacity(min: Int) {
27 | if (min <= bytes.size) return
28 | var newCap = bytes.size.coerceAtLeast(1)
29 | while (newCap < min) newCap = newCap shl 1
30 | bytes = bytes.copyOf(newCap)
31 | }
32 |
33 | fun toByteBuffer(): ByteBuffer = ByteBuffer.wrap(bytes, 0, count)
34 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Marc Rousavy
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/shared/c++/HybridImageUtilsSpec.cpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridImageUtilsSpec.cpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #include "HybridImageUtilsSpec.hpp"
9 |
10 | namespace margelo::nitro::image {
11 |
12 | void HybridImageUtilsSpec::loadHybridMethods() {
13 | // load base methods/properties
14 | HybridObject::loadHybridMethods();
15 | // load custom methods/properties
16 | registerHybrids(this, [](Prototype& prototype) {
17 | prototype.registerHybridGetter("supportsHeicLoading", &HybridImageUtilsSpec::getSupportsHeicLoading);
18 | prototype.registerHybridGetter("supportsHeicWriting", &HybridImageUtilsSpec::getSupportsHeicWriting);
19 | prototype.registerHybridMethod("thumbHashToBase64String", &HybridImageUtilsSpec::thumbHashToBase64String);
20 | prototype.registerHybridMethod("thumbhashFromBase64String", &HybridImageUtilsSpec::thumbhashFromBase64String);
21 | });
22 | }
23 |
24 | } // namespace margelo::nitro::image
25 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/NitroWebImage.podspec:
--------------------------------------------------------------------------------
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 = "NitroWebImage"
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, :visionos => 1.0 }
14 | s.source = { :git => "https://github.com/mrousavy/nitro.git", :tag => "#{s.version}" }
15 |
16 | s.source_files = [
17 | # Implementation (Swift)
18 | "ios/**/*.{swift}",
19 | # Autolinking/Registration (Objective-C++)
20 | "ios/**/*.{m,mm}",
21 | # Implementation (C++ objects)
22 | "cpp/**/*.{hpp,cpp}",
23 | ]
24 |
25 | load 'nitrogen/generated/ios/NitroWebImage+autolinking.rb'
26 | add_nitrogen_files(s)
27 |
28 | s.dependency 'React-jsi'
29 | s.dependency 'React-callinvoker'
30 | s.dependency 'SDWebImage'
31 | s.dependency 'NitroImage'
32 | install_modules_dependencies(s)
33 | end
34 |
--------------------------------------------------------------------------------
/example/ios/NitroImageExample/PrivacyInfo.xcprivacy:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSPrivacyAccessedAPITypes
6 |
7 |
8 | NSPrivacyAccessedAPIType
9 | NSPrivacyAccessedAPICategoryFileTimestamp
10 | NSPrivacyAccessedAPITypeReasons
11 |
12 | C617.1
13 |
14 |
15 |
16 | NSPrivacyAccessedAPIType
17 | NSPrivacyAccessedAPICategoryUserDefaults
18 | NSPrivacyAccessedAPITypeReasons
19 |
20 | CA92.1
21 |
22 |
23 |
24 | NSPrivacyAccessedAPIType
25 | NSPrivacyAccessedAPICategorySystemBootTime
26 | NSPrivacyAccessedAPITypeReasons
27 |
28 | 35F9.1
29 |
30 |
31 |
32 | NSPrivacyCollectedDataTypes
33 |
34 | NSPrivacyTracking
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/nitrogen/generated/ios/NitroWebImageAutolinking.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// NitroWebImageAutolinking.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | public final class NitroWebImageAutolinking {
9 | public typealias bridge = margelo.nitro.web.image.bridge.swift
10 |
11 | /**
12 | * Creates an instance of a Swift class that implements `HybridWebImageFactorySpec`,
13 | * and wraps it in a Swift class that can directly interop with C++ (`HybridWebImageFactorySpec_cxx`)
14 | *
15 | * This is generated by Nitrogen and will initialize the class specified
16 | * in the `"autolinking"` property of `nitro.json` (in this case, `HybridWebImageFactory`).
17 | */
18 | public static func createWebImageFactory() -> bridge.std__shared_ptr_HybridWebImageFactorySpec_ {
19 | let hybridObject = HybridWebImageFactory()
20 | return { () -> bridge.std__shared_ptr_HybridWebImageFactorySpec_ in
21 | let __cxxWrapped = hybridObject.getCxxWrapper()
22 | return __cxxWrapped.getCxxPart()
23 | }()
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/swift/ImageFormat.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// ImageFormat.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | /**
9 | * Represents the JS union `ImageFormat`, backed by a C++ enum.
10 | */
11 | public typealias ImageFormat = margelo.nitro.image.ImageFormat
12 |
13 | public extension ImageFormat {
14 | /**
15 | * Get a ImageFormat for the given String value, or
16 | * return `nil` if the given value was invalid/unknown.
17 | */
18 | init?(fromString string: String) {
19 | switch string {
20 | case "jpg":
21 | self = .jpg
22 | case "png":
23 | self = .png
24 | case "heic":
25 | self = .heic
26 | default:
27 | return nil
28 | }
29 | }
30 |
31 | /**
32 | * Get the String value this ImageFormat represents.
33 | */
34 | var stringValue: String {
35 | switch self {
36 | case .jpg:
37 | return "jpg"
38 | case .png:
39 | return "png"
40 | case .heic:
41 | return "heic"
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/nitroimageexample/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.nitroimageexample
2 |
3 | import com.facebook.react.ReactActivity
4 | import com.facebook.react.ReactActivityDelegate
5 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
6 | import com.facebook.react.defaults.DefaultReactActivityDelegate
7 | import android.os.Bundle
8 |
9 | class MainActivity : ReactActivity() {
10 |
11 | /**
12 | * Returns the name of the main component registered from JavaScript. This is used to schedule
13 | * rendering of the component.
14 | */
15 | override fun getMainComponentName(): String = "NitroImageExample"
16 |
17 | /**
18 | * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
19 | * which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
20 | */
21 | override fun createReactActivityDelegate(): ReactActivityDelegate =
22 | DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)
23 |
24 | /**
25 | * For react-navigation
26 | */
27 | override fun onCreate(savedInstanceState: Bundle?) {
28 | super.onCreate(null)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/example/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Resolve react_native_pods.rb with node to allow for hoisting
2 | require Pod::Executable.execute_command('node', ['-p',
3 | 'require.resolve(
4 | "react-native/scripts/react_native_pods.rb",
5 | {paths: [process.argv[1]]},
6 | )', __dir__]).strip
7 |
8 | platform :ios, min_ios_version_supported
9 | prepare_react_native_project!
10 |
11 | linkage = ENV['USE_FRAMEWORKS']
12 | if linkage != nil
13 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
14 | use_frameworks! :linkage => linkage.to_sym
15 | end
16 |
17 | target 'NitroImageExample' do
18 | config = use_native_modules!
19 | pod 'SDWebImage', :modular_headers => true
20 |
21 | use_react_native!(
22 | :path => config[:reactNativePath],
23 | # An absolute path to your application root.
24 | :app_path => "#{Pod::Config.instance.installation_root}/.."
25 | )
26 |
27 | post_install do |installer|
28 | # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
29 | react_native_post_install(
30 | installer,
31 | config[:reactNativePath],
32 | :mac_catalyst_enabled => false,
33 | # :ccache_enabled => true
34 | )
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/src/OptionalWebLoader.ts:
--------------------------------------------------------------------------------
1 | // biome-ignore lint/suspicious/noTsIgnore: Type Compilation is a race-condition
2 | // @ts-ignore
3 | type WebImagesType = typeof import("react-native-nitro-web-image")["WebImages"];
4 |
5 | let createWebImageLoader: WebImagesType["createWebImageLoader"] = () => {
6 | throw new Error(
7 | `Web Images are not supported because react-native-nitro-web-image is not installed!`,
8 | );
9 | };
10 | let loadFromURLAsync: WebImagesType["loadFromURLAsync"] = () => {
11 | throw new Error(
12 | `Web Images are not supported because react-native-nitro-web-image is not installed!`,
13 | );
14 | };
15 |
16 | export type OptionalAsyncOptions = Parameters<
17 | WebImagesType["loadFromURLAsync"]
18 | >[1];
19 |
20 | try {
21 | const WebImages = require("react-native-nitro-web-image")
22 | .WebImages as WebImagesType;
23 | createWebImageLoader = WebImages.createWebImageLoader.bind(WebImages);
24 | loadFromURLAsync = WebImages.loadFromURLAsync.bind(WebImages);
25 | } catch {
26 | // react-native-nitro-web-image is not installed, so only local images are supported.
27 | }
28 |
29 | export const OptionalWebImages = { createWebImageLoader, loadFromURLAsync };
30 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/ios/SDWebImageManager+loadImage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SDWebImageManager+loadImage.swift
3 | // NitroWebImage
4 | //
5 | // Created by Marc Rousavy on 30.06.25.
6 | //
7 |
8 | import Foundation
9 | import SDWebImage
10 | import NitroModules
11 |
12 | extension SDWebImageManager {
13 | func loadImage(with url: URL, options: AsyncImageLoadOptions?) async throws -> UIImage {
14 | let webImageOptions = options?.toSDWebImageOptions() ?? []
15 | let webImageContext = options?.toSDWebImageContext() ?? [:]
16 |
17 | return try await withUnsafeThrowingContinuation { continuation in
18 | self.loadImage(with: url, options: webImageOptions, context: webImageContext) { current, total, url in
19 | print("\(url): Loaded \(current)/\(total) bytes")
20 | } completed: { image, data, error, cacheType, finished, url in
21 | if let image {
22 | continuation.resume(returning: image)
23 | } else {
24 | if let error {
25 | continuation.resume(throwing: error)
26 | } else {
27 | continuation.resume(throwing: RuntimeError.error(withMessage: "No Image or error was returned!"))
28 | }
29 | }
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/RawPixelData.kt:
--------------------------------------------------------------------------------
1 | ///
2 | /// RawPixelData.kt
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | package com.margelo.nitro.image
9 |
10 | import androidx.annotation.Keep
11 | import com.facebook.proguard.annotations.DoNotStrip
12 | import com.margelo.nitro.core.ArrayBuffer
13 |
14 | /**
15 | * Represents the JavaScript object/struct "RawPixelData".
16 | */
17 | @DoNotStrip
18 | @Keep
19 | data class RawPixelData(
20 | @DoNotStrip
21 | @Keep
22 | val buffer: ArrayBuffer,
23 | @DoNotStrip
24 | @Keep
25 | val width: Double,
26 | @DoNotStrip
27 | @Keep
28 | val height: Double,
29 | @DoNotStrip
30 | @Keep
31 | val pixelFormat: PixelFormat
32 | ) {
33 | private companion object {
34 | /**
35 | * Constructor called from C++
36 | */
37 | @DoNotStrip
38 | @Keep
39 | @Suppress("unused")
40 | @JvmStatic
41 | private fun fromCpp(buffer: ArrayBuffer, width: Double, height: Double, pixelFormat: PixelFormat): RawPixelData {
42 | return RawPixelData(buffer, width, height, pixelFormat)
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/shared/c++/HybridNitroImageViewSpec.cpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridNitroImageViewSpec.cpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #include "HybridNitroImageViewSpec.hpp"
9 |
10 | namespace margelo::nitro::image {
11 |
12 | void HybridNitroImageViewSpec::loadHybridMethods() {
13 | // load base methods/properties
14 | HybridObject::loadHybridMethods();
15 | // load custom methods/properties
16 | registerHybrids(this, [](Prototype& prototype) {
17 | prototype.registerHybridGetter("image", &HybridNitroImageViewSpec::getImage);
18 | prototype.registerHybridSetter("image", &HybridNitroImageViewSpec::setImage);
19 | prototype.registerHybridGetter("resizeMode", &HybridNitroImageViewSpec::getResizeMode);
20 | prototype.registerHybridSetter("resizeMode", &HybridNitroImageViewSpec::setResizeMode);
21 | prototype.registerHybridGetter("recyclingKey", &HybridNitroImageViewSpec::getRecyclingKey);
22 | prototype.registerHybridSetter("recyclingKey", &HybridNitroImageViewSpec::setRecyclingKey);
23 | });
24 | }
25 |
26 | } // namespace margelo::nitro::image
27 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/nitrogen/generated/ios/swift/AsyncImagePriority.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// AsyncImagePriority.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | /**
9 | * Represents the JS union `AsyncImagePriority`, backed by a C++ enum.
10 | */
11 | public typealias AsyncImagePriority = margelo.nitro.web.image.AsyncImagePriority
12 |
13 | public extension AsyncImagePriority {
14 | /**
15 | * Get a AsyncImagePriority for the given String value, or
16 | * return `nil` if the given value was invalid/unknown.
17 | */
18 | init?(fromString string: String) {
19 | switch string {
20 | case "low":
21 | self = .low
22 | case "default":
23 | self = .default
24 | case "high":
25 | self = .high
26 | default:
27 | return nil
28 | }
29 | }
30 |
31 | /**
32 | * Get the String value this AsyncImagePriority represents.
33 | */
34 | var stringValue: String {
35 | switch self {
36 | case .low:
37 | return "low"
38 | case .default:
39 | return "default"
40 | case .high:
41 | return "high"
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/NitroImageOnLoad.kt:
--------------------------------------------------------------------------------
1 | ///
2 | /// NitroImageOnLoad.kt
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | package com.margelo.nitro.image
9 |
10 | import android.util.Log
11 |
12 | internal class NitroImageOnLoad {
13 | companion object {
14 | private const val TAG = "NitroImageOnLoad"
15 | private var didLoad = false
16 | /**
17 | * Initializes the native part of "NitroImage".
18 | * This method is idempotent and can be called more than once.
19 | */
20 | @JvmStatic
21 | fun initializeNative() {
22 | if (didLoad) return
23 | try {
24 | Log.i(TAG, "Loading NitroImage C++ library...")
25 | System.loadLibrary("NitroImage")
26 | Log.i(TAG, "Successfully loaded NitroImage C++ library!")
27 | didLoad = true
28 | } catch (e: Error) {
29 | Log.e(TAG, "Failed to load NitroImage C++ library! Is it properly installed and linked? " +
30 | "Is the name correct? (see `CMakeLists.txt`, at `add_library(...)`)", e)
31 | throw e
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/EncodedImageData.kt:
--------------------------------------------------------------------------------
1 | ///
2 | /// EncodedImageData.kt
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | package com.margelo.nitro.image
9 |
10 | import androidx.annotation.Keep
11 | import com.facebook.proguard.annotations.DoNotStrip
12 | import com.margelo.nitro.core.ArrayBuffer
13 |
14 | /**
15 | * Represents the JavaScript object/struct "EncodedImageData".
16 | */
17 | @DoNotStrip
18 | @Keep
19 | data class EncodedImageData(
20 | @DoNotStrip
21 | @Keep
22 | val buffer: ArrayBuffer,
23 | @DoNotStrip
24 | @Keep
25 | val width: Double,
26 | @DoNotStrip
27 | @Keep
28 | val height: Double,
29 | @DoNotStrip
30 | @Keep
31 | val imageFormat: ImageFormat
32 | ) {
33 | private companion object {
34 | /**
35 | * Constructor called from C++
36 | */
37 | @DoNotStrip
38 | @Keep
39 | @Suppress("unused")
40 | @JvmStatic
41 | private fun fromCpp(buffer: ArrayBuffer, width: Double, height: Double, imageFormat: ImageFormat): EncodedImageData {
42 | return EncodedImageData(buffer, width, height, imageFormat)
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/Bitmap+saveToFile.kt:
--------------------------------------------------------------------------------
1 | package com.margelo.nitro.image
2 |
3 | import android.graphics.Bitmap
4 | import java.io.File
5 | import java.io.FileOutputStream
6 |
7 | fun ImageFormat.toBitmapFormat(): Bitmap.CompressFormat {
8 | return when (this) {
9 | ImageFormat.JPG -> Bitmap.CompressFormat.JPEG
10 | ImageFormat.PNG -> Bitmap.CompressFormat.PNG
11 | ImageFormat.HEIC -> {
12 | throw Error("Saving Images as HEIC is not yet supported on Android!")
13 | }
14 | }
15 | }
16 |
17 | fun Bitmap.saveToFile(path: String, format: ImageFormat, quality: Int) {
18 | if (quality < 0 || quality > 100) {
19 | throw Error("Image quality has to be between 0 and 100! (Received: $quality)")
20 | }
21 | // 1. Make sure all parent directories exist
22 | File(path).parentFile?.mkdirs()
23 | // 2. Create a file output stream
24 | FileOutputStream(path).use { out ->
25 | val bitmapFormat = format.toBitmapFormat()
26 | val successful = this.compress(bitmapFormat, quality, out)
27 | if (!successful) {
28 | throw Error("Failed to compress ${width}x${height} Image to file (\"$path\")! (Format: $format, Quality: $quality)")
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/nitrogen/generated/android/kotlin/com/margelo/nitro/web/image/NitroWebImageOnLoad.kt:
--------------------------------------------------------------------------------
1 | ///
2 | /// NitroWebImageOnLoad.kt
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | package com.margelo.nitro.web.image
9 |
10 | import android.util.Log
11 |
12 | internal class NitroWebImageOnLoad {
13 | companion object {
14 | private const val TAG = "NitroWebImageOnLoad"
15 | private var didLoad = false
16 | /**
17 | * Initializes the native part of "NitroWebImage".
18 | * This method is idempotent and can be called more than once.
19 | */
20 | @JvmStatic
21 | fun initializeNative() {
22 | if (didLoad) return
23 | try {
24 | Log.i(TAG, "Loading NitroWebImage C++ library...")
25 | System.loadLibrary("NitroWebImage")
26 | Log.i(TAG, "Successfully loaded NitroWebImage C++ library!")
27 | didLoad = true
28 | } catch (e: Error) {
29 | Log.e(TAG, "Failed to load NitroWebImage C++ library! Is it properly installed and linked? " +
30 | "Is the name correct? (see `CMakeLists.txt`, at `add_library(...)`)", e)
31 | throw e
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/ios/HybridImageUtils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HybridImageFactory.swift
3 | // react-native-nitro-image
4 | //
5 | // Created by Marc Rousavy on 10.06.25.
6 | //
7 |
8 | import Foundation
9 | import NitroModules
10 | import UniformTypeIdentifiers
11 |
12 | class HybridImageUtils: HybridImageUtilsSpec {
13 | var supportsHeicLoading: Bool {
14 | // Check if the type is supported by the OS
15 | let types = CGImageDestinationCopyTypeIdentifiers() as! [String]
16 | return types.contains(UTType.heic.identifier)
17 | }
18 | var supportsHeicWriting: Bool {
19 | // HEIC .heicData() is only available on iOS 17
20 | if #available(iOS 17.0, *) {
21 | return true
22 | } else {
23 | return false
24 | }
25 | }
26 |
27 | func thumbHashToBase64String(thumbhash: ArrayBuffer) throws -> String {
28 | let data = thumbhash.toData(copyIfNeeded: false)
29 | return data.base64EncodedString()
30 | }
31 |
32 | func thumbhashFromBase64String(thumbhashBase64: String) throws -> ArrayBuffer {
33 | guard let data = Data(base64Encoded: thumbhashBase64) else {
34 | throw RuntimeError.error(withMessage: "The given ThumbHash (\(thumbhashBase64)) is not a valid Base64 encoded Hash!")
35 | }
36 | return try ArrayBuffer.copy(data: data)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/swift/ResizeMode.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// ResizeMode.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | /**
9 | * Represents the JS union `ResizeMode`, backed by a C++ enum.
10 | */
11 | public typealias ResizeMode = margelo.nitro.image.ResizeMode
12 |
13 | public extension ResizeMode {
14 | /**
15 | * Get a ResizeMode for the given String value, or
16 | * return `nil` if the given value was invalid/unknown.
17 | */
18 | init?(fromString string: String) {
19 | switch string {
20 | case "cover":
21 | self = .cover
22 | case "contain":
23 | self = .contain
24 | case "center":
25 | self = .center
26 | case "stretch":
27 | self = .stretch
28 | default:
29 | return nil
30 | }
31 | }
32 |
33 | /**
34 | * Get the String value this ResizeMode represents.
35 | */
36 | var stringValue: String {
37 | switch self {
38 | case .cover:
39 | return "cover"
40 | case .contain:
41 | return "contain"
42 | case .center:
43 | return "center"
44 | case .stretch:
45 | return "stretch"
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/android/src/main/java/com/margelo/nitro/web/image/HybridWebImageLoader.kt:
--------------------------------------------------------------------------------
1 | package com.margelo.nitro.web.image
2 |
3 | import android.content.Context
4 | import android.widget.ImageView
5 | import coil3.ImageLoader
6 | import coil3.load
7 | import com.margelo.nitro.core.Promise
8 | import com.margelo.nitro.image.HybridImageSpec
9 | import com.margelo.nitro.image.HybridImageLoaderSpec
10 | import com.margelo.nitro.image.HybridNitroImageViewSpec
11 |
12 | class HybridWebImageLoader(private val imageLoader: ImageLoader,
13 | private val url: String,
14 | private val options: AsyncImageLoadOptions?,
15 | private val context: Context) : HybridImageLoaderSpec() {
16 | override fun loadImage(): Promise {
17 | return imageLoader.loadImageAsync(url, options, context)
18 | }
19 |
20 | override fun requestImage(forView: HybridNitroImageViewSpec) {
21 | val imageView = forView.view as? ImageView ?: return
22 |
23 | imageView.load(url, imageLoader) {
24 | this.applyOptions(options)
25 | }
26 | }
27 |
28 | override fun dropImage(forView: HybridNitroImageViewSpec) {
29 | // Coil automatically handles recycling here - I _think_.
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/src/NitroImage.tsx:
--------------------------------------------------------------------------------
1 | // biome-ignore lint/correctness/noUnusedImports: Needed for JSX runtime
2 | import React from "react";
3 | import type { HostComponent } from "react-native";
4 | import type { AsyncImageSource } from "./AsyncImageSource";
5 | import { NativeNitroImage } from "./NativeNitroImage";
6 | import { useImageLoader } from "./useImageLoader";
7 |
8 | type ReactProps = T extends HostComponent ? P : never;
9 | type NativeImageProps = ReactProps;
10 |
11 | export interface NitroImageProps extends Omit {
12 | image: AsyncImageSource;
13 | }
14 |
15 | /**
16 | * The renderable asynchronous `` view.
17 | *
18 | * This is a JS-based abstraction on-top of the
19 | * {@linkcode NativeNitroImage | } view to simplify
20 | * image loading.
21 | * @example
22 | * ```tsx
23 | * function App() {
24 | * return (
25 | *
29 | * )
30 | * }
31 | * ```
32 | */
33 | export function NitroImage({ image, ...props }: NitroImageProps) {
34 | const actualImage = useImageLoader(image);
35 | return ;
36 | }
37 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/shared/c++/HybridImageLoaderFactorySpec.cpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridImageLoaderFactorySpec.cpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #include "HybridImageLoaderFactorySpec.hpp"
9 |
10 | namespace margelo::nitro::image {
11 |
12 | void HybridImageLoaderFactorySpec::loadHybridMethods() {
13 | // load base methods/properties
14 | HybridObject::loadHybridMethods();
15 | // load custom methods/properties
16 | registerHybrids(this, [](Prototype& prototype) {
17 | prototype.registerHybridMethod("createFileImageLoader", &HybridImageLoaderFactorySpec::createFileImageLoader);
18 | prototype.registerHybridMethod("createResourceImageLoader", &HybridImageLoaderFactorySpec::createResourceImageLoader);
19 | prototype.registerHybridMethod("createSymbolImageLoader", &HybridImageLoaderFactorySpec::createSymbolImageLoader);
20 | prototype.registerHybridMethod("createRawPixelDataImageLoader", &HybridImageLoaderFactorySpec::createRawPixelDataImageLoader);
21 | prototype.registerHybridMethod("createEncodedImageDataImageLoader", &HybridImageLoaderFactorySpec::createEncodedImageDataImageLoader);
22 | });
23 | }
24 |
25 | } // namespace margelo::nitro::image
26 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/HybridImageLoaderFactory.kt:
--------------------------------------------------------------------------------
1 | package com.margelo.nitro.image
2 |
3 | import androidx.annotation.Keep
4 | import com.facebook.proguard.annotations.DoNotStrip
5 | import com.margelo.nitro.core.ArrayBuffer
6 | import com.margelo.nitro.core.Promise
7 |
8 | @Keep
9 | @DoNotStrip
10 | class HybridImageLoaderFactory: HybridImageLoaderFactorySpec() {
11 | private val factory = HybridImageFactory()
12 |
13 | override fun createFileImageLoader(filePath: String): HybridImageLoaderSpec {
14 | return HybridImageLoader { factory.loadFromFileAsync(filePath) }
15 | }
16 |
17 | override fun createResourceImageLoader(name: String): HybridImageLoaderSpec {
18 | return HybridImageLoader { factory.loadFromResourcesAsync(name) }
19 | }
20 |
21 | override fun createSymbolImageLoader(symbolName: String): HybridImageLoaderSpec {
22 | return HybridImageLoader { Promise.resolved(factory.loadFromSymbol(symbolName)) }
23 | }
24 |
25 | override fun createRawPixelDataImageLoader(data: RawPixelData): HybridImageLoaderSpec {
26 | return HybridImageLoader { factory.loadFromRawPixelDataAsync(data, false) }
27 | }
28 |
29 | override fun createEncodedImageDataImageLoader(data: EncodedImageData): HybridImageLoaderSpec {
30 | return HybridImageLoader { factory.loadFromEncodedImageDataAsync(data) }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | **/.xcode.env.local
24 |
25 | # Android/IntelliJ
26 | #
27 | build/
28 | .idea
29 | .gradle
30 | local.properties
31 | *.iml
32 | *.hprof
33 | .cxx/
34 | *.keystore
35 | !debug.keystore
36 | .kotlin/
37 |
38 | # node.js
39 | #
40 | node_modules/
41 | npm-debug.log
42 | yarn-error.log
43 |
44 | # fastlane
45 | #
46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
47 | # screenshots whenever they are needed.
48 | # For more information about the recommended setup visit:
49 | # https://docs.fastlane.tools/best-practices/source-control/
50 |
51 | **/fastlane/report.xml
52 | **/fastlane/Preview.html
53 | **/fastlane/screenshots
54 | **/fastlane/test_output
55 |
56 | # Bundle artifact
57 | *.jsbundle
58 |
59 | # Ruby / CocoaPods
60 | **/Pods/
61 | /vendor/bundle/
62 |
63 | # Temporary files created by Metro to check the health of the file watcher
64 | .metro-health-check*
65 |
66 | # testing
67 | /coverage
68 |
69 | # Yarn
70 | .yarn/*
71 | !.yarn/patches
72 | !.yarn/plugins
73 | !.yarn/releases
74 | !.yarn/sdks
75 | !.yarn/versions
76 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/android/src/main/java/com/margelo/nitro/web/image/NitroWebImagePackage.java:
--------------------------------------------------------------------------------
1 | package com.margelo.nitro.web.image;
2 |
3 | import android.util.Log;
4 |
5 | import androidx.annotation.NonNull;
6 | import androidx.annotation.Nullable;
7 |
8 | import com.facebook.react.bridge.NativeModule;
9 | import com.facebook.react.bridge.ReactApplicationContext;
10 | import com.facebook.react.module.model.ReactModuleInfoProvider;
11 | import com.facebook.react.TurboReactPackage;
12 | import com.facebook.react.uimanager.ViewManager;
13 | import com.margelo.nitro.core.HybridObject;
14 |
15 | import java.util.ArrayList;
16 | import java.util.HashMap;
17 | import java.util.List;
18 | import java.util.function.Supplier;
19 |
20 | public class NitroWebImagePackage extends TurboReactPackage {
21 | @Nullable
22 | @Override
23 | public NativeModule getModule(String name, ReactApplicationContext reactContext) {
24 | return null;
25 | }
26 |
27 | @Override
28 | public ReactModuleInfoProvider getReactModuleInfoProvider() {
29 | return () -> {
30 | return new HashMap<>();
31 | };
32 | }
33 |
34 |
35 | @Override
36 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) {
37 | List viewManagers = new ArrayList<>();
38 | return viewManagers;
39 | }
40 |
41 | static {
42 | NitroWebImageOnLoad.initializeNative();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/src/AsyncImageSource.ts:
--------------------------------------------------------------------------------
1 | import type { HybridObject } from "react-native-nitro-modules";
2 | import type { OptionalAsyncOptions } from "./OptionalWebLoader";
3 | import type {
4 | EncodedImageData,
5 | Image,
6 | RawPixelData,
7 | } from "./specs/Image.nitro";
8 | import type { ImageLoader } from "./specs/ImageLoader.nitro";
9 |
10 | export type RequireType = number;
11 | export type AsyncImageSource =
12 | | Image
13 | | ImageLoader
14 | | { filePath: string }
15 | | { rawPixelData: RawPixelData }
16 | | { encodedImageData: EncodedImageData }
17 | | { resource: string }
18 | | { symbolName: string }
19 | | { url: string; options?: OptionalAsyncOptions }
20 | | RequireType;
21 |
22 | // @ts-expect-error i know what I'm doing
23 | export function isHybridObject(obj: T): obj is HybridObject {
24 | // @ts-expect-error
25 | return typeof obj === "object" && obj != null && obj.dispose != null;
26 | }
27 | // @ts-expect-error i know what I'm doing
28 | export function isHybridImage(obj: T): obj is Image {
29 | // @ts-expect-error
30 | return typeof obj === "object" && obj != null && obj.toRawPixelData != null;
31 | }
32 | export function isHybridImageLoader(
33 | obj: T,
34 | // @ts-expect-error i know what I'm doing
35 | ): obj is ImageLoader {
36 | // @ts-expect-error
37 | return typeof obj === "object" && obj != null && obj.loadImage != null;
38 | }
39 |
--------------------------------------------------------------------------------
/example/ios/NitroImageExample/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import React
3 | import React_RCTAppDelegate
4 | import ReactAppDependencyProvider
5 |
6 | @main
7 | class AppDelegate: UIResponder, UIApplicationDelegate {
8 | var window: UIWindow?
9 |
10 | var reactNativeDelegate: ReactNativeDelegate?
11 | var reactNativeFactory: RCTReactNativeFactory?
12 |
13 | func application(
14 | _ application: UIApplication,
15 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
16 | ) -> Bool {
17 | let delegate = ReactNativeDelegate()
18 | let factory = RCTReactNativeFactory(delegate: delegate)
19 | delegate.dependencyProvider = RCTAppDependencyProvider()
20 |
21 | reactNativeDelegate = delegate
22 | reactNativeFactory = factory
23 |
24 | window = UIWindow(frame: UIScreen.main.bounds)
25 |
26 | factory.startReactNative(
27 | withModuleName: "NitroImageExample",
28 | in: window,
29 | launchOptions: launchOptions
30 | )
31 |
32 | return true
33 | }
34 | }
35 |
36 | class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate {
37 | override func sourceURL(for bridge: RCTBridge) -> URL? {
38 | self.bundleURL()
39 | }
40 |
41 | override func bundleURL() -> URL? {
42 | #if DEBUG
43 | RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
44 | #else
45 | Bundle.main.url(forResource: "main", withExtension: "jsbundle")
46 | #endif
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/swift/Func_void.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// Func_void.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | import NitroModules
9 |
10 |
11 | /**
12 | * Wraps a Swift `() -> Void` as a class.
13 | * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`.
14 | */
15 | public final class Func_void {
16 | public typealias bridge = margelo.nitro.image.bridge.swift
17 |
18 | private let closure: () -> Void
19 |
20 | public init(_ closure: @escaping () -> Void) {
21 | self.closure = closure
22 | }
23 |
24 | @inline(__always)
25 | public func call() -> Void {
26 | self.closure()
27 | }
28 |
29 | /**
30 | * Casts this instance to a retained unsafe raw pointer.
31 | * This acquires one additional strong reference on the object!
32 | */
33 | @inline(__always)
34 | public func toUnsafe() -> UnsafeMutableRawPointer {
35 | return Unmanaged.passRetained(self).toOpaque()
36 | }
37 |
38 | /**
39 | * Casts an unsafe pointer to a `Func_void`.
40 | * The pointer has to be a retained opaque `Unmanaged`.
41 | * This removes one strong reference from the object!
42 | */
43 | @inline(__always)
44 | public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void {
45 | return Unmanaged.fromOpaque(pointer).takeRetainedValue()
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/HybridImageUtils.kt:
--------------------------------------------------------------------------------
1 | package com.margelo.nitro.image
2 |
3 | import android.os.Build
4 | import androidx.annotation.Keep
5 | import com.facebook.common.internal.DoNotStrip
6 | import com.margelo.nitro.core.ArrayBuffer
7 | import java.nio.ByteBuffer
8 | import kotlin.io.encoding.Base64
9 | import kotlin.io.encoding.ExperimentalEncodingApi
10 |
11 | @DoNotStrip
12 | @Keep
13 | class HybridImageUtils: HybridImageUtilsSpec() {
14 | override val supportsHeicLoading: Boolean
15 | get() {
16 | // Since Android 10, HEIF/HEIC is standard.
17 | // https://source.android.com/docs/core/camera/heif
18 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
19 | }
20 | override val supportsHeicWriting: Boolean
21 | get() {
22 | // Android does not support saving HEIF data yet
23 | return false
24 | }
25 |
26 |
27 | @OptIn(ExperimentalEncodingApi::class)
28 | override fun thumbHashToBase64String(thumbhash: ArrayBuffer): String {
29 | val buffer = thumbhash.toByteArray()
30 | val base64 = Base64.encode(buffer)
31 | return base64
32 | }
33 |
34 | @OptIn(ExperimentalEncodingApi::class)
35 | override fun thumbhashFromBase64String(thumbhashBase64: String): ArrayBuffer {
36 | val bytes = Base64.decode(thumbhashBase64)
37 | val buffer = ByteBuffer.wrap(bytes)
38 | return ArrayBuffer.wrap(buffer)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/NitroImagePackage.java:
--------------------------------------------------------------------------------
1 | package com.margelo.nitro.image;
2 |
3 | import android.util.Log;
4 |
5 | import androidx.annotation.NonNull;
6 | import androidx.annotation.Nullable;
7 |
8 | import com.facebook.react.bridge.NativeModule;
9 | import com.facebook.react.bridge.ReactApplicationContext;
10 | import com.facebook.react.module.model.ReactModuleInfoProvider;
11 | import com.facebook.react.TurboReactPackage;
12 | import com.facebook.react.uimanager.ViewManager;
13 | import com.margelo.nitro.core.HybridObject;
14 | import com.margelo.nitro.image.views.HybridNitroImageViewManager;
15 |
16 | import java.util.ArrayList;
17 | import java.util.HashMap;
18 | import java.util.List;
19 | import java.util.function.Supplier;
20 |
21 | public class NitroImagePackage extends TurboReactPackage {
22 | @Nullable
23 | @Override
24 | public NativeModule getModule(String name, ReactApplicationContext reactContext) {
25 | return null;
26 | }
27 |
28 | @Override
29 | public ReactModuleInfoProvider getReactModuleInfoProvider() {
30 | return () -> {
31 | return new HashMap<>();
32 | };
33 | }
34 |
35 |
36 | @Override
37 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) {
38 | List viewManagers = new ArrayList<>();
39 | viewManagers.add(new HybridNitroImageViewManager());
40 | return viewManagers;
41 | }
42 |
43 | static {
44 | NitroImageOnLoad.initializeNative();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/ios/CGImage+getPixelFormat.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGImage+pixelFormat.swift
3 | // react-native-nitro-image
4 | //
5 | // Created by Marc Rousavy on 22.10.25.
6 | //
7 |
8 | import Foundation
9 | import CoreGraphics
10 |
11 | extension CGImage {
12 | private var isLittleEndian: Bool {
13 | switch self.byteOrderInfo {
14 | case .orderDefault:
15 | // iOS uses little endian by default
16 | return true
17 | case .order16Little, .order32Little:
18 | return true
19 | case .order16Big, .order32Big:
20 | return false
21 | case .orderMask:
22 | fatalError(".orderMask is an unknown value for endianness!")
23 | @unknown default:
24 | fatalError("CGImage has unknown .byteOrderInfo!")
25 | }
26 | }
27 |
28 | var pixelFormat: PixelFormat {
29 | guard self.bitsPerComponent == 8, self.bitsPerPixel == 32 else {
30 | return .unknown
31 | }
32 | switch self.alphaInfo {
33 | case .premultipliedFirst, .first:
34 | // A___
35 | return self.isLittleEndian ? .bgra : .argb
36 | case .premultipliedLast, .last:
37 | // ___A
38 | return self.isLittleEndian ? .abgr : .rgba
39 | case .noneSkipFirst:
40 | // X___
41 | return self.isLittleEndian ? .bgra : .argb
42 | case .noneSkipLast:
43 | // ___X
44 | return self.isLittleEndian ? .abgr : .rgba
45 | case .none:
46 | // ___
47 | return self.isLittleEndian ? .bgr : .rgb
48 | default:
49 | return .unknown
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/ios/HybridImageLoaderFactory.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HybridImageLoaderFactory.swift
3 | // Pods
4 | //
5 | // Created by Marc Rousavy on 25.07.25.
6 | //
7 |
8 | import NitroModules
9 |
10 | class HybridImageLoaderFactory: HybridImageLoaderFactorySpec {
11 | private let imageFactory = HybridImageFactory()
12 |
13 | func createFileImageLoader(filePath: String) throws -> any HybridImageLoaderSpec {
14 | return HybridImageLoader(load: {
15 | try self.imageFactory.loadFromFileAsync(filePath: filePath)
16 | })
17 | }
18 |
19 | func createResourceImageLoader(name: String) throws -> any HybridImageLoaderSpec {
20 | return HybridImageLoader(load: {
21 | try self.imageFactory.loadFromResourcesAsync(name: name)
22 | })
23 | }
24 |
25 | func createSymbolImageLoader(symbolName: String) throws -> any HybridImageLoaderSpec {
26 | return HybridImageLoader(load: {
27 | let image = try self.imageFactory.loadFromSymbol(symbolName: symbolName)
28 | return Promise.resolved(withResult: image)
29 | })
30 | }
31 |
32 | func createRawPixelDataImageLoader(data: RawPixelData) throws -> any HybridImageLoaderSpec {
33 | return HybridImageLoader(load: {
34 | try self.imageFactory.loadFromRawPixelDataAsync(data: data, allowGpu: false)
35 | })
36 | }
37 |
38 | func createEncodedImageDataImageLoader(data: EncodedImageData) throws -> any HybridImageLoaderSpec {
39 | return HybridImageLoader(load: {
40 | try self.imageFactory.loadFromEncodedImageDataAsync(data: data)
41 | })
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/nitroimageexample/MainApplication.kt:
--------------------------------------------------------------------------------
1 | package com.nitroimageexample
2 |
3 | import android.app.Application
4 | import com.facebook.react.PackageList
5 | import com.facebook.react.ReactApplication
6 | import com.facebook.react.ReactHost
7 | import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
8 | import com.facebook.react.ReactNativeHost
9 | import com.facebook.react.ReactPackage
10 | import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
11 | import com.facebook.react.defaults.DefaultReactNativeHost
12 |
13 | class MainApplication : Application(), ReactApplication {
14 |
15 | override val reactNativeHost: ReactNativeHost =
16 | object : DefaultReactNativeHost(this) {
17 | override fun getPackages(): List =
18 | PackageList(this).packages.apply {
19 | // Packages that cannot be autolinked yet can be added manually here, for example:
20 | // add(MyReactNativePackage())
21 | }
22 |
23 | override fun getJSMainModuleName(): String = "index"
24 |
25 | override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
26 |
27 | override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
28 | override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
29 | }
30 |
31 | override val reactHost: ReactHost
32 | get() = getDefaultReactHost(applicationContext, reactNativeHost)
33 |
34 | override fun onCreate() {
35 | super.onCreate()
36 | loadReactNative(this)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/android/src/main/java/com/margelo/nitro/web/image/ImageLoader+loadImageAsync.kt:
--------------------------------------------------------------------------------
1 | package com.margelo.nitro.web.image
2 |
3 | import android.content.Context
4 | import coil3.BitmapImage
5 | import coil3.Image
6 | import coil3.ImageLoader
7 | import coil3.request.ImageRequest
8 | import com.margelo.nitro.core.Promise
9 | import com.margelo.nitro.image.HybridImage
10 | import com.margelo.nitro.image.HybridImageSpec
11 |
12 | suspend fun ImageLoader.loadCoilImageAsync(url: String,
13 | options: AsyncImageLoadOptions?,
14 | context: Context): Image {
15 | // 1. Create the Coil Request
16 | val request = ImageRequest.Builder(context)
17 | .data(url)
18 | .applyOptions(options)
19 | .build()
20 | // 2. Execute it (async)
21 | val result = this.execute(request)
22 | val image = result.image ?: throw Error("Failed to load Image!")
23 | return image
24 | }
25 |
26 | fun ImageLoader.loadImageAsync(url: String,
27 | options: AsyncImageLoadOptions?,
28 | context: Context
29 | ): Promise {
30 | return Promise.async {
31 | // 1. Load the coil image
32 | val image = loadCoilImageAsync(url, options, context)
33 | // 3. Downcast to a Bitmap - if that fails, it might be an Animated Image...
34 | val bitmap = image as? BitmapImage ?: throw Error("Requested Image is not a Bitmap!")
35 | return@async HybridImage(bitmap.bitmap)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/android/c++/JVariant_HybridImageSpec_HybridImageLoaderSpec.cpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// JVariant_HybridImageSpec_HybridImageLoaderSpec.cpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #include "JVariant_HybridImageSpec_HybridImageLoaderSpec.hpp"
9 |
10 | namespace margelo::nitro::image {
11 | /**
12 | * Converts JVariant_HybridImageSpec_HybridImageLoaderSpec to std::variant, std::shared_ptr>
13 | */
14 | std::variant, std::shared_ptr> JVariant_HybridImageSpec_HybridImageLoaderSpec::toCpp() const {
15 | if (isInstanceOf(JVariant_HybridImageSpec_HybridImageLoaderSpec_impl::First::javaClassStatic())) {
16 | // It's a `std::shared_ptr`
17 | auto jniValue = static_cast(this)->getValue();
18 | return jniValue->cthis()->shared_cast();
19 | } else if (isInstanceOf(JVariant_HybridImageSpec_HybridImageLoaderSpec_impl::Second::javaClassStatic())) {
20 | // It's a `std::shared_ptr`
21 | auto jniValue = static_cast(this)->getValue();
22 | return jniValue->cthis()->shared_cast();
23 | }
24 | throw std::invalid_argument("Variant is unknown Kotlin instance!");
25 | }
26 | } // namespace margelo::nitro::image
27 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/ios/HybridWebImageFactory.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HybridWebImageFactory.swift
3 | // react-native-nitro-web-image
4 | //
5 | // Created by Marc Rousavy on 10.06.25.
6 | //
7 |
8 | import Foundation
9 | import NitroModules
10 | import SDWebImage
11 | import NitroImage
12 |
13 | class HybridWebImageFactory: HybridWebImageFactorySpec {
14 | func loadFromURLAsync(url urlString: String, options: AsyncImageLoadOptions?) throws -> Promise {
15 | guard let url = URL(string: urlString) else {
16 | throw RuntimeError.error(withMessage: "URL string \"\(urlString)\" is not a valid URL!")
17 | }
18 |
19 | return HybridWebImageLoader.loadImage(url: url, options: options)
20 | }
21 |
22 | private let queue = DispatchQueue(label: "image-loader",
23 | qos: .default,
24 | attributes: .concurrent)
25 |
26 | /**
27 | * Load Image from URL
28 | */
29 | func createWebImageLoader(url urlString: String, options: AsyncImageLoadOptions?) throws -> any HybridImageLoaderSpec {
30 | guard let url = URL(string: urlString) else {
31 | throw RuntimeError.error(withMessage: "URL string \"\(urlString)\" is not a valid URL!")
32 | }
33 |
34 | return HybridWebImageLoader(url: url, options: options)
35 | }
36 |
37 | func preload(url urlString: String) throws {
38 | guard let url = URL(string: urlString) else {
39 | throw RuntimeError.error(withMessage: "URL string \"\(urlString)\" is not a valid URL!")
40 | }
41 | SDWebImagePrefetcher.shared.prefetchURLs([url])
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/.github/workflows/build-android-release.yml:
--------------------------------------------------------------------------------
1 | name: Build Android (Release)
2 |
3 | on:
4 | release:
5 | types: [published]
6 | pull_request:
7 | paths:
8 | - '.github/workflows/build-android-release.yml'
9 |
10 | jobs:
11 | build_release:
12 | name: Build Android Example App (release)
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v4
16 | - uses: oven-sh/setup-bun@v2
17 |
18 | - name: Install npm dependencies (bun)
19 | run: bun install
20 | - name: Install npm dependencies in example/ (bun)
21 | working-directory: example
22 | run: bun install
23 |
24 | - name: Setup JDK 17
25 | uses: actions/setup-java@v4
26 | with:
27 | distribution: 'zulu'
28 | java-version: 17
29 | java-package: jdk
30 |
31 | - name: Run Gradle Build for example/android/
32 | working-directory: example
33 | run: bun run build:android-release
34 |
35 | - name: Upload APK to Release
36 | uses: actions/upload-release-asset@v1
37 | env:
38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
39 | with:
40 | upload_url: ${{ github.event.release.upload_url }}
41 | asset_path: example/android/app/build/outputs/apk/release/app-release.apk
42 | asset_name: "NitroImageExample-${{ github.event.release.tag_name }}.apk"
43 | asset_content_type: application/vnd.android.package-archive
44 |
45 | # Gradle cache doesn't like daemons
46 | - name: Stop Gradle Daemon
47 | working-directory: example/android
48 | run: ./gradlew --stop
49 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/android/src/main/java/com/margelo/nitro/web/image/HybridWebImageFactory.kt:
--------------------------------------------------------------------------------
1 | package com.margelo.nitro.web.image
2 |
3 | import androidx.annotation.Keep
4 | import coil3.ImageLoader
5 | import coil3.request.ImageRequest
6 | import com.facebook.common.internal.DoNotStrip
7 | import com.facebook.react.bridge.ReactApplicationContext
8 | import com.margelo.nitro.NitroModules
9 | import com.margelo.nitro.core.Promise
10 | import com.margelo.nitro.image.HybridImageLoaderSpec
11 | import com.margelo.nitro.image.HybridImageSpec
12 |
13 | @DoNotStrip
14 | @Keep
15 | class HybridWebImageFactory: HybridWebImageFactorySpec() {
16 | private val context: ReactApplicationContext
17 | get() = NitroModules.applicationContext ?: throw Error("No context - NitroModules.applicationContext was null!")
18 | private val imageLoader = ImageLoader(context)
19 |
20 | override fun createWebImageLoader(
21 | url: String,
22 | options: AsyncImageLoadOptions?
23 | ): HybridImageLoaderSpec {
24 | return HybridWebImageLoader(imageLoader, url, options, context)
25 | }
26 |
27 | override fun loadFromURLAsync(
28 | url: String,
29 | options: AsyncImageLoadOptions?
30 | ): Promise {
31 | return imageLoader.loadImageAsync(url, options, context)
32 | }
33 |
34 | override fun preload(url: String) {
35 | // 1. Create the Coil Request
36 | val request = ImageRequest.Builder(context)
37 | .data(url)
38 | .build()
39 | // 2. Enqueue the request to prefetch it
40 | imageLoader.enqueue(request)
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/swift/Func_void_std__string.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// Func_void_std__string.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | import NitroModules
9 |
10 |
11 | /**
12 | * Wraps a Swift `(_ value: String) -> Void` as a class.
13 | * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`.
14 | */
15 | public final class Func_void_std__string {
16 | public typealias bridge = margelo.nitro.image.bridge.swift
17 |
18 | private let closure: (_ value: String) -> Void
19 |
20 | public init(_ closure: @escaping (_ value: String) -> Void) {
21 | self.closure = closure
22 | }
23 |
24 | @inline(__always)
25 | public func call(value: std.string) -> Void {
26 | self.closure(String(value))
27 | }
28 |
29 | /**
30 | * Casts this instance to a retained unsafe raw pointer.
31 | * This acquires one additional strong reference on the object!
32 | */
33 | @inline(__always)
34 | public func toUnsafe() -> UnsafeMutableRawPointer {
35 | return Unmanaged.passRetained(self).toOpaque()
36 | }
37 |
38 | /**
39 | * Casts an unsafe pointer to a `Func_void_std__string`.
40 | * The pointer has to be a retained opaque `Unmanaged`.
41 | * This removes one strong reference from the object!
42 | */
43 | @inline(__always)
44 | public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void_std__string {
45 | return Unmanaged.fromOpaque(pointer).takeRetainedValue()
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/nitrogen/generated/android/NitroWebImageOnLoad.cpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// NitroWebImageOnLoad.cpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #ifndef BUILDING_NITROWEBIMAGE_WITH_GENERATED_CMAKE_PROJECT
9 | #error NitroWebImageOnLoad.cpp is not being built with the autogenerated CMakeLists.txt project. Is a different CMakeLists.txt building this?
10 | #endif
11 |
12 | #include "NitroWebImageOnLoad.hpp"
13 |
14 | #include
15 | #include
16 | #include
17 |
18 | #include "JHybridWebImageFactorySpec.hpp"
19 | #include
20 |
21 | namespace margelo::nitro::web::image {
22 |
23 | int initialize(JavaVM* vm) {
24 | using namespace margelo::nitro;
25 | using namespace margelo::nitro::web::image;
26 | using namespace facebook;
27 |
28 | return facebook::jni::initialize(vm, [] {
29 | // Register native JNI methods
30 | margelo::nitro::web::image::JHybridWebImageFactorySpec::registerNatives();
31 |
32 | // Register Nitro Hybrid Objects
33 | HybridObjectRegistry::registerHybridObjectConstructor(
34 | "WebImageFactory",
35 | []() -> std::shared_ptr {
36 | static DefaultConstructableObject object("com/margelo/nitro/web/image/HybridWebImageFactory");
37 | auto instance = object.create();
38 | return instance->cthis()->shared();
39 | }
40 | );
41 | });
42 | }
43 |
44 | } // namespace margelo::nitro::web::image
45 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/swift/Func_void_RawPixelData.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// Func_void_RawPixelData.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | import NitroModules
9 |
10 |
11 | /**
12 | * Wraps a Swift `(_ value: RawPixelData) -> Void` as a class.
13 | * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`.
14 | */
15 | public final class Func_void_RawPixelData {
16 | public typealias bridge = margelo.nitro.image.bridge.swift
17 |
18 | private let closure: (_ value: RawPixelData) -> Void
19 |
20 | public init(_ closure: @escaping (_ value: RawPixelData) -> Void) {
21 | self.closure = closure
22 | }
23 |
24 | @inline(__always)
25 | public func call(value: RawPixelData) -> Void {
26 | self.closure(value)
27 | }
28 |
29 | /**
30 | * Casts this instance to a retained unsafe raw pointer.
31 | * This acquires one additional strong reference on the object!
32 | */
33 | @inline(__always)
34 | public func toUnsafe() -> UnsafeMutableRawPointer {
35 | return Unmanaged.passRetained(self).toOpaque()
36 | }
37 |
38 | /**
39 | * Casts an unsafe pointer to a `Func_void_RawPixelData`.
40 | * The pointer has to be a retained opaque `Unmanaged`.
41 | * This removes one strong reference from the object!
42 | */
43 | @inline(__always)
44 | public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void_RawPixelData {
45 | return Unmanaged.fromOpaque(pointer).takeRetainedValue()
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/ios/AsyncImageLoadOptions+toSDWebImageOptions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AsyncImageLoadOptions+toSDWebImageOptions.swift
3 | // NitroWebImage
4 | //
5 | // Created by Marc Rousavy on 30.06.25.
6 | //
7 |
8 | import Foundation
9 | import SDWebImage
10 |
11 | extension AsyncImageLoadOptions {
12 | func toSDWebImageOptions() -> SDWebImageOptions {
13 | var options: SDWebImageOptions = []
14 |
15 | switch priority {
16 | case .default, .none:
17 | break
18 | case .low:
19 | options.insert(.lowPriority)
20 | case .high:
21 | options.insert(.highPriority)
22 | }
23 |
24 | if forceRefresh == true {
25 | options.insert(.refreshCached)
26 | }
27 |
28 | if continueInBackground == true {
29 | options.insert(.continueInBackground)
30 | }
31 |
32 | if allowInvalidSSLCertificates == true {
33 | options.insert(.allowInvalidSSLCertificates)
34 | }
35 |
36 | if scaleDownLargeImages == true {
37 | options.insert(.scaleDownLargeImages)
38 | }
39 |
40 | if queryMemoryDataSync == true {
41 | options.insert(.queryMemoryDataSync)
42 | }
43 |
44 | if queryDiskDataSync == true {
45 | options.insert(.queryDiskDataSync)
46 | }
47 |
48 | if decodeImage == false {
49 | options.insert(.avoidDecodeImage)
50 | }
51 |
52 | return options
53 | }
54 |
55 | func toSDWebImageContext() -> [SDWebImageContextOption: Any] {
56 | var context: [SDWebImageContextOption: Any] = [:]
57 |
58 | if let cacheKey {
59 | context[.cacheKeyFilter] = SDWebImageCacheKeyFilter { _ in
60 | return cacheKey
61 | }
62 | }
63 |
64 | return context
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/swift/RawPixelData.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// RawPixelData.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | import NitroModules
9 |
10 | /**
11 | * Represents an instance of `RawPixelData`, backed by a C++ struct.
12 | */
13 | public typealias RawPixelData = margelo.nitro.image.RawPixelData
14 |
15 | public extension RawPixelData {
16 | private typealias bridge = margelo.nitro.image.bridge.swift
17 |
18 | /**
19 | * Create a new instance of `RawPixelData`.
20 | */
21 | init(buffer: ArrayBuffer, width: Double, height: Double, pixelFormat: PixelFormat) {
22 | self.init(buffer.getArrayBuffer(), width, height, pixelFormat)
23 | }
24 |
25 | var buffer: ArrayBuffer {
26 | @inline(__always)
27 | get {
28 | return ArrayBuffer(self.__buffer)
29 | }
30 | @inline(__always)
31 | set {
32 | self.__buffer = newValue.getArrayBuffer()
33 | }
34 | }
35 |
36 | var width: Double {
37 | @inline(__always)
38 | get {
39 | return self.__width
40 | }
41 | @inline(__always)
42 | set {
43 | self.__width = newValue
44 | }
45 | }
46 |
47 | var height: Double {
48 | @inline(__always)
49 | get {
50 | return self.__height
51 | }
52 | @inline(__always)
53 | set {
54 | self.__height = newValue
55 | }
56 | }
57 |
58 | var pixelFormat: PixelFormat {
59 | @inline(__always)
60 | get {
61 | return self.__pixelFormat
62 | }
63 | @inline(__always)
64 | set {
65 | self.__pixelFormat = newValue
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/swift/Func_void_EncodedImageData.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// Func_void_EncodedImageData.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | import NitroModules
9 |
10 |
11 | /**
12 | * Wraps a Swift `(_ value: EncodedImageData) -> Void` as a class.
13 | * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`.
14 | */
15 | public final class Func_void_EncodedImageData {
16 | public typealias bridge = margelo.nitro.image.bridge.swift
17 |
18 | private let closure: (_ value: EncodedImageData) -> Void
19 |
20 | public init(_ closure: @escaping (_ value: EncodedImageData) -> Void) {
21 | self.closure = closure
22 | }
23 |
24 | @inline(__always)
25 | public func call(value: EncodedImageData) -> Void {
26 | self.closure(value)
27 | }
28 |
29 | /**
30 | * Casts this instance to a retained unsafe raw pointer.
31 | * This acquires one additional strong reference on the object!
32 | */
33 | @inline(__always)
34 | public func toUnsafe() -> UnsafeMutableRawPointer {
35 | return Unmanaged.passRetained(self).toOpaque()
36 | }
37 |
38 | /**
39 | * Casts an unsafe pointer to a `Func_void_EncodedImageData`.
40 | * The pointer has to be a retained opaque `Unmanaged`.
41 | * This removes one strong reference from the object!
42 | */
43 | @inline(__always)
44 | public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void_EncodedImageData {
45 | return Unmanaged.fromOpaque(pointer).takeRetainedValue()
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// Func_void_std__exception_ptr.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | import NitroModules
9 |
10 |
11 | /**
12 | * Wraps a Swift `(_ error: Error) -> Void` as a class.
13 | * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`.
14 | */
15 | public final class Func_void_std__exception_ptr {
16 | public typealias bridge = margelo.nitro.image.bridge.swift
17 |
18 | private let closure: (_ error: Error) -> Void
19 |
20 | public init(_ closure: @escaping (_ error: Error) -> Void) {
21 | self.closure = closure
22 | }
23 |
24 | @inline(__always)
25 | public func call(error: std.exception_ptr) -> Void {
26 | self.closure(RuntimeError.from(cppError: error))
27 | }
28 |
29 | /**
30 | * Casts this instance to a retained unsafe raw pointer.
31 | * This acquires one additional strong reference on the object!
32 | */
33 | @inline(__always)
34 | public func toUnsafe() -> UnsafeMutableRawPointer {
35 | return Unmanaged.passRetained(self).toOpaque()
36 | }
37 |
38 | /**
39 | * Casts an unsafe pointer to a `Func_void_std__exception_ptr`.
40 | * The pointer has to be a retained opaque `Unmanaged`.
41 | * This removes one strong reference from the object!
42 | */
43 | @inline(__always)
44 | public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void_std__exception_ptr {
45 | return Unmanaged.fromOpaque(pointer).takeRetainedValue()
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/ios/UIImage+getData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+getData.swift
3 | // react-native-nitro-image
4 | //
5 | // Created by Marc Rousavy on 11.06.25.
6 | //
7 |
8 | import UIKit
9 | import NitroModules
10 |
11 | extension UIImage {
12 | /**
13 | * Convert/Compress this Image into the given `format`.
14 | * `quality` specifies compression quality from 0(most)...100(least).
15 | */
16 | func getData(in format: ImageFormat, quality: CGFloat) throws -> Data {
17 | switch format {
18 | case .jpg:
19 | guard quality >= 0 && quality <= 100 else {
20 | throw RuntimeError.error(withMessage: "Image quality has to be between 0 and 100! (Received: \(quality))")
21 | }
22 | let qualityNormalized = quality / 100.0
23 | guard let data = self.jpegData(compressionQuality: qualityNormalized) else {
24 | throw RuntimeError.error(withMessage: "Failed to compress \(size.width)x\(size.height) Image to JPEG! (Quality: \(quality))")
25 | }
26 | return data
27 | case .png:
28 | guard let data = self.pngData() else {
29 | throw RuntimeError.error(withMessage: "Failed to convert \(size.width)x\(size.height) Image to PNG!")
30 | }
31 | return data
32 | case .heic:
33 | guard #available(iOS 17.0, *) else {
34 | throw RuntimeError.error(withMessage: "HEIC writing is only available on iOS 17.0 or higher! " +
35 | "Check ImageUtils.supportsHeicWriting before calling this method.")
36 | }
37 | guard let data = self.heicData() else {
38 | throw RuntimeError.error(withMessage: "Failed to convert \(size.width)x\(size.height) Image to HEIC!")
39 | }
40 | return data
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/swift/EncodedImageData.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// EncodedImageData.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | import NitroModules
9 |
10 | /**
11 | * Represents an instance of `EncodedImageData`, backed by a C++ struct.
12 | */
13 | public typealias EncodedImageData = margelo.nitro.image.EncodedImageData
14 |
15 | public extension EncodedImageData {
16 | private typealias bridge = margelo.nitro.image.bridge.swift
17 |
18 | /**
19 | * Create a new instance of `EncodedImageData`.
20 | */
21 | init(buffer: ArrayBuffer, width: Double, height: Double, imageFormat: ImageFormat) {
22 | self.init(buffer.getArrayBuffer(), width, height, imageFormat)
23 | }
24 |
25 | var buffer: ArrayBuffer {
26 | @inline(__always)
27 | get {
28 | return ArrayBuffer(self.__buffer)
29 | }
30 | @inline(__always)
31 | set {
32 | self.__buffer = newValue.getArrayBuffer()
33 | }
34 | }
35 |
36 | var width: Double {
37 | @inline(__always)
38 | get {
39 | return self.__width
40 | }
41 | @inline(__always)
42 | set {
43 | self.__width = newValue
44 | }
45 | }
46 |
47 | var height: Double {
48 | @inline(__always)
49 | get {
50 | return self.__height
51 | }
52 | @inline(__always)
53 | set {
54 | self.__height = newValue
55 | }
56 | }
57 |
58 | var imageFormat: ImageFormat {
59 | @inline(__always)
60 | get {
61 | return self.__imageFormat
62 | }
63 | @inline(__always)
64 | set {
65 | self.__imageFormat = newValue
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// Func_void_std__exception_ptr.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | import NitroModules
9 |
10 |
11 | /**
12 | * Wraps a Swift `(_ error: Error) -> Void` as a class.
13 | * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`.
14 | */
15 | public final class Func_void_std__exception_ptr {
16 | public typealias bridge = margelo.nitro.web.image.bridge.swift
17 |
18 | private let closure: (_ error: Error) -> Void
19 |
20 | public init(_ closure: @escaping (_ error: Error) -> Void) {
21 | self.closure = closure
22 | }
23 |
24 | @inline(__always)
25 | public func call(error: std.exception_ptr) -> Void {
26 | self.closure(RuntimeError.from(cppError: error))
27 | }
28 |
29 | /**
30 | * Casts this instance to a retained unsafe raw pointer.
31 | * This acquires one additional strong reference on the object!
32 | */
33 | @inline(__always)
34 | public func toUnsafe() -> UnsafeMutableRawPointer {
35 | return Unmanaged.passRetained(self).toOpaque()
36 | }
37 |
38 | /**
39 | * Casts an unsafe pointer to a `Func_void_std__exception_ptr`.
40 | * The pointer has to be a retained opaque `Unmanaged`.
41 | * This removes one strong reference from the object!
42 | */
43 | @inline(__always)
44 | public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void_std__exception_ptr {
45 | return Unmanaged.fromOpaque(pointer).takeRetainedValue()
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "NitroImageExample",
3 | "version": "0.9.0",
4 | "private": true,
5 | "scripts": {
6 | "android": "react-native run-android",
7 | "ios": "react-native run-ios",
8 | "lint": "biome check .",
9 | "start": "react-native start --client-logs",
10 | "pods": "bundle install && cd ios && bundle exec pod install",
11 | "bundle-install": "bundle install",
12 | "build:android-release": "cd android && ./gradlew assembleRelease --no-daemon"
13 | },
14 | "dependencies": {
15 | "@react-navigation/bottom-tabs": "^7.4.6",
16 | "@react-navigation/native": "^7.1.17",
17 | "react": "19.1.0",
18 | "react-native": "0.81.0",
19 | "react-native-fast-image": "^8.6.3",
20 | "react-native-nitro-image": "../packages/react-native-nitro-image",
21 | "react-native-nitro-web-image": "../packages/react-native-nitro-web-image",
22 | "react-native-nitro-modules": "0.31.5",
23 | "react-native-safe-area-context": "^5.6.0",
24 | "react-native-screens": "^4.14.1"
25 | },
26 | "devDependencies": {
27 | "@babel/core": "^7.27.4",
28 | "@babel/preset-env": "^7.27.2",
29 | "@babel/runtime": "^7.27.6",
30 | "@react-native-community/cli": "20.0.0",
31 | "@react-native-community/cli-platform-android": "20.0.0",
32 | "@react-native-community/cli-platform-ios": "20.0.0",
33 | "@react-native/babel-preset": "0.81.0",
34 | "@react-native/metro-config": "0.81.0",
35 | "@react-native/typescript-config": "0.81.0",
36 | "@types/react": "^19.1.0",
37 | "typescript": "5.8.3"
38 | },
39 | "engines": {
40 | "node": ">=18"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_ArrayBuffer_.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// Func_void_std__shared_ptr_ArrayBuffer_.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | import NitroModules
9 | import NitroModules
10 |
11 | /**
12 | * Wraps a Swift `(_ value: ArrayBuffer) -> Void` as a class.
13 | * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`.
14 | */
15 | public final class Func_void_std__shared_ptr_ArrayBuffer_ {
16 | public typealias bridge = margelo.nitro.image.bridge.swift
17 |
18 | private let closure: (_ value: ArrayBuffer) -> Void
19 |
20 | public init(_ closure: @escaping (_ value: ArrayBuffer) -> Void) {
21 | self.closure = closure
22 | }
23 |
24 | @inline(__always)
25 | public func call(value: ArrayBuffer) -> Void {
26 | self.closure(value)
27 | }
28 |
29 | /**
30 | * Casts this instance to a retained unsafe raw pointer.
31 | * This acquires one additional strong reference on the object!
32 | */
33 | @inline(__always)
34 | public func toUnsafe() -> UnsafeMutableRawPointer {
35 | return Unmanaged.passRetained(self).toOpaque()
36 | }
37 |
38 | /**
39 | * Casts an unsafe pointer to a `Func_void_std__shared_ptr_ArrayBuffer_`.
40 | * The pointer has to be a retained opaque `Unmanaged`.
41 | * This removes one strong reference from the object!
42 | */
43 | @inline(__always)
44 | public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void_std__shared_ptr_ArrayBuffer_ {
45 | return Unmanaged.fromOpaque(pointer).takeRetainedValue()
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/example/ios/NitroImageExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | NitroImageExample
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(MARKETING_VERSION)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(CURRENT_PROJECT_VERSION)
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 | NSAllowsArbitraryLoads
30 |
31 | NSAllowsLocalNetworking
32 |
33 |
34 | NSLocationWhenInUseUsageDescription
35 |
36 | RCTNewArchEnabled
37 |
38 | UILaunchStoryboardName
39 | LaunchScreen
40 | UIRequiredDeviceCapabilities
41 |
42 | arm64
43 |
44 | UISupportedInterfaceOrientations
45 |
46 | UIInterfaceOrientationPortrait
47 | UIInterfaceOrientationLandscapeLeft
48 | UIInterfaceOrientationLandscapeRight
49 |
50 | UIViewControllerBasedStatusBarAppearance
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/HybridImageLoaderSpec.kt:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridImageLoaderSpec.kt
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | package com.margelo.nitro.image
9 |
10 | import androidx.annotation.Keep
11 | import com.facebook.jni.HybridData
12 | import com.facebook.proguard.annotations.DoNotStrip
13 | import com.margelo.nitro.core.Promise
14 | import com.margelo.nitro.core.HybridObject
15 |
16 | /**
17 | * A Kotlin class representing the ImageLoader HybridObject.
18 | * Implement this abstract class to create Kotlin-based instances of ImageLoader.
19 | */
20 | @DoNotStrip
21 | @Keep
22 | @Suppress(
23 | "KotlinJniMissingFunction", "unused",
24 | "RedundantSuppression", "RedundantUnitReturnType", "SimpleRedundantLet",
25 | "LocalVariableName", "PropertyName", "PrivatePropertyName", "FunctionName"
26 | )
27 | abstract class HybridImageLoaderSpec: HybridObject() {
28 | @DoNotStrip
29 | private var mHybridData: HybridData = initHybrid()
30 |
31 | init {
32 | super.updateNative(mHybridData)
33 | }
34 |
35 | override fun updateNative(hybridData: HybridData) {
36 | mHybridData = hybridData
37 | super.updateNative(hybridData)
38 | }
39 |
40 | // Properties
41 |
42 |
43 | // Methods
44 | @DoNotStrip
45 | @Keep
46 | abstract fun loadImage(): Promise
47 |
48 | @DoNotStrip
49 | @Keep
50 | abstract fun requestImage(forView: HybridNitroImageViewSpec): Unit
51 |
52 | @DoNotStrip
53 | @Keep
54 | abstract fun dropImage(forView: HybridNitroImageViewSpec): Unit
55 |
56 | private external fun initHybrid(): HybridData
57 |
58 | companion object {
59 | protected const val TAG = "HybridImageLoaderSpec"
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/.github/workflows/build-android.yml:
--------------------------------------------------------------------------------
1 | name: Build Android
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | - '.github/workflows/build-android.yml'
9 | - 'example/android/**'
10 | - '**/nitrogen/generated/shared/**'
11 | - '**/nitrogen/generated/android/**'
12 | - 'android/**'
13 | - '**/bun.lock'
14 | - '**/react-native.config.js'
15 | - '**/nitro.json'
16 | pull_request:
17 | paths:
18 | - '.github/workflows/build-android.yml'
19 | - 'example/android/**'
20 | - '**/nitrogen/generated/shared/**'
21 | - '**/nitrogen/generated/android/**'
22 | - 'android/**'
23 | - '**/bun.lock'
24 | - '**/react-native.config.js'
25 | - '**/nitro.json'
26 |
27 | jobs:
28 | build:
29 | name: Build Android Example App
30 | runs-on: ubuntu-latest
31 | steps:
32 | - uses: actions/checkout@v4
33 | - uses: oven-sh/setup-bun@v2
34 |
35 | - name: Install npm dependencies (bun)
36 | run: bun install
37 |
38 | - name: Setup JDK 17
39 | uses: actions/setup-java@v4
40 | with:
41 | distribution: 'zulu'
42 | java-version: 17
43 | java-package: jdk
44 |
45 | - name: Restore Gradle cache
46 | uses: actions/cache@v4
47 | with:
48 | path: |
49 | ~/.gradle/caches
50 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
51 | restore-keys: |
52 | ${{ runner.os }}-gradle-
53 | - name: Run Gradle Build for example/android/
54 | working-directory: example/android
55 | run: ./gradlew assembleDebug --no-daemon --build-cache
56 |
57 | # Gradle cache doesn't like daemons
58 | - name: Stop Gradle Daemon
59 | working-directory: example/android
60 | run: ./gradlew --stop
61 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageLoaderSpec.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridImageLoaderSpec.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | import Foundation
9 | import NitroModules
10 | import NitroModules
11 |
12 | /// See ``HybridImageLoaderSpec``
13 | public protocol HybridImageLoaderSpec_protocol: HybridObject {
14 | // Properties
15 |
16 |
17 | // Methods
18 | func loadImage() throws -> Promise<(any HybridImageSpec)>
19 | func requestImage(forView: (any HybridNitroImageViewSpec)) throws -> Void
20 | func dropImage(forView: (any HybridNitroImageViewSpec)) throws -> Void
21 | }
22 |
23 | /// See ``HybridImageLoaderSpec``
24 | open class HybridImageLoaderSpec_base {
25 | private weak var cxxWrapper: HybridImageLoaderSpec_cxx? = nil
26 | public init() { }
27 | public func getCxxWrapper() -> HybridImageLoaderSpec_cxx {
28 | #if DEBUG
29 | guard self is HybridImageLoaderSpec else {
30 | fatalError("`self` is not a `HybridImageLoaderSpec`! Did you accidentally inherit from `HybridImageLoaderSpec_base` instead of `HybridImageLoaderSpec`?")
31 | }
32 | #endif
33 | if let cxxWrapper = self.cxxWrapper {
34 | return cxxWrapper
35 | } else {
36 | let cxxWrapper = HybridImageLoaderSpec_cxx(self as! HybridImageLoaderSpec)
37 | self.cxxWrapper = cxxWrapper
38 | return cxxWrapper
39 | }
40 | }
41 | }
42 |
43 | /**
44 | * A Swift base-protocol representing the ImageLoader HybridObject.
45 | * Implement this protocol to create Swift-based instances of ImageLoader.
46 | * ```swift
47 | * class HybridImageLoader : HybridImageLoaderSpec {
48 | * // ...
49 | * }
50 | * ```
51 | */
52 | public typealias HybridImageLoaderSpec = HybridImageLoaderSpec_protocol & HybridImageLoaderSpec_base
53 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageUtilsSpec.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridImageUtilsSpec.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | import Foundation
9 | import NitroModules
10 | import NitroModules
11 |
12 | /// See ``HybridImageUtilsSpec``
13 | public protocol HybridImageUtilsSpec_protocol: HybridObject {
14 | // Properties
15 | var supportsHeicLoading: Bool { get }
16 | var supportsHeicWriting: Bool { get }
17 |
18 | // Methods
19 | func thumbHashToBase64String(thumbhash: ArrayBuffer) throws -> String
20 | func thumbhashFromBase64String(thumbhashBase64: String) throws -> ArrayBuffer
21 | }
22 |
23 | /// See ``HybridImageUtilsSpec``
24 | open class HybridImageUtilsSpec_base {
25 | private weak var cxxWrapper: HybridImageUtilsSpec_cxx? = nil
26 | public init() { }
27 | public func getCxxWrapper() -> HybridImageUtilsSpec_cxx {
28 | #if DEBUG
29 | guard self is HybridImageUtilsSpec else {
30 | fatalError("`self` is not a `HybridImageUtilsSpec`! Did you accidentally inherit from `HybridImageUtilsSpec_base` instead of `HybridImageUtilsSpec`?")
31 | }
32 | #endif
33 | if let cxxWrapper = self.cxxWrapper {
34 | return cxxWrapper
35 | } else {
36 | let cxxWrapper = HybridImageUtilsSpec_cxx(self as! HybridImageUtilsSpec)
37 | self.cxxWrapper = cxxWrapper
38 | return cxxWrapper
39 | }
40 | }
41 | }
42 |
43 | /**
44 | * A Swift base-protocol representing the ImageUtils HybridObject.
45 | * Implement this protocol to create Swift-based instances of ImageUtils.
46 | * ```swift
47 | * class HybridImageUtils : HybridImageUtilsSpec {
48 | * // ...
49 | * }
50 | * ```
51 | */
52 | public typealias HybridImageUtilsSpec = HybridImageUtilsSpec_protocol & HybridImageUtilsSpec_base
53 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/views/HybridNitroImageViewManager.kt:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridNitroImageViewManager.kt
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | package com.margelo.nitro.image.views
9 |
10 | import android.view.View
11 | import com.facebook.react.uimanager.ReactStylesDiffMap
12 | import com.facebook.react.uimanager.SimpleViewManager
13 | import com.facebook.react.uimanager.StateWrapper
14 | import com.facebook.react.uimanager.ThemedReactContext
15 | import com.margelo.nitro.image.*
16 |
17 | /**
18 | * Represents the React Native `ViewManager` for the "NitroImageView" Nitro HybridView.
19 | */
20 | open class HybridNitroImageViewManager: SimpleViewManager() {
21 | private val views = hashMapOf()
22 |
23 | override fun getName(): String {
24 | return "NitroImageView"
25 | }
26 |
27 | override fun createViewInstance(reactContext: ThemedReactContext): View {
28 | val hybridView = HybridImageView(reactContext)
29 | val view = hybridView.view
30 | views[view] = hybridView
31 | return view
32 | }
33 |
34 | override fun onDropViewInstance(view: View) {
35 | super.onDropViewInstance(view)
36 | views.remove(view)
37 | }
38 |
39 | override fun updateState(view: View, props: ReactStylesDiffMap, stateWrapper: StateWrapper): Any? {
40 | val hybridView = views[view] ?: throw Error("Couldn't find view $view in local views table!")
41 |
42 | // 1. Update each prop individually
43 | hybridView.beforeUpdate()
44 | HybridNitroImageViewStateUpdater.updateViewProps(hybridView, stateWrapper)
45 | hybridView.afterUpdate()
46 |
47 | // 2. Continue in base View props
48 | return super.updateState(view, props, stateWrapper)
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/android/src/main/java/com/margelo/nitro/image/Bitmap+pixelFormat.kt:
--------------------------------------------------------------------------------
1 | package com.margelo.nitro.image
2 |
3 | import android.graphics.Bitmap
4 | import android.hardware.HardwareBuffer
5 | import android.os.Build
6 | import java.nio.ByteOrder
7 |
8 | val Bitmap.pixelFormat: PixelFormat
9 | get() {
10 | when (config) {
11 | Bitmap.Config.ARGB_8888 -> {
12 | // On Android, ARGB_8888 defines memory layout inside an Int, not the byte order.
13 | // So where iOS would define Pixel Format ARGB as byte order [A, R, G, B],
14 | // Android instead defines the Pixel Format ARGB as 0xAARRGGBB (32-bit Int) -
15 | // which - if read on a little-endian machine, is reversed ([B, G, R, A]).
16 | if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) {
17 | // almost every device nowadays is little endian
18 | return PixelFormat.BGRA
19 | } else {
20 | // no devices use big endian anymore, but we keep this to highlight
21 | // why ARGB becomes BGRA on little endian.
22 | return PixelFormat.ARGB
23 | }
24 | }
25 | Bitmap.Config.HARDWARE -> {
26 | // Hardware Buffer is either RGBA or RGBX
27 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
28 | when (hardwareBuffer.format) {
29 | HardwareBuffer.RGBA_8888 -> return PixelFormat.RGBA
30 | HardwareBuffer.RGBX_8888 -> return PixelFormat.RGBX
31 | HardwareBuffer.RGB_888 -> return PixelFormat.RGB
32 | }
33 | }
34 | return PixelFormat.UNKNOWN
35 | }
36 | else -> return PixelFormat.UNKNOWN
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridNitroImageViewSpec.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridNitroImageViewSpec.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | import Foundation
9 | import NitroModules
10 |
11 | /// See ``HybridNitroImageViewSpec``
12 | public protocol HybridNitroImageViewSpec_protocol: HybridObject, HybridView {
13 | // Properties
14 | var image: Variant__any_HybridImageSpec___any_HybridImageLoaderSpec_? { get set }
15 | var resizeMode: ResizeMode? { get set }
16 | var recyclingKey: String? { get set }
17 |
18 | // Methods
19 |
20 | }
21 |
22 | /// See ``HybridNitroImageViewSpec``
23 | open class HybridNitroImageViewSpec_base {
24 | private weak var cxxWrapper: HybridNitroImageViewSpec_cxx? = nil
25 | public init() { }
26 | public func getCxxWrapper() -> HybridNitroImageViewSpec_cxx {
27 | #if DEBUG
28 | guard self is HybridNitroImageViewSpec else {
29 | fatalError("`self` is not a `HybridNitroImageViewSpec`! Did you accidentally inherit from `HybridNitroImageViewSpec_base` instead of `HybridNitroImageViewSpec`?")
30 | }
31 | #endif
32 | if let cxxWrapper = self.cxxWrapper {
33 | return cxxWrapper
34 | } else {
35 | let cxxWrapper = HybridNitroImageViewSpec_cxx(self as! HybridNitroImageViewSpec)
36 | self.cxxWrapper = cxxWrapper
37 | return cxxWrapper
38 | }
39 | }
40 | }
41 |
42 | /**
43 | * A Swift base-protocol representing the NitroImageView HybridObject.
44 | * Implement this protocol to create Swift-based instances of NitroImageView.
45 | * ```swift
46 | * class HybridNitroImageView : HybridNitroImageViewSpec {
47 | * // ...
48 | * }
49 | * ```
50 | */
51 | public typealias HybridNitroImageViewSpec = HybridNitroImageViewSpec_protocol & HybridNitroImageViewSpec_base
52 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/HybridNitroImageViewSpec.kt:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridNitroImageViewSpec.kt
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | package com.margelo.nitro.image
9 |
10 | import androidx.annotation.Keep
11 | import com.facebook.jni.HybridData
12 | import com.facebook.proguard.annotations.DoNotStrip
13 | import com.margelo.nitro.views.HybridView
14 |
15 | /**
16 | * A Kotlin class representing the NitroImageView HybridObject.
17 | * Implement this abstract class to create Kotlin-based instances of NitroImageView.
18 | */
19 | @DoNotStrip
20 | @Keep
21 | @Suppress(
22 | "KotlinJniMissingFunction", "unused",
23 | "RedundantSuppression", "RedundantUnitReturnType", "SimpleRedundantLet",
24 | "LocalVariableName", "PropertyName", "PrivatePropertyName", "FunctionName"
25 | )
26 | abstract class HybridNitroImageViewSpec: HybridView() {
27 | @DoNotStrip
28 | private var mHybridData: HybridData = initHybrid()
29 |
30 | init {
31 | super.updateNative(mHybridData)
32 | }
33 |
34 | override fun updateNative(hybridData: HybridData) {
35 | mHybridData = hybridData
36 | super.updateNative(hybridData)
37 | }
38 |
39 | // Properties
40 | @get:DoNotStrip
41 | @get:Keep
42 | @set:DoNotStrip
43 | @set:Keep
44 | abstract var image: Variant_HybridImageSpec_HybridImageLoaderSpec?
45 |
46 | @get:DoNotStrip
47 | @get:Keep
48 | @set:DoNotStrip
49 | @set:Keep
50 | abstract var resizeMode: ResizeMode?
51 |
52 | @get:DoNotStrip
53 | @get:Keep
54 | @set:DoNotStrip
55 | @set:Keep
56 | abstract var recyclingKey: String?
57 |
58 | // Methods
59 |
60 |
61 | private external fun initHybrid(): HybridData
62 |
63 | companion object {
64 | protected const val TAG = "HybridNitroImageViewSpec"
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/swift/Color.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// Color.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | import NitroModules
9 |
10 | /**
11 | * Represents an instance of `Color`, backed by a C++ struct.
12 | */
13 | public typealias Color = margelo.nitro.image.Color
14 |
15 | public extension Color {
16 | private typealias bridge = margelo.nitro.image.bridge.swift
17 |
18 | /**
19 | * Create a new instance of `Color`.
20 | */
21 | init(r: Double, g: Double, b: Double, a: Double?) {
22 | self.init(r, g, b, { () -> bridge.std__optional_double_ in
23 | if let __unwrappedValue = a {
24 | return bridge.create_std__optional_double_(__unwrappedValue)
25 | } else {
26 | return .init()
27 | }
28 | }())
29 | }
30 |
31 | var r: Double {
32 | @inline(__always)
33 | get {
34 | return self.__r
35 | }
36 | @inline(__always)
37 | set {
38 | self.__r = newValue
39 | }
40 | }
41 |
42 | var g: Double {
43 | @inline(__always)
44 | get {
45 | return self.__g
46 | }
47 | @inline(__always)
48 | set {
49 | self.__g = newValue
50 | }
51 | }
52 |
53 | var b: Double {
54 | @inline(__always)
55 | get {
56 | return self.__b
57 | }
58 | @inline(__always)
59 | set {
60 | self.__b = newValue
61 | }
62 | }
63 |
64 | var a: Double? {
65 | @inline(__always)
66 | get {
67 | return self.__a.value
68 | }
69 | @inline(__always)
70 | set {
71 | self.__a = { () -> bridge.std__optional_double_ in
72 | if let __unwrappedValue = newValue {
73 | return bridge.create_std__optional_double_(__unwrappedValue)
74 | } else {
75 | return .init()
76 | }
77 | }()
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/src/useImage.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 | import { type AsyncImageSource, isHybridObject } from "./AsyncImageSource";
3 | import { loadImage } from "./loadImage";
4 | import { markHybridObject } from "./markHybridObject";
5 | import type { Image } from "./specs/Image.nitro";
6 |
7 | type Result =
8 | // Loading State
9 | | {
10 | image: undefined;
11 | error: undefined;
12 | }
13 | // Loaded state
14 | | {
15 | image: Image;
16 | error: undefined;
17 | }
18 | // Error state
19 | | {
20 | image: undefined;
21 | error: Error;
22 | };
23 |
24 | /**
25 | * A hook to asynchronously load an image from the
26 | * given {@linkcode AsyncImageSource} into memory.
27 | * @example
28 | * ```ts
29 | * const { image, error } = useImage({ filePath: '/tmp/image.jpg' })
30 | * ```
31 | */
32 | export function useImage(source: AsyncImageSource): Result {
33 | const [image, setImage] = useState({
34 | image: undefined,
35 | error: undefined,
36 | });
37 |
38 | // biome-ignore lint: The dependencies array is a bit hacky.
39 | useEffect(() => {
40 | (async () => {
41 | try {
42 | // 1. Create the Image/ImageLoader instance
43 | const result = await loadImage(source);
44 | // 2. Add `__source` as a property on the JS side so React diffs properly
45 | markHybridObject(result, source);
46 | // 3. Update the state
47 | setImage({ image: result, error: undefined });
48 | } catch (e) {
49 | const error = e instanceof Error ? e : new Error(`${e}`);
50 | setImage({ image: undefined, error: error });
51 | }
52 | })();
53 | }, [isHybridObject(source) ? source : JSON.stringify(source)]);
54 |
55 | return image;
56 | }
57 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/HybridImageUtilsSpec.kt:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridImageUtilsSpec.kt
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | package com.margelo.nitro.image
9 |
10 | import androidx.annotation.Keep
11 | import com.facebook.jni.HybridData
12 | import com.facebook.proguard.annotations.DoNotStrip
13 | import com.margelo.nitro.core.ArrayBuffer
14 | import com.margelo.nitro.core.HybridObject
15 |
16 | /**
17 | * A Kotlin class representing the ImageUtils HybridObject.
18 | * Implement this abstract class to create Kotlin-based instances of ImageUtils.
19 | */
20 | @DoNotStrip
21 | @Keep
22 | @Suppress(
23 | "KotlinJniMissingFunction", "unused",
24 | "RedundantSuppression", "RedundantUnitReturnType", "SimpleRedundantLet",
25 | "LocalVariableName", "PropertyName", "PrivatePropertyName", "FunctionName"
26 | )
27 | abstract class HybridImageUtilsSpec: HybridObject() {
28 | @DoNotStrip
29 | private var mHybridData: HybridData = initHybrid()
30 |
31 | init {
32 | super.updateNative(mHybridData)
33 | }
34 |
35 | override fun updateNative(hybridData: HybridData) {
36 | mHybridData = hybridData
37 | super.updateNative(hybridData)
38 | }
39 |
40 | // Properties
41 | @get:DoNotStrip
42 | @get:Keep
43 | abstract val supportsHeicLoading: Boolean
44 |
45 | @get:DoNotStrip
46 | @get:Keep
47 | abstract val supportsHeicWriting: Boolean
48 |
49 | // Methods
50 | @DoNotStrip
51 | @Keep
52 | abstract fun thumbHashToBase64String(thumbhash: ArrayBuffer): String
53 |
54 | @DoNotStrip
55 | @Keep
56 | abstract fun thumbhashFromBase64String(thumbhashBase64: String): ArrayBuffer
57 |
58 | private external fun initHybrid(): HybridData
59 |
60 | companion object {
61 | protected const val TAG = "HybridImageUtilsSpec"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/nitrogen/generated/android/kotlin/com/margelo/nitro/web/image/AsyncImageLoadOptions.kt:
--------------------------------------------------------------------------------
1 | ///
2 | /// AsyncImageLoadOptions.kt
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | package com.margelo.nitro.web.image
9 |
10 | import androidx.annotation.Keep
11 | import com.facebook.proguard.annotations.DoNotStrip
12 |
13 |
14 | /**
15 | * Represents the JavaScript object/struct "AsyncImageLoadOptions".
16 | */
17 | @DoNotStrip
18 | @Keep
19 | data class AsyncImageLoadOptions(
20 | @DoNotStrip
21 | @Keep
22 | val priority: AsyncImagePriority?,
23 | @DoNotStrip
24 | @Keep
25 | val forceRefresh: Boolean?,
26 | @DoNotStrip
27 | @Keep
28 | val cacheKey: String?,
29 | @DoNotStrip
30 | @Keep
31 | val continueInBackground: Boolean?,
32 | @DoNotStrip
33 | @Keep
34 | val allowInvalidSSLCertificates: Boolean?,
35 | @DoNotStrip
36 | @Keep
37 | val scaleDownLargeImages: Boolean?,
38 | @DoNotStrip
39 | @Keep
40 | val queryMemoryDataSync: Boolean?,
41 | @DoNotStrip
42 | @Keep
43 | val queryDiskDataSync: Boolean?,
44 | @DoNotStrip
45 | @Keep
46 | val decodeImage: Boolean?
47 | ) {
48 | private companion object {
49 | /**
50 | * Constructor called from C++
51 | */
52 | @DoNotStrip
53 | @Keep
54 | @Suppress("unused")
55 | @JvmStatic
56 | private fun fromCpp(priority: AsyncImagePriority?, forceRefresh: Boolean?, cacheKey: String?, continueInBackground: Boolean?, allowInvalidSSLCertificates: Boolean?, scaleDownLargeImages: Boolean?, queryMemoryDataSync: Boolean?, queryDiskDataSync: Boolean?, decodeImage: Boolean?): AsyncImageLoadOptions {
57 | return AsyncImageLoadOptions(priority, forceRefresh, cacheKey, continueInBackground, allowInvalidSSLCertificates, scaleDownLargeImages, queryMemoryDataSync, queryDiskDataSync, decodeImage)
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/swift/PixelFormat.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// PixelFormat.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | /**
9 | * Represents the JS union `PixelFormat`, backed by a C++ enum.
10 | */
11 | public typealias PixelFormat = margelo.nitro.image.PixelFormat
12 |
13 | public extension PixelFormat {
14 | /**
15 | * Get a PixelFormat for the given String value, or
16 | * return `nil` if the given value was invalid/unknown.
17 | */
18 | init?(fromString string: String) {
19 | switch string {
20 | case "ARGB":
21 | self = .argb
22 | case "BGRA":
23 | self = .bgra
24 | case "ABGR":
25 | self = .abgr
26 | case "RGBA":
27 | self = .rgba
28 | case "XRGB":
29 | self = .xrgb
30 | case "BGRX":
31 | self = .bgrx
32 | case "XBGR":
33 | self = .xbgr
34 | case "RGBX":
35 | self = .rgbx
36 | case "RGB":
37 | self = .rgb
38 | case "BGR":
39 | self = .bgr
40 | case "unknown":
41 | self = .unknown
42 | default:
43 | return nil
44 | }
45 | }
46 |
47 | /**
48 | * Get the String value this PixelFormat represents.
49 | */
50 | var stringValue: String {
51 | switch self {
52 | case .argb:
53 | return "ARGB"
54 | case .bgra:
55 | return "BGRA"
56 | case .abgr:
57 | return "ABGR"
58 | case .rgba:
59 | return "RGBA"
60 | case .xrgb:
61 | return "XRGB"
62 | case .bgrx:
63 | return "BGRX"
64 | case .xbgr:
65 | return "XBGR"
66 | case .rgbx:
67 | return "RGBX"
68 | case .rgb:
69 | return "RGB"
70 | case .bgr:
71 | return "BGR"
72 | case .unknown:
73 | return "unknown"
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/ios/HybridWebImageLoader.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HybridWebImageLoader.swift
3 | // NitroWebImage
4 | //
5 | // Created by Marc Rousavy on 28.07.25.
6 | //
7 |
8 | import NitroModules
9 | import SDWebImage
10 | import NitroImage
11 |
12 | fileprivate class HybridImage: HybridImageSpec, NativeImage {
13 | let uiImage: UIImage
14 | init(uiImage: UIImage) {
15 | self.uiImage = uiImage
16 | super.init()
17 | }
18 | }
19 |
20 | class HybridWebImageLoader: HybridImageLoaderSpec {
21 | private let url: URL
22 | private let options: AsyncImageLoadOptions?
23 |
24 | init(url: URL, options: AsyncImageLoadOptions?) {
25 | self.url = url
26 | self.options = options
27 | super.init()
28 | }
29 |
30 | func loadImage() throws -> Promise<(any HybridImageSpec)> {
31 | return Self.loadImage(url: self.url, options: self.options)
32 | }
33 |
34 | func requestImage(forView view: (any HybridNitroImageViewSpec)) throws {
35 | guard let view = view as? NativeImageView else { throw RuntimeError.error(withMessage: "Invalid view type!") }
36 |
37 | let webImageOptions = options?.toSDWebImageOptions() ?? []
38 | let webImageContext = options?.toSDWebImageContext()
39 | view.imageView.sd_setImage(with: url,
40 | placeholderImage: view.imageView.image,
41 | options: webImageOptions,
42 | context: webImageContext)
43 | }
44 |
45 | func dropImage(forView view: (any HybridNitroImageViewSpec)) throws {
46 | // TODO: Do we need to reset the image here or not?
47 | }
48 |
49 | public static func loadImage(url: URL, options: AsyncImageLoadOptions?) -> Promise {
50 | return Promise.async {
51 | let uiImage = try await SDWebImageManager.shared.loadImage(with: url,
52 | options: options)
53 | return HybridImage(uiImage: uiImage)
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/nitrogen/generated/ios/swift/HybridWebImageFactorySpec.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridWebImageFactorySpec.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | import Foundation
9 | import NitroModules
10 | import NitroImage
11 | import NitroModules
12 |
13 | /// See ``HybridWebImageFactorySpec``
14 | public protocol HybridWebImageFactorySpec_protocol: HybridObject {
15 | // Properties
16 |
17 |
18 | // Methods
19 | func createWebImageLoader(url: String, options: AsyncImageLoadOptions?) throws -> (any HybridImageLoaderSpec)
20 | func loadFromURLAsync(url: String, options: AsyncImageLoadOptions?) throws -> Promise<(any HybridImageSpec)>
21 | func preload(url: String) throws -> Void
22 | }
23 |
24 | /// See ``HybridWebImageFactorySpec``
25 | open class HybridWebImageFactorySpec_base {
26 | private weak var cxxWrapper: HybridWebImageFactorySpec_cxx? = nil
27 | public init() { }
28 | public func getCxxWrapper() -> HybridWebImageFactorySpec_cxx {
29 | #if DEBUG
30 | guard self is HybridWebImageFactorySpec else {
31 | fatalError("`self` is not a `HybridWebImageFactorySpec`! Did you accidentally inherit from `HybridWebImageFactorySpec_base` instead of `HybridWebImageFactorySpec`?")
32 | }
33 | #endif
34 | if let cxxWrapper = self.cxxWrapper {
35 | return cxxWrapper
36 | } else {
37 | let cxxWrapper = HybridWebImageFactorySpec_cxx(self as! HybridWebImageFactorySpec)
38 | self.cxxWrapper = cxxWrapper
39 | return cxxWrapper
40 | }
41 | }
42 | }
43 |
44 | /**
45 | * A Swift base-protocol representing the WebImageFactory HybridObject.
46 | * Implement this protocol to create Swift-based instances of WebImageFactory.
47 | * ```swift
48 | * class HybridWebImageFactory : HybridWebImageFactorySpec {
49 | * // ...
50 | * }
51 | * ```
52 | */
53 | public typealias HybridWebImageFactorySpec = HybridWebImageFactorySpec_protocol & HybridWebImageFactorySpec_base
54 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/shared/c++/HybridImageUtilsSpec.hpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridImageUtilsSpec.hpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #pragma once
9 |
10 | #if __has_include()
11 | #include
12 | #else
13 | #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14 | #endif
15 |
16 |
17 |
18 | #include
19 | #include
20 |
21 | namespace margelo::nitro::image {
22 |
23 | using namespace margelo::nitro;
24 |
25 | /**
26 | * An abstract base class for `ImageUtils`
27 | * Inherit this class to create instances of `HybridImageUtilsSpec` in C++.
28 | * You must explicitly call `HybridObject`'s constructor yourself, because it is virtual.
29 | * @example
30 | * ```cpp
31 | * class HybridImageUtils: public HybridImageUtilsSpec {
32 | * public:
33 | * HybridImageUtils(...): HybridObject(TAG) { ... }
34 | * // ...
35 | * };
36 | * ```
37 | */
38 | class HybridImageUtilsSpec: public virtual HybridObject {
39 | public:
40 | // Constructor
41 | explicit HybridImageUtilsSpec(): HybridObject(TAG) { }
42 |
43 | // Destructor
44 | ~HybridImageUtilsSpec() override = default;
45 |
46 | public:
47 | // Properties
48 | virtual bool getSupportsHeicLoading() = 0;
49 | virtual bool getSupportsHeicWriting() = 0;
50 |
51 | public:
52 | // Methods
53 | virtual std::string thumbHashToBase64String(const std::shared_ptr& thumbhash) = 0;
54 | virtual std::shared_ptr thumbhashFromBase64String(const std::string& thumbhashBase64) = 0;
55 |
56 | protected:
57 | // Hybrid Setup
58 | void loadHybridMethods() override;
59 |
60 | protected:
61 | // Tag for logging
62 | static constexpr auto TAG = "ImageUtils";
63 | };
64 |
65 | } // namespace margelo::nitro::image
66 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/rn_edit_text_material.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
22 |
23 |
24 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_HybridImageSpec_.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// Func_void_std__shared_ptr_HybridImageSpec_.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | import NitroModules
9 |
10 |
11 | /**
12 | * Wraps a Swift `(_ value: (any HybridImageSpec)) -> Void` as a class.
13 | * This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`.
14 | */
15 | public final class Func_void_std__shared_ptr_HybridImageSpec_ {
16 | public typealias bridge = margelo.nitro.image.bridge.swift
17 |
18 | private let closure: (_ value: (any HybridImageSpec)) -> Void
19 |
20 | public init(_ closure: @escaping (_ value: (any HybridImageSpec)) -> Void) {
21 | self.closure = closure
22 | }
23 |
24 | @inline(__always)
25 | public func call(value: bridge.std__shared_ptr_HybridImageSpec_) -> Void {
26 | self.closure({ () -> HybridImageSpec in
27 | let __unsafePointer = bridge.get_std__shared_ptr_HybridImageSpec_(value)
28 | let __instance = HybridImageSpec_cxx.fromUnsafe(__unsafePointer)
29 | return __instance.getHybridImageSpec()
30 | }())
31 | }
32 |
33 | /**
34 | * Casts this instance to a retained unsafe raw pointer.
35 | * This acquires one additional strong reference on the object!
36 | */
37 | @inline(__always)
38 | public func toUnsafe() -> UnsafeMutableRawPointer {
39 | return Unmanaged.passRetained(self).toOpaque()
40 | }
41 |
42 | /**
43 | * Casts an unsafe pointer to a `Func_void_std__shared_ptr_HybridImageSpec_`.
44 | * The pointer has to be a retained opaque `Unmanaged`.
45 | * This removes one strong reference from the object!
46 | */
47 | @inline(__always)
48 | public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void_std__shared_ptr_HybridImageSpec_ {
49 | return Unmanaged.fromOpaque(pointer).takeRetainedValue()
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/nitrogen/generated/android/kotlin/com/margelo/nitro/web/image/HybridWebImageFactorySpec.kt:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridWebImageFactorySpec.kt
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | package com.margelo.nitro.web.image
9 |
10 | import androidx.annotation.Keep
11 | import com.facebook.jni.HybridData
12 | import com.facebook.proguard.annotations.DoNotStrip
13 | import com.margelo.nitro.image.HybridImageLoaderSpec
14 | import com.margelo.nitro.image.HybridImageSpec
15 | import com.margelo.nitro.core.Promise
16 | import com.margelo.nitro.core.HybridObject
17 |
18 | /**
19 | * A Kotlin class representing the WebImageFactory HybridObject.
20 | * Implement this abstract class to create Kotlin-based instances of WebImageFactory.
21 | */
22 | @DoNotStrip
23 | @Keep
24 | @Suppress(
25 | "KotlinJniMissingFunction", "unused",
26 | "RedundantSuppression", "RedundantUnitReturnType", "SimpleRedundantLet",
27 | "LocalVariableName", "PropertyName", "PrivatePropertyName", "FunctionName"
28 | )
29 | abstract class HybridWebImageFactorySpec: HybridObject() {
30 | @DoNotStrip
31 | private var mHybridData: HybridData = initHybrid()
32 |
33 | init {
34 | super.updateNative(mHybridData)
35 | }
36 |
37 | override fun updateNative(hybridData: HybridData) {
38 | mHybridData = hybridData
39 | super.updateNative(hybridData)
40 | }
41 |
42 | // Properties
43 |
44 |
45 | // Methods
46 | @DoNotStrip
47 | @Keep
48 | abstract fun createWebImageLoader(url: String, options: AsyncImageLoadOptions?): com.margelo.nitro.image.HybridImageLoaderSpec
49 |
50 | @DoNotStrip
51 | @Keep
52 | abstract fun loadFromURLAsync(url: String, options: AsyncImageLoadOptions?): Promise
53 |
54 | @DoNotStrip
55 | @Keep
56 | abstract fun preload(url: String): Unit
57 |
58 | private external fun initHybrid(): HybridData
59 |
60 | companion object {
61 | protected const val TAG = "HybridWebImageFactorySpec"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/NitroImageAutolinking.mm:
--------------------------------------------------------------------------------
1 | ///
2 | /// NitroImageAutolinking.mm
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #import
9 | #import
10 | #import "NitroImage-Swift-Cxx-Umbrella.hpp"
11 | #import
12 |
13 | #include "HybridImageFactorySpecSwift.hpp"
14 | #include "HybridImageLoaderFactorySpecSwift.hpp"
15 | #include "HybridImageUtilsSpecSwift.hpp"
16 | #include "HybridNitroImageViewSpecSwift.hpp"
17 |
18 | @interface NitroImageAutolinking : NSObject
19 | @end
20 |
21 | @implementation NitroImageAutolinking
22 |
23 | + (void) load {
24 | using namespace margelo::nitro;
25 | using namespace margelo::nitro::image;
26 |
27 | HybridObjectRegistry::registerHybridObjectConstructor(
28 | "ImageFactory",
29 | []() -> std::shared_ptr {
30 | std::shared_ptr hybridObject = NitroImage::NitroImageAutolinking::createImageFactory();
31 | return hybridObject;
32 | }
33 | );
34 | HybridObjectRegistry::registerHybridObjectConstructor(
35 | "ImageLoaderFactory",
36 | []() -> std::shared_ptr {
37 | std::shared_ptr hybridObject = NitroImage::NitroImageAutolinking::createImageLoaderFactory();
38 | return hybridObject;
39 | }
40 | );
41 | HybridObjectRegistry::registerHybridObjectConstructor(
42 | "ImageUtils",
43 | []() -> std::shared_ptr {
44 | std::shared_ptr hybridObject = NitroImage::NitroImageAutolinking::createImageUtils();
45 | return hybridObject;
46 | }
47 | );
48 | HybridObjectRegistry::registerHybridObjectConstructor(
49 | "NitroImageView",
50 | []() -> std::shared_ptr {
51 | std::shared_ptr hybridObject = NitroImage::NitroImageAutolinking::createNitroImageView();
52 | return hybridObject;
53 | }
54 | );
55 | }
56 |
57 | @end
58 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/shared/c++/HybridImageFactorySpec.cpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridImageFactorySpec.cpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #include "HybridImageFactorySpec.hpp"
9 |
10 | namespace margelo::nitro::image {
11 |
12 | void HybridImageFactorySpec::loadHybridMethods() {
13 | // load base methods/properties
14 | HybridObject::loadHybridMethods();
15 | // load custom methods/properties
16 | registerHybrids(this, [](Prototype& prototype) {
17 | prototype.registerHybridMethod("createBlankImage", &HybridImageFactorySpec::createBlankImage);
18 | prototype.registerHybridMethod("createBlankImageAsync", &HybridImageFactorySpec::createBlankImageAsync);
19 | prototype.registerHybridMethod("loadFromFile", &HybridImageFactorySpec::loadFromFile);
20 | prototype.registerHybridMethod("loadFromFileAsync", &HybridImageFactorySpec::loadFromFileAsync);
21 | prototype.registerHybridMethod("loadFromResources", &HybridImageFactorySpec::loadFromResources);
22 | prototype.registerHybridMethod("loadFromResourcesAsync", &HybridImageFactorySpec::loadFromResourcesAsync);
23 | prototype.registerHybridMethod("loadFromSymbol", &HybridImageFactorySpec::loadFromSymbol);
24 | prototype.registerHybridMethod("loadFromRawPixelData", &HybridImageFactorySpec::loadFromRawPixelData);
25 | prototype.registerHybridMethod("loadFromRawPixelDataAsync", &HybridImageFactorySpec::loadFromRawPixelDataAsync);
26 | prototype.registerHybridMethod("loadFromEncodedImageData", &HybridImageFactorySpec::loadFromEncodedImageData);
27 | prototype.registerHybridMethod("loadFromEncodedImageDataAsync", &HybridImageFactorySpec::loadFromEncodedImageDataAsync);
28 | prototype.registerHybridMethod("loadFromThumbHash", &HybridImageFactorySpec::loadFromThumbHash);
29 | prototype.registerHybridMethod("loadFromThumbHashAsync", &HybridImageFactorySpec::loadFromThumbHashAsync);
30 | });
31 | }
32 |
33 | } // namespace margelo::nitro::image
34 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-web-image/android/src/main/java/com/margelo/nitro/web/image/ImageRequestBuilder+applyOptions.kt:
--------------------------------------------------------------------------------
1 | package com.margelo.nitro.web.image
2 |
3 | import coil3.annotation.ExperimentalCoilApi
4 | import coil3.decode.BlackholeDecoder
5 | import coil3.request.CachePolicy
6 | import coil3.request.ImageRequest
7 | import coil3.size.Precision
8 |
9 | @OptIn(ExperimentalCoilApi::class)
10 | fun ImageRequest.Builder.applyOptions(options: AsyncImageLoadOptions?): ImageRequest.Builder {
11 | if (options == null) return this
12 | var result = this
13 |
14 | if (options.priority != null) {
15 | options.priority.toCoroutineContext()?.let { context ->
16 | result = result.coroutineContext(context)
17 | }
18 | }
19 |
20 | if (options.forceRefresh == true) {
21 | // don't allow reading from cache, only writing.
22 | result = result.diskCachePolicy(CachePolicy.WRITE_ONLY)
23 | result = result.memoryCachePolicy(CachePolicy.WRITE_ONLY)
24 | result = result.networkCachePolicy(CachePolicy.WRITE_ONLY)
25 | }
26 |
27 | if (options.continueInBackground == true) {
28 | // TODO: Implement .continueInBackground
29 | }
30 |
31 | if (options.allowInvalidSSLCertificates == true) {
32 | // TODO: Implement .allowInvalidSSLCertificates
33 | }
34 |
35 | if (options.scaleDownLargeImages == true) {
36 | // Limit to 4096x4096 (~60 MB)
37 | result = result.size(4096, 4096)
38 | result = result.precision(Precision.INEXACT)
39 | }
40 |
41 | if (options.queryMemoryDataSync == true) {
42 | // TODO: Implement .queryMemoryDataSync
43 | }
44 |
45 | if (options.queryDiskDataSync == true) {
46 | // TODO: Implement .queryDiskDataSync
47 | }
48 |
49 | if (options.decodeImage == false) {
50 | result = result.decoderFactory(BlackholeDecoder.Factory())
51 | }
52 |
53 | if (options.cacheKey != null) {
54 | result = result.diskCacheKey(options.cacheKey)
55 | result = result.memoryCacheKey(options.cacheKey)
56 | }
57 |
58 | return result
59 | }
60 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/android/c++/views/JHybridNitroImageViewStateUpdater.hpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// JHybridNitroImageViewStateUpdater.hpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #pragma once
9 |
10 | #ifndef RN_SERIALIZABLE_STATE
11 | #error NitroImage was compiled without the 'RN_SERIALIZABLE_STATE' flag. This flag is required for Nitro Views - set it in your CMakeLists!
12 | #endif
13 |
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include "JHybridNitroImageViewSpec.hpp"
21 | #include "views/HybridNitroImageViewComponent.hpp"
22 |
23 | namespace margelo::nitro::image::views {
24 |
25 | using namespace facebook;
26 |
27 | class JHybridNitroImageViewStateUpdater: public jni::JavaClass {
28 | public:
29 | static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/image/views/HybridNitroImageViewStateUpdater;";
30 |
31 | public:
32 | static void updateViewProps(jni::alias_ref /* class */,
33 | jni::alias_ref view,
34 | jni::alias_ref stateWrapperInterface);
35 |
36 | public:
37 | static void registerNatives() {
38 | // Register JNI calls
39 | javaClassStatic()->registerNatives({
40 | makeNativeMethod("updateViewProps", JHybridNitroImageViewStateUpdater::updateViewProps),
41 | });
42 | // Register React Native view component descriptor
43 | auto provider = react::concreteComponentDescriptorProvider();
44 | auto providerRegistry = react::CoreComponentsRegistry::sharedProviderRegistry();
45 | providerRegistry->add(provider);
46 | }
47 | };
48 |
49 | } // namespace margelo::nitro::image::views
50 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | # AndroidX package structure to make it clearer which packages are bundled with the
21 | # Android operating system, and which are packaged with your app's APK
22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
23 | android.useAndroidX=true
24 |
25 | # Use this property to specify which architecture you want to build.
26 | # You can also override it from the CLI using
27 | # ./gradlew -PreactNativeArchitectures=x86_64
28 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
29 |
30 | # Use this property to enable support to the new architecture.
31 | # This will allow you to use TurboModules and the Fabric render in
32 | # your application. You should enable this flag either if you want
33 | # to write custom TurboModules/Fabric components OR use libraries that
34 | # are providing them.
35 | newArchEnabled=true
36 |
37 | # Use this property to enable or disable the Hermes JS engine.
38 | # If set to false, you will be using JSC instead.
39 | hermesEnabled=true
40 |
41 | # Use this property to enable edge-to-edge display support.
42 | # This allows your app to draw behind system bars for an immersive UI.
43 | # Note: Only works with ReactActivity and should not be used with custom Activity.
44 | edgeToEdgeEnabled=false
45 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/Variant_HybridImageSpec_HybridImageLoaderSpec.kt:
--------------------------------------------------------------------------------
1 | ///
2 | /// Variant_HybridImageSpec_HybridImageLoaderSpec.kt
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | package com.margelo.nitro.image
9 |
10 | import com.facebook.proguard.annotations.DoNotStrip
11 |
12 |
13 | /**
14 | * Represents the TypeScript variant "HybridImageSpec | HybridImageLoaderSpec".
15 | */
16 | @Suppress("ClassName")
17 | @DoNotStrip
18 | sealed class Variant_HybridImageSpec_HybridImageLoaderSpec {
19 | @DoNotStrip
20 | data class First(@DoNotStrip val value: HybridImageSpec): Variant_HybridImageSpec_HybridImageLoaderSpec()
21 | @DoNotStrip
22 | data class Second(@DoNotStrip val value: HybridImageLoaderSpec): Variant_HybridImageSpec_HybridImageLoaderSpec()
23 |
24 | @Deprecated("getAs() is not type-safe. Use fold/asFirstOrNull/asSecondOrNull instead.", level = DeprecationLevel.ERROR)
25 | inline fun getAs(): T? = when (this) {
26 | is First -> value as? T
27 | is Second -> value as? T
28 | }
29 |
30 | val isFirst: Boolean
31 | get() = this is First
32 | val isSecond: Boolean
33 | get() = this is Second
34 |
35 | fun asFirstOrNull(): HybridImageSpec? {
36 | val value = (this as? First)?.value ?: return null
37 | return value
38 | }
39 | fun asSecondOrNull(): HybridImageLoaderSpec? {
40 | val value = (this as? Second)?.value ?: return null
41 | return value
42 | }
43 |
44 | inline fun match(first: (HybridImageSpec) -> R, second: (HybridImageLoaderSpec) -> R): R {
45 | return when (this) {
46 | is First -> first(value)
47 | is Second -> second(value)
48 | }
49 | }
50 |
51 | companion object {
52 | @JvmStatic
53 | @DoNotStrip
54 | fun create(value: HybridImageSpec): Variant_HybridImageSpec_HybridImageLoaderSpec = First(value)
55 | @JvmStatic
56 | @DoNotStrip
57 | fun create(value: HybridImageLoaderSpec): Variant_HybridImageSpec_HybridImageLoaderSpec = Second(value)
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/HybridImageLoaderFactorySpec.kt:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridImageLoaderFactorySpec.kt
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | package com.margelo.nitro.image
9 |
10 | import androidx.annotation.Keep
11 | import com.facebook.jni.HybridData
12 | import com.facebook.proguard.annotations.DoNotStrip
13 | import com.margelo.nitro.core.HybridObject
14 |
15 | /**
16 | * A Kotlin class representing the ImageLoaderFactory HybridObject.
17 | * Implement this abstract class to create Kotlin-based instances of ImageLoaderFactory.
18 | */
19 | @DoNotStrip
20 | @Keep
21 | @Suppress(
22 | "KotlinJniMissingFunction", "unused",
23 | "RedundantSuppression", "RedundantUnitReturnType", "SimpleRedundantLet",
24 | "LocalVariableName", "PropertyName", "PrivatePropertyName", "FunctionName"
25 | )
26 | abstract class HybridImageLoaderFactorySpec: HybridObject() {
27 | @DoNotStrip
28 | private var mHybridData: HybridData = initHybrid()
29 |
30 | init {
31 | super.updateNative(mHybridData)
32 | }
33 |
34 | override fun updateNative(hybridData: HybridData) {
35 | mHybridData = hybridData
36 | super.updateNative(hybridData)
37 | }
38 |
39 | // Properties
40 |
41 |
42 | // Methods
43 | @DoNotStrip
44 | @Keep
45 | abstract fun createFileImageLoader(filePath: String): HybridImageLoaderSpec
46 |
47 | @DoNotStrip
48 | @Keep
49 | abstract fun createResourceImageLoader(name: String): HybridImageLoaderSpec
50 |
51 | @DoNotStrip
52 | @Keep
53 | abstract fun createSymbolImageLoader(symbolName: String): HybridImageLoaderSpec
54 |
55 | @DoNotStrip
56 | @Keep
57 | abstract fun createRawPixelDataImageLoader(data: RawPixelData): HybridImageLoaderSpec
58 |
59 | @DoNotStrip
60 | @Keep
61 | abstract fun createEncodedImageDataImageLoader(data: EncodedImageData): HybridImageLoaderSpec
62 |
63 | private external fun initHybrid(): HybridData
64 |
65 | companion object {
66 | protected const val TAG = "HybridImageLoaderFactorySpec"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/swift/HybridImageLoaderFactorySpec.swift:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridImageLoaderFactorySpec.swift
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | import Foundation
9 | import NitroModules
10 |
11 | /// See ``HybridImageLoaderFactorySpec``
12 | public protocol HybridImageLoaderFactorySpec_protocol: HybridObject {
13 | // Properties
14 |
15 |
16 | // Methods
17 | func createFileImageLoader(filePath: String) throws -> (any HybridImageLoaderSpec)
18 | func createResourceImageLoader(name: String) throws -> (any HybridImageLoaderSpec)
19 | func createSymbolImageLoader(symbolName: String) throws -> (any HybridImageLoaderSpec)
20 | func createRawPixelDataImageLoader(data: RawPixelData) throws -> (any HybridImageLoaderSpec)
21 | func createEncodedImageDataImageLoader(data: EncodedImageData) throws -> (any HybridImageLoaderSpec)
22 | }
23 |
24 | /// See ``HybridImageLoaderFactorySpec``
25 | open class HybridImageLoaderFactorySpec_base {
26 | private weak var cxxWrapper: HybridImageLoaderFactorySpec_cxx? = nil
27 | public init() { }
28 | public func getCxxWrapper() -> HybridImageLoaderFactorySpec_cxx {
29 | #if DEBUG
30 | guard self is HybridImageLoaderFactorySpec else {
31 | fatalError("`self` is not a `HybridImageLoaderFactorySpec`! Did you accidentally inherit from `HybridImageLoaderFactorySpec_base` instead of `HybridImageLoaderFactorySpec`?")
32 | }
33 | #endif
34 | if let cxxWrapper = self.cxxWrapper {
35 | return cxxWrapper
36 | } else {
37 | let cxxWrapper = HybridImageLoaderFactorySpec_cxx(self as! HybridImageLoaderFactorySpec)
38 | self.cxxWrapper = cxxWrapper
39 | return cxxWrapper
40 | }
41 | }
42 | }
43 |
44 | /**
45 | * A Swift base-protocol representing the ImageLoaderFactory HybridObject.
46 | * Implement this protocol to create Swift-based instances of ImageLoaderFactory.
47 | * ```swift
48 | * class HybridImageLoaderFactory : HybridImageLoaderFactorySpec {
49 | * // ...
50 | * }
51 | * ```
52 | */
53 | public typealias HybridImageLoaderFactorySpec = HybridImageLoaderFactorySpec_protocol & HybridImageLoaderFactorySpec_base
54 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/shared/c++/HybridImageSpec.cpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// HybridImageSpec.cpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #include "HybridImageSpec.hpp"
9 |
10 | namespace margelo::nitro::image {
11 |
12 | void HybridImageSpec::loadHybridMethods() {
13 | // load base methods/properties
14 | HybridObject::loadHybridMethods();
15 | // load custom methods/properties
16 | registerHybrids(this, [](Prototype& prototype) {
17 | prototype.registerHybridGetter("width", &HybridImageSpec::getWidth);
18 | prototype.registerHybridGetter("height", &HybridImageSpec::getHeight);
19 | prototype.registerHybridMethod("toRawPixelData", &HybridImageSpec::toRawPixelData);
20 | prototype.registerHybridMethod("toRawPixelDataAsync", &HybridImageSpec::toRawPixelDataAsync);
21 | prototype.registerHybridMethod("toEncodedImageData", &HybridImageSpec::toEncodedImageData);
22 | prototype.registerHybridMethod("toEncodedImageDataAsync", &HybridImageSpec::toEncodedImageDataAsync);
23 | prototype.registerHybridMethod("resize", &HybridImageSpec::resize);
24 | prototype.registerHybridMethod("resizeAsync", &HybridImageSpec::resizeAsync);
25 | prototype.registerHybridMethod("rotate", &HybridImageSpec::rotate);
26 | prototype.registerHybridMethod("rotateAsync", &HybridImageSpec::rotateAsync);
27 | prototype.registerHybridMethod("crop", &HybridImageSpec::crop);
28 | prototype.registerHybridMethod("cropAsync", &HybridImageSpec::cropAsync);
29 | prototype.registerHybridMethod("saveToFileAsync", &HybridImageSpec::saveToFileAsync);
30 | prototype.registerHybridMethod("saveToTemporaryFileAsync", &HybridImageSpec::saveToTemporaryFileAsync);
31 | prototype.registerHybridMethod("toThumbHash", &HybridImageSpec::toThumbHash);
32 | prototype.registerHybridMethod("toThumbHashAsync", &HybridImageSpec::toThumbHashAsync);
33 | prototype.registerHybridMethod("renderInto", &HybridImageSpec::renderInto);
34 | prototype.registerHybridMethod("renderIntoAsync", &HybridImageSpec::renderIntoAsync);
35 | });
36 | }
37 |
38 | } // namespace margelo::nitro::image
39 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/ios/HybridImageLoader.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HybridImageLoader.swift
3 | // Pods
4 | //
5 | // Created by Marc Rousavy on 25.07.25.
6 | //
7 |
8 | import NitroModules
9 |
10 | class HybridImageLoader: HybridImageLoaderSpec {
11 | typealias LoadFunc = () throws -> Promise
12 | typealias RequestFunc = (_ for: any HybridNitroImageViewSpec) throws -> Void
13 | typealias DropFunc = (_ for: any HybridNitroImageViewSpec) throws -> Void
14 |
15 | private let load: LoadFunc
16 | private let requestImage: RequestFunc
17 | private let dropImage: DropFunc
18 |
19 | init(load: @escaping LoadFunc) {
20 | self.load = load
21 | self.requestImage = Self.defaultRequestFunc(forLoadFunc: load)
22 | self.dropImage = Self.defaultDropFunc()
23 | }
24 | init(load: @escaping LoadFunc,
25 | requestImage: @escaping RequestFunc,
26 | dropImage: @escaping DropFunc) {
27 | self.load = load
28 | self.requestImage = requestImage
29 | self.dropImage = dropImage
30 | }
31 |
32 | func loadImage() throws -> Promise {
33 | return try load()
34 | }
35 |
36 | func requestImage(forView view: any HybridNitroImageViewSpec) throws {
37 | return try requestImage(view)
38 | }
39 |
40 | func dropImage(forView view: any HybridNitroImageViewSpec) throws {
41 | return try dropImage(view)
42 | }
43 | }
44 |
45 | fileprivate extension HybridImageLoader {
46 | static func defaultRequestFunc(forLoadFunc loadFunc: @escaping LoadFunc) -> RequestFunc {
47 | return { view in
48 | guard let view = view as? NativeImageView else { return }
49 | let promise = try loadFunc()
50 | promise.then { image in
51 | guard let image = image as? NativeImage else { return }
52 | Task { @MainActor in
53 | view.imageView.image = image.uiImage
54 | }
55 | }
56 | promise.catch { _ in
57 | Task { @MainActor in
58 | view.imageView.image = nil
59 | }
60 | }
61 | }
62 | }
63 | static func defaultDropFunc() -> DropFunc {
64 | return { view in
65 | guard let view = view as? NativeImageView else { return }
66 | Task { @MainActor in
67 | view.imageView.image = nil
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/android/c++/JImageFormat.hpp:
--------------------------------------------------------------------------------
1 | ///
2 | /// JImageFormat.hpp
3 | /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | /// https://github.com/mrousavy/nitro
5 | /// Copyright © 2025 Marc Rousavy @ Margelo
6 | ///
7 |
8 | #pragma once
9 |
10 | #include
11 | #include "ImageFormat.hpp"
12 |
13 | namespace margelo::nitro::image {
14 |
15 | using namespace facebook;
16 |
17 | /**
18 | * The C++ JNI bridge between the C++ enum "ImageFormat" and the the Kotlin enum "ImageFormat".
19 | */
20 | struct JImageFormat final: public jni::JavaClass {
21 | public:
22 | static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/image/ImageFormat;";
23 |
24 | public:
25 | /**
26 | * Convert this Java/Kotlin-based enum to the C++ enum ImageFormat.
27 | */
28 | [[maybe_unused]]
29 | [[nodiscard]]
30 | ImageFormat toCpp() const {
31 | static const auto clazz = javaClassStatic();
32 | static const auto fieldOrdinal = clazz->getField("value");
33 | int ordinal = this->getFieldValue(fieldOrdinal);
34 | return static_cast(ordinal);
35 | }
36 |
37 | public:
38 | /**
39 | * Create a Java/Kotlin-based enum with the given C++ enum's value.
40 | */
41 | [[maybe_unused]]
42 | static jni::alias_ref fromCpp(ImageFormat value) {
43 | static const auto clazz = javaClassStatic();
44 | static const auto fieldJPG = clazz->getStaticField("JPG");
45 | static const auto fieldPNG = clazz->getStaticField("PNG");
46 | static const auto fieldHEIC = clazz->getStaticField("HEIC");
47 |
48 | switch (value) {
49 | case ImageFormat::JPG:
50 | return clazz->getStaticFieldValue(fieldJPG);
51 | case ImageFormat::PNG:
52 | return clazz->getStaticFieldValue(fieldPNG);
53 | case ImageFormat::HEIC:
54 | return clazz->getStaticFieldValue(fieldHEIC);
55 | default:
56 | std::string stringValue = std::to_string(static_cast(value));
57 | throw std::invalid_argument("Invalid enum value (" + stringValue + "!");
58 | }
59 | }
60 | };
61 |
62 | } // namespace margelo::nitro::image
63 |
--------------------------------------------------------------------------------
/packages/react-native-nitro-image/nitrogen/generated/ios/NitroImage+autolinking.rb:
--------------------------------------------------------------------------------
1 | #
2 | # NitroImage+autolinking.rb
3 | # This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4 | # https://github.com/mrousavy/nitro
5 | # Copyright © 2025 Marc Rousavy @ Margelo
6 | #
7 |
8 | # This is a Ruby script that adds all files generated by Nitrogen
9 | # to the given podspec.
10 | #
11 | # To use it, add this to your .podspec:
12 | # ```ruby
13 | # Pod::Spec.new do |spec|
14 | # # ...
15 | #
16 | # # Add all files generated by Nitrogen
17 | # load 'nitrogen/generated/ios/NitroImage+autolinking.rb'
18 | # add_nitrogen_files(spec)
19 | # end
20 | # ```
21 |
22 | def add_nitrogen_files(spec)
23 | Pod::UI.puts "[NitroModules] 🔥 NitroImage is boosted by nitro!"
24 |
25 | spec.dependency "NitroModules"
26 |
27 | current_source_files = Array(spec.attributes_hash['source_files'])
28 | spec.source_files = current_source_files + [
29 | # Generated cross-platform specs
30 | "nitrogen/generated/shared/**/*.{h,hpp,c,cpp,swift}",
31 | # Generated bridges for the cross-platform specs
32 | "nitrogen/generated/ios/**/*.{h,hpp,c,cpp,mm,swift}",
33 | ]
34 |
35 | current_public_header_files = Array(spec.attributes_hash['public_header_files'])
36 | spec.public_header_files = current_public_header_files + [
37 | # Generated specs
38 | "nitrogen/generated/shared/**/*.{h,hpp}",
39 | # Swift to C++ bridging helpers
40 | "nitrogen/generated/ios/NitroImage-Swift-Cxx-Bridge.hpp"
41 | ]
42 |
43 | current_private_header_files = Array(spec.attributes_hash['private_header_files'])
44 | spec.private_header_files = current_private_header_files + [
45 | # iOS specific specs
46 | "nitrogen/generated/ios/c++/**/*.{h,hpp}",
47 | # Views are framework-specific and should be private
48 | "nitrogen/generated/shared/**/views/**/*"
49 | ]
50 |
51 | current_pod_target_xcconfig = spec.attributes_hash['pod_target_xcconfig'] || {}
52 | spec.pod_target_xcconfig = current_pod_target_xcconfig.merge({
53 | # Use C++ 20
54 | "CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
55 | # Enables C++ <-> Swift interop (by default it's only C)
56 | "SWIFT_OBJC_INTEROP_MODE" => "objcxx",
57 | # Enables stricter modular headers
58 | "DEFINES_MODULE" => "YES",
59 | })
60 | end
61 |
--------------------------------------------------------------------------------