How to edit a BitmapImage in Windows Phone 8.1 RT? - c#

I have to edit a bitmapimage at pixel level. But Writeablebitmap Class for windows phone doesn't have WritePixels function.

I hope this is something you are looking for.
I had the same problem and managed to solve it with this. If you need some other explanation please leave a comment.
private static async Task<BitmapImage> ConvertImage(byte[] imageSource)
{
if (imageSource == null)
{
return null;
}
MemoryStream memoryStream = new MemoryStream(imageSource);
using (IRandomAccessStream randomAccessStream = memoryStream.AsRandomAccessStream())
{
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(randomAccessStream);
PixelDataProvider provider = await decoder.GetPixelDataAsync(decoder.BitmapPixelFormat, decoder.BitmapAlphaMode, new BitmapTransform(), ExifOrientationMode.RespectExifOrientation, ColorManagementMode.ColorManageToSRgb);
byte[] pixels = provider.DetachPixelData();
//Each Pixel is composed of 4 bytes [0]: Blue, [1]: Green, [2]: Red, [3] Alpha
//Do Here your magic with this pixels byte array
using (InMemoryRandomAccessStream memoryRandomAccessStream = new InMemoryRandomAccessStream())
{
var imageBytes = await EncodeImageBytes(memoryRandomAccessStream, decoder, pixels);
using (InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream())
{
BitmapImage image = new BitmapImage();
await stream.WriteAsync(imageBytes.AsBuffer());
stream.Seek(0);
image.SetSource(stream);
return image;
}
}
}
}
private static async Task<byte[]> EncodeImageBytes(InMemoryRandomAccessStream memoryRandomAccessStream, BitmapDecoder decoder, byte[] pixels)
{
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, memoryRandomAccessStream);
encoder.SetPixelData(decoder.BitmapPixelFormat, decoder.BitmapAlphaMode, decoder.PixelWidth, decoder.PixelHeight, 96, 96, pixels);
await encoder.FlushAsync();
var imageBytes = new byte[memoryRandomAccessStream.Size];
await memoryRandomAccessStream.ReadAsync(imageBytes.AsBuffer(), (uint) memoryRandomAccessStream.Size, InputStreamOptions.None);
return imageBytes;
}

Related

Error when convert byte array to BitmapImage in UWP

I want to get a BitmapImage when capture view's screenshot. So I start to get byte array data first, then convert to BitmapImage.
RenderTargetBitmap renderTarget = new RenderTargetBitmap();
await renderTarget.RenderAsync(swapChainPanel);
IBuffer pixelBuffer = await renderTarget.GetPixelsAsync();
await GetBitmapAsync(pixelBuffer.ToArray());
...
public static async Task<BitmapImage> GetBitmapAsync(byte[] data)
{
var bitmapImage = new BitmapImage();
try
{
using (var stream = new InMemoryRandomAccessStream())
{
using (var writer = new DataWriter(stream))
{
writer.WriteBytes(data);
await writer.StoreAsync();
await writer.FlushAsync();
writer.DetachStream();
}
stream.Seek(0);
await bitmapImage.SetSourceAsync(stream); // throw Exception
}
return bitmapImage;
}
catch (Exception e)
{
return null;
}
}
But it give error :
The component cannot be found. (Exception from HRESULT: 0x88982F50)
Please help me to find the problem.
Error when convert byte array to BitmapImage in UWP
The problem is you have not specific BitmapEncoder for BitmapImage when convertering. In general, we often use the following code to get BitmapImage from bytes.
_backAction = new Action<byte[]>(async (bytes) =>
{
InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream();
BitmapImage img = new BitmapImage();
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
encoder.SetPixelData(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Straight,
(uint)48,
(uint)32,
DisplayInformation.GetForCurrentView().LogicalDpi,
DisplayInformation.GetForCurrentView().LogicalDpi,
bytes);
await encoder.FlushAsync();
await img.SetSourceAsync(stream);
});

Create Tiff File from multiple BitmapImage

Background:
I'm developing Win 10 Universal App, have list of BitmapImage:
List<BitmapImage> ImagesList = new List<BitmapImage>();
Each list item is created by converting byte[] to BitmapImage by this code:
public async Task<BitmapImage> GetBitmapImage(byte[] array)
{
using (InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream())
{
using (DataWriter writer = new DataWriter(stream.GetOutputStreamAt(0)))
{
writer.WriteBytes(array);
await writer.StoreAsync();
}
BitmapImage image = new BitmapImage();
List<BitmapImage> ImagesList = new List<BitmapImage>();
await image.SetSourceAsync(stream);
return image;
}
}
Question:
How to convert this list to single multi-page Tiff file?
Notes:
I've found many related answers like this but all are based on System.Drawing library which is not supported in Windows 10 Universal Apps, so as you can see in my code, I'm using Windows.Ui.Xaml.Media.Imaging.BitmapImage object instead of System.Drawing.Bitmap to get the image.
How to convert this list to single multi-page Tiff file
In UWP app, we can use BitmapEncoder to encode a Tiff image file to contain several frames. BitmapEncoder.SetPixelData method can be used for setting pixel data on one frame and then BitmapEncoder.GoToNextFrameAsync can asynchronously commits the current frame data and appends a new empty frame to be edited. So the Tiff image can be created by multiply images.
Suppose I want to create a Tiff image file from three images that are located on my local folder, I decode and read pixel data from them and set to the Tiff image. Sample code as follows:
private async void btnConvert_Click(object sender, RoutedEventArgs e)
{
StorageFolder localfolder = ApplicationData.Current.LocalFolder;
StorageFile image1 = await localfolder.GetFileAsync("caffe1.jpg");
StorageFile image2 = await localfolder.GetFileAsync("caffe2.jpg");
StorageFile image3 = await localfolder.GetFileAsync("caffe3.jpg");
StorageFile targettiff = await localfolder.CreateFileAsync("temp.tiff", CreationCollisionOption.ReplaceExisting);
WriteableBitmap writeableimage1;
WriteableBitmap writeableimage2;
WriteableBitmap writeableimage3;
using (IRandomAccessStream stream = await image1.OpenAsync(FileAccessMode.Read))
{
SoftwareBitmap softwareBitmap;
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
softwareBitmap = await decoder.GetSoftwareBitmapAsync();
writeableimage1 = new WriteableBitmap(softwareBitmap.PixelWidth, softwareBitmap.PixelHeight);
writeableimage1.SetSource(stream);
}
using (IRandomAccessStream stream = await image2.OpenAsync(FileAccessMode.Read))
{
SoftwareBitmap softwareBitmap;
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
softwareBitmap = await decoder.GetSoftwareBitmapAsync();
writeableimage2 = new WriteableBitmap(softwareBitmap.PixelWidth, softwareBitmap.PixelHeight);
writeableimage2.SetSource(stream);
}
using (IRandomAccessStream stream = await image3.OpenAsync(FileAccessMode.Read))
{
SoftwareBitmap softwareBitmap;
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
softwareBitmap = await decoder.GetSoftwareBitmapAsync();
writeableimage3 = new WriteableBitmap(softwareBitmap.PixelWidth, softwareBitmap.PixelHeight);
writeableimage3.SetSource(stream);
}
using (IRandomAccessStream ras = await targettiff.OpenAsync(FileAccessMode.ReadWrite, StorageOpenOptions.None))
{
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.TiffEncoderId, ras);
var stream = writeableimage1.PixelBuffer.AsStream();
byte[] buffer = new byte[stream.Length];
await stream.ReadAsync(buffer, 0, buffer.Length);
var stream2 = writeableimage2.PixelBuffer.AsStream();
byte[] buffer2 = new byte[stream2.Length];
await stream2.ReadAsync(buffer2, 0, buffer2.Length);
var stream3 = writeableimage3.PixelBuffer.AsStream();
byte[] buffer3 = new byte[stream3.Length];
await stream3.ReadAsync(buffer3, 0, buffer3.Length);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)writeableimage1.PixelWidth, (uint)writeableimage1.PixelHeight, 96.0, 96.0, buffer);
await encoder.GoToNextFrameAsync();
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)writeableimage2.PixelWidth, (uint)writeableimage2.PixelHeight, 96.0, 96.0, buffer2);
await encoder.GoToNextFrameAsync();
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, (uint)writeableimage3.PixelWidth, (uint)writeableimage3.PixelHeight, 96.0, 96.0, buffer3);
await encoder.FlushAsync();
}
}
The temp.tiff will be created successfully. I'm not sure how you got the image byte array, but BitmapImage cannot be directly written to or updated, you need to got WriteableBitmap object from your byte array. If you don't know how to get the WriteableBitmap please try to reference the following code or save the BitmapImage to local folder and using the code I provided above.
public async Task<WriteableBitmap> SaveToImageSource(byte[] imageBuffer)
{
using (MemoryStream stream = new MemoryStream(imageBuffer))
{
var ras = stream.AsRandomAccessStream();
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(BitmapDecoder.JpegDecoderId, ras);
var provider = await decoder.GetPixelDataAsync();
byte[] buffer = provider.DetachPixelData();
WriteableBitmap ablebitmap = new WriteableBitmap((int)decoder.PixelWidth, (int)decoder.PixelHeight);
await ablebitmap.PixelBuffer.AsStream().WriteAsync(buffer, 0, buffer.Length);
return ablebitmap;
}
}
More details please reference the official sample.

BitmapEncoder flush throws Argument Exception

I'm trying to resize images in my UWP application. Most of the time the appended code works, but sometimes await encoder.FlushAsync(); throws an ArgumentException.
I've headed over to MSDN (https://msdn.microsoft.com/en-us/library/windows/apps/windows.graphics.imaging.bitmapencoder.bitmaptransform.aspx) and they tell me (at "Remarks"):
If you try scale an image stored in an indexed pixel format using the BitmapTransform member, FlushAsync fails with HRESULT WINCODEC_ERR_INVALIDPARAMETER . Instead, you must use GetPixelDataAsync to obtain the scaled pixel data and then use SetPixelData to set it on the encoder.
I've tried to do that, see the two commented lines (which look somehow wrong to me due to the repetition). On the second line (where I try to SetPixelData) the Encoder rewards me with an buffer allocated not sufficient Exception.
var decoder = await BitmapDecoder.CreateAsync(streamToReadFrom.AsStream().AsRandomAccessStream());
if (decoder.OrientedPixelHeight > height ||
decoder.OrientedPixelWidth > width)
{
var resizedStream = new InMemoryRandomAccessStream();
BitmapEncoder encoder = await BitmapEncoder.CreateForTranscodingAsync(resizedStream, decoder);
encoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.Fant;
encoder.BitmapTransform.ScaledHeight = newHeight;
encoder.BitmapTransform.ScaledWidth = newWidth;
//"buffer allocated not sufficient"
// var pd = await decoder.GetPixelDataAsync(BitmapPixelFormat.Rgba16, BitmapAlphaMode.Ignore,
// encoder.BitmapTransform, ExifOrientationMode.IgnoreExifOrientation, ColorManagementMode.DoNotColorManage);
// encoder.SetPixelData(BitmapPixelFormat.Rgba16, BitmapAlphaMode.Ignore,
// decoder.OrientedPixelWidth, decoder.OrientedPixelHeight, decoder.DpiX, decoder.DpiY, pd.DetachPixelData());
// write out to the stream
// might fail cause https://msdn.microsoft.com/en-us/library/windows/apps/windows.graphics.imaging.bitmapencoder.bitmaptransform.aspx
await encoder.FlushAsync();
// Read out resizedStream and return
}
Example image which causes this issue: http://www.spiegel.de/images/image-1028227-hppano-lqbn.jpg. Unit Test here: https://github.com/famoser/OfflineMedia/blob/master/Famoser.OfflineMedia.UnitTests/Presentation/ImageResizeTest.cs
How can I avoid the ArgumentException? How do I know an image is in an "indexed pixel format", and how can I resize this format too?
On the second line (where I try to SetPixelData) the Encoder rewards me with an buffer allocated not sufficient Exception.
This is because when you SetPixelData, the pixel data doesn't match it from GetPixelDataAsync. You can for example code like this:
if (file != null)
{
BitmapImage bmp = new BitmapImage();
using(var imageStream = await file.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite))
{
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(imageStream);
InMemoryRandomAccessStream pixelras = new InMemoryRandomAccessStream();
BitmapEncoder pixelencoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, pixelras);
BitmapTransform transform = new BitmapTransform();
transform.InterpolationMode = BitmapInterpolationMode.Fant;
transform.ScaledHeight = 400;
transform.ScaledWidth = 400;
var provider = await decoder.GetPixelDataAsync(BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Ignore,
transform,
ExifOrientationMode.RespectExifOrientation,
ColorManagementMode.DoNotColorManage);
var pixels = provider.DetachPixelData();
pixelencoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, 400,
400, decoder.DpiX, decoder.DpiY, pixels);
try
{
await pixelencoder.FlushAsync();
}
catch(Exception ex)
{
}
bmp.SetSource(pixelras);
img.Source = bmp;
}
}
How do I know an image is in an "indexed pixel format", and how can I resize this format too?
I couldn't find any effective way to detect an indexed pixel format image, but since it is said
If you try scale an image stored in an indexed pixel format using the BitmapTransform member, FlushAsync fails with HRESULT WINCODEC_ERR_INVALIDPARAMETER . Instead, you must use GetPixelDataAsync to obtain the scaled pixel data and then use SetPixelData to set it on the encoder.
It is a method to use catch the exception and use SetPixelData again, for example:
if (file != null)
{
BitmapImage bmp = new BitmapImage();
using(var imageStream = await file.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite))
{
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(imageStream);
InMemoryRandomAccessStream ras = new InMemoryRandomAccessStream();
BitmapEncoder encoder = await BitmapEncoder.CreateForTranscodingAsync(ras, decoder);
encoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.Fant;
encoder.BitmapTransform.ScaledHeight = 400;
encoder.BitmapTransform.ScaledWidth = 400;
try
{
await encoder.FlushAsync();
bmp.SetSource(ras);
}
catch (Exception ex)
{
if (ex.HResult.ToString() == "WINCODEC_ERR_INVALIDPARAMETER")
{
InMemoryRandomAccessStream pixelras = new InMemoryRandomAccessStream();
BitmapEncoder pixelencoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, pixelras)
BitmapTransform transform = new BitmapTransform();
transform.InterpolationMode = BitmapInterpolationMode.Fant;
transform.ScaledHeight = 400;
transform.ScaledWidth = 400;
var provider = await decoder.GetPixelDataAsync(BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Ignore,
transform,
ExifOrientationMode.RespectExifOrientation,
ColorManagementMode.DoNotColorManage);
var pixels = provider.DetachPixelData();
pixelencoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, 400,
400, decoder.DpiX, decoder.DpiY, pixels);
try
{
await pixelencoder.FlushAsync();
bmp.SetSource(pixelras);
}
catch
{
}
}
}
img.Source = bmp;
}
}

Convert BitmapImage or IRandomAccessStream to byte array in Windows 10 UAP

Who can help me. I don't understand how i can convert BitmapImage or IRandomAccessStream to byte array.
I try:
foreach (StorageFile file in files)
{
BitmapImage src = new BitmapImage();
using (IRandomAccessStream stream = await file.OpenReadAsync())
{
await src.SetSourceAsync(stream);
WriteableBitmap bitMap = new WriteableBitmap(src.PixelWidth, src.PixelHeight);
await bitMap.SetSourceAsync(stream);
}
}
then i have WriteableBitmap and try this:
private byte[] ImageToByeArray(WriteableBitmap wbm)
{
using (Stream stream = wbm.PixelBuffer.AsStream())
using (MemoryStream memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
return memoryStream.ToArray();
}
}
but it's don't work for me ;(
This should do it:
async Task<byte[]> Convert(IRandomAccessStream s)
{
var dr = new DataReader(s.GetInputStreamAt(0));
var bytes = new byte[s.Size];
await dr.LoadAsync((uint)s.Size);
dr.ReadBytes(bytes);
return bytes;
}
I used this solution in my WPF applications to save images in database as byte[]. It should also work in your case.
public static byte[] ImageToString(System.Windows.Media.Imaging.BitmapImage img) {
System.IO.MemoryStream stream = new System.IO.MemoryStream();
System.Windows.Media.Imaging.BmpBitmapEncoder encoder = new System.Windows.Media.Imaging.BmpBitmapEncoder();
encoder.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create((System.Windows.Media.Imaging.BitmapSource)img));
encoder.Save(stream);
stream.Flush();
return stream.ToArray();
}

Convert a RenderTargetBitmap to BitmapImage

How can I convert a RenderTargetBitmap to BitmapImage in C# XAML, Windows 8.1?
I tried
// rendered is the RenderTargetBitmap
BitmapImage img = new BitmapImage();
InMemoryRandomAccessStream randomAccessStream = new InMemoryRandomAccessStream();
await randomAccessStream.WriteAsync(await rendered.GetPixelsAsync());
randomAccessStream.Seek(0);
await img.SetSourceAsync(randomAccessStream);
But it always gives error at
img.SetSourceAsync(randomAccessStream);
There are many ways in WPF but in WinRT? How can I do that ?
Thanks a lot!
this is the one that worked
Sharing render to bitmap image in windows phone 8.1
turned out that i just can't fill the stream directly using
stream.WriteAsync(byteArray.AsBuffer());
you have to use bitmap encoder , final working code :
InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream();
var buffer = await rendered.GetPixelsAsync();
// await stream.ReadAsync(buffer, (uint)buffer.Length, InputStreamOptions.None);
BitmapImage img = new BitmapImage();
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
encoder.SetPixelData(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Straight,
(uint)rendered.PixelWidth,
(uint)rendered.PixelHeight,
DisplayInformation.GetForCurrentView().LogicalDpi,
DisplayInformation.GetForCurrentView().LogicalDpi,
buffer.ToArray());
await encoder.FlushAsync();
await img.SetSourceAsync(stream);
preview.Source = img;
Have you tried this :
var bitmap = new RenderTargetBitmap();
await bitmap.RenderAsync(elementToRender);
image.Source = bitmap;
Reference : http://social.technet.microsoft.com/wiki/contents/articles/20648.using-the-rendertargetbitmap-in-windows-store-apps-with-xaml-and-c.aspx
UPDATE :
Another Refs.. may help :
Sharing render to bitmap image in windows phone 8.1
https://code.msdn.microsoft.com/windowsapps/File-access-sample-d723e597 .
UPDATE 2 :
Try this one :
private async Task<BitmapImage> ByteArrayToBitmapImage(byte[] byteArray)
{
var bitmapImage = new BitmapImage();
var stream = new InMemoryRandomAccessStream();
await stream.WriteAsync(byteArray.AsBuffer());
stream.Seek(0);
bitmapImage.SetSource(stream);
return bitmapImage;
}
Ref : C# Windows 8 Store (Metro, WinRT) Byte array to BitmapImage

Categories

Resources