expo-image-to-video 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  apply plugin: 'com.android.library'
2
2
 
3
3
  group = 'expo.modules.imagetovideo'
4
- version = '0.1.2'
4
+ version = '0.1.3'
5
5
 
6
6
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
7
7
  apply from: expoModulesCorePlugin
@@ -35,9 +35,27 @@ android {
35
35
  namespace "expo.modules.imagetovideo"
36
36
  defaultConfig {
37
37
  versionCode 1
38
- versionName "0.1.2"
38
+ versionName "0.1.3"
39
39
  }
40
40
  lintOptions {
41
41
  abortOnError false
42
42
  }
43
43
  }
44
+
45
+ dependencies {
46
+ // 2. Kotlin Standard Library (SDK 54 uses Kotlin 1.9+)
47
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
48
+
49
+ // 3. Android KTX for cleaner Kotlin code
50
+ implementation "androidx.core:core-ktx:1.12.0"
51
+
52
+ // 4. Coroutines for background processing (Critical for VideoEncoder)
53
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3"
54
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
55
+
56
+ // 5. EXIF support for image rotation handling
57
+ implementation "androidx.exifinterface:exifinterface:1.3.6"
58
+
59
+ // 6. Lifecycle KTX (Useful if you want to scope encoding to the app lifecycle)
60
+ implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.7.0"
61
+ }
@@ -0,0 +1,80 @@
1
+ package expo.modules.imagetovideo
2
+
3
+ import android.content.Context
4
+ import android.graphics.Bitmap
5
+ import android.graphics.BitmapFactory
6
+ import android.graphics.Matrix
7
+ import android.net.Uri
8
+ import androidx.exifinterface.media.ExifInterface
9
+ import java.io.InputStream
10
+
11
+ object ImageUtils {
12
+ /**
13
+ * Loads a bitmap from a URI, scales it to fit the target dimensions,
14
+ * and corrects the orientation based on EXIF data.
15
+ */
16
+ fun loadBitmap(context: Context, uriString: String, targetWidth: Int, targetHeight: Int): Bitmap? {
17
+ val uri = Uri.parse(uriString)
18
+ var inputStream: InputStream? = null
19
+
20
+ try {
21
+ // 1. Get dimensions without loading into memory
22
+ val options = BitmapFactory.Options().apply {
23
+ inJustDecodeBounds = true
24
+ }
25
+ context.contentResolver.openInputStream(uri).use {
26
+ BitmapFactory.decodeStream(it, null, options)
27
+ }
28
+
29
+ // 2. Calculate scaling to save memory (OOM Prevention)
30
+ options.inSampleSize = calculateInSampleSize(options, targetWidth, targetHeight)
31
+ options.inJustDecodeBounds = false
32
+
33
+ // 3. Decode the actual bitmap
34
+ val decodedBitmap = context.contentResolver.openInputStream(uri).use {
35
+ BitmapFactory.decodeStream(it, null, options)
36
+ } ?: return null
37
+
38
+ // 4. Correct rotation if needed (common with phone photos)
39
+ return rotateImageIfRequired(context, decodedBitmap, uri)
40
+ } catch (e: Exception) {
41
+ e.printStackTrace()
42
+ return null
43
+ }
44
+ }
45
+
46
+ private fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
47
+ val (height: Int, width: Int) = options.outHeight to options.outWidth
48
+ var inSampleSize = 1
49
+
50
+ if (height > reqHeight || width > reqWidth) {
51
+ val halfHeight: Int = height / 2
52
+ val halfWidth: Int = width / 2
53
+ while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
54
+ inSampleSize *= 2
55
+ }
56
+ }
57
+ return inSampleSize
58
+ }
59
+
60
+ private fun rotateImageIfRequired(context: Context, img: Bitmap, selectedImage: Uri): Bitmap {
61
+ val input = context.contentResolver.openInputStream(selectedImage) ?: return img
62
+ val ei = ExifInterface(input)
63
+ val orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
64
+
65
+ return when (orientation) {
66
+ ExifInterface.ORIENTATION_ROTATE_90 -> rotateImage(img, 90f)
67
+ ExifInterface.ORIENTATION_ROTATE_180 -> rotateImage(img, 180f)
68
+ ExifInterface.ORIENTATION_ROTATE_270 -> rotateImage(img, 270f)
69
+ else -> img
70
+ }
71
+ }
72
+
73
+ private fun rotateImage(img: Bitmap, degree: Float): Bitmap {
74
+ val matrix = Matrix()
75
+ matrix.postRotate(degree)
76
+ val rotatedImg = Bitmap.createBitmap(img, 0, 0, img.width, img.height, matrix, true)
77
+ img.recycle()
78
+ return rotatedImg
79
+ }
80
+ }
@@ -20,7 +20,8 @@ Pod::Spec.new do |s|
20
20
 
21
21
  s.dependency 'ExpoModulesCore'
22
22
 
23
- s.frameworks = 'AVFoundation', 'CoreMedia', 'CoreVideo', 'UIKit'
23
+ #s.frameworks = 'AVFoundation', 'CoreMedia', 'CoreVideo', 'UIKit'
24
+ s.frameworks = 'AVFoundation', 'CoreMedia', 'CoreVideo', 'UIKit', 'CoreGraphics'
24
25
 
25
26
  # Swift/Objective-C compatibility
26
27
  s.pod_target_xcconfig = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-image-to-video",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "High-performance native image-to-video conversion for Expo",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",