UI is Freezing when compressing an image - flutter

I'm trying to compress image from camera or gallery, but i tried answer in this question Flutter & Firebase: Compression before upload image
But the UI was freeze , so do you guys have any solution for that, and why the image plugin meet that problem ?
UPDATE:
compressImage(imageFile).then((File file) {
imageFile = file;
});
Future<File> compressImage(File imageFile) async {
return compute(decodeImage, imageFile);
}
File decodeImage(File imageFile) {
Im.Image image = Im.decodeImage(imageFile.readAsBytesSync());
Im.Image smallerImage = Im.copyResize(
image, 150); // choose the size here, it will maintain aspect ratio
return new File('123.jpg')
..writeAsBytesSync(Im.encodeJpg(smallerImage, quality: 85));
}
I meet "unhandled exception" in this code

This is because compression is done in the UI thread.
You can move computation to a new thread using compute() https://docs.flutter.io/flutter/foundation/compute.html
There are currently serious limitations what a non-UI thread can do.
If you pass the image data, it is copied from one thread to the other, which can be slow. If you have the image in a file like you get it from image_picker it is better to pass the file path and read the image in the new thread.
You can only pass values that can be encoded as JSON (it's not actually encoded as JSON, but it supports the same types)
You can not use plugins. This means you need to move the compressed data back to the UI thread by passing the data (which again is copied) or by writing in a file and passing back the path to the file, but in this case copying might be faster because writing a file in one thread and reading it in the other is even slower).
Then you can for example invoke image uploading to Firebase Cloud Storage in the UI thread, but because this is a plugin it will run in native code and not in the UI thread. It's just the UI thread that needs to pass the image along.

Related

How to decrypt audio file in the form of stream in Flutter/Dart?

Project Requirement: Music player app which will download audio files, encrypt and save them. The audio files should be playable in the app only. No other app should be able to play the files. Nor the user should be able to copy the files.
Approach: I don't want the entire decryted audio file to exist at any moment. So I want to encrypt the audio file as soon as it is downloaded. Then when the file is to be played, I want it to be decrypted chunk-by-chunk and played. I believe this can be achieved by using stream. As far as I searched, a package named "just_audio" can play the audio from stream source.
Problem: I cannot find any encryption package for Flutter/Dart which will output the decrypted data in the form of a stream. This is the first time I am trying to implement encryption/decryption, so my knowledge is very poor in this regard.
Notes:
The encryption does not need to be heavy. A typical user not being able to copy the files and play them elsewhere would suffice.
Audio files are going to be large, some of them even hours in length.
I need all the usual functions of a music player (e.g. albums, playlists, progress bars with seeking function, etc.)
Options:
It will be best if there is a package which can do what I need, off the shelf.
To find a basic package and then modifying it into doing what is needed.
Some radically different solution, which takes entirely different path but provides all the solutions.
Firstly, to encrypt or decrypt data, have a look at https://pub.dev/packages/cryptography or https://pub.dev/packages/encrypt or something like that.
Secondly, since you want seeking, it may not be an optimal solution to use streams - the "Stream" abstraction is more like a sequence of data, instead of arbitrarily jumping (seeking) here and there. Instead, divide whole audio (say 1hour) into chunks (say 1minute), and upload/encrypt/decrypt/download each chunk separately and as a whole without using streams. If your chunk is small enough, download/decrypt a chunk will be fast and you do not stuck the UI. If the decrypting is still too slow, have a look at isolates, which are "threads" in Flutter. Run decrypt in a separate isolate then your UI will be smooth.
I need all the usual functions of a music player (e.g. albums, playlists, progress bars with seeking function, etc.)
Seeking is implemented above. For albums/playlists, you may modify any normal audio players in flutter to implement that, or directly implement by your own. it is more like just some UI things, no special things, and anyone familiar with Flutter can write it, no worry.
if you're open to a 3rd package, don't reinvent the wheel..
try this here https://morioh.com/p/34a06006b299 with various CipherStream Options
If you can forego stream encryption and do it after you have the file then try this package, Credit: I used the sample from this answer by Hoaea Varghese
With AES all you need is the path to the file, and you can encrypt files or albums with something as simple as
encrypted_file_path = EncryptData.encrypt_file('your/file/path');
With the code below
import 'dart:io';
import 'package:aes_crypt/aes_crypt.dart';
class EncryptData {
static String encrypt_file(String path) {
AesCrypt crypt = AesCrypt();
crypt.setOverwriteMode(AesCryptOwMode.on);
crypt.setPassword('my cool password');
String encFilepath;
try {
encFilepath = crypt.encryptFileSync(path);
print('The encryption has been completed successfully.');
print('Encrypted file: $encFilepath');
} catch (e) {
if (e.type == AesCryptExceptionType.destFileExists) {
print('The encryption has been completed unsuccessfully.');
print(e.message);
}
else{
return 'ERROR';
}
}
return encFilepath;
}
static String decrypt_file(String path) {
AesCrypt crypt = AesCrypt();
crypt.setOverwriteMode(AesCryptOwMode.on);
crypt.setPassword('my cool password');
String decFilepath;
try {
decFilepath = crypt.decryptFileSync(path);
print('The decryption has been completed successfully.');
print('Decrypted file 1: $decFilepath');
print('File content: ' + File(decFilepath).path);
} catch (e) {
if (e.type == AesCryptExceptionType.destFileExists) {
print('The decryption has been completed unsuccessfully.');
print(e.message);
}
else{
return 'ERROR';
}
}
return decFilepath;
}
}

How to convert file back to Asset

I am using the multiple file picker package on pub.dev, the link is below https://pub.dev/packages/multi_image_picker in conjunction with the image cropper package by Yalantis
https://pub.dev/packages/image_cropper
to let my user pick multiple images and then crop them at will.
I am using this code to convert my asset into a file and feed it into the cropper. And it works.
final temp = await Directory.systemTemp.createTemp();
final data = await finalList[index].getByteData();
File failo = await File('${temp.path}/img').writeAsBytes(
data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
print("The path is ${temp.path}");
File croppedFailo = await ImageCropper.cropImage(
sourcePath: failo.path,
androidUiSettings: AndroidUiSettings(toolbarTitle: "My App"),
);
The tricky bit is to convert it back to an asset so that i can replace the old uncropped asset with this new cropped one..I read through the Asset documentation of the package and I tried this but it made my app crash
Asset croppedPic = new Asset(
croppedFailo.path,
DateTime.now().millisecondsSinceEpoch.toString(),
300,
300,
);
finalList.replaceRange(index, index + 1, [croppedPic]);
EDIT: When i say "asset", i am not referring to images i manually added to the assets/images folder in the app. The multi image picker plugin has a file- type called asset in which it returns images. That is the type to which i want to convert my file back into.
Never mind. I figured it's too unnecessarily complicated to do that. So, i instead just reformatted my entire code to handle images as files instead of assets. And, it actually made my life a lot simpler coz files give you more versatility and less problems than assets.

Reading asset image dimensions without loading into memory

I'm trying to find out the dimensions (width and height) of an asset file but without loading it into memory, or with as little memory usage as possible.
I'm currently doing:
Image _mainMenuTable = Image.asset("images/main_menu_table.png");
but won't that make a widget into memory and also load the image onto it ? Even if I don't display it anywhere.
I've also tried creating an AssetImage, which I'm already doing to set as the background image of a Container, but I can't retrieve the dimensions of the image from it.
Any ideas if there's a better way that Image.asset? This way, I am indeed getting my width and height dynamically, but it loads it into memory twice, no? Once as an Image and once as an AssetImage
Disclaimer: it's my second week into Flutter, so please be gentle in case the answer is obvious.
Cheers!
You just need to use the File class from the dart:io package.
Example -
import 'dart:io';
void main(){
var file = File('file_path');
file.length().then((len) => print(len)); //prints bytes
}

JPEG encoder super slow, how to Optimize it?

I'm building an App with actionscript 3.0 in my Flash builder. This is a followup question this question.
I need to upload the bytearray to my server, but the function i use to convert the bitmapdata to a ByteArray is super slow, so slow it freezes up my mobile device. My code is as follows:
var jpgenc:JPEGEncoder = new JPEGEncoder(50);
trace('encode');
//encode the bitmapdata object and keep the encoded ByteArray
var imgByteArray:ByteArray = jpgenc.encode(bitmap);
temp2 = File.applicationStorageDirectory.resolvePath("snapshot.jpg");
var fs:FileStream = new FileStream();
trace('fs');
try{
//open file in write mode
fs.open(temp2,FileMode.WRITE);
//write bytes from the byte array
fs.writeBytes(imgByteArray);
//close the file
fs.close();
}catch(e:Error){
Is there a different way to convert it to a byteArray? Is there a better way?
Try to use blooddy library: http://www.blooddy.by . But i didn't test it on mobile devices. Comment if you will have success.
Use BitmapData.encode(), it's faster by orders of magnitude on mobile http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/BitmapData.html#encode%28%29
You should try to find a JPEG encoder that is capable of encoding asynchronously. That way the app can still be used while the image is being compressed. I haven't tried any of the libraries, but this one looks promising:
http://segfaultlabs.com/devlogs/alchemy-asynchronous-jpeg-encoding-2
It uses Alchemy, which should make it faster than the JPEGEncoder from as3corelib (which I guess is the one you're using at the moment.)
A native JPEG encoder is ideal, asynchronous would be good, but possibly still slow (just not blocking). Another option:
var pixels:ByteArray = bitmapData.getPixels(bitmapData.rect);
pixels.compress();
I'm not sure of native performance, and performance definitely depends on what kind of images you have.
The answer from Ilya was what did it for me. I downloaded the library and there is an example of how to use it inside. I have been working on getting the CameraUI in flashbuilder to take a picture, encode / compress it, then send it over via a web service to my server (the data was sent as a compressed byte array). I did this:
by.blooddy.crypto.image.JPEGEncoder.encode( bmp, 30 );
Where bmp is my bitmap data. The encode took under 3 seconds and was easily able to fit into my flow of control synchronously. I tried async methods but they ultimately took a really long time and were difficult to track for things like when a user moved from cell service to wifi or from tower to tower while an upload was going on.
Comment here if you need more details.

Storing images in windows phone 8

I have been really cracking my head trying to write and read png files into a folder in Windows Phone 8. From few blogs sites and codeplex i found that the there is an extension to the WritableBitmap Class which provides few extra functionalities. ImageTools has PNG encoder and decoder. But I really cant find examples to use them.
What Im trying to achieve here is to create a folder called page and then in it a file called Ink File. I want to convert the bitmap to a PNG and store it there. The bitmap is created from the strokes drawn on a canvas. The class ImageTools provides a function called ToImage to convert the strokes from the canvas to image.
For storing
ExtendedImage myImage = InkCanvas.ToImage();
var encoder = new PngEncoder();
var dataFolder = await local.CreateFolderAsync("Page", CreationCollisionOption.OpenIfExists);
StorageFile Ink_File = await dataFolder.CreateFileAsync("InkFile", CreationCollisionOption.ReplaceExisting);
using (var stream = await Ink_File.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite))
{
using (var s = await Ink_File.OpenStreamForWriteAsync())
{
encoder.Encode(myImage, s);
await s.FlushAsync();
s.Close();
}
}
Is this a correct method? I receive some null exceptions for this. How do i find if the image is saved as png. How is this image saved? Is it encoded and saved in a file or is it saved as a png itsef. And how do we read this back?
I have checked out this, this , this and lot more like this.
I'm developing app for WP8
I have used the PNG Writer Library found in ToolStack and it works :)