In my application I let the users select an image (of any size). The application will then load it to an image control, resize it if needed, and display it on the screen.
I have all the saving / loading from the preferences working correctly right now as I use the Xamarin Forms Plugin.FilePicker and grab the byte array from that to save to my preferences.
The challenge I am facing is that if the uses pics a large image from their device, the large version of the image is what is uploaded to the FilePicker plugin and the byte array is too large to save. (I get the error ' The size of the state manager setting value has exceeded the limit.')
So what I would like to do is take the contents of the image control, which have been resized to a manageable size, convert that to a byte array and then save that in my preferences.
Any idea how I can convert the content of the image control into a byte array so I can serialize it in JSON and save it to my preferences?? Below is that code that saves the Byte Array from the file picker.
private async void btnChooseFile_Clicked(object sender, System.EventArgs e)
{
try
{
FileData fileData = await CrossFilePicker.Current.PickFile();
if (fileData == null)
return; // user canceled file picking
//lblFilePath.Text = fileData.FileName;
imgIcon.Source = ImageSource.FromStream(() => fileData.GetStream());
// THIS IS THE LINE OF CODE I NEED TO CHANGE TO IT SAVES THE
// BYTE ARRAY OF THE SMALLER IMAGE AS DISPLAYED BY THE
// IMAGE CONTROL INSTEAD OF THE FULL SIZE FILE THE USER
// SELECTED
ViewModelObjects.AppSettings.KioskIcon = fileData.DataArray;
}
catch (Exception ex)
{
System.Console.WriteLine("Exception choosing file: " + ex.ToString());
}
}
you could resize the image size befor assigning the value to the image control:
#if __IOS__
public static byte[] ResizeImageIOS(byte[] imageData, float width, float height)
{
UIImage originalImage = ImageFromByteArray(imageData);
UIImageOrientation orientation = originalImage.Orientation;
//create a 24bit RGB image
using (CGBitmapContext context = new CGBitmapContext(IntPtr.Zero,
(int)width, (int)height, 8,
4 * (int)width, CGColorSpace.CreateDeviceRGB(),
CGImageAlphaInfo.PremultipliedFirst))
{
RectangleF imageRect = new RectangleF(0, 0, width, height);
// draw the image
context.DrawImage(imageRect, originalImage.CGImage);
UIKit.UIImage resizedImage = UIKit.UIImage.FromImage(context.ToImage(), 0, orientation);
// save the image as a jpeg
return resizedImage.AsJPEG().ToArray();
}
}
#if __ANDROID__
public static byte[] ResizeImageAndroid (byte[] imageData, float width, float height)
{
// Load the bitmap
Bitmap originalImage = BitmapFactory.DecodeByteArray (imageData, 0, imageData.Length);
Bitmap resizedImage = Bitmap.CreateScaledBitmap(originalImage, (int)width, (int)height, false);
using (MemoryStream ms = new MemoryStream())
{
resizedImage.Compress (Bitmap.CompressFormat.Jpeg, 100, ms);
return ms.ToArray ();
}
}
you could refer to ImageResizer
Related
I am working on a project to number features on an image. Each item being numbered is a textblock object being printed on top of a canvas that has a jpg(or bmp/gif/png) drawn to it's background using an image brush.
When it comes time to save the graphic I want to have two options, one to save it as a new jpg (or other format) that has the textblock objects rendered into it. or option b which would be to save the placements of those textblocks to a seperate file (XML, XAML what have you) so you could come back and continue to edit the entries.
Here is some of my code.
This is where I am trying to save the image
private void miSaveImage_Click(object sender, RoutedEventArgs e)
{
ImageBrush bi = (ImageBrush)canvas.Background;
Rect bounds = VisualTreeHelper.GetDescendantBounds(canvas);
double pw = (bi.ImageSource as BitmapSource).PixelWidth;
double ph = (bi.ImageSource as BitmapSource).PixelHeight;
double px = (bi.ImageSource as BitmapSource).DpiX;
double py = (bi.ImageSource as BitmapSource).DpiY;
//Get the actual width and height of the image, then the dpi and render an image of it
RenderTargetBitmap rtb = new RenderTargetBitmap((int)pw, (int)ph, px*96, py*96, System.Windows.Media.PixelFormats.Default);
rtb.Render(canvas);
//create the encoder and save the image data to it.
BitmapEncoder pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(BitmapFrame.Create(rtb));
try
{
//eventually I need to make this a save dialog but that will come later.
System.IO.MemoryStream ms = new System.IO.MemoryStream();
pngEncoder.Save(ms);
ms.Close();
System.IO.File.WriteAllBytes("Sample.png", ms.ToArray());
}
catch (Exception err)
{
MessageBox.Show(err.ToString(), "Problem saving image", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
This is the code I use to load the image
//Global members
Image i;
static Size _Size = new Size();
private void miLoadImage_Click(object sender, RoutedEventArgs e)
{
// Create OpenFileDialog
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
// Set filter for file extension and default file extension
dlg.DefaultExt = ".png";
dlg.Filter = #"All supported graphics|*.jpg;*.jpeg;*.png;*.gif;*.bmp|
JPEG Files (*.jpeg)|*.jpeg|
PNG Files (*.png)|*.png|
JPG Files (*.jpg)|*.jpg|
GIF Files (*.gif)|*.gif|
BMP Files (*.bmp)|*.bmp";
// Display OpenFileDialog by calling ShowDialog method
Nullable<bool> result = dlg.ShowDialog();
// Get the selected file name and display in a TextBox
if (result == true)
{
// Open document
string filename = dlg.FileName;
Uri uri = new Uri(#dlg.FileName, UriKind.Relative);
//Use canvas rather than image
ImageBrush ib = new ImageBrush();
ib.ImageSource = new BitmapImage(new Uri(filename, UriKind.Relative));
canvas.Background = ib;
if (i == null)
i = new Image();
i.Source = new BitmapImage(new Uri(dlg.FileName));
_Size = new Size(i.Source.Width, i.Source.Height);
}
}
Here is the code I use to write the text to the image.
public void WriteTextToImage(Point position)
{
SolidColorBrush brush = new SolidColorBrush((Color)cpColor.SelectedColor);
//Get something to write on (not an old receipt...)
TextBlock textBlock = new TextBlock();
//Say something useful... well something atleast...
textBlock.Text = tbCurrentLabel.Text;
textBlock.FontSize = slFontSize.Value;
textBlock.Foreground = brush;
Canvas.SetLeft(textBlock, position.X);
Canvas.SetTop(textBlock, position.Y);
canvas.Children.Add(textBlock);
//Need to update the canvas, was not seeing children before doing this.
canvas.UpdateLayout();
//Iterate the label text
IterateLabel();
}
Right now I am only getting a corner of the image that is currently visible in the canvas, and that is being cropped down, by fiddling with the DPI and _Size values I can change how much of the image is shown, but it is static to that one image (If I load a different image all the values are wrong and again I only get a small portion of the image)
If someone could please help me pull my head out of (ahem) I would be most grateful!
TIA
Save entire Canvas
RenderTargetBitmap bmp = new RenderTargetBitmap((int)Cnv.ActualWidth, (int)Cnv.ActualHeight, 100.0, 100.0, PixelFormats.Default);
bmp.Render(Cnv);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));
System.IO.FileStream stream = System.IO.File.Create("G:\\Canvas.png");
encoder.Save(stream);
stream.Close();
Save only Background
BitmapFrame bmp = (BitmapFrame)(Cnv.Background as ImageBrush).ImageSource;
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(bmp);
System.IO.FileStream stream = System.IO.File.Create("G:\\Canvas.png");
encoder.Save(stream);
stream.Close();
I'm building a small app using C#/WPF.
The application receives (from an unmanaged C++ library) a byte array (byte[]) from a bitmap source
In my WPF window, I have an (System.windows.Controls.Image) image which I will use to display the bitmap.
In the code behind (C#) I need to able to take that byte array, create BitmapSource /ImageSource and assign the source for my image control.
// byte array source from unmanaged librariy
byte[] imageData;
// Image Control Definition
System.Windows.Controls.Image image = new Image() {width = 100, height = 100 };
// Assign the Image Source
image.Source = ConvertByteArrayToImageSource(imageData);
private BitmapSource ConvertByteArrayToImagesource(byte[] imageData)
{
??????????
}
I've been working on this for a bit here and haven't been able to figure this out. I've tried several solutions that I've found by goolging around. To date, I haven't been able to figure this out.
I've tried:
1) Creating a BitmapSource
var stride = ((width * PixelFormats.Bgr24 +31) ?32) *4);
var imageSrc = BitmapSource.Create(width, height, 96d, 96d, PixelFormats.Bgr24, null, imageData, stride);
That through a runtime exception saying buffer was too small
Buffer size is not sufficient
2) I tried using a memory stream:
BitmapImage bitmapImage = new BitmapImage();
using (var mem = new MemoryStream(imageData))
{
bitmapImage.BeginInit();
bitmapImage.CrateOptions = BitmapCreateOptions.PreservePixelFormat;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = mem;
bitmapImage.EndInit();
return bitmapImage;
}
This code through an exception on the EndInit() call.
"No imaging component suitableto complete this operation was found."
SOS! I've spent a couple of days on this one and am clearly stuck.
Any help/ideas/direction would be greatly appreciated.
Thanks,
JohnB
Your stride calculation is wrong. It is the number of full bytes per scan line, and should therefore be calculated like this:
var format = PixelFormats.Bgr24;
var stride = (width * format.BitsPerPixel + 7) / 8;
var imageSrc = BitmapSource.Create(
width, height, 96d, 96d, format, null, imageData, stride);
Of course you also have to make sure that you use the correct image size, i.e. that the width and height values actually correspond with the data in imageBuffer.
I have come across a couple of different ways to write images to isolated storage on some Windows Phone sites, however I am unsure which is the best to use for a camera app or if there are some that are better than others:
The first is from this post on a basic camera application: http://msdn.microsoft.com/en-us/library/hh202956(v=VS.92).aspx It takes the jpeg from the camera and writes it to isolated storage directly.
void cam_CaptureImageAvailable(object sender, Microsoft.Devices.ContentReadyEventArgs e)
{
string fileName = savedCounter + ".jpg";
try
{ // Write message to the UI thread.
Deployment.Current.Dispatcher.BeginInvoke(delegate()
{
txtDebug.Text = "Captured image available, saving picture.";
});
// Save picture to the library camera roll.
library.SavePictureToCameraRoll(fileName, e.ImageStream);
// Write message to the UI thread.
Deployment.Current.Dispatcher.BeginInvoke(delegate()
{
txtDebug.Text = "Picture has been saved to camera roll.";
});
// Set the position of the stream back to start
e.ImageStream.Seek(0, SeekOrigin.Begin);
// Save picture as JPEG to isolated storage.
using (IsolatedStorageFile isStore = IsolatedStorageFile.GetUserStoreForApplication())
{
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 image to isolated storage.
while ((bytesRead = e.ImageStream.Read(readBuffer, 0, readBuffer.Length)) > 0)
{
targetStream.Write(readBuffer, 0, bytesRead);
}
}
}
// Write message to the UI thread.
Deployment.Current.Dispatcher.BeginInvoke(delegate()
{
txtDebug.Text = "Picture has been saved to isolated storage.";
});
}
finally
{
// Close image stream
e.ImageStream.Close();
}
}
It seems to use a 4kb Buffer, is there any point in doing it this way? Seems a bit more complicated than this method which converts the image to a bitmap then uses the save as Jpeg method (http://www.windowsphonegeek.com/tips/All-about-WP7-Isolated-Storage---Read-and-Save-Images):
// Create a filename for JPEG file in isolated storage.
String tempJPEG = "logo.jpg";
// Create virtual store and file stream. Check for duplicate tempJPEG files.
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (myIsolatedStorage.FileExists(tempJPEG))
{
myIsolatedStorage.DeleteFile(tempJPEG);
}
IsolatedStorageFileStream fileStream = myIsolatedStorage.CreateFile(tempJPEG);
StreamResourceInfo sri = null;
Uri uri = new Uri(tempJPEG, UriKind.Relative);
sri = Application.GetResourceStream(uri);
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(sri.Stream);
WriteableBitmap wb = new WriteableBitmap(bitmap);
// Encode WriteableBitmap object to a JPEG stream.
Extensions.SaveJpeg(wb, fileStream, wb.PixelWidth, wb.PixelHeight, 0, 85);
//wb.SaveJpeg(fileStream, wb.PixelWidth, wb.PixelHeight, 0, 85);
fileStream.Close();
}
If there are any alternative methods you have also I'd be interested, thanks!
Think you should check this out:
I believe I already answered this and got a +50 bounty SO Save image to Isolated Storaged using byte[]
I have c# windows form which have several controls on it, part of the controls are located one on another. I want a function that will take for input a control from the form and will return the image that has to be behind the control. for ex: if the form has backgroundimage and contains a button on it - if I'll run this function I'll got the part of backgroundimage that located behind the button. any Idea - and code?
H-E-L-P!!!
That's my initial guess, but have to test it.
Put button invisible
capture current screen
Crop screen captured to the clientRectangle of the button
Restablish button.
public static Image GetBackImage(Control c) {
c.Visible = false;
var bmp = GetScreen();
var img = CropImage(bmp, c.ClientRectangle);
c.Visible = true;
}
public static Bitmap GetScreen() {
int width = SystemInformation.PrimaryMonitorSize.Width;
int height = SystemInformation.PrimaryMonitorSize.Height;
Rectangle screenRegion = Screen.AllScreens[0].Bounds;
var bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
Graphics graphics = Graphics.FromImage(bitmap);
graphics.CopyFromScreen(screenRegion.Left, screenRegion.Top, 0, 0, screenRegion.Size);
return bitmap;
}
public static void CropImage(Image imagenOriginal, Rectangle areaCortar) {
Graphics g = null;
try {
//create the destination (cropped) bitmap
var bmpCropped = new Bitmap(areaCortar.Width, areaCortar.Height);
//create the graphics object to draw with
g = Graphics.FromImage(bmpCropped);
var rectDestination = new Rectangle(0, 0, bmpCropped.Width, bmpCropped.Height);
//draw the areaCortar of the original image to the rectDestination of bmpCropped
g.DrawImage(imagenOriginal, rectDestination, areaCortar, GraphicsUnit.Pixel);
//release system resources
} finally {
if (g != null) {
g.Dispose();
}
}
}
This is pretty easy to do. Each control on the form has a Size and a Location property, which you can use to instantiate a new Rectangle, like so:
Rectangle rect = new Rectangle(button1.Location, button1.Size);
To get a Bitmap that contains the portion of the background image located behind the control, you first create a Bitmap of the proper dimensions:
Bitmap bmp = new Bitmap(rect.Width, rect.Height);
You then create a Graphics object for the new Bitmap, and use that object's DrawImage method to copy a portion of the background image:
using (Graphics g = Graphics.FromImage(bmp))
{
g.DrawImage(...); // sorry, I don't recall which of the 30 overloads
// you need here, but it will be one that uses form1.Image as
// the source, and rect for the coordinates of the source
}
This will leave you with the new Bitmap (bmp) containing the portion of the background image underneath that control.
Sorry I can't be more specific in the code - I'm at a public terminal. But the intellisense info will tell you what you need to pass in for the DrawImage method.
I'd like to open a .jpg file in WPF, scale it down to around 50%, then save it back to the file system. What's a good/efficient way to go about doing that?
If you want to save on the memory usage you should look into specifying DecodePixelWidth/DecodePixelHeight on BitmapImage or the JpegDecoder.
The scaling can be accomplished using TransformedBitmap.
I have used 2 functions here. ResizeImage accepts Original Image as Byte Array and target size in pixel. This function Returns resized Image as Byte Array. You can write it in file.
Follow these steps
1) Read Image as Byte array using BinaryReader
2) Call ResizeImage function by Passing this array and target size of imag.
3) Store Returned value in Byte array
4) Write it to file using BinaryWriter
byte[] ResizeImage(byte[] imageFile, int targetSize)
{
System.Drawing.Image oldImage = System.Drawing.Image.FromStream(new MemoryStream(imageFile));
System.Drawing.Size newSize = CalculateDimensions(oldImage.Size, targetSize);
using (System.Drawing.Bitmap newImage = new System.Drawing.Bitmap(oldImage, (int)newSize.Width, (int)newSize.Height))
{
using (System.Drawing.Graphics canvas = System.Drawing.Graphics.FromImage(newImage))
{
canvas.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
canvas.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
canvas.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
canvas.DrawImage(oldImage, new System.Drawing.Rectangle(new System.Drawing.Point(0, 0), newSize));
MemoryStream m = new MemoryStream();
newImage.Save(m, System.Drawing.Imaging.ImageFormat.Jpeg);
return m.GetBuffer();
}
}
}
System.Drawing.Size CalculateDimensions(System.Drawing.Size oldSize, int targetSize)
{
System.Drawing.Size newSize = new System.Drawing.Size();
if (oldSize.Height > oldSize.Width)
{
newSize.Width = (int)(oldSize.Width * ((float)targetSize / (float)oldSize.Height));
newSize.Height = targetSize;
}
else
{
newSize.Width = targetSize;
newSize.Height = (int)(oldSize.Height * ((float)targetSize / (float)oldSize.Width));
}
return newSize;
}
Take a look at the answer with the most upvotes to this question. It offers a helper class for manipulating images. Take a look at the ResizeImage function and SaveJpeg function.
EDIT:
I have found something more WPF-specific here.