@akylas/nativescript-app-utils 2.1.5 → 2.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [2.1.6](https://github.com/akylas/nativescript-app-utils/compare/v2.1.5...v2.1.6) (2024-11-08)
7
+
8
+ ### Bug Fixes
9
+
10
+ * regression fix ([88f15db](https://github.com/akylas/nativescript-app-utils/commit/88f15db1e7b5be77f398c659d650c8500f6436e6))
11
+
6
12
  ## [2.1.5](https://github.com/akylas/nativescript-app-utils/compare/v2.1.4...v2.1.5) (2024-11-08)
7
13
 
8
14
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akylas/nativescript-app-utils",
3
- "version": "2.1.5",
3
+ "version": "2.1.6",
4
4
  "description": "Provides API for changing the styles of SystemUI (StatusBar, NavigationBar...) on iOS.",
5
5
  "main": "index",
6
6
  "sideEffects": false,
@@ -63,5 +63,5 @@
63
63
  "bugs": {
64
64
  "url": "https://github.com/akylas/nativescript-app-utils/issues"
65
65
  },
66
- "gitHead": "4653c117a97d1999227bc500ec7ed0783a2e3601"
66
+ "gitHead": "f9d8fa107bd8ef7a0bba2b7cd091e8b53402368d"
67
67
  }
@@ -1,231 +1,419 @@
1
- import Foundation
2
- import UIKit
3
-
4
- extension UIImage.Orientation {
5
- init(_ cgOrientation: CGImagePropertyOrientation) {
6
- switch cgOrientation {
7
- case .up: self = .up
8
- case .upMirrored: self = .upMirrored
9
- case .down: self = .down
10
- case .downMirrored: self = .downMirrored
11
- case .left: self = .left
12
- case .leftMirrored: self = .leftMirrored
13
- case .right: self = .right
14
- case .rightMirrored: self = .rightMirrored
15
- }
16
- }
17
- }
18
- @objcMembers
19
- @objc(ImageUtils)
20
- class ImageUtils : NSObject {
21
-
22
- static func toJSON(_ str: String?) -> NSDictionary? {
23
- guard let data = str?.data(using: .utf8, allowLossyConversion: false) else { return nil }
24
- return try? (JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! NSDictionary?)
25
- }
26
- class LoadImageOptions {
27
- var width = 0.0
28
- var maxWidth = 0.0
29
- var height = 0.0
30
- var maxHeight = 0.0
31
- var keepAspectRatio = true
32
- var autoScaleFactor = true
33
-
34
- func initWithJSONOptions(_ jsonOpts:NSDictionary?){
35
- if let jsonOpts = jsonOpts {
36
- if ((jsonOpts["resizeThreshold"]) != nil) {
37
- maxWidth = jsonOpts["resizeThreshold"] as! Double
38
- maxHeight = maxWidth
39
- } else if ((jsonOpts["maxSize"]) != nil) {
40
- maxWidth = jsonOpts["maxSize"] as! Double
41
- maxHeight = maxWidth
42
- }
43
- if ((jsonOpts["width"]) != nil) {
44
- width = jsonOpts["width"] as! Double
45
- } else if ((jsonOpts["maxWidth"]) != nil) {
46
- maxWidth = jsonOpts["maxWidth"] as! Double
47
- }
48
- if ((jsonOpts["height"]) != nil) {
49
- height = jsonOpts["height"] as! Double
50
- } else if ((jsonOpts["maxHeight"]) != nil) {
51
- maxHeight = jsonOpts["maxHeight"] as! Double
52
- }
53
- if ((jsonOpts["keepAspectRatio"]) != nil) {
54
- keepAspectRatio = jsonOpts["keepAspectRatio"] as! Bool
55
- }
56
- if ((jsonOpts["autoScaleFactor"]) != nil) {
57
- autoScaleFactor = jsonOpts["autoScaleFactor"] as! Bool
58
- }
59
- }
60
- }
61
-
62
- init(_ optionsStr:String?) {
63
- initWithJSONOptions(toJSON(optionsStr))
64
- }
65
- init( jsonOpts:NSDictionary?) {
66
- initWithJSONOptions(jsonOpts)
67
- }
68
- }
69
- class ImageAssetOptions {
70
- var width = 0.0
71
- var height = 0.0
72
- var keepAspectRatio = true
73
- var autoScaleFactor = true
74
- init (_ size: CGSize, options: LoadImageOptions?) {
75
- width = size.width
76
- height = size.height
77
- if (options != nil) {
78
- if (options!.width > 0) {
79
- width = options!.width
80
- }
81
- if (options!.height > 0) {
82
- height = options!.height
83
- }
84
- if (options!.maxWidth > 0) {
85
- width = min(
86
- width,
87
- options!.maxWidth
88
- )
89
- }
90
- if (options!.maxHeight > 0) {
91
- height = min(
92
- height,
93
- options!.maxHeight
94
- )
95
- }
96
- keepAspectRatio = options!.keepAspectRatio
97
- autoScaleFactor = options!.autoScaleFactor
98
- }
99
- }
100
- }
101
-
102
- static func getAspectSafeDimensions(
103
- _ sourceWidth: Double,
104
- _ sourceHeight: Double,
105
- _ reqWidth: Double,
106
- _ reqHeight: Double
107
- ) -> CGSize {
108
- let widthCoef = sourceWidth / reqWidth
109
- let heightCoef = sourceHeight / reqHeight
110
- let aspectCoef = max(widthCoef, heightCoef)
111
- return CGSize(width: floor((sourceWidth / aspectCoef)), height: floor((sourceHeight / aspectCoef)))
112
- }
113
- static func getRequestedImageSize(_ size: CGSize, _ options: ImageAssetOptions) -> CGSize {
114
- var reqWidth = options.width
115
- if (reqWidth <= 0) {
116
- reqWidth = size.width
117
- }
118
- var reqHeight = options.height
119
- if (reqHeight <= 0) {
120
- reqHeight = size.height
121
- }
122
- if (options.keepAspectRatio) {
123
- let size2 = getAspectSafeDimensions(
124
- size.width,
125
- size.height,
126
- reqWidth,
127
- reqHeight
128
- )
129
- reqWidth = size2.width
130
- reqHeight = size2.height
131
- }
132
- return CGSize(width: reqWidth, height: reqHeight)
133
- }
134
-
135
- // this scales an image but also return the image "rotated"
136
- // based on imageOrientation
137
- static func scaleImage(_ image: UIImage, _ scaledImageSize: CGSize) -> UIImage? {
138
- // Create a graphics context
139
- UIGraphicsBeginImageContextWithOptions(scaledImageSize, false, image.scale)
140
- // Draw the image in the new size
141
- image.draw(in: CGRect(
142
- origin: .zero,
143
- size: scaledImageSize
144
- ))
145
- // Get the resized, scaled, and rotated image from the context
146
- let resizedScaledRotatedImage = UIGraphicsGetImageFromCurrentImageContext()
147
-
148
- // End the graphics context
149
- UIGraphicsEndImageContext()
150
-
151
- return resizedScaledRotatedImage
152
- }
153
-
154
- static func getImageSize(_ src: String) -> Dictionary<String, Any>? {
155
- let url = NSURL.fileURL(withPath: src)
156
- let imageSource = CGImageSourceCreateWithURL(url as CFURL, nil);
157
- if (imageSource == nil) {
158
- // Error loading image
159
- return nil;
160
- }
161
-
162
- let options = [kCGImageSourceShouldCache:false];
163
- let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource!, 0, options as CFDictionary) as! [NSString: Any]? ;
164
- var result: Dictionary<String, Any>?;
165
- if (imageProperties != nil) {
166
- let width = imageProperties![kCGImagePropertyPixelWidth] as! Double;
167
- let height = imageProperties![kCGImagePropertyPixelHeight] as! Double;
168
- var degrees: Int = 0
169
- let orientation = imageProperties![kCGImagePropertyOrientation];
170
- if (orientation != nil) {
171
- let uiOrientation = UIImage.Orientation.init(CGImagePropertyOrientation(rawValue: UInt32(orientation as! Int))!);
172
- switch uiOrientation {
173
- case .down, .downMirrored:
174
- degrees = 180
175
- break
176
- case .right, .rightMirrored:
177
- degrees = -90
178
- break
179
- case .left, .leftMirrored:
180
- degrees = 90
181
- break
182
- default:
183
- degrees = 0
184
- }
185
- }
186
-
187
- result = ["width": width, "height": height, "rotation":degrees];
1
+ package com.nativescript.apputils
2
+
3
+ import android.content.ContentResolver
4
+ import android.content.Context
5
+ import android.graphics.Bitmap
6
+ import android.graphics.BitmapFactory
7
+ import android.graphics.Matrix
8
+ import android.net.Uri
9
+ import android.os.ParcelFileDescriptor
10
+ import android.util.Log
11
+ import androidx.exifinterface.media.ExifInterface
12
+ import org.json.JSONException
13
+ import org.json.JSONObject
14
+ import java.io.FileDescriptor
15
+ import java.io.FileNotFoundException
16
+ import java.io.IOException
17
+ import kotlin.math.floor
18
+ import kotlin.math.max
19
+ import kotlin.math.min
20
+ import kotlin.concurrent.thread
21
+
22
+ import com.nativescript.apputils.FunctionCallback
23
+
24
+ /**
25
+ * This class contains helper functions for processing images
26
+ *
27
+ * @constructor creates image util
28
+ */
29
+ class ImageUtils {
30
+
31
+ class LoadImageOptions {
32
+ var options: JSONObject? = null
33
+ var sourceWidth = 0
34
+ var sourceHeight = 0
35
+ var width = 0
36
+ var maxWidth = 0
37
+ var height = 0
38
+ var maxHeight = 0
39
+ var keepAspectRatio = true
40
+ var autoScaleFactor = true
41
+
42
+ fun initWithJSON(jsonOpts: JSONObject)
43
+ {
44
+ options = jsonOpts
45
+ if (jsonOpts.has("resizeThreshold")) {
46
+ maxWidth = jsonOpts.optInt("resizeThreshold", maxWidth)
47
+ maxHeight = maxWidth
48
+ } else if (jsonOpts.has("maxSize")) {
49
+ maxWidth = jsonOpts.optInt("maxSize", maxWidth)
50
+ maxHeight = maxWidth
51
+ }
52
+ if (jsonOpts.has("width")) {
53
+ width = jsonOpts.optInt("width", width)
54
+ } else if (jsonOpts.has("maxWidth")) {
55
+ maxWidth = jsonOpts.optInt("maxWidth", maxWidth)
56
+ }
57
+ if (jsonOpts.has("height")) {
58
+ height = jsonOpts.optInt("height", height)
59
+ } else if (jsonOpts.has("maxHeight")) {
60
+ maxHeight = jsonOpts.optInt("maxHeight", maxHeight)
61
+ }
62
+ sourceWidth = jsonOpts.optInt("sourceWidth", sourceWidth)
63
+ sourceHeight = jsonOpts.optInt("sourceHeight", sourceHeight)
64
+ keepAspectRatio = jsonOpts.optBoolean("keepAspectRatio", keepAspectRatio)
65
+ autoScaleFactor = jsonOpts.optBoolean("autoScaleFactor", autoScaleFactor)
66
+
67
+ }
68
+ constructor(options: String?) {
69
+ if (options != null) {
70
+ try {
71
+ val jsonOpts = JSONObject(options)
72
+ initWithJSON(jsonOpts)
73
+ } catch (ignored: JSONException) {
74
+ }
75
+ }
76
+ }
77
+ constructor(jsonOpts: JSONObject) {
78
+ initWithJSON(jsonOpts)
79
+ }
80
+
81
+ var resizeThreshold = 0
82
+ get() { return min(maxWidth, maxHeight)}
83
+
84
+
188
85
  }
189
- return result;
190
- }
191
-
192
-
193
- static func readImageFromFileSync(_ src: String, options: NSDictionary?) -> UIImage? {
194
- let image = UIImage(contentsOfFile: src)
195
- if let image {
196
- let size = image.size
197
- let imageOrientation = image.imageOrientation
198
- let loadImageOptions = LoadImageOptions(jsonOpts: options)
199
- let opts = ImageAssetOptions(size, options: loadImageOptions)
200
- let requestedSize = getRequestedImageSize(size, opts)
201
- var result: UIImage? = image
202
- if (requestedSize.width != size.width || requestedSize.height != size.height || imageOrientation != .up) {
203
- result = scaleImage(image, requestedSize )
204
- }
205
- if (options?["jpegQuality"] != nil) {
206
- result = UIImage.init(data: result!.jpegData(compressionQuality: CGFloat((options!["jpegQuality"] as! Int)) / 100.0)!)
207
- }
208
-
209
- return result
86
+
87
+ class ImageAssetOptions {
88
+ var width = 0
89
+ var height = 0
90
+ var keepAspectRatio = true
91
+ var autoScaleFactor = true
92
+
93
+ constructor(sourceSize: Pair<Int, Int>) {
94
+ width = sourceSize.first
95
+ height = sourceSize.second
96
+ }
97
+ constructor(sourceSize: Pair<Int, Int>, options: LoadImageOptions?) {
98
+ width = sourceSize.first
99
+ height = sourceSize.second
100
+ if (options != null) {
101
+ if (options.width > 0) {
102
+ width = options.width
103
+ }
104
+ if (options.height > 0) {
105
+ height = options.height
106
+ }
107
+ if (options.maxWidth > 0) {
108
+ width = min(
109
+ width,
110
+ options.maxWidth
111
+ )
112
+ }
113
+ if (options.maxHeight > 0) {
114
+ height = min(
115
+ height,
116
+ options.maxHeight
117
+ )
118
+ }
119
+ keepAspectRatio = options.keepAspectRatio
120
+ autoScaleFactor = options.autoScaleFactor
121
+ }
122
+ }
210
123
  }
211
- return nil
212
-
213
- }
214
-
215
- static func readImageFromFileSync(_ src: String, _ stringOptions: String?) -> UIImage? {
216
- let options = toJSON(stringOptions)
217
- return readImageFromFileSync(src, options: options)
218
- }
219
- static func readImageFromFile(_ src: String, _ delegate: NCompletionDelegate?, _ stringOptions: String?) {
220
- DispatchQueue.global(qos: .userInitiated).async {
221
- let options = toJSON(stringOptions)
222
- // do {
223
- delegate?.onComplete(readImageFromFileSync(src, stringOptions) as NSObject?, error: nil)
224
-
225
- // } catch {
226
- // delegate?.onComplete(readImageFromFileSync(src, stringOptions) as NSObject?, error: error as NSError?)
227
- //
228
- // }
124
+ companion object {
125
+ fun getTargetFormat(format: String?): Bitmap.CompressFormat {
126
+ return when (format) {
127
+ "jpeg", "jpg" -> Bitmap.CompressFormat.JPEG
128
+ else -> Bitmap.CompressFormat.PNG
129
+ }
130
+ }
131
+ /**
132
+ * Calculate an inSampleSize for use in a [BitmapFactory.Options] object when decoding
133
+ * bitmaps using the decode* methods from [BitmapFactory]. This implementation calculates
134
+ * the closest inSampleSize that is a power of 2 and will result in the final decoded bitmap
135
+ * having a width and height equal to or larger than the requested width and height.
136
+ *
137
+ * @param imageWidth The original width of the resulting bitmap
138
+ * @param imageHeight The original height of the resulting bitmap
139
+ * @param reqWidth The requested width of the resulting bitmap
140
+ * @param reqHeight The requested height of the resulting bitmap
141
+ * @return The value to be used for inSampleSize
142
+ */
143
+ fun calculateInSampleSize(
144
+ imageWidth: Int,
145
+ imageHeight: Int,
146
+ reqWidth: Int,
147
+ reqHeight: Int
148
+ ): Int {
149
+ // BEGIN_INCLUDE (calculate_sample_size)
150
+ // Raw height and width of image
151
+ var reqWidth = reqWidth
152
+ var reqHeight = reqHeight
153
+ reqWidth = if (reqWidth > 0) reqWidth else imageWidth
154
+ reqHeight = if (reqHeight > 0) reqHeight else imageHeight
155
+ var inSampleSize = 1
156
+ if (imageHeight > reqHeight || imageWidth > reqWidth) {
157
+ val halfHeight = imageHeight / 2
158
+ val halfWidth = imageWidth / 2
159
+
160
+ // Calculate the largest inSampleSize value that is a power of 2 and keeps both
161
+ // height and width larger than the requested height and width.
162
+ while (halfHeight / inSampleSize > reqHeight && halfWidth / inSampleSize > reqWidth) {
163
+ inSampleSize *= 2
164
+ }
165
+
166
+ // This offers some additional logic in case the image has a strange
167
+ // aspect ratio. For example, a panorama may have a much larger
168
+ // width than height. In these cases the total pixels might still
169
+ // end up being too large to fit comfortably in memory, so we should
170
+ // be more aggressive with sample down the image (=larger inSampleSize).
171
+ var totalPixels =
172
+ (imageWidth / inSampleSize * (imageHeight / inSampleSize)).toLong()
173
+
174
+ // Anything more than 2x the requested pixels we'll sample down further
175
+ val totalReqPixelsCap = (reqWidth * reqHeight * 2).toLong()
176
+ while (totalPixels > totalReqPixelsCap) {
177
+ inSampleSize *= 2
178
+ totalPixels =
179
+ (imageWidth / inSampleSize * (imageHeight / inSampleSize)).toLong()
180
+ }
181
+ }
182
+ return inSampleSize
183
+ // END_INCLUDE (calculate_sample_size)
184
+ }
185
+
186
+ private fun getAspectSafeDimensions(
187
+ sourceWidth: Int,
188
+ sourceHeight: Int,
189
+ reqWidth: Int,
190
+ reqHeight: Int
191
+ ): Pair<Int, Int> {
192
+ val widthCoef = sourceWidth.toDouble() / reqWidth.toDouble()
193
+ val heightCoef = sourceHeight.toDouble() / reqHeight.toDouble()
194
+ val imageRatio = sourceWidth.toDouble() / sourceHeight.toDouble()
195
+ // val aspectCoef = max(widthCoef, heightCoef)
196
+ if (widthCoef > heightCoef) {
197
+ return Pair(reqWidth, (reqWidth/imageRatio).toInt())
198
+ } else {
199
+ return Pair((reqHeight*imageRatio).toInt(), reqHeight)
200
+
201
+ }
202
+ // return Pair(
203
+ // ((sourceWidth / aspectCoef)).toInt(),
204
+ // ((sourceHeight / aspectCoef)).toInt()
205
+ // )
206
+ }
207
+ private fun getRequestedImageSize(
208
+ src: Pair<Int, Int>,
209
+ options: ImageAssetOptions
210
+ ): Pair<Int, Int> {
211
+ var reqWidth = options.width
212
+ if (reqWidth <= 0) {
213
+ reqWidth = src.first
214
+ }
215
+ var reqHeight = options.height
216
+ if (reqHeight <= 0) {
217
+ reqHeight = src.second
218
+ }
219
+ if (options.keepAspectRatio) {
220
+ val (first, second) = getAspectSafeDimensions(
221
+ src.first,
222
+ src.second,
223
+ reqWidth,
224
+ reqHeight
225
+ )
226
+ reqWidth = first
227
+ reqHeight = second
228
+ }
229
+ return Pair(reqWidth, reqHeight)
230
+ }
231
+
232
+ private fun closePfd(pfd: ParcelFileDescriptor?) {
233
+ if (pfd != null) {
234
+ try {
235
+ pfd.close()
236
+ } catch (ignored: IOException) {
237
+ }
238
+ }
239
+ }
240
+
241
+ private fun calculateAngleFromFile(filename: String): Int {
242
+ var rotationAngle = 0
243
+ val ei: ExifInterface
244
+ try {
245
+ ei = ExifInterface(filename)
246
+ val orientation = ei.getAttributeInt(
247
+ ExifInterface.TAG_ORIENTATION,
248
+ ExifInterface.ORIENTATION_NORMAL
249
+ )
250
+ when (orientation) {
251
+ ExifInterface.ORIENTATION_ROTATE_90 -> rotationAngle = 90
252
+ ExifInterface.ORIENTATION_ROTATE_180 -> rotationAngle = 180
253
+ ExifInterface.ORIENTATION_ROTATE_270 -> rotationAngle = 270
254
+ }
255
+ } catch (ignored: IOException) {
256
+ }
257
+ return rotationAngle
258
+ }
259
+
260
+
261
+ private fun calculateAngleFromFileDescriptor(fd: FileDescriptor): Int {
262
+ var rotationAngle = 0
263
+ val ei: ExifInterface
264
+ try {
265
+ ei = ExifInterface(fd)
266
+ val orientation = ei.getAttributeInt(
267
+ ExifInterface.TAG_ORIENTATION,
268
+ ExifInterface.ORIENTATION_NORMAL
269
+ )
270
+ when (orientation) {
271
+ ExifInterface.ORIENTATION_ROTATE_90 -> rotationAngle = 90
272
+ ExifInterface.ORIENTATION_ROTATE_180 -> rotationAngle = 180
273
+ ExifInterface.ORIENTATION_ROTATE_270 -> rotationAngle = 270
274
+ }
275
+ } catch (ignored: IOException) {
276
+ }
277
+ return rotationAngle
278
+ }
279
+ fun getImageSize(context: Context, src: String): IntArray {
280
+ val bitmapOptions = BitmapFactory.Options()
281
+ bitmapOptions.inJustDecodeBounds = true
282
+ var pfd: ParcelFileDescriptor? = null
283
+ if (src.startsWith("content://")) {
284
+ val uri = Uri.parse(src)
285
+ val resolver: ContentResolver = context.getContentResolver()
286
+ pfd = try {
287
+ resolver.openFileDescriptor(uri, "r")
288
+ } catch (e: FileNotFoundException) {
289
+ closePfd(pfd)
290
+ throw e;
291
+ }
292
+ BitmapFactory.decodeFileDescriptor(pfd!!.fileDescriptor, null, bitmapOptions)
293
+ } else {
294
+ BitmapFactory.decodeFile(src, bitmapOptions)
295
+ }
296
+ val rotationAngle: Int
297
+ if (pfd != null) {
298
+ rotationAngle = calculateAngleFromFileDescriptor(pfd.fileDescriptor)
299
+ closePfd(pfd)
300
+ } else {
301
+ rotationAngle = calculateAngleFromFile(src)
302
+ }
303
+ return intArrayOf(bitmapOptions.outWidth, bitmapOptions.outHeight, rotationAngle)
304
+ }
305
+
306
+ fun readBitmapFromFileSync(context: Context, src: String, options: LoadImageOptions?, sourceSize:Pair<Int, Int>?): Bitmap? {
307
+ // val start = System.currentTimeMillis()
308
+ var sourceSize = sourceSize
309
+ if (sourceSize == null && options?.sourceWidth != 0 && options?.sourceHeight != 0) {
310
+ sourceSize = Pair(options!!.sourceWidth, options!!.sourceHeight)
311
+ }
312
+ var bitmap: Bitmap?
313
+ val bitmapOptions = BitmapFactory.Options()
314
+ var pfd: ParcelFileDescriptor? = null
315
+ if (src.startsWith("content://")) {
316
+ val uri = Uri.parse(src)
317
+ val resolver: ContentResolver = context.getContentResolver()
318
+ pfd = try {
319
+ resolver.openFileDescriptor(uri, "r")
320
+ } catch (e: FileNotFoundException) {
321
+ closePfd(pfd)
322
+ throw e;
323
+ }
324
+ }
325
+ if (sourceSize == null) {
326
+ bitmapOptions.inJustDecodeBounds = true
327
+
328
+ if (pfd != null) {
329
+ BitmapFactory.decodeFileDescriptor(pfd!!.fileDescriptor, null, bitmapOptions)
330
+ } else {
331
+ BitmapFactory.decodeFile(src, bitmapOptions)
332
+ }
333
+ sourceSize = Pair(bitmapOptions.outWidth, bitmapOptions.outHeight)
334
+ }
335
+ val opts = ImageAssetOptions(sourceSize, options)
336
+
337
+ val (first, second) = getRequestedImageSize(sourceSize, opts)
338
+ val sampleSize: Int = calculateInSampleSize(
339
+ sourceSize.first, sourceSize.second,
340
+ first,
341
+ second
342
+ )
343
+ val finalBitmapOptions = BitmapFactory.Options()
344
+ finalBitmapOptions.inSampleSize = sampleSize
345
+ if (sampleSize != 1) {
346
+ finalBitmapOptions.inScaled = true;
347
+ finalBitmapOptions.inDensity = sourceSize.first;
348
+ finalBitmapOptions.inTargetDensity = first * sampleSize;
349
+ } else {
350
+ finalBitmapOptions.inScaled = false;
351
+ }
352
+ // read as minimum bitmap as possible (slightly bigger than the requested size)
353
+ bitmap = if (pfd != null) {
354
+ BitmapFactory.decodeFileDescriptor(pfd.fileDescriptor, null, finalBitmapOptions)
355
+ } else {
356
+ BitmapFactory.decodeFile(src, finalBitmapOptions)
357
+ }
358
+ // Log.d("ImageAnalysis", "readBitmapFromFile in ${System.currentTimeMillis() - start} ms")
359
+ if (bitmap != null) {
360
+ val rotationAngle: Int
361
+ if (pfd != null) {
362
+ rotationAngle = calculateAngleFromFileDescriptor(pfd.fileDescriptor)
363
+ closePfd(pfd)
364
+ } else {
365
+ rotationAngle = calculateAngleFromFile(src)
366
+ }
367
+ // if (first !== bitmap.getWidth() || second !== bitmap.getHeight() || rotationAngle != 0) {
368
+ //
369
+ // val matrix = Matrix()
370
+ // if (first !== bitmap.getWidth() || second !== bitmap.getHeight()) {
371
+ // val scale = first.toFloat() / bitmap.width
372
+ // matrix.postScale(scale, scale)
373
+ // }
374
+ // if (rotationAngle != 0) {
375
+ // matrix.postRotate(rotationAngle.toFloat())
376
+ // }
377
+ // bitmap = Bitmap.createBitmap(
378
+ // bitmap,
379
+ // 0,
380
+ // 0,
381
+ // bitmap.getWidth(),
382
+ // bitmap.getHeight(),
383
+ // matrix,
384
+ // false
385
+ // )
386
+ // }
387
+
388
+ if (rotationAngle != 0) {
389
+ val matrix = Matrix()
390
+ matrix.postRotate(rotationAngle.toFloat())
391
+ bitmap = Bitmap.createBitmap(
392
+ bitmap,
393
+ 0,
394
+ 0,
395
+ bitmap.getWidth(),
396
+ bitmap.getHeight(),
397
+ matrix,
398
+ true
399
+ )
400
+ }
401
+ // Log.d("ImageAnalysis", "readBitmapFromFile2 in ${System.currentTimeMillis() - start} ms")
402
+ }
403
+ return bitmap
404
+ }
405
+
406
+ fun readBitmapFromFileSync(context: Context, src: String, opts: String?): Bitmap? {
407
+ return readBitmapFromFileSync(context, src, LoadImageOptions(opts), null)
408
+ }
409
+ fun readBitmapFromFile(context: Context, src: String, callback: FunctionCallback, opts: String?) {
410
+ thread(start = true) {
411
+ try {
412
+ callback.onResult(null, readBitmapFromFileSync(context, src, opts))
413
+ } catch (e: Exception) {
414
+ callback.onResult(e, null)
415
+ }
416
+ }
417
+ }
229
418
  }
230
- }
231
- }
419
+ }
@@ -2,18 +2,18 @@ import Foundation
2
2
  import UIKit
3
3
 
4
4
  extension UIImage.Orientation {
5
- init(_ cgOrientation: CGImagePropertyOrientation) {
6
- switch cgOrientation {
7
- case .up: self = .up
8
- case .upMirrored: self = .upMirrored
9
- case .down: self = .down
10
- case .downMirrored: self = .downMirrored
11
- case .left: self = .left
12
- case .leftMirrored: self = .leftMirrored
13
- case .right: self = .right
14
- case .rightMirrored: self = .rightMirrored
15
- }
5
+ init(_ cgOrientation: CGImagePropertyOrientation) {
6
+ switch cgOrientation {
7
+ case .up: self = .up
8
+ case .upMirrored: self = .upMirrored
9
+ case .down: self = .down
10
+ case .downMirrored: self = .downMirrored
11
+ case .left: self = .left
12
+ case .leftMirrored: self = .leftMirrored
13
+ case .right: self = .right
14
+ case .rightMirrored: self = .rightMirrored
16
15
  }
16
+ }
17
17
  }
18
18
  @objcMembers
19
19
  @objc(ImageUtils)
@@ -165,22 +165,25 @@ class ImageUtils : NSObject {
165
165
  if (imageProperties != nil) {
166
166
  let width = imageProperties![kCGImagePropertyPixelWidth] as! Double;
167
167
  let height = imageProperties![kCGImagePropertyPixelHeight] as! Double;
168
- let orientation = imageProperties![kCGImagePropertyOrientation] as! Int;
169
- let uiOrientation = UIImage.Orientation.init(CGImagePropertyOrientation(rawValue: UInt32(orientation))!);
170
168
  var degrees: Int = 0
171
- switch uiOrientation {
172
- case .down, .downMirrored:
173
- degrees = 180
174
- break
175
- case .right, .rightMirrored:
176
- degrees = -90
177
- break
178
- case .left, .leftMirrored:
179
- degrees = 90
180
- break
181
- default:
182
- degrees = 0
169
+ let orientation = imageProperties![kCGImagePropertyOrientation];
170
+ if (orientation != nil) {
171
+ let uiOrientation = UIImage.Orientation.init(CGImagePropertyOrientation(rawValue: UInt32(orientation as! Int))!);
172
+ switch uiOrientation {
173
+ case .down, .downMirrored:
174
+ degrees = 180
175
+ break
176
+ case .right, .rightMirrored:
177
+ degrees = -90
178
+ break
179
+ case .left, .leftMirrored:
180
+ degrees = 90
181
+ break
182
+ default:
183
+ degrees = 0
184
+ }
183
185
  }
186
+
184
187
  result = ["width": width, "height": height, "rotation":degrees];
185
188
  }
186
189
  return result;
@@ -214,15 +217,15 @@ class ImageUtils : NSObject {
214
217
  return readImageFromFileSync(src, options: options)
215
218
  }
216
219
  static func readImageFromFile(_ src: String, _ delegate: NCompletionDelegate?, _ stringOptions: String?) {
217
- DispatchQueue.global(qos: .userInitiated).async {
220
+ DispatchQueue.global(qos: .userInitiated).async {
218
221
  let options = toJSON(stringOptions)
219
- // do {
220
- delegate?.onComplete(readImageFromFileSync(src, stringOptions) as NSObject?, error: nil)
221
-
222
- // } catch {
223
- // delegate?.onComplete(readImageFromFileSync(src, stringOptions) as NSObject?, error: error as NSError?)
224
- //
225
- // }
226
- }
222
+ // do {
223
+ delegate?.onComplete(readImageFromFileSync(src, stringOptions) as NSObject?, error: nil)
224
+
225
+ // } catch {
226
+ // delegate?.onComplete(readImageFromFileSync(src, stringOptions) as NSObject?, error: error as NSError?)
227
+ //
228
+ // }
229
+ }
227
230
  }
228
231
  }