Converting Bitmap to Icon - c#

I'm trying to convert an image from a Bitmap to a Windows icon. This is the code.
private void btnCnvrtSave_Click(object sender, EventArgs e)
{
Bitmap bmp = (Bitmap)picturePanel.BackgroundImage;
Bitmap newBmp = new Bitmap(bmp);
Bitmap targetBmp = newBmp.Clone(new Rectangle(0, 0, newBmp.Width, newBmp.Height), PixelFormat.Format64bppArgb);
IntPtr Hicon = targetBmp.GetHicon();
Icon myIcon = Icon.FromHandle(Hicon);
SaveFileDialog sfd = new SaveFileDialog();
sfd.Title = "Save Icon";
sfd.Filter = "Icon|*.ico";
sfd.ShowDialog();
FileStream fileStream = new FileStream(sfd.FileName,FileMode.OpenOrCreate);
myIcon.Save(fileStream);
fileStream.Flush();
fileStream.Close();
MessageBox.Show("Image is converted successfully!");
}
The code is working fine but the problem is, when I convert the picture to an icon the converted icon loses its true colors and gradients (shown in image). So, is there any way by which I can convert the image without losing its colors?
This is what my icon looks like.

This is a known issue with .Net since it doesn't have an icon encoder. See the following for possible workarounds.
Create Valid Icon Files
Convert Bitmap to Icon problem

Related

How to delete image after save using Bitmap object with Selenium c#?

I am using selenium C# to take screenshots. Convert as Bitmap and then Save to specific path. After save complete. I want to delete that same screenshot. It doesn't allow me to delete and throw the file that is being used by another process.
Below is my code:
public Bitmap GetScreenshot(IWebDriver driver)
{
//Take Screenshot
Screenshot screenshot = (driver as ITakesScreenshot).GetScreenshot();
//Convert Screenshot to Bitmap
var img = Image.FromStream(new MemoryStream(screenshot.AsByteArray)) as Bitmap;
return img;
}
I used above function like this:
var img = GetScreenshot(driver);
seleniumExtras.SaveImageAsPDF(img, "fullpage.jpeg");
Here is the function SaveImageAsPDF:
public void SaveImageAsPDF(Bitmap imageData, string ImagePath)
{
imageData.Save(ImagePath, ImageFormat.Jpeg);
imageData.Dispose();
//Save as PDF
System.Drawing.Image image = System.Drawing.Image.FromFile(ImagePath);
iTextSharp.text.Document doc = new iTextSharp.text.Document(iTextSharp.text.PageSize.A4);
doc.SetPageSize(iTextSharp.text.PageSize.A4.Rotate());
iTextSharp.text.pdf.PdfWriter.GetInstance(doc, new FileStream(ImagePath.Replace(".jpeg", ".pdf"), FileMode.Create));
doc.Open();
iTextSharp.text.Image pdfImage = iTextSharp.text.Image.GetInstance(image, System.Drawing.Imaging.ImageFormat.Jpeg);
//pdfImage.SetAbsolutePosition(0, 0); // set the position to bottom left corner of pdf
pdfImage.ScaleAbsolute(iTextSharp.text.PageSize.A4.Width, iTextSharp.text.PageSize.A4.Height); // set the height and width of image to PDF page size
pdfImage.ScaleToFit(iTextSharp.text.PageSize.A4.Width, iTextSharp.text.PageSize.A4.Height);
doc.Add(pdfImage);
doc.Close();
doc.Dispose();
//Delete image file after PDF created
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
//imageData.Dispose();
imageData.Dispose();
if (File.Exists(ImagePath))
File.Delete(ImagePath);
}

Loading an image to an ink canvas, editing the image with drawing and then saving the image maintianing the size and resolution

Currently I have a simple application where one button loads an image into an ink canvas as its background; as shown here:
private void Button_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.OpenFileDialog openFileDlg = new Microsoft.Win32.OpenFileDialog();
Nullable<bool> result = openFileDlg.ShowDialog();
if (result == true)
{
global.canvas1filepath = openFileDlg.FileName;
System.Windows.Controls.Image myImage = new System.Windows.Controls.Image();
ImageBrush canvas1Background = new ImageBrush();
canvas1Background.ImageSource = new BitmapImage(new Uri(global.canvas1filepath, UriKind.Relative));
inkCanvas1.Background = canvas1Background;
}
}
From here I can draw on the image loaded and then when I press the second button the image will save, this second subroutine is shown below:
private void Button_Click_1(object sender, RoutedEventArgs e)
{
MemoryStream ms = new MemoryStream();
FileStream fs = new FileStream(global.saveLocation, FileMode.Create);
RenderTargetBitmap rtb = new RenderTargetBitmap((int)inkCanvas1.ActualWidth, (int)inkCanvas1.ActualHeight, 96d, 96d, PixelFormats.Default);
rtb.Render(inkCanvas1);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
encoder.Save(fs);
fs.Close();
}
My issue lies in that the image I load in has dimensions of 6000x4000 and when I save the result this is reduced to 350x250. Is there a method of maintaining the size of the image throughout the process?

how to make Image save as a 24-bit image?

How do I get C# to force bitmap images that are saved to be saved as 24-bit images as can be seen when you get the right-click properties of the image in Windows. All the images I save are set to 32-bit. I tried the below code with no luck. The source images are all 24-bit as well but are always saved as 32-bit image
public void Save(int index)
{
string savestrFilename;
SaveFileDialog dialog = new SaveFileDialog();
dialog.Title = "image save...";
dialog.OverwritePrompt = true;
dialog.Filter = "JPEG File(*.jpg)|*.jpg|Bitmap File(*.bmp)|*.bmp";
if (dialog.ShowDialog() == DialogResult.OK)
{
savestrFilename = dialog.FileName;
System.IO.FileStream stream = new System.IO.FileStream(savestrFilename , System.IO.FileMode.Create);
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.QualityLevel = 70;
encoder.Frames.Add(BitmapFrame.Create(_grabImageList[index].BitmapImage));
encoder.Save(stream);
stream.Close();
}
}

Unlocking image from PictureBox

I'm currently developing an application to help scanning and showing images here at my work.
My application is build with multiple forms, the most important forms here is my mainForm to show statistics about the current scanning and a menustrip with different functinalities. I also have ImageViewerForm with a PictureBox which shows on the secondary monitor to view the current scanned image.
I'm using a Timer to poll the folder where the images are scanned to. When a new image has been scanned and the image is unlocked, i'll grap it into a FileStream and show it in the PictureBox, see below:
public static void SetPicture(string filename, PictureBox pb)
{
try
{
Image currentImage;
//currentImage = ImageFast.FromFile(filename);
using (FileStream fsImage = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
currentImage = ScaleImage(Image.FromStream(fsImage), new Size(pb.Width, pb.Height));
if (pb.InvokeRequired)
{
pb.Invoke(new MethodInvoker(
delegate()
{
pb.Image = currentImage;
}));
}
else
{
pb.Image = currentImage;
}
}
}
catch (Exception imageEx)
{
throw new ExceptionHandler("Error when showing image", imageEx);
}
}
public static Image ScaleImage(Image imgToResize, Size size)
{
int sourceWidth = imgToResize.Width;
int sourceHeight = imgToResize.Height;
float nPercent = 0;
float nPercentW = 0;
float nPercentH = 0;
nPercentW = ((float)size.Width / (float)sourceWidth);
nPercentH = ((float)size.Height / (float)sourceHeight);
if (nPercentH < nPercentW)
nPercent = nPercentH;
else
nPercent = nPercentW;
int destWidth = (int)(sourceWidth * nPercent);
int destHeight = (int)(sourceHeight * nPercent);
Bitmap b = new Bitmap(destWidth, destHeight);
using (Graphics g = Graphics.FromImage(b))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
}
return b;
}
This way the image shown in the PictureBox shouldn't be locked, but it is.
The problem is that the scanned image might have to be rescanned, and if I do that i'll get a sharing violation error when trying to overwrite the image file from the scanning software.
Anyone who's got an answer to what I can do?
SOLUTION
Thanks to #SPFiredrake I've got a solution to create a temp file to show in the PictureBox, leaving the original image unlocked.
public static void SetPicture(string filename, PictureBox pb)
{
try
{
Image currentImage;
//currentImage = ImageFast.FromFile(filename);
using (FileStream fsImage = new FileStream(CreateTempFile(filename), FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
currentImage = ScaleImage(Image.FromStream(fsImage), new Size(pb.Width, pb.Height));
if (pb.InvokeRequired)
{
pb.Invoke(new MethodInvoker(
delegate()
{
pb.Image = currentImage;
}));
}
else
{
pb.Image = currentImage;
}
}
}
catch (Exception imageEx)
{
throw new ExceptionHandler("Error when showing image", imageEx);
}
}
public static string CreateTempFile(string fileName)
{
if (string.IsNullOrEmpty(fileName))
throw new ArgumentNullException("fileName");
if (!File.Exists(fileName))
throw new ArgumentException("Specified file must exist!", "fileName");
string tempFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + Path.GetExtension(fileName));
File.Copy(fileName, tempFile);
Log.New("Temp file created: " + tempFile);
return tempFile;
}
The problem here is that the image is being loaded from a FileStream, which is being locked by the PictureBox because it's holding a reference to the stream. What you should do is first load the picture into local memory (via a byte[] array) and then load the image from a MemoryStream instead. In your SetPicture method, you should try the following change and see if it works:
public static void SetPicture(string filename, PictureBox pb)
{
try
{
Image currentImage;
byte[] imageBytes = File.ReadAllBytes(filename);
using(MemoryStream msImage = new MemoryStream(imageBytes))
{
currentImage = ScaleImage(Image.FromStream(msImage), new Size(pb.Width, pb.Height));
....
}
Edit: After our conversation in Chat, updating with the fix that you ended up using:
public static void SetPicture(string filename, PictureBox pb)
{
try
{
Image currentImage;
string tempFile = Path.Combine(Path.GetTempDirectory(), Guid.NewGuid().ToString() + Path.GetExtension(filename));
File.Copy(filename, tempFile);
//currentImage = ImageFast.FromFile(filename);
using (FileStream fsImage = new FileStream(tempFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
...
This way you are using the temp files to actually load the picture box, leaving the original file untouched (outside of the initial copy).
Once a Bitmap is loaded, you're not holding onto the filestream anymore, so everything should work. However, if you're talking about the instant when loading is taking place and the scanning tries to overwrite that file - always scan to a "temporary" or junk-named file (use a GUID as the name). Once scanning is complete, rename that file to JPG - which your display form will then pick up and display properly.
This way, re-scans will only involve trying to rename the temporary file multiple times with a "wait" to prevent that little area of overlap.
Your code works fine for me. I took an exact copy and called it repeatedly with the same image file.
SetPicture(#"c:\temp\logo.png", pictureBox1);
Something else is locking the file. Can you share your calling code?
I guess you're done with your work by now.
Still, I am posting in case someone else has the same issue.
I had the same problem : I load an image in a PictureBox control
picture.Image = new Bitmap(imagePath);
and when attempting to move it
File.Move(source, destination);
mscorlib throws an exception :
The process cannot access the file because it is beeing used by another process
I found a solution ( although in VB.Net rather than C# ) here PictureBox "locks" file, cannot move/delete
The author of the post clones the original image and loads the cloned image to the PictureBox control.
I slighty changed the code and came up with this :
private Bitmap CloneImage(string aImagePath) {
// create original image
Image originalImage = new Bitmap(aImagePath);
// create an empty clone of the same size of original
Bitmap clone = new Bitmap(originalImage.Width, originalImage.Height);
// get the object representing clone's currently empty drawing surface
Graphics g = Graphics.FromImage(clone);
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed;
// copy the original image onto this surface
g.DrawImage(originalImage, 0, 0, originalImage.Width, originalImage.Height);
// free graphics and original image
g.Dispose();
originalImage.Dispose();
return clone;
}
So, we code will be :
picture.Image = (Image)CloneImage(imagePath);
Doing so, I had no more exception when moving files.
I think it is a good alternative way to do that, and you don't need a temporary file.
this is Jack code but in Visual Basic .NET and the casting goes inside the function
Private Function CloneImage(aImagePath As String) As Image
' create original image
Dim originalImage As Image = New Bitmap(aImagePath)
' create an empty clone of the same size of original
Dim clone As Bitmap = New Bitmap(originalImage.Width, originalImage.Height)
' get the object representing clone's currently empty drawing surface
Dim g As Graphics = Graphics.FromImage(clone)
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed
' copy the original image onto this surface
g.DrawImage(originalImage, 0, 0, originalImage.Width, originalImage.Height)
' free graphics and original image
g.Dispose()
originalImage.Dispose()
Return CType(clone, Image)
End Function
So calling will be
picture.Image = CloneImage(imagePath)
Thank you Jack,
Response to this question from MS ...
for me is workikng ok ...
internal void UpdateLastImageDownloaded(string fullfilename)
{
this.BeginInvoke((MethodInvoker)delegate()
{
try
{
//pictureBoxImage.Image = Image.FromFile(fullfilename);
//Bitmap bmp = new Bitmap(fullfilename);
//pictureBoxImage.Image = bmp;
System.IO.FileStream fs;
// Specify a valid picture file path on your computer.
fs = new System.IO.FileStream(fullfilename, System.IO.FileMode.Open, System.IO.FileAccess.Read);
pictureBoxImage.Image = System.Drawing.Image.FromStream(fs);
fs.Close();
}
catch (Exception exc)
{
Logging.Log.WriteException(exc);
}
});
}
While trying to figure out a solution for my C# Windows Form I came across a helpful article mentioning how to load a picture in picture box without "Locking" the original picture itself but an instance of it.
Hence if you try to delete, rename or do whatever you want to the original file you won't be notified by an error message mentioning "The file it's in use by another process" or anything else!
This is a reference to the article.
To Sum Up I believe that this solution is VERY useful when handling with SMALL amount of pictures, because applying this method in larger numbers could lead to lack of memory.

Saving Panel as an Image

I'm doing this paint application. It's kind of simple. It consist of a panel where I will draw on and then finally I will save as JPG or BMP or PNG file.
My application work perfectly but the problem I'm facing is that when I'm saving the output is not what drawn on the panel its black Image nothing just black.
all my work is been saved as
Thepic = new Bitmap(panel1.ClientRectangle.Width, this.ClientRectangle.Height);
and on the mouse (down,up thing) I have
snapshot = (Bitmap)tempDraw.Clone();
and it saved the work normally but again the rsult is black Image not what the panel contain.
I think the problem may be that you're using the "Clone" method.
Try "DrawToBitmap" - that's worked for me in the past.
Here's a sample that saves a bitmap from a control called "plotPrinter":
int width = plotPrinter.Size.Width;
int height = plotPrinter.Size.Height;
Bitmap bm = new Bitmap(width, height);
plotPrinter.DrawToBitmap(bm, new Rectangle(0, 0, width, height));
bm.Save(#"D:\TestDrawToBitmap.bmp", ImageFormat.Bmp);
Be aware of saving directly to the C directly as this is not
permitted with newer versions of window, try using SaveFileDialog.
SaveFileDialog sf = new SaveFileDialog();
sf.Filter = "Bitmap Image (.bmp)|*.bmp|Gif Image (.gif)|*.gif|JPEG Image (.jpeg)|*.jpeg|Png Image (.png)|*.png|Tiff Image (.tiff)|*.tiff|Wmf Image (.wmf)|*.wmf";
sf.ShowDialog();
var path = sf.FileName;
You could try this, this works for me.
I used MemoryStream.
MemoryStream ms = new MemoryStream();
Bitmap bmp = new Bitmap(panel1.Width, panel1.Height);
panel1.DrawToBitmap(bmp, new System.Drawing.Rectangle(0, 0, panel1.Width, panel1.Height));
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); //you could ave in BPM, PNG etc format.
byte[] Pic_arr = new byte[ms.Length];
ms.Position = 0;
ms.Read(Pic_arr, 0, Pic_arr.Length);
ms.Close();

Categories