mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
* Do not depend on current device screen while calculating Image Asset size. * Scale the image asset to the exact requested size. * Process image assets natively, pass keepAspectRatio based on the stretch property and Asset options. * Fixed the splashscreen resource name as it cannot be read when containing a dot. * Updated the Image Asset scale and rotate logic based on the Native one. * Make the ImageAsset size more important than the Image decode size as its more specific. * Fixed tslint errors. * Added filePath support in the ImageAsset constructor for iOS in order to unify it with the Android implementation, support for relative files and file not found support errors. * Added unit tests for ImageAssets. * Added a sample app for UI testing of image-view with ImageAsset src. * chore: apply PR comments
119 lines
4.5 KiB
TypeScript
119 lines
4.5 KiB
TypeScript
import * as platform from "../platform";
|
|
import * as common from "./image-asset-common";
|
|
import { path as fsPath, knownFolders } from "../file-system";
|
|
|
|
global.moduleMerge(common, exports);
|
|
|
|
export class ImageAsset extends common.ImageAsset {
|
|
private _android: string; //file name of the image
|
|
|
|
constructor(asset: string) {
|
|
super();
|
|
let fileName = typeof asset === "string" ? asset.trim() : "";
|
|
if (fileName.indexOf("~/") === 0) {
|
|
fileName = fsPath.join(knownFolders.currentApp().path, fileName.replace("~/", ""));
|
|
}
|
|
this.android = fileName;
|
|
}
|
|
|
|
get android(): string {
|
|
return this._android;
|
|
}
|
|
|
|
set android(value: string) {
|
|
this._android = value;
|
|
}
|
|
|
|
public getImageAsync(callback: (image, error) => void) {
|
|
let bitmapOptions = new android.graphics.BitmapFactory.Options();
|
|
bitmapOptions.inJustDecodeBounds = true;
|
|
// read only the file size
|
|
let bitmap = android.graphics.BitmapFactory.decodeFile(this.android, bitmapOptions);
|
|
let sourceSize = {
|
|
width: bitmapOptions.outWidth,
|
|
height: bitmapOptions.outHeight
|
|
};
|
|
let requestedSize = common.getRequestedImageSize(sourceSize, this.options);
|
|
|
|
let sampleSize = org.nativescript.widgets.image.Fetcher.calculateInSampleSize(bitmapOptions.outWidth, bitmapOptions.outHeight, requestedSize.width, requestedSize.height);
|
|
|
|
let finalBitmapOptions = new android.graphics.BitmapFactory.Options();
|
|
finalBitmapOptions.inSampleSize = sampleSize;
|
|
try {
|
|
let error = null;
|
|
// read as minimum bitmap as possible (slightly bigger than the requested size)
|
|
bitmap = android.graphics.BitmapFactory.decodeFile(this.android, finalBitmapOptions);
|
|
|
|
if (bitmap) {
|
|
if (requestedSize.width !== bitmap.getWidth() || requestedSize.height !== bitmap.getHeight()) {
|
|
// scale to exact size
|
|
bitmap = android.graphics.Bitmap.createScaledBitmap(bitmap, requestedSize.width, requestedSize.height, true);
|
|
}
|
|
|
|
const rotationAngle = calculateAngleFromFile(this.android);
|
|
if (rotationAngle !== 0) {
|
|
const matrix = new android.graphics.Matrix();
|
|
matrix.postRotate(rotationAngle);
|
|
bitmap = android.graphics.Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
|
}
|
|
}
|
|
|
|
if (!bitmap) {
|
|
error = "Asset '" + this.android + "' cannot be found.";
|
|
}
|
|
|
|
callback(bitmap, error);
|
|
}
|
|
catch (ex) {
|
|
callback(null, ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
var calculateAngleFromFile = function (filename: string) {
|
|
let rotationAngle = 0;
|
|
const ei = new android.media.ExifInterface(filename);
|
|
const orientation = ei.getAttributeInt(android.media.ExifInterface.TAG_ORIENTATION, android.media.ExifInterface.ORIENTATION_NORMAL);
|
|
|
|
switch (orientation) {
|
|
case android.media.ExifInterface.ORIENTATION_ROTATE_90:
|
|
rotationAngle = 90;
|
|
break;
|
|
case android.media.ExifInterface.ORIENTATION_ROTATE_180:
|
|
rotationAngle = 180;
|
|
break;
|
|
case android.media.ExifInterface.ORIENTATION_ROTATE_270:
|
|
rotationAngle = 270;
|
|
break;
|
|
}
|
|
|
|
return rotationAngle;
|
|
}
|
|
|
|
var calculateInSampleSize = function (imageWidth, imageHeight, reqWidth, reqHeight) {
|
|
let sampleSize = 1;
|
|
let displayWidth = platform.screen.mainScreen.widthDIPs;
|
|
let displayHeigth = platform.screen.mainScreen.heightDIPs;
|
|
reqWidth = (reqWidth > 0 && reqWidth < displayWidth) ? reqWidth : displayWidth;
|
|
reqHeight = (reqHeight > 0 && reqHeight < displayHeigth) ? reqHeight : displayHeigth;
|
|
if (imageWidth > reqWidth && imageHeight > reqHeight) {
|
|
let halfWidth = imageWidth / 2;
|
|
let halfHeight = imageHeight / 2;
|
|
while ((halfWidth / sampleSize) > reqWidth && (halfHeight / sampleSize) > reqHeight) {
|
|
sampleSize *= 2;
|
|
}
|
|
}
|
|
|
|
var totalPixels = (imageWidth / sampleSize) * (imageHeight / sampleSize);
|
|
|
|
// Anything more than 2x the requested pixels we'll sample down further
|
|
var totalReqPixelsCap = reqWidth * reqHeight * 2;
|
|
|
|
while (totalPixels > totalReqPixelsCap) {
|
|
sampleSize *= 2;
|
|
totalPixels = (imageWidth / sampleSize) * (imageHeight / sampleSize);
|
|
}
|
|
|
|
return sampleSize;
|
|
}
|