Skip to content

Commit 2b236ae

Browse files
committed
feat: Add HEIC to ImageFormats
1 parent a7ce27a commit 2b236ae

21 files changed

Lines changed: 107 additions & 38 deletions

README.md

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,22 @@ const image = useImage(require('./image.png'))
112112

113113
#### `ArrayBuffer`
114114

115-
The `Image` type can be converted to- and from- an `ArrayBuffer`, which gives you access to the raw pixel data in ARGB format:
115+
The `Image` type can be converted to- and from- an `ArrayBuffer`, which gives you access to the raw pixel data in an RGB format:
116116

117117
```ts
118118
const image = // ...
119-
const arrayBuffer = await image.toArrayBufferAsync()
120-
const sameImageCopied = await Images.loadFromArrayBufferAsync(arrayBuffer)
119+
const pixelData = await image.toRawPixelData()
120+
const sameImageCopied = await Images.loadFromRawPixelData(pixelData)
121+
```
122+
123+
#### `EncodedImageData`
124+
125+
The `Image` type can be encoded to- and decoded from- an `ArrayBuffer` using a container format like `jpg`, `png` or `heic`:
126+
127+
```ts
128+
const image = // ...
129+
const imageData = await image.toEncodedImageData('jpg', 90)
130+
const sameImageCopied = await Images.loadFromEncodedImageData(imageData)
121131
```
122132

123133
#### Resizing
@@ -143,8 +153,18 @@ const smaller = await webImage.cropAsync(100, 100, 50, 50)
143153
An in-memory `Image` object can also be written/saved to a file:
144154

145155
```ts
146-
const smaller = ...
147-
const path = await smaller.saveToTemporaryFileAsync('jpg', 90)
156+
const image = ...
157+
const path = await image.saveToTemporaryFileAsync('jpg', 90)
158+
```
159+
160+
#### Compressing
161+
162+
Images can be compressed using the `jpg` container format - either in-memory or when writing to a file:
163+
164+
```ts
165+
const image = ...
166+
const path = await image.saveToTemporaryFileAsync('jpg', 50) // 50% compression
167+
const compressed = await image.toEncodedImageData('jpg', 50) // 50% compression
148168
```
149169

150170
### Hooks

packages/react-native-nitro-image/ios/HybridImageFactory.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,19 @@
77

88
import Foundation
99
import NitroModules
10+
import UniformTypeIdentifiers
1011

1112
class HybridImageFactory: HybridImageFactorySpec {
13+
var supportsHEIC: Bool {
14+
if #unavailable(iOS 17.0) {
15+
// HEIC .heicData() is only available on iOS 17
16+
return false
17+
}
18+
// Check if the type is supported by the OS
19+
let types = CGImageDestinationCopyTypeIdentifiers() as! [String]
20+
return types.contains(UTType.heic.identifier)
21+
}
22+
1223
/**
1324
* Load Image from file path
1425
*/

packages/react-native-nitro-image/ios/ImageFormat+toUTType.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ extension ImageFormat {
1515
return .jpeg
1616
case .png:
1717
return .png
18+
case .heic:
19+
return .heic
1820
}
1921
}
2022
}

packages/react-native-nitro-image/ios/NativeImage.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,7 @@ public extension NativeImage {
9595
}
9696

9797
private func saveImage(to path: String, format: ImageFormat, quality: Double) throws {
98-
guard let data = uiImage.getData(in: format, quality: quality) else {
99-
throw RuntimeError.error(withMessage: "Failed to get Image data in format \(format.stringValue) with quality \(quality)!")
100-
}
98+
let data = try uiImage.getData(in: format, quality: quality)
10199
guard let url = URL(string: path) else {
102100
throw RuntimeError.error(withMessage: "The given path \"\(path)\" is not a valid URL!")
103101
}

packages/react-native-nitro-image/ios/UIImage+getData.swift

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,30 @@
66
//
77

88
import UIKit
9+
import NitroModules
910

1011
extension UIImage {
11-
func getData(in format: ImageFormat, quality: CGFloat) -> Data? {
12+
func getData(in format: ImageFormat, quality: CGFloat) throws -> Data {
1213
switch format {
1314
case .jpg:
14-
return self.jpegData(compressionQuality: quality)
15+
guard let data = self.jpegData(compressionQuality: quality) else {
16+
throw RuntimeError.error(withMessage: "Failed to compress \(size.width)x\(size.height) Image to JPEG! (Quality: \(quality))")
17+
}
18+
return data
1519
case .png:
16-
return self.pngData()
20+
guard let data = self.pngData() else {
21+
throw RuntimeError.error(withMessage: "Failed to convert \(size.width)x\(size.height) Image to PNG!")
22+
}
23+
return data
24+
case .heic:
25+
guard #available(iOS 17.0, *) else {
26+
throw RuntimeError.error(withMessage: "HEIC is only available on iOS 17.0 or higher! " +
27+
"Check ImageFactory.supportsHEIC before calling this method.")
28+
}
29+
guard let data = self.heicData() else {
30+
throw RuntimeError.error(withMessage: "Failed to convert \(size.width)x\(size.height) Image to HEIC!")
31+
}
32+
return data
1733
}
1834
}
1935
}

packages/react-native-nitro-image/ios/UIImage+toEncodedImageData.swift

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,11 @@ import CoreGraphics
1010
import NitroModules
1111

1212
extension UIImage {
13-
private func toData(format: ImageFormat, quality: Double = 1.0) throws -> Data {
14-
switch format {
15-
case .jpg:
16-
guard let data = self.jpegData(compressionQuality: quality) else {
17-
throw RuntimeError.error(withMessage: "Failed to compress Image into JPEG data!")
18-
}
19-
return data
20-
case .png:
21-
guard let data = self.pngData() else {
22-
throw RuntimeError.error(withMessage: "Failed to convert Image to PNG data!")
23-
}
24-
return data
25-
}
26-
}
27-
2813
/**
2914
* Returns encoded Image data of this Image (JPG, PNG, ...)
3015
*/
3116
func toEncodedImageData(format: ImageFormat, quality: Double = 1.0) throws -> EncodedImageData {
32-
let data = try toData(format: format, quality: quality)
17+
let data = try getData(in: format, quality: quality)
3318
let arrayBuffer = try ArrayBuffer.copy(data: data)
3419
return EncodedImageData(buffer: arrayBuffer,
3520
width: self.size.width,

packages/react-native-nitro-image/nitrogen/generated/android/c++/JHybridImageFactorySpec.cpp

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/react-native-nitro-image/nitrogen/generated/android/c++/JHybridImageFactorySpec.hpp

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/react-native-nitro-image/nitrogen/generated/android/c++/JImageFormat.hpp

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/react-native-nitro-image/nitrogen/generated/android/kotlin/com/margelo/nitro/image/HybridImageFactorySpec.kt

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)