Why Flutter (dart) is so slow to process images? - dart

I have this method, that loads a photo from the user's phone to memory, resizes it and encodes it as JPEG:
List<int> processPersonProfilePhoto(File file) {
var rawPhoto = file.readAsBytesSync();
var jpg = Image.decodeJpg(rawPhoto);
jpg = Image.copyResize(jpg, 512);
return Image.encodeJpg(jpg, quality: 70);
}
I am running the method above on a separated isolate, via:
var jpgByteArray = await compute(processPersonProfilePhoto, file);
This whole process sometimes takes 20 - 30 seconds (the release mode is even worse than debug) and I am running on middle and high end devices.
This is the package I am using to process:
image: ^2.0.7
What am I doing wrong? How to improve that?
PS.: I done some more debugging and realized that the line that takes much longer to process is the deconding one:
var jpg = Image.decodeJpg(rawPhoto);

I found out that this is a library issue. You can track the problem on the lib's github page, there are a lot of issues complaining about the slowness. (https://github.com/brendan-duncan/image/issues/104)
Until it gets fixed, use that library instead:
https://pub.dartlang.org/packages/flutter_image_compress
Edit: Seems now that the library has fixed the slowness problem on RELEASE builds: https://github.com/brendan-duncan/image/issues/104#issuecomment-490794535

Related

React Native HTML to PDF not displaying local images

In case you guys don't know, there was a problem previously with this library not rendering local images on Android as well, but apparently it was solved. Now, I'm facing the exact same issue on iOS, with a difference that I can use static images like assets/src/assets/images/logo.png. But when the images start with something like file:///, storage://, ph:// it simply does not get rendered.
What I'm doing is trying to generate a PDF report file, which must be generated independently the user has an internet connection or not. That is the reason why I have to use local images.
The static image is the logo of the company, and the local image which is not getting rendered is an image saved to the phone's storage through Image Picker or Camera Roll. The React Native Image component displays the image perfectly, so I don't think I'm using a wrong path.
What I have tried so far:
Removing the file:/// or storage:// or ph:// from the beginning of the path string;
In some cases, when I save an image to the phone's library with Camera Roll, it will return a path that starts with ph:// but without an extension, such as .jpg or .png. I tried to put the extension manually, and still does not make any difference;
I tried to convert the image to base64 using rn-fetch-blob (with RNFetchBlob.fs.base64.encode(path)), but still got no success.
Devices:
iPhone SE with iOS 14 (also simulator iPhone 11 with iOS 15)
MacBook Air 2017 Core i5 1.8GHz and 8gb RAM (macOS Big Sur 11.5.2)
Environment
node: 12.22.7
npm: 6.14.15
react: 16.9.0
react-native: 0.61.5
react-native-html-to-pdf: ^0.11.0 (updating it to 0.12.0 also got me the same result)
Code:
sharePDF = async () => {
try {
this.changeVisibilityOptions(false);
this.changeVisibilityLoading('Gerando PDF...');
let htmlTemplate = '';
htmlTemplate = await getPDFDespesa(this.state);
const pdfOptions = {
html: htmlTemplate,
fileName: 'RelatorioDespesas',
directory: 'Relatorios'
};
let pdfFile = await RNHTMLtoPDF.convert(pdfOptions);
this.changeVisibilityLoading(false);
const shareOptions = {
title: 'Compartilhar com:',
url: `file://${pdfFile.filePath}`,
type: 'application/pdf',
failOnCancel: false
};
const ShareResponse = await Share.open(shareOptions);
} catch (error) {
this.setState({ visibilityLoadingScreen: false });
console.log('Error =>', error);
}
}
Final thoughts:
Well, since the code is stored at a private repository, I can't show the whole thing here for ethical reasons. But I'm doing my best to give you as much details as possible.
The output the code produces an almost complete PDF, with the only point that I see broken image icons where the images were supposed to be. For Android it works perfectly now.
I think this might be an issue related to WebView, since react-native-html-to-pdf uses WebView to generate the PDF from HTML code. I reached this conclusion after another developer at my job was trying to create a screen with a preview of the PDF before it could be shared got the very same problem for both Android and iOS. The library he used was react-native-webview.
Update with solution
Alright guys, after a long time of research, me and a colleague got to a solution which may not be the best but does what we expected.
First of all, one thing that was discovered is that we have to divide the problem in two, because we actually had two problems.
Images from react-native-image-picker: After a long time trying to find the problem which was preventing the local images from getting rendered, I tried updating the library to version 4.7.3 (latest version at that day) and did a number of required changes to the code, as the version we were using was considerably aged. Well, it happened to work out for my surprise, even with the response uri's format not being changed;
Images from #react-native-community/cameraroll: This one was a bit more complicated. It took me some time to realize that the iOS' PHAsset was not supported in the WebView or react-native-html-to-pdf (which uses WebView in background). So, after some research, me and my colleague found a workaround that lead us to a relatively easy solution. Basically we used react-native-fs to copy the PHAsset media file to a temporary directory, which would return a uri that started with file:// and could be rendered by WebView. That's the code we used to do this:
export default function getImageNameFromUrl(imageUrl = "") {
if (imageUrl) {
const splittedImageUrl = imageUrl.split('/');
return splittedImageUrl.pop();
}
return null;
};
export default async function copyAssetsFileIOSAndReturnURI(remoteURL = '', localURI = '') {
try {
if (remoteURL && localURI) {
const imageName = getImageNameFromUrl(remoteURL);
const imgPath = await RNFS.copyAssetsFileIOS(localURI, RNFS.TemporaryDirectoryPath+imageName, 0, 0);
return imgPath;
}
return null;
} catch (err) {
console.log(err);
return null;
}
}

Mobile Safari/WebKit memory issues

We are writing an HTML 5 app that requires large files to be loaded.
We are loading binary data files via ajax. The app always crashes on iPad.
Upon further investigation with instruments on a MAC we noticed that while downloading the file, the memory allocated for webkit exponentially grows then runs out of memory. (100MB file grows memory over 550MB then browser crashes)
We tested loading all kinds of other types of files via other methods such as the Image obj as shown below.
function testImage() {
var download = new Image();
download.src = 'data/33MB.jpg?n=' + new Date().getTime();
}
After the above 33MB image loads, Instruments show us that the memory has grown to 309 MB.
And here is a sample XHR call with same results loading a large binary file.
function testXHR() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'data/100MB.bin?n=' + new Date().getTime());
xhr.send();
}
We are baffled as to what is happening.
How could we load large files on safari mobile?
Any insight would be appreciated. Thanks in advance.

Determine memory limit of iOS today extension

I'm developing an iOS today extension, that can read an image from UIPasteboard and save it on disk. This process fails with large images because iOS extensions can't use much memory. To workaround this issue, I'm checking the size of the image first and try to decide, if the widget can save it or should delegate this task to its host app:
let MAXIMUM_IMAGE_SIZE_BYTES = <SomeMagicNumber>
if let clipboardImage = UIPasteboard.generalPasteboard().image {
let imageSize = CGImageGetHeight(clipboardImage.CGImage) * CGImageGetBytesPerRow(clipboardImage.CGImage)
if imageSize > MAXIMUM_IMAGE_SIZE_BYTES {
// Open host app to save image
}
else {
// Save image directly
}
}
I have the following questions:
Is my size calculation correct? I took it from this thread. I cannot instantiate a JPEG or PNG representation and read its size because of the memory limitations mentioned above.
Can I get rid of that magic number for the maximum image size in bytes? If not, are there any official specifications from Apple that I can use? I cannot test my app on every available iOS model and don't want to risk crashes on older devices.
Thanks a lot for your help!
I'm just starting to look at the memory that a notification service extension is using. I found this presentation. Might be helpful for others.
https://cocoaheads.tv/memory-use-in-extensions-by-conrad-kramer/
What was your solution to this issue?

Adobe air on iOS - FileReference Download Error

When I use the Download method of the FileReference class, everything works fine on Desktop and Android, but I get an error on iOS.
This is the code:
var req = new URLRequest(url);
var localRef:FileReference = new FileReference();
localRef.download(req);
On iOS I'm getting an Alert:
Download Error
File downloads not supported.
I already tried to NavigateToUrl() and it asks save the file in Dropbox or another App.
How can I fix this error?
You shouldn't use FileReference on mobile (or AIR, in general, though it does open the Load/Save dialog on desktop so there can be some use there). You should instead use File and FileStream, which give you far more control over the file system.
In this case, you could try to use File.download() and save it to File.applicationStorageDirectory, but I don't know if it will have any difference since it extends FileReference.
What I generally do is use URLStream instead of URLLoader. It gives you access to the raw bytes of the file you are downloading and then use File and FileStream
So something like (and this is untested off the top of my head, though I have used similar in the past):
var urlStream:URLStream = new URLStream();
urlStream.addEventListener(Event.COMPLETE, completeHandler);
urlStream.load(new URLLoader('url');
function completeHandler(e:Event):void {
var bytes:ByteArray = new ByteArray();
urlStream.readBytes(bytes);
var f:File = File.applicationStorageDirectory.resolvePath('filename');
var fs:FileStream = new FileStream();
fs.open(f, FileMode.WRITE);
fs.writeBytes(bytes);
fs.close();
}
Now, obviously, there is a lot more you want to account for (errors, progress, etc). That should be enough to point you in the right direction, however.
It's possible to create a full download manager using this method (something I did for an iOS project two years ago), since you can save as-you-go to the file system rather than waiting until Event.COMPLETE fires (using the ProgressEvent.PROGRESS event). That allows you to avoid having a 500MB file in memory, something most devices can't handle.

Adobe AIR 3.8 Flash Builder 4.6 for iOS - Secondary SWF with Actionscript works on fast packaging but fails on Releast Build

I have a simple Flex Mobile project that just allows user to swap between 2 different .swf games. I load the .swf with the following code:
private function loadFile(f:String):void{
var _urlRequest:URLRequest = new URLRequest(f);
var _loader:Loader = new Loader();
var _lc:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain, null);
_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onSWFLoaded);
_loader.load(_urlRequest, _lc);
txt.text="loading";
// add loader to container
grp.addChild(_loader);
}
private function onSWFLoaded(e:Event):void {
// status text to show it loaded
txt.text="loaded!";
}
Then on user button click I just do:
loadFile("file1.swf");
file1.swf is packaged into the build. I run this on debug under fast packaging on my iPod Touch and everything works like a charm, but when I do an export release build, my status text still says "loaded", but the swf loads very strangely - as if there's some code that is failing to run properly.
Since it works on fast packaging with no issue, what would be the difference between release build and fast build that can cause code in child swfs to fail?
Any ideas would be appreciated.
Thanks.
Solved the issues based on comments from here:
http://forums.adobe.com/message/5398137
Using var file:File = File.applicationDirectory.resolvePath("file1.swf"); and adding the Flex Compiler option "swf-version=19" fixed the issue for me. Not sure which one of the two changes fixed it but it all works now.

Resources