Skip to content

Commit 89ed02a

Browse files
fix(android): Setting String Value to Width And Height
1 parent f6eab0d commit 89ed02a

File tree

3 files changed

+97
-22
lines changed

3 files changed

+97
-22
lines changed

packages/core/image-asset/index.android.ts

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
import { ImageAssetBase, getRequestedImageSize } from './image-asset-common';
2-
import { path as fsPath, knownFolders } from '../file-system';
3-
import { ad } from '../utils';
4-
import { Screen } from '../platform';
5-
export * from './image-asset-common';
1+
import { ImageAssetBase, getRequestedImageSize } from "./image-asset-common";
2+
import { path as fsPath, knownFolders } from "../file-system";
3+
import { ad } from "../utils";
4+
import { Screen } from "../platform";
5+
export * from "./image-asset-common";
66

77
export class ImageAsset extends ImageAssetBase {
88
private _android: string; //file name of the image
99

1010
constructor(asset: string) {
1111
super();
12-
let fileName = typeof asset === 'string' ? asset.trim() : '';
13-
if (fileName.indexOf('~/') === 0) {
14-
fileName = fsPath.join(knownFolders.currentApp().path, fileName.replace('~/', ''));
12+
let fileName = typeof asset === "string" ? asset.trim() : "";
13+
if (fileName.indexOf("~/") === 0) {
14+
fileName = fsPath.join(knownFolders.currentApp().path, fileName.replace("~/", ""));
1515
}
1616
this.android = fileName;
1717
}
@@ -25,7 +25,58 @@ export class ImageAsset extends ImageAssetBase {
2525
this._android = value;
2626
}
2727

28-
public getImageAsync(callback: (image, error) => void) {
28+
/**
29+
* Validates and adjusts image dimensions to prevent bitmap size exceeding Android's 32-bit limit.
30+
* Android has a limitation where bitmap size (width * height) cannot exceed 2^31-1.
31+
* This method ensures the dimensions are within safe bounds while maintaining aspect ratio.
32+
*
33+
* @param {number|string} width - The desired width of the image
34+
* @param {number|string} height - The desired height of the image
35+
* @returns {{ width: number; height: number }} Object containing validated dimensions
36+
*
37+
* @example
38+
* // Returns safe dimensions that won't exceed Android's bitmap size limit
39+
* const safe = validateDimensions("4000", "3000");
40+
*/
41+
private _validateDimensions(width: number | string, height: number | string): { width: number; height: number } {
42+
const parseSize = (size: number | string): number => {
43+
return typeof size === "string" ? parseInt(size, 10) : size;
44+
};
45+
46+
let w = parseSize(width);
47+
let h = parseSize(height);
48+
49+
// Check for 32-bit limitation (2^31 - 1, leaving some headroom)
50+
const MAX_DIMENSION = Math.floor(Math.sqrt(Math.pow(2, 31) - 1));
51+
52+
// Check if each dimension exceeds MAX_DIMENSION
53+
w = Math.min(w, MAX_DIMENSION);
54+
h = Math.min(h, MAX_DIMENSION);
55+
56+
// Check the total pixel count
57+
if (w * h > Math.pow(2, 31) - 1) {
58+
const scale = Math.sqrt((Math.pow(2, 31) - 1) / (w * h));
59+
w = Math.floor(w * scale);
60+
h = Math.floor(h * scale);
61+
}
62+
63+
return { width: w, height: h };
64+
}
65+
66+
/**
67+
* Asynchronously loads an image with the specified dimensions.
68+
* Handles string/number dimension types and applies size validation for Android bitmap limitations.
69+
*
70+
* @param {Function} callback - Callback function that receives (image, error)
71+
* @param {{ width?: number; height?: number }} [options] - Optional dimensions for the image
72+
*/
73+
public getImageAsync(callback: (image, error) => void, options?: { width?: number; height?: number }) {
74+
if (options?.width || options?.height) {
75+
const validDimensions = this._validateDimensions(options.width || this.options.width || 0, options.height || this.options.height || 0);
76+
options.width = validDimensions.width;
77+
options.height = validDimensions.height;
78+
}
79+
2980
org.nativescript.widgets.Utils.loadImageAsync(
3081
ad.getApplicationContext(),
3182
this.android,
@@ -39,7 +90,7 @@ export class ImageAsset extends ImageAssetBase {
3990
onError(ex) {
4091
callback(null, ex);
4192
},
42-
})
93+
}),
4394
);
4495
}
4596
}

packages/core/image-asset/index.d.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Observable } from '../data/observable';
1+
import { Observable } from "../data/observable";
22

33
export class ImageAsset extends Observable {
44
constructor(asset: any);
@@ -10,8 +10,8 @@ export class ImageAsset extends Observable {
1010
}
1111

1212
export interface ImageAssetOptions {
13-
width?: number;
14-
height?: number;
13+
width?: number | string;
14+
height?: number | string;
1515
keepAspectRatio?: boolean;
1616
autoScaleFactor?: boolean;
1717
}

packages/core/image-asset/index.ios.ts

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
import { ImageAssetBase, getRequestedImageSize } from './image-asset-common';
2-
import { path as fsPath, knownFolders } from '../file-system';
3-
import { queueGC } from '../utils';
1+
import { ImageAssetBase, getRequestedImageSize } from "./image-asset-common";
2+
import { path as fsPath, knownFolders } from "../file-system";
3+
import { queueGC } from "../utils";
44

5-
export * from './image-asset-common';
5+
export * from "./image-asset-common";
66

77
export class ImageAsset extends ImageAssetBase {
88
private _ios: PHAsset;
99

1010
constructor(asset: string | PHAsset | UIImage) {
1111
super();
12-
if (typeof asset === 'string') {
13-
if (asset.indexOf('~/') === 0) {
14-
asset = fsPath.join(knownFolders.currentApp().path, asset.replace('~/', ''));
12+
if (typeof asset === "string") {
13+
if (asset.indexOf("~/") === 0) {
14+
asset = fsPath.join(knownFolders.currentApp().path, asset.replace("~/", ""));
1515
}
1616

1717
this.nativeImage = UIImage.imageWithContentsOfFile(asset);
@@ -31,9 +31,25 @@ export class ImageAsset extends ImageAssetBase {
3131
this._ios = value;
3232
}
3333

34-
public getImageAsync(callback: (image, error) => void) {
34+
/**
35+
* Asynchronously loads an image and optionally resizes it.
36+
* Handles both string and number type dimensions by converting strings to numbers.
37+
*
38+
* @param {Function} callback - Callback function that receives (image, error)
39+
* @param {{ width?: number|string; height?: number|string }} [options] - Optional dimensions for the image
40+
*/
41+
public getImageAsync(callback: (image, error) => void, options?: { width?: number | string; height?: number | string }) {
42+
if (options) {
43+
if (typeof options.width === "string") {
44+
options.width = parseInt(options.width, 10);
45+
}
46+
if (typeof options.height === "string") {
47+
options.height = parseInt(options.height, 10);
48+
}
49+
}
50+
3551
if (!this.ios && !this.nativeImage) {
36-
callback(null, 'Asset cannot be found.');
52+
callback(null, "Asset cannot be found.");
3753
}
3854

3955
const srcWidth = this.nativeImage ? this.nativeImage.size.width : this.ios.pixelWidth;
@@ -60,6 +76,14 @@ export class ImageAsset extends ImageAssetBase {
6076
});
6177
}
6278

79+
/**
80+
* Scales the image to the requested size while respecting device scale factor settings.
81+
*
82+
* @param {UIImage} image - The source UIImage to scale
83+
* @param {{ width: number; height: number }} requestedSize - Target dimensions
84+
* @returns {UIImage} Scaled image
85+
* @private
86+
*/
6387
private scaleImage(image: UIImage, requestedSize: { width: number; height: number }): UIImage {
6488
return NativeScriptUtils.scaleImageWidthHeightScaleFactor(image, requestedSize.width, requestedSize.height, this.options?.autoScaleFactor === false ? 1.0 : 0.0);
6589
}

0 commit comments

Comments
 (0)