I can load JPEGs fine using the specialised JPEG load jpeg method, I can also save PNGs fine using the many methods detailed on SO.
However whenever I create a stream for loading a PNG from isolated storage it results in a BitmapImage of zero size.
Here is what I have ...
public static async Task<BitmapImage> ReadBitmapImageFromIsolatedStorage(string fn)
{
StorageFolder local = Windows.Storage.ApplicationData.Current.LocalFolder;
if (local != null)
{
Stream file = await local.OpenStreamForReadAsync(fn);
BitmapImage bi = new BitmapImage();
bi.SetSource(file);
return bi;
}
return null;
}
I've tried many variations as well. It seems there is some kind of delay to reading the stream when creating BitmapImages which means often the stream is disposed of before the BitmapImage reads it. In WPF there is an option that can be set but WIndows Phone BitmapImage does not have this option.
Try this:
BitmapImage image = new BitmapImage();
image.DecodePixelWidth = 500; //desired width, optional
image.DecodePixelHeight = 500; //desired height, optional
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (myIsolatedStorage.FileExists("imagepath"))
{
using (IsolatedStorageFileStream fileStream = myIsolatedStorage.OpenFile("imagepath", FileMode.Open, FileAccess.Read))
{
image.SetSource(fileStream);
}
}
}
This is what I found works in the end using the new Windows.Storage APIs
I'm not 100% clear what the problem was originally but this works. This has some extra functionality in that if it fails to read, it retries a second or so later ...
public static async Task<BitmapImage> ReadBitmapImageFromIsolatedStorage(string fn)
{
Uri uri = new Uri(String.Format("ms-appdata:///local/{0}", fn));
// FileAccessAttempts is just an int that determines how many time
// to try reading before giving up
for (int i = 0; i < AppConfig.FileAccessAttempts; i++)
{
try
{
StorageFile file = await Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(uri);
Stream stream = await file.OpenStreamForReadAsync();
BitmapImage bi = new BitmapImage();
bi.SetSource(stream);
return bi;
}
catch
{
// Similar functions also have a ~1 second retry interval so introduce
// a random element to prevent blocking from accidentally synched retries
Random r = new Random();
System.Threading.Thread.Sleep(950 + (int)Math.Floor(r.Next() * 50.0));
}
}
return new BitmapImage();
}
Related
I'm developing a WP8 app using Nokia Imaging SDK.
I'm trying to add filter effect to an image and render it into a WriteableBitmap.
Here is my code:
private async void PhotoChosen(object sender, PhotoResult photoResult)
{
if (photoResult != null)
{
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(photoResult.ChosenPhoto);
WriteableBitmap wb = new WriteableBitmap(bitmap.PixelWidth, bitmap.PixelHeight);
StreamImageSource source = new StreamImageSource(photoResult.ChosenPhoto);
var effects = new FilterEffect(source);
effects.Filters = new IFilter[] { new SketchFilter() };
var renderer = new WriteableBitmapRenderer(effects, wb);
await renderer.RenderAsync();
}
}
All is going fine, but when this line is processing:
await renderer.RenderAsync();
This ArgumentException is thrown:
Value does not fall within the expected range
I think I've made a mistake creating the IImageProvider effects or the WriteableBitmap wb
Does anyone got this problem and found an issue ?
Thanks :)
You need to set the stream position before setting it as source for StreamImageSource.
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(photoResult.ChosenPhoto);
WriteableBitmap wb = new WriteableBitmap(bitmap.PixelWidth, bitmap.PixelHeight);
photoResult.ChosenPhoto.Position = 0;
StreamImageSource source = new StreamImageSource(photoResult.ChosenPhoto);
You need to do this because you have called bitmap.SetSource(photoResult.ChosenPhoto). That means that the stream has already been read once, therefore it's position is at the very end of the stream. When StreamImageSource tries to read it, it is already at the end, thus "Value does not fall within the expected range."
I got a OutOfmemory error when i create a list of bitmapimage...
What am i supposed to do...?
Thanks for your help ;)
Here is my code :
foreach (var bytearray in imageDataBlocksPresta)
{
if (bytearray != null)
{
MemoryStream ms;
using (ms = new MemoryStream(bytearray, 0, bytearray.Length))
{
BitmapImage photo = new BitmapImage();
photo.DecodePixelHeight = 800;
photo.DecodePixelWidth = 624;
photo.SetSource(ms);//ERROR
listphotoPresta.Add(photo);
}
}
else//si photo null
{
BitmapImage photo = new BitmapImage();
photo.DecodePixelHeight = 800;
photo.DecodePixelWidth = 624;
photo.UriSource = new Uri("/Images/NoImageIcon.jpg", UriKind.RelativeOrAbsolute);
listphotoPresta.Add(photo);
}
Try setting photo to null and calling GC.Collect() once you've added it. Like this:
listphotoPresta.Add(photo);
photo = null;
GC.Collect();
Don't create NoImageIcon all the time. Use only one reference
Don't try to store all images in memory. It's a mobile.
Limit your app for devices with at least 512MB RAM
Store lower resuolution pictures. 800*600 is full screen on many devices
Load images on demand
Call GC right after you release an image
I have stored few images in isolatedstorage and I am trying to replace them by using
using (IsolatedStorageFile isStore = IsolatedStorageFile.GetUserStoreForApplication()){
if (isStore.FileExists(fileName)){
isStore.DeleteFile(fileName);
}
using (IsolatedStorageFileStream targetStream = isStore.OpenFile(fileName, FileMode.Create, FileAccess.Write)){
// Initialize the buffer for 4KB disk pages.
byte[] readBuffer = new byte[4096];
int bytesRead = -1;
// Copy the thumbnail to the local folder.
while ((bytesRead = e.ImageStream.Read(readBuffer, 0, readBuffer.Length)) > 0){
targetStream.Write(readBuffer, 0, bytesRead);
targetStream.Close();
}
}
Now When I am trying to access the new file I end up seeing the old photo. the new photo is not replaced immediately.
But when I close the app and again fetch it I get the new photo. What is wrong?
I was using an ImageBrush to paint the background grid and was binding only the ImageSource of the ImageBrush. I guess this ImageBrush was not getting updated so instead of changing the source of the ImageBrush I created a new object of it and assigned it to Grid.Background. so now it works :)
I'm using the PhotoCaptureDevice class and I can capture the camera frame, but when I am getting an error while copying the image data in the CameraCaptureFrame.CaptureSequence of the CameraCaptureSequence into a MemoryStream and then save it in the Camera Roll. This is the code snippet of what I'm trying to do.
PhotoCaptureDevice cam;
cam = await PhotoCaptureDevice.OpenAsync(<front/rear depending on user input>,<resolution depends on user input>);
CameraCaptureSequence seq;
seq = cam.CreateCaptureSequence(1);
cam.SetProperty(KnownCameraGeneralProperties.PlayShutterSoundOnCapture, true);
MemoryStream captureStream1 = new MemoryStream();
seq.Frames[0].CaptureStream = captureStream1.AsOutputStream();//This stream is for saving the image data to camera roll
await cam.PrepareCaptureSequenceAsync(seq);
await seq.StartCaptureAsync();
bool a = seq.Frames[0].CaptureStream.Equals(0);//This value is false during debugging
if(capturestream1.Length>0)//This condition evaluates to false
{
MediaLibrary library = new MediaLibrary();
Picture picture1 = library.SavePictureToCameraRoll("image1", captureStream1);
}
else
{
//Logic to handle condition
}
As I've added in the comments, the variable bool a evaluates to false which I checked by debugging the code. But for some reason the capturestream1.Length property is 0.
Here's a code snippet on how to capture a sequence with a single image and saving that image to the MediaLibrary. Obviously this is a bit of a trivial example for this API since sequence are really good for capturing multiple images and meshing them up together with post-processing.
private async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
using (MemoryStream stream = new MemoryStream())
using (var camera = await PhotoCaptureDevice.OpenAsync(CameraSensorLocation.Back,
PhotoCaptureDevice.GetAvailableCaptureResolutions(CameraSensorLocation.Back).First()))
{
var sequence = camera.CreateCaptureSequence(1);
sequence.Frames[0].CaptureStream = stream.AsOutputStream();
camera.PrepareCaptureSequenceAsync(sequence);
await sequence.StartCaptureAsync();
stream.Seek(0, SeekOrigin.Begin);
using (var library = new MediaLibrary())
{
library.SavePictureToCameraRoll("currentImage.jpg", stream);
}
}
}
When you run this code snippet you can see the image stored on the device:
You can find a full working sample as part of Nokia's Camera Explorer app that demos end-to-end usecases for the WP8 camera APIs: http://projects.developer.nokia.com/cameraexplorer
I'm using fzip to zip the contents of a folder. The folder contains 4 files of 500MB, the problem is that this uses all of my computers memory. Is there a way to prevent this? And lock the memory at 20% or something?
this is my code:
public function packageFileParser(filesToZip:Array):void {
for (var i:uint = 0; i < filesToZip.length; i++) {
if (filesToZip[i]["isDirectory"] == true) {
packageFileParser(filesToZip[i].getDirectoryListing());
} else {
if (filesToZip[i]["isHidden"] == false) {
var byteLoader:UrlURLLoader = new UrlURLLoader();
byteLoader.dataFormat = URLLoaderDataFormat.BINARY;
byteLoader.addEventListener (flash.events.Event.COMPLETE, urlLoaderCompleteHandler);
var fileRequest:URLRequest = new URLRequest ("/"+filesToZip[i]["nativePath"]);
byteLoader.load (fileRequest);
}
}
}
}
private function urlLoaderCompleteHandler(event:flash.events.Event):void {
var saveZip : Function = function(zip : FZip) : void {
var out : ByteArray = new ByteArray();
zip.serialize(out);
var fs : FileStream = new FileStream;
targetFile = File.desktopDirectory.resolvePath(zipName);
fs.open(targetFile, FileMode.WRITE);
fs.writeBytes(out);
out.clear();
fs.close();
};
var fullpath:String = event.target.urlRequest.url;
var filename:String = fullpath.substr(fullpath.lastIndexOf("/")+1,fullpath.length);
var filepath:String = fullpath.substr(0,fullpath.lastIndexOf("/")+1);
filepath = filepath.split("/Users/Thibaut/Desktop/testfolder").join("");
zip.addFile(filepath+filename, event.target.data);
saveZip(zip);
}
AFAIK, FZip library creates entire archive in memory and then saves it to disk. There's nothing you can do except to change library. Try airxzip from coltware, it served me well (I think there was no big memory loads even on large archives, because they were flushed to disk while adding files to them.)
Also, if you're using AIR capabilities (FileStream class), you should add "air" tag to the question, it changes a lot compared to plain AS3.