I want to know whether it is possible to programmatically change an image encoding on the fly without saving the image to a file?
Use Case: When the user copies a binary image from a source, would it be possible to change the image encoding from binary to base64?
You could change the encoding of an image without saving it to a file, but not without saving it to a variable in your code. The Clipboard class basically just has some Get and Set methods. The only way to change what's in the clipboard is to call one of the Get methods into a local variable, change whatever it is you just got, and then call one of the Set methods, passing in your changed object. This results in a changed clipboard object, but not without the intermediate step of "saving" it to a variable.
Clipboard does not expose any methods for directly manipulating the memory of the object in the clipboard. Even if such a method were exposed, changing the encoding of an image from binary to Base64 involves fundamentally changing all of the memory, so there wouldn't be much value to it.
Update: here's a method that will take an image from the clipboard, convert it to a base64 string, and put it back into the clipboard:
if (Clipboard.ContainsImage())
{
using (MemoryStream memory = new MemoryStream())
{
using (Image img = Clipboard.GetImage())
{
img.Save(memory, img.RawFormat);
}
string base64 = Convert.ToBase64String(memory.ToArray());
Clipboard.SetText(base64);
}
}
And you'll need these two using statements:
using System.IO;
using System.Windows.Forms;
It's untested (because it's past my bedtime), but it should work. It does involve the use of local variables, but this is unavoidable (as well as normal).
Using the new ClipBoard class in WPF
The below example reads a stream from a file but you could use any stream
var image = new BitmapImage();
image.BeginInit();
image.StreamSource = File.Open("image.png", FileMode.Open);
image.EndInit();
System.Windows.Clipboard.SetImage(image);
http://msdn.microsoft.com/en-us/library/system.windows.clipboard.setimage.aspx
I think that you might be asking how to intercept the copy operation and replace the logical contents of the clipboard with new contents, all from some background app, rather than how to replace the memory bytes allocated to the original copy operation.
If that's the intention, you should look for Win32 API calls to hook into the clipboard so that the background app can process the data copied before it is available for paste.
This article might get you going:
http://www.radsoftware.com.au/articles/clipboardmonitor.aspx
Related
I am trying to validate an image submitted to backend in Base64 string format by parsing it into an Image object, extracting it from the same Image object and finally comparing input byte array and output byte array assuming these two should be the same or there was something wrong in the input image. Here is the code:
private void UpdatePhoto(string photoBase64)
{
var imageDataInBytes = Convert.FromBase64String(photoBase64);
ValidateImageContent(imageDataInBytes);
}
private void ValidateImageContent(byte[] imageDataInBytes)
{
using (var inputMem = new MemoryStream(imageDataInBytes))
{
var img = Image.FromStream(inputMem, false, true);
using (MemoryStream outputMemStream = new MemoryStream())
{
img.Save(outputMemStream, img.RawFormat);
var outputSerialized = outputMemStream.ToArray();
if (!outputSerialized.SequenceEqual(imageDataInBytes))
throw new Exception("Invalid image. Identified extra data in the input. Please upload another photo.");
}
}
}
and it fails on an image that I know is a valid one.
Is my assumption wrong that output of Image.Save must be the same as what Image.FromStream is fed with? Is there a way to correct this logic to achieve this way of validation correctly?
If you compare the original image with the created image, you will notice a few differences in the metadata: For my sample image, I could observe that some metadata was stripped (XMP data was completely removed). In addition, while the EXIF data was preserved, the endianness it is written in was reversed from little endian to big endian. This alone explains why the data won’t match.
In my example, the actual image data was identical but you won’t be able to tell easily from just looking at the bytes.
If you wanted to produce a result identical to the source, you would have to produce the metadata in the exact same way as the source did. You won’t be able to do so without actually looking closely at the metadata of the original photo though. .NET’s Image simply isn’t able to pertain all the metadata that a file could contain. And even if you were able to extract all the metadata and store it in the right format again, there are lots of fine nuances between metadata serializers that make it very difficult to produce the exact same result.
So if you wanted to compare the images, you should probably strip the metadata and just compare the image data. But then, when you think about how you save the image (Raw), then you will just get the exact same blob of data again, so I wouldn’t expect differences there.
I'm trying to insert an image for a barcode into an XML document using C#; the method passes a path to an image, which is then inserted (alongside other data) into an XML file.
I've tried following some other people's recommendations (like this one) , but I'm not getting the correct result; all I get is the raw byte string output in the file.
byte[] bytes = File.ReadAllBytes(barcodePath);
string barcodeString = Convert.ToBase64String(bytes);
//find barcode position
XmlNodeList barcodePossiblesList = doc.SelectNodes("//w:t", namespaceManager);
foreach(XmlNode n in barcodePossiblesList)
{
if(n.InnerText == "Barcode")
{
n.InnerText = barcodeString;
}
}
I've also tried the following, but I still get the same result:
Bitmap bmp = new Bitmap(imageFileName);
TypeConverter converter = TypeDescriptor.GetConverter(typeof(Bitmap));
XElement img = new XElement("image",
Convert.ToBase64String(
(byte[])converter.ConvertTo(bmp, typeof(byte[]))));
element.Add(img);
Can anyone help me? Thanks
I went through all the links in the linked question and found the one that you have been using. What seems to be the issue is that in your program you haven't placed code in there to convert the data in the xml file back into an image, just like Chuck pointed out. The code you provided is most likely saving the data correctly, but if you are using a generic editor (like Notepad or the XML editor built-in to Visual Studio) you will only see the string equivalents to the pixel data of the bitmap. Since you are trying to convert the data back to an image, just use the code provided in the post under the heading Reading the Image from the XML file. Namely,
string val = currentXml.Element("image").Value;
byte[] bytes = Convert.FromBase64String(val);
MemoryStream mem = new MemoryStream(bytes);
Bitmap bmp2 = new Bitmap(mem);
This is a one-liner of the above code. Also taken from the linked site.
Bitmap bmp = new Bitmap(new MemoryStream(Convert.FromBase64String(currentXml.Element("image").Value)));
UPDATE: I tested the code provided in the OP as well as in this post. It does work.
UPDATE 2: In response to your questions, I have decided to expand upon what the blog had posted. I understood it, but I do know LINQ-to-XML well, which eliminated any bumps in the road when the blogger made a terminology error.
To start off, the entire process is to take the pixel data from an image and place it within an xml file. That way the image can be sent with other things. The string that you see when opening the xml file in a plain text editor or xml editor is the image data that has been encoded. You could think of that as the raw data.
In my test case, I created a texture atlas (all frames to an animation placed within a single image file by tiling them in a given order), placed within the xml file some metadata (the number of frames horizontally and vertically). When explaining some of this in the context of experience, the above is for some background.
Something that I should point out now is the using directives used to make this all work:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Xml.Linq;
I will step through the image to xml code. Here is the code modified to work as a static method:
public static XElement ImageToXElement(System.Drawing.Image image)
{
Bitmap bitmap = new Bitmap(image);
TypeConverter converter = TypeDescriptor.GetConverter(typeof(Bitmap));
return new XElement("Image",
new XAttribute("PixelData",
Convert.ToBase64String(
(byte[])converter.ConvertTo(bmp, typeof(byte[]))));
}
The first lines are pretty self explanatory. The last line, the return line all has to do with LINQ-to-XML. It first creates an xml node with an attribute to store the data. I set it up this way in case you wanted to include some other data. I put the animation metadata in there when tweaking my test case. The final portion converts the pixel data from the Image parameter into a string, encoded using base 64 encoding.
Now to convert the Image data from xml to Bitmap format:
public static Bitmap XmlToBitmap(XElement xmlData)
{
string imageAsBase64String = xmlData.Attribute("PixelData").Value;
byte[] imageAsBytes = Convert.FromBase64String(val);
Bitmap result;
using (MemoryStream imageAsMemoryStream = new MemoryStream(bytes))
{
result = new Bitmap(imageAsMemoryStream);
}
return result;
}
I hope that this version explains itself better than the copied code I posted originally, if it does, great! Here's a little explanation of what's going on, which will also serve as a crash course in understanding LINQ-to-XML. First off, this uses an XElement not, I repeat NOT an XmlElement. I know the blogger posted that it's an XmlElement, but as you have already found out, there is a substantial difference between the 2. The first line actually has been totally rewritten to utilize the Image to xml code, so that should help a little bit. What it's doing is getting the image data that is stored as a string. If you noticed, I placed it within an Attribute. This is considered by many to be the proper way of doing it, and I agree with them. There are subtle bugs that can be created by placing data outside of an Attribute. In this example, I was able to retrieve my animation metadata from the same XElement. The next line converts the string into a byte array, which as you can imagine, is more like how an actual image is constructed on a computer. The last lines are wrapping the image data, now in a byte array format, in a MemoryStream and creating a new Bitmap instance to be returned.
If you want the code to play with, just grab the code portions written in the update and place them in a public class file.
I find when I use the Bitmap.Save method with format ImageFormat.Bmp that the bitmap header info in the file has zero in the biSizeImage field.
From what I can see this is ok according to the specs, but unfortunately the crappy embedded device I am forced to work with insists on it being set correctly.
So the question is how to go about getting this header info field always set when the bitmap is saved?
The issue, as you suspect, is that 0 is a perfectly allowable value for the image size field of an RGB bitmap. The MSDN documentation for the BITMAPINFOHEADER structure confirms this fact:
biSizeImage
The size, in bytes, of the image. This may be set to zero for BI_RGB bitmaps.
If biCompression is BI_JPEG or BI_PNG, biSizeImage indicates the size of the JPEG or PNG image buffer, respectively.
So the implementation in the .NET Framework is simply doing things the standard, documented way. Since you need something else, you'll have to handle the save to file process yourself, which will allow you to manually modify the biSizeImage member of the BITMAPINFOHEADER structure.
A pretty comprehensive example is available here: Saving a Control Image to a Bitmap File. Of course, it's written for the Compact Framework, but most of the individual components are still applicable. You'll need to P/Invoke several functions from the Windows API (visit www.pinvoke.net for the definitions), and work with a DC.
Taking the drawing into your own hands will probably also be faster, because you're using the GDI subsystem, rather than GDI+. An article on that general approach is available here: 1bpp in C#, and it appears to demonstrate taking matters into your own hands with the BITMAPINFOHEADER struct as well.
If you understand unmanaged C++ code, there's a tutorial available here that might give you some idea of what you'd have to implement in C# to do the same thing: Loading and Saving Bitmaps. As you can see, the code required really isn't that long, albeit still more than the one line Bitmap.Save method.
I encountered with this problem too and it solved with a simple trick.
save the bitmap, then open it as a file stream and change the biSizeimage field manually.
picbpp.Save("pic.bmp", ImageFormat.Bmp);
string path = "pic.bmp";
FileStream fs = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.None);
// Change biSizeimage field manually.
Byte[] info = new byte[] {0x10,0x59};
fs.Seek(34, SeekOrigin.Begin);
fs.Write(info, 0, info.Length);
fs.Close();
Using C#/.NET for my application, I've got a series of classes in my main data model that represent "binary" (as opposed to text) content. I've got a inheritance setup like this:
Basically, the abstract class BinaryContent contains a MemoryStream that stores arbitrary binary data. That data is read from a file on disk. Each type of binary data I plan to store will be a derived type, such as ImageContent and FontContent. Those derived types will interpret the binary data in BinaryContent.Content. ImageContent for example, would create a BitmapImage (stored in an ImageSource) from the MemoryStream. FontContent would of course create a font from the BinaryContent.Content. I chose this way of doing things because I wanted to be able to basically store a copy of the content file (ie an image) and not have to rely on the file being in a particular location on disk time after time.
I'm also storing instances of these classes in a "project file" using binary serialization. I've done this to basically "package" everything together. I'm having trouble when I attempt to deserialize the MemoryStream, it seems. The problem happens when I create the image from the MemoryStream. When the following method runs after deserialization, a FileFormatexception occurs.
private void RefreshImageFromContent()
{
BitmapImage image = null;
if (Content != null &&
Content.Length != 0L)
{
image = new BitmapImage();
image.BeginInit();
image.StreamSource = Content;
image.EndInit(); //throws FileFormatException
}
Image = image;
}
The FileFormatException message is:
"The image cannot be decoded. The image header might be corrupted."
Inner Exception: "Exception from HRESULT: 0x88982F61"
My best guess right now is that something is happening to corrupt the data in BinaryContent.Content when during serialization or deserialization.
This leads me to ask 2 questions.
Does anyone have any suggestions to fix this problem?
Does anyone have other suggested ways to store arbitrary binary data that is going to be (de)serialized?
Please feel free to ask for clarification on anything about my question.
Thanks.
What is the content.Position at image.StreamSource = Content;?
It is likely that the position of the stream is not set to the start (or the correct position in thw stream).
I must admit that I never understood what are the streams are all about- I always thought it's an internet thing. But now I run into a code that used a stream to load a file localy and I wonder if there is advantage for using a stream over... well the way I always loaded files:
private void loadingfromStream()
{
DirectoryInfo dirInfo = new DirectoryInfo("c:/");
FileInfo[] fileInfoArr = dirInfo.GetFiles();
FileInfo fileInfo = fileInfoArr[0];
// creating a bitmap from a stream
FileStream fileStream = fileInfo.OpenRead();
Bitmap bitmap = new Bitmap(fileStream);
Image currentPicture = (Image)bitmap
}
vs.
private void loadingUsingImageClass
{
Image currentPicture = Image.FromFile(originalPath);
}
If you know your code will be loading the data from a file, use Image.FromFile - it's obviously rather simpler code, and it's just possible that there are optimizations within the framework when it's dealing with files.
Using a stream is more flexible, but unless you need that flexibility, go with the file solution.
If you want to deal with image files, of course the second solution is better. In your first section, you have Bitmap bitmap = new Bitmap(fileStream); you know that an image file is not always Bitmap, it also can be JPEG/PNG/TIFF and so on. While Image.FromFile is quite professional to deal with image files with different extensions.
Generally speaking, FileStream is common at file issues, while Image.FromFile is more particular at image files. It depends on what kind of files you are going to deal with.
Well, a file is often treated as a stream as well. That's why the primary class to open files is called FileStream. But there's a specific operating system feature that can make dealing with image files a lot more efficient. It is called 'memory mapped files', a feature that maps the content of a file directly to memory. There's some smoke and mirrors involved, but it essentially makes the file directly available without having to read it. The memory you need to store the file data doesn't take space in the paging file.
Very efficient, you'll get it for free when you use FromFile() or the Bitmap(string) constructor for an image in the .bmp format. Loading an image from a stream tends to require twice the amount of memory, always a problem with big images.
As an a addition to Jon´s answer:
As far as I see, the two methods don't do the same thing either. The first is given you the first image in "C:\" where the second just give you a image from a path. So the added complexity in the first is not just because it is using streams.
This would be equivalent:
using (var fs = File.OpenRead(path))
using (var img = Image.FromStream(fs))
{
//...
}
and in that case, it is certainly better to just do it with Image.FromFile as Jon explained.