I am trying to save an image to the internal storage of an Android device. Then I want to access it in the shared project for the icons in my navigation bar. The image is coming back from an API. Its giving Android.Content.Res.Resources+NotFoundException Resource ID #0x0.
Saving the image:
public async Task<bool> SaveImage(string filename, ImageSource img)
{
System.IO.Stream outputStream = null;
bool success = false;
var renderer = GetHandler(img);
Bitmap photo = Task.Run(async () => await renderer.LoadImageAsync(img, Forms.Context)).Result;
string savedImageFilename = System.IO.Path.Combine(documentsPath, filename);
using (outputStream = new System.IO.FileStream(savedImageFilename, FileMode.Create))
{
if (System.IO.Path.GetExtension(filename).ToLower() == ".png")
success = await photo.CompressAsync(Bitmap.CompressFormat.Png, 100, outputStream);
else
success = await photo.CompressAsync(Bitmap.CompressFormat.Jpeg, 100, outputStream);
}
return success;
}
Loading the Image:
public FileImageSource LoadImage(string filename)
{
var filePath = Path.Combine(documentsPath, MyBlueAssets);
filePath = Path.Combine(filePath, filename);
FileImageSource image = (FileImageSource)ImageSource.FromFile(filePath);
return image;
}
Accessing and displaying in shared project:
DependencyService.Get<IImageCacheDependency>().SaveImage(imageName, Image, count);
FileImageSource loadedImage = DependencyService.Get<IImageCacheDependency ().LoadImage(imageName);
navigation.Icon = loadedImage;
Related
I am making a UWP app where i want to pick a user signature scanned by the user and make the picture transparent.
now first things first:
I am using FileOpenPicker to pick the storage file.
Work tried by me
public async void Button_AddSign_Click(object sender, RoutedEventArgs e)
{
try
{
var _filePicker = new FileOpenPicker();
_filePicker.SuggestedStartLocation = PickerLocationId.Desktop;
_filePicker.ViewMode = PickerViewMode.Thumbnail;
_filePicker.FileTypeFilter.Add(".png");
IStorageFile _file = await _filePicker.PickSingleFileAsync();
StorageFolder storageFolder = await ApplicationData.Current.LocalFolder.GetFolderAsync(CurrentUser);
if (_file != null)
{
StorageFile storageFile = await _file.CopyAsync(storageFolder, "Signature.png");
await MakeSignTransparentAsync(storageFile);
}
}
catch{Exception ex}
}
public static async Task MakeSignTransparentAsync(StorageFile Inputfile)
{
var memStream = new InMemoryRandomAccessStream();
using (IRandomAccessStream fileStream = await Inputfile.OpenAsync(FileAccessMode.ReadWrite))
{
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);
BitmapEncoder encoder = await BitmapEncoder.CreateForTranscodingAsync(memStream, decoder);
encoder.BitmapTransform.ScaledWidth = 600;
encoder.BitmapTransform.ScaledHeight = 200;
await encoder.FlushAsync();
memStream.Seek(0);
fileStream.Seek(0);
fileStream.Size = 0;
await RandomAccessStream.CopyAsync(memStream, fileStream);
memStream.Dispose();
}
Bitmap bmp;
using (MemoryStream ms = new MemoryStream(memStream)) //Getting an error at this line
{
bmp = new Bitmap(ms);
}
bmp.MakeTransparent();
bmp.Save(bmpInput.Path + "test.png", ImageFormat.Png);
}
Error:
Argument 1: cannot convert from 'Windows.Storage.Streams.InMemoryRandomAccessStream' to 'byte[]
Any help is appreciated.
If there is another way around other than this that is also appreciated.
Found a solution using a library ImageMagick
using ImageMagick;
public static async Task MakeSignTransparentAsync(StorageFile bmpInput)
{
using (var img = new MagickImage(bmpInput.Path))
{
// -fuzz XX%
img.ColorFuzz = new Percentage(10);
// -transparent white
img.Transparent(MagickColors.White);
img.Write(bmpInput.Path);
}
}
I am trying to resize image using bitmap from Memorystream and save to directory. It works on the first run but if i try to update the image second time i am getting ArgumentException.
public IActionResult UpdatePhoto(int id, IFormFile file)
{
var company = _context.Companies.FirstOrDefault(x => x.Id == id);
var image = company.Logo;
var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/companies", image);
if (System.IO.File.Exists(path))
{
System.IO.File.Delete(path);
}
ResizeImage(file, file.FileName);
company.Logo = file.FileName;
_context.Companies.Update(company);
_context.SaveChanges();
return RedirectToAction(nameof(Index));
}
I am getting error in Resize Method
public void ResizeImage(IFormFile file, string FileName)
{
using (var memoryStream = new MemoryStream())
{
file.CopyToAsync(memoryStream);
Bitmap original = (Bitmap)Image.FromStream(memoryStream);
Bitmap processed = new Bitmap(original,new Size(300,300));
var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/companies", FileName );
processed.Save(path);
}
you shouldn't be using any of the async methods inside the methods which are not awaitable. updating your code to following should fix the issue.
public void ResizeImage(IFormFile file, string FileName)
{
using (var memoryStream = new MemoryStream())
{
file.CopyTo(memoryStream);
Bitmap original = (Bitmap)Image.FromStream(memoryStream);
Bitmap processed = new Bitmap(original,new Size(300,300));
var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/companies", FileName );
processed.Save(path);
}
}
Is there a way to save the picture in an image control to the Android gallery in Xamarin Forms? All help is appreciated.
var image = new Image();
image.Source = "test.png";
Screenshot
You could use Dependency Service to get the stream from Resource/drawable image.
Create the IDependency interface.
public interface IDependency
{
MemoryStream DrawableByNameToByteArray(string fileName);
}
Android implementation
public class DependencyImplementation : IDependency
{
public MemoryStream DrawableByNameToByteArray(string fileName)
{
var context = Application.Context;
using (var drawable = Xamarin.Forms.Platform.Android.ResourceManager.GetDrawable(context, fileName))
using (var bitmap = ((BitmapDrawable)drawable).Bitmap)
{
var stream = new MemoryStream();
bitmap.Compress(Bitmap.CompressFormat.Png, 100, stream);
bitmap.Recycle();
return stream;
}
}
}
For the IOS implementation, you could refer to the thread in SO.
Convert Image (from drawable folder) to ByteArray
And then register in Android Mainactivity.
DependencyService.Register<IDependency, DependencyImplementation>();
If your android version is highter than android 6.0, you need runtime permission for storage in this question. Please check the Plugin.Permissions with runtime permission.
https://github.com/jamesmontemagno/PermissionsPlugin
After that, you could save the image to picture internal storage.
var filename = "";
var source = image.Source as FileImageSource;
if (source != null)
{
filename = source.File;
}
var savingFile = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyPictures);
//var savingFile1 = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData), "save.png");
var S = DependencyService.Get<IDependency>().DrawableByNameToByteArray(filename);
if (!File.Exists(savingFile))
{
File.WriteAllBytes(savingFile, S.ToArray());
}
In Internal Storage, you couldn't see the files without root permission.
If you want to view it, you could use adb tool.
Please check the way in link.
How to write the username in a local txt file when login success and check on file for next login?
You can use Media Plugin and it can solve your issue. https://github.com/jamesmontemagno/MediaPlugin
You can visit the above link.
takePhoto.Clicked += async (sender, args) =>
{
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
DisplayAlert("No Camera", ":( No camera available.", "OK");
return;
}
var file = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
{
Directory = "Sample",
Name = "test.jpg"
});
if (file == null)
return;
await DisplayAlert("File Location", file.Path, "OK");
image.Source = ImageSource.FromStream(() =>
{
var stream = file.GetStream();
return stream;
});
};
I create livetiles with the following code:
// wide 310x150
var tileXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileWide310x150PeekImage03);
tileXml.GetElementsByTagName(textElementName).LastOrDefault().InnerText = string.Format(artist + " - " + trackname);
var image = tileXml.GetElementsByTagName(imageElementName).FirstOrDefault();
if (image != null)
{
var src = tileXml.CreateAttribute("src");
if (albumart == String.Empty)
src.Value = "Assets/onemusic_logo_wide.scale-240.png";
else
src.Value = albumart;
image.Attributes.SetNamedItem(src);
}
// square 150x150
var squaredTileXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileSquare150x150PeekImageAndText01);
squaredTileXml.GetElementsByTagName(textElementName).FirstOrDefault().InnerText = string.Format(artist + " - " + trackname);
image = squaredTileXml.GetElementsByTagName(imageElementName).LastOrDefault();
if (image != null)
{
var src = squaredTileXml.CreateAttribute("src");
if (albumart == String.Empty)
src.Value = "Assets/onemusic_logo_square.scale-240.png";
else
src.Value = albumart;
image.Attributes.SetNamedItem(src);
}
updater.Update(new TileNotification(tileXml));
updater.Update(new TileNotification(squaredTileXml));
The problem I face is that the images shown on the livetile aren't sharp (in the app they are). I think this is because of the 310x150 pixels size of the template. I looked at the templates, there aren't any higher resolution ones. Is there a way to make the images sharper?
I noticed that providing an image with a resolution of exactly 744x360 pixels solves the problem. So I wrote this function to resize my albumarts (maybe it will come in handy for someone);
private async static Task<string> CropAndSaveImage(string filePath)
{
const string croppedimage = "cropped_albumart.jpg";
// read file
StorageFile file = await StorageFile.GetFileFromPathAsync(filePath);
if (file == null)
return String.Empty;
// create a stream from the file and decode the image
var fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);
// create a new stream and encoder for the new image
using (InMemoryRandomAccessStream writeStream = new InMemoryRandomAccessStream())
{
// create encoder
BitmapEncoder enc = await BitmapEncoder.CreateForTranscodingAsync(writeStream, decoder);
enc.BitmapTransform.InterpolationMode = BitmapInterpolationMode.Linear;
// convert the entire bitmap to a 744px by 744px bitmap
enc.BitmapTransform.ScaledHeight = 744;
enc.BitmapTransform.ScaledWidth = 744;
enc.BitmapTransform.Bounds = new BitmapBounds()
{
Height = 360,
Width = 744,
X = 0,
Y = 192
};
await enc.FlushAsync();
StorageFile albumartfile = await ApplicationData.Current.LocalFolder.CreateFileAsync(croppedimage, CreationCollisionOption.ReplaceExisting);
using (var stream = await albumartfile.OpenAsync(FileAccessMode.ReadWrite))
{
await RandomAccessStream.CopyAndCloseAsync(writeStream.GetInputStreamAt(0), stream.GetOutputStreamAt(0));
}
// return image path
return albumartfile.Path;
}
}
I am using the following code to choose a photo on both iOS and Droid; however, on Droid, images taken in portrait are saved in landscape orientation. On iOS, the image saves with the correct orientation.
Mvx.Resolve<IMvxPictureChooserTask>().TakePicture(2000, 64, CaptureImageStream, () =>
{
/* don't do anything on cancel */
});
protected virtual void CaptureImageStream(Stream stream)
{
var fileStore = Mvx.Resolve<IMvxFileStore>();
const string folderName = "Observation_Photos";
fileStore.EnsureFolderExists(folderName);
//get file name
var fileName = RandomString(10);
while (fileStore.Exists(string.Format("{0}/{1}.jpg", folderName, fileName)))
{
fileName = RandomString(10);
}
//get file bytes
var fileContents = GetBytes(stream);
//write file
var fullPath = string.Format("{0}/{1}.jpg", folderName, fileName);
fileStore.WriteFile(fullPath, fileContents);
}
private static IEnumerable<byte> GetBytes(Stream stream)
{
using (var memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
return memoryStream.ToArray();
}
}
Any ideas why the photo has the wrong orientation on droid or how to resolve the problem?
Update version of the MvxPictureChooserTask.cs from this pull request, https://github.com/MvvmCross/MvvmCross/pull/627, fixed the problem.