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;
}
}
Related
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);
});
I'm trying to convert my cropped image (and/or Grid) to new Normal Image , but it returns as it cropped , in same position, how to fix it, there is how works my program after cropping
private async void Save(object sender, RoutedEventArgs e)
{
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(GGrid, 4096 , 2448);
// IBuffer pixels = await renderTargetBitmap.GetPixelsAsync();
IBuffer pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
FileSavePicker fileSavePicker = new FileSavePicker();
fileSavePicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
fileSavePicker.FileTypeChoices.Add("JPEG files", new List<string>() { ".png"});
fileSavePicker.SuggestedFileName = "image";
var outputFile = await fileSavePicker.PickSaveFileAsync();
if (outputFile == null)
{
// The user cancelled the picking operation
return;
}
using (IRandomAccessStream stream = await outputFile.OpenAsync(FileAccessMode.ReadWrite))
{
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
encoder.SetPixelData(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Straight,
(uint)renderTargetBitmap.PixelWidth,
(uint)renderTargetBitmap.PixelHeight,
DisplayInformation.GetForCurrentView().LogicalDpi,
DisplayInformation.GetForCurrentView().LogicalDpi,
pixelBuffer.ToArray());
try
{
await encoder.FlushAsync();
}
catch (Exception err)
{
switch (err.HResult)
{
case unchecked((int)0x88982F81): //WINCODEC_ERR_UNSUPPORTEDOPERATION
// If the encoder does not support writing a thumbnail, then try again
// but disable thumbnail generation.
encoder.IsThumbnailGenerated = false;
break;
default:
throw err;
}
}
//if (encoder.IsThumbnailGenerated == false)
//{
// await encoder.FlushAsync();
//}
}
}
You need to translate and crop the bitmap.
double x,y,w,h;
GGrid.RenderTransform = new TranslateTransform
{
X = -x, // translate back to origin (0,0)
Y = -y
};
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(GGrid, w, h); // use crop size
Hi all I want to convert pixel buffer to image and print it. This is information that I have in my program:
PixelBuffer: int width, int height, IntPtr buffer, stride. This is how I cannot do it because of some assembly errors:
Windows.UI.Xaml.Controls.Image image = new Windows.UI.Xaml.Controls.Image();
System.Windows.Media.Imaging.BitmapFrame frame = System.Windows.Media.Imaging.BitmapFrame.Create(System.Windows.Media.Imaging.BitmapSource.Create(
(int)pr.pixelBuffer.width,
(int)pr.pixelBuffer.height,
96,
96,
System.Windows.Media.PixelFormats.Rgb24,
null,
pr.pixelBuffer.buffer,
(int)(pr.pixelBuffer.stride * pr.pixelBuffer.height),
(int)pr.pixelBuffer.stride));
Windows.UI.Xaml.Media.Imaging.BitmapImage bitmapImage = new Windows.UI.Xaml.Media.Imaging.BitmapImage(frame.BaseUri);
image.Source = bitmapImage;//frame;
stackPanel.Children.Add(image)
Is there any way to do this without System.Windowxs.Media.Imaging? Using only Windows.UI.Xaml ?
Thanks!
I want to convert pixel buffer to image and print it.
I'm not sure what your pixel buffer come from since your code only shows getting from pr obejct. In uwp app
if you got it from WriteableBitmap instance, you may not need to get pixel buffer firstly, just set the WriteableBitmap as the source of image. Both BitmapImage and WriteableBitmap can be the image source, not only BitmapImage.
StorageFile imagefile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/caffe1.jpg"));
WriteableBitmap writeableimage;
using (IRandomAccessStream stream = await imagefile.OpenAsync(FileAccessMode.Read))
{
SoftwareBitmap softwareBitmap;
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
softwareBitmap = await decoder.GetSoftwareBitmapAsync();
writeableimage = new WriteableBitmap(softwareBitmap.PixelWidth, softwareBitmap.PixelHeight);
writeableimage.SetSource(stream);
}
Windows.UI.Xaml.Controls.Image image = new Windows.UI.Xaml.Controls.Image();
image.Source = writeableimage;
stackPanel.Children.Add(image);
If it doesn't from WriteableBitmap, you may need to create a WriteableBitmap with the known width, height, and write pixel data to it.
StorageFile imagefile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/caffe2.jpg"));
int width;
int height;
byte[] Inptrbuffer;
using (IRandomAccessStream stream = await imagefile.OpenAsync(FileAccessMode.Read))
{
SoftwareBitmap softwareBitmap;
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
softwareBitmap = await decoder.GetSoftwareBitmapAsync();
width = softwareBitmap.PixelWidth;
height = softwareBitmap.PixelHeight;
Windows.Graphics.Imaging.PixelDataProvider pixelData = await decoder.GetPixelDataAsync();
Inptrbuffer = pixelData.DetachPixelData();
}
WriteableBitmap newfrompixel=new WriteableBitmap(width,height);
using (Stream stream = newfrompixel.PixelBuffer.AsStream())
{
await stream.WriteAsync(Inptrbuffer, 0, Inptrbuffer.Length);
}
Windows.UI.Xaml.Controls.Image image = new Windows.UI.Xaml.Controls.Image();
image.Source = newfrompixel;
stackPanel.Children.Add(image);
If you need to edit the image before present it, please reference Create, edit, and save bitmap images.
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.
I get an image by URI (web or file system) and want to encode it into PNG and save to a temporary file:
var bin = new MemoryStream(raw).AsRandomAccessStream(); //raw is byte[]
var dec = await BitmapDecoder.CreateAsync(bin);
var pix = (await dec.GetPixelDataAsync()).DetachPixelData();
var res = new FileStream(Path.Combine(ApplicationData.Current.LocalFolder.Path, "tmp.png"), FileMode.Create);
var enc = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, res.AsRandomAccessStream());
enc.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, dec.PixelWidth, dec.PixelHeight, 96, 96, pix);
await enc.FlushAsync(); //hangs here
res.Dispose();
Problem is, this code hangs on the await enc.FlushAsync() line.
Please help! Thanks.
I don't know for sure why your code hangs -- but you're using several IDisposable thingies, which may be related. At any rate, here's some code that does pretty much what you're trying to do, and it does work:
StorageFile file = await ApplicationData.Current.TemporaryFolder
.CreateFileAsync("image", CreationCollisionOption.GenerateUniqueName);
using (IRandomAccessStream outputStream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
using (MemoryStream imageStream = new MemoryStream())
{
using (Stream pixelBufferStream = image.PixelBuffer.AsStream())
{
pixelBufferStream.CopyTo(imageStream);
}
BitmapEncoder encoder = await BitmapEncoder
.CreateAsync(BitmapEncoder.PngEncoderId, outputStream);
encoder.SetPixelData(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Ignore,
(uint)image.PixelWidth,
(uint)image.PixelHeight,
dpiX: 96,
dpiY: 96,
pixels: imageStream.ToArray());
await encoder.FlushAsync();
}
}
(My image is a WriteableBitmap; not sure what your raw is?)