Process images extracted with PdfPig - c#

Images extracted using PdfPig are the type of XObject Image or InlineImage (both inherit from IPdfImage). I would like to save and display them in a simple WPF application. In order to do so, I would need to have them in more accessible form, for example BitmapImage format. What is the correct way to achieve that? Library documentation does not help here and my miserable attempts were unsuccessful.

I haven't tested any of this, but it should at least put you on the right path if it doesn't work.
Looking at the PdfPig source on GitHub I can see both XObjectImage and InlineImage have a function TryGetPng. From the looks of it, I would assume that this byte array would match up with the contents of a normal PNG file, which means you should be able to load it straight into a BitmapImage.
Taking some code from this answer. Something like this might work:
InlineImage pdfImage;
byte[] png;
if (pdfImage.TryGetPng(out png))
{
var bitmap = (BitmapSource)new ImageSourceConverter().ConvertFrom(png);
}
Note: both classes also have a TryGetBytes method, which might work in place of TryGetPng. I'm just not sure what format the output of TryGetBytes is in, so I'd be more confident with TryGetPng. Still, I'd try both if one doesn't work.

FWIW, by trial and error, my current approach is to start with TryGetPng and fall back to RawBytes if it fails. I then interpret the extracted bytes as a System.Drawing.Image. I don't use TryGetBytes at all. Here's my code (F#, but should be easy to convert to C#):
let bytes =
match pdfImage.TryGetPng() with
| true, bytes -> bytes
| _ -> Seq.toArray pdfImage.RawBytes
use stream = new MemoryStream(bytes)
use image = Image.FromStream(stream)

I find the following code for me works in most cases. It simply tries all three options available to extract an image (TryGetPng, TryGetBytes and rawBytes) and converts those to an BmpSource.
private static BitmapSource TryGetImage(IPdfImage image)
{
BitmapSource bmp;
byte[] bytes;
if (image.TryGetPng(out bytes))
{
bmp = (BitmapSource)new ImageSourceConverter().ConvertFrom(bytes);
Debug.WriteLine("Converted using TryGetPng.");
}
else
{
IReadOnlyList<byte> iroBytes;
if (image.TryGetBytes(out iroBytes))
{
bmp = (BitmapSource)new ImageSourceConverter().ConvertFrom(bytes);
Debug.WriteLine("Converted using TryGetBytes.");
}
else
{
var rawB=image.RawBytes.ToArray<Byte>();
Bitmap nbmp;
using (var ms = new MemoryStream(rawB))
{
nbmp = new Bitmap(ms);
}
bmp = ConvertBmpToBmpSource(nbmp);
Debug.WriteLine("Converted using RawBytes.");
}
}
return bmp;
}
public static BitmapSource ConvertBmpToBmpSource(Bitmap bitmap)
{
var bitmapData = bitmap.LockBits(
new Rectangle(0, 0, bitmap.Width, bitmap.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmap.PixelFormat);
var bitmapSource = BitmapSource.Create(
bitmapData.Width, bitmapData.Height,
bitmap.HorizontalResolution, bitmap.VerticalResolution,
PixelFormats.Bgr24, null,
bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride);
bitmap.UnlockBits(bitmapData);
return bitmapSource;
}

Related

File.ReadAllBytes doesn't read the PNG image pixels properly

I am trying to read the bytes of a .png image using File.ReadAllBytes(string) method without success.
My images are of size 2464x2056x3 (15.197.952 bytes), but this method returns an array of about 12.000.000 bytes.
I tried with a white image of the same size, and I am getting a byte array of 25.549, and checking the byte array I can see all kind of values, that obviously is not correct because is a white image.
The code I am using is:
var frame = File.ReadAllBytes("C:\\workspace\\white.png");
I've also tried to open the image first as an Image object then get the byte array with the following:
using (var ms = new MemoryStream())
{
var imageIn = Image.FromFile("C:\\workspace\\white.png");
imageIn.Save(ms, imageIn.RawFormat);
var array = ms.ToArray();
}
But the result is the same as before...
Any idea of what's happening?
How can I read the byte array?
PNG is a compressed format.
See some info about it: Portable Network Graphics - Wikipedia.
This means that the binary representation is not the actual pixel values that you expect.
You need some kind of a PNG decoder to get the pixel values from the compressed data.
This post might steer you in the right direction: Reading a PNG image file in .Net 2.0. Note that it's quite old, maybe there are newer methods for doing it.
On a side note: even a non compressed format like BMP has a header, so you can't simply read the binary file and get the pixel values in a trivial way.
Update:
One way to get the pixel values from a PNG file is demonstrated below:
using System.Drawing.Imaging;
byte[] GetPngPixels(string filename)
{
byte[] rgbValues = null;
// Load the png and convert to Bitmap. This will use a .NET PNG decoder:
using (var imageIn = Image.FromFile(filename))
using (var bmp = new Bitmap(imageIn))
{
// Lock the pixel data to gain low level access:
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
rgbValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
// Unlock the pixel data:
bmp.UnlockBits(bmpData);
}
// Here rgbValues is an array of the pixel values.
return rgbValues;
}
This method will return a byte array with the size that you expect.
In order to use the data with opencv (or any similar usage), I advise you to enhance my code example and return also the image metadata (width, height, stride, pixel-format). You will need this metadata to construct a cv::Mat.

What is the best way to output Bitmap in a window (WPF)?

What I want to do:
Given a simple Bitmap (System.Drawing.Bitmap) I am willing to output it to my window in WPF application. I am also willing to do it frequently, creating a frame stream.
What I've used:
Firstly, I've been converting Bitmap to a BitmapImage and then assigning it to Source field of an Image control.
The problem with this method is the conversion itself. It's very slow. I haven't found a way that works fast enough, the best one took around 20 ms for a 640x480 Bitmap, which is slow. I am hoping to find a way that would do this for less than 5 ms for any common resolution or a different approach to the whole problem. Perhaps, there is a different control other than Image that would work with pure Bitmap and I wouldn't need to convert. I am also not tied to using WPF, does UWP or the new WinUI 3 have this problem?
I have checked UWP uses WriteableBitmap, which would also require conversion, but maybe there is a different way as well?
I have found various conversions some of which are slow and some of which just produce a white image as a result for some reason. I am providing a list below (I've tried more but I don't remember what exactly):
Using the method below. This conversion works but it takes around 20 ms to convert 640x480 ms Bitmap.
Method (source):
public BitmapImage ToBitmapImage(Bitmap bitmap)
{
using (MemoryStream memory = new MemoryStream())
{
bitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
}
Using Asmak9.EssentialToolKit library (source), but that conversion took around 27 ms, so that was not an option.
Using the method below. This one does not work for me for some strange reason. It runs with no problem, but the result of the conversion is a blank (white) image and not something that was fed into it.
Method (source):
private BitmapSource Convert(Bitmap bmp)
{
var bitmapData = bmp.LockBits(
new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
var bitmapSource = BitmapSource.Create(
bitmapData.Width, bitmapData.Height,
bitmap.HorizontalResolution, bitmap.VerticalResolution,
PixelFormats.Bgr24, null,
bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride);
bmp.UnlockBits(bitmapData);
return bitmapSource;
}
Using the method below.This produces the same result as the previous conversion - blank BitmapImage. I am also not sure what could possibly go wrong here.
Method (source):
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
private BitmapSource Bitmap2BitmapImage(Bitmap bitmap)
{
IntPtr hBitmap = bitmap.GetHbitmap();
BitmapSource retval;
try
{
retval = Imaging.CreateBitmapSourceFromHBitmap(
hBitmap,
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
finally
{
DeleteObject(hBitmap);
}
return retval;
}
Perhaps, the last 2 conversions are better, but they do not convert properly, or I am doing something wrong? Or is there a better way in general to discard the conversion step and display the Bitmap straight?
As Clement pointed out above, Method 3 is the fastest, but it requires 2 things:
Correct pixel format set.
The Convert method should be run in the main thread.

C# Waiting for an operation to complete prior to deleting a file [duplicate]

I have the Image of a PictureBox pointing to a certain file "A". At execution time I want to change the Image of the PictureBox to a different one "B" but I get the following error:
"A first chance exception of type 'System.IO.IOException' occurred in mscorlib.dll
Additional information: The process cannot access the file "A" because it is being used by another process."
I'm setting the Image as follows:
pbAvatar.Image = new Bitmap(filePath);
How can I unlock the first file?
Here is my approach to opening an image without locking the file...
public static Image FromFile(string path)
{
var bytes = File.ReadAllBytes(path);
var ms = new MemoryStream(bytes);
var img = Image.FromStream(ms);
return img;
}
UPDATE: I did some perf tests to see which method was the fastest. I compared it to #net_progs "copy from bitmap" answer (which seems to be the closest to correct, though does have some issues). I loaded the image 10000 times for each method and calculated the average time per image. Here are the results:
Loading from bytes: ~0.26 ms per image.
Copying from bitmap: ~0.50 ms per image.
The results seem to make sense since you have to create the image twice using the copy from bitmap method.
UPDATE:
if you need a BitMap you can do:
return (Bitmap)Image.FromStream(ms);
This is a common locking question widely discussed over the web.
The suggested trick with stream will not work, actually it works initially, but causes problems later. For example, it will load the image and the file will remain unlocked, but if you try to save the loaded image via Save() method, it will throw a generic GDI+ exception.
Next, the way with per pixel replication doesn't seem to be solid, at least it is noisy.
What I found working is described here: http://www.eggheadcafe.com/microsoft/Csharp/35017279/imagefromfile--locks-file.aspx
This is how the image should be loaded:
Image img;
using (var bmpTemp = new Bitmap("image_file_path"))
{
img = new Bitmap(bmpTemp);
}
I was looking for a solution to this problem and this method works fine for me so far, so I decided to describe it, since I found that many people advise the incorrect stream approach here and over the web.
Using a filestream will unlock the file once it has been read from and disposed:
using (var fs = new System.IO.FileStream("c:\\path to file.bmp", System.IO.FileMode.Open))
{
var bmp = new Bitmap(fs);
pct.Image = (Bitmap) bmp.Clone();
}
Edit: Updated to allow the original bitmap to be disposed, and allow the FileStream to be closed.
THIS ANSWER IS NOT SAFE - See comments, and see discussion in net_prog's answer. The Edit to use Clone does not make it any safer - Clone clones all fields, including the filestream reference, which in certain circumstances will cause a problem.
You can't dispose / close a stream while a bitmap object is still using it. (Whether the bitmap object will need access to it again is only deterministic if you know what type of file you are working with and exactly what operations you will be performing. -- for example for SOME .gif format images, the stream is closed before the constructor returns.)
Clone creates an "exact copy" of the bitmap (per documentation; ILSpy shows it calling native methods, so it's too much to track down right now) likely, it copies that Stream data as well -- or else it wouldn't be an exact copy.
Your best bet is creating a pixel-perfect replica of the image -- though YMMV (with certain types of images there may be more than one frame, or you may have to copy palette data as well.) But for most images, this works:
static Bitmap LoadImage(Stream stream)
{
Bitmap retval = null;
using (Bitmap b = new Bitmap(stream))
{
retval = new Bitmap(b.Width, b.Height, b.PixelFormat);
using (Graphics g = Graphics.FromImage(retval))
{
g.DrawImage(b, Point.Empty);
g.Flush();
}
}
return retval;
}
And then you can invoke it like such:
using (Stream s = ...)
{
Bitmap x = LoadImage(s);
}
As far as I know, this is 100% safe, since the resulting image is 100% created in memory, without any linked resources, and with no open streams left behind in memory. It acts like any other Bitmap that's created from a constructor that doesn't specify any input sources, and unlike some of the other answers here, it preserves the original pixel format, meaning it can be used on indexed formats.
Based on this answer, but with extra fixes and without external library import.
/// <summary>
/// Clones an image object to free it from any backing resources.
/// Code taken from http://stackoverflow.com/a/3661892/ with some extra fixes.
/// </summary>
/// <param name="sourceImage">The image to clone</param>
/// <returns>The cloned image</returns>
public static Bitmap CloneImage(Bitmap sourceImage)
{
Rectangle rect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
Bitmap targetImage = new Bitmap(rect.Width, rect.Height, sourceImage.PixelFormat);
targetImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
BitmapData sourceData = sourceImage.LockBits(rect, ImageLockMode.ReadOnly, sourceImage.PixelFormat);
BitmapData targetData = targetImage.LockBits(rect, ImageLockMode.WriteOnly, targetImage.PixelFormat);
Int32 actualDataWidth = ((Image.GetPixelFormatSize(sourceImage.PixelFormat) * rect.Width) + 7) / 8;
Int32 h = sourceImage.Height;
Int32 origStride = sourceData.Stride;
Boolean isFlipped = origStride < 0;
origStride = Math.Abs(origStride); // Fix for negative stride in BMP format.
Int32 targetStride = targetData.Stride;
Byte[] imageData = new Byte[actualDataWidth];
IntPtr sourcePos = sourceData.Scan0;
IntPtr destPos = targetData.Scan0;
// Copy line by line, skipping by stride but copying actual data width
for (Int32 y = 0; y < h; y++)
{
Marshal.Copy(sourcePos, imageData, 0, actualDataWidth);
Marshal.Copy(imageData, 0, destPos, actualDataWidth);
sourcePos = new IntPtr(sourcePos.ToInt64() + origStride);
destPos = new IntPtr(destPos.ToInt64() + targetStride);
}
targetImage.UnlockBits(targetData);
sourceImage.UnlockBits(sourceData);
// Fix for negative stride on BMP format.
if (isFlipped)
targetImage.RotateFlip(RotateFlipType.Rotate180FlipX);
// For indexed images, restore the palette. This is not linking to a referenced
// object in the original image; the getter of Palette creates a new object when called.
if ((sourceImage.PixelFormat & PixelFormat.Indexed) != 0)
targetImage.Palette = sourceImage.Palette;
// Restore DPI settings
targetImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
return targetImage;
}
To call, simply use:
/// <summary>Loads an image without locking the underlying file.</summary>
/// <param name="path">Path of the image to load</param>
/// <returns>The image</returns>
public static Bitmap LoadImageSafe(String path)
{
using (Bitmap sourceImage = new Bitmap(path))
{
return CloneImage(sourceImage);
}
}
Or, from bytes:
/// <summary>Loads an image from bytes without leaving open a MemoryStream.</summary>
/// <param name="fileData">Byte array containing the image to load.</param>
/// <returns>The image</returns>
public static Bitmap LoadImageSafe(Byte[] fileData)
{
using (MemoryStream stream = new MemoryStream(fileData))
using (Bitmap sourceImage = new Bitmap(stream)) {
{
return CloneImage(sourceImage);
}
}
Here's the technique I'm currently using, and seems to work best. It has the advantage of producing a Bitmap object with the same pixel format (24-bit or 32-bit) and resolution (72 dpi, 96 dpi, whatever) as the source file.
// ImageConverter object used to convert JPEG byte arrays into Image objects. This is static
// and only gets instantiated once.
private static readonly ImageConverter _imageConverter = new ImageConverter();
This can be used as often as needed, as follows:
Bitmap newBitmap = (Bitmap)_imageConverter.ConvertFrom(File.ReadAllBytes(fileName));
Edit:
Here's an update of the above technique: https://stackoverflow.com/a/16576471/253938
(The accepted answer is wrong. When you try to LockBits(...) on the cloned bitmap eventually you will encounter GDI+ errors.)
I see only 3 ways to get out of this:
copy your file to a temporary file and open that the easy way new Bitmap(temp_filename)
open your file, read image, create a pixel-size-pixelformat copy (don't Clone()) and dispose the first bitmap
(accept the locked-file-feature)
I suggest to use PixelMap (available on NuGet)
or Github
Very easy to use and much faster than standard Bitmap from .NET
PixelMap pixelMap = new PixelMap(bild);
pictureBox1.Image = pixelMap.GetBitmap();
Read it into the stream, create bitmap, close the stream.

Saving 32-bit Unsigned Byte Array to File System

I am trying to save a byte array (of an image) to the file system using C# and it isn't working using either method I have tried, here is code sample 1:
string path = #"c:\ppd" + g.ToString() + ".jpg";
using (MemoryStream inputStream = new MemoryStream(image))
{
Image returnImage = Image.FromStream(inputStream);
returnImage.Save(path, ImageFormat.Jpeg);
}
With this code I get 'Parameter is not valid' when sending the data to the service. So that's obviously the wrong code for the array, so I hit google and came up with this code:
string path = #"c:\ppd\" + g.ToString() + ".jpg";
using (MemoryStream inputStream = new MemoryStream(image))
{
using (Stream file = File.Create(path))
{
byte[] buffer = new byte[8 * 1024];
int len;
while ((len = inputStream.Read(buffer, 0, buffer.Length)) > 0)
{
file.Write(buffer, 0, len);
}
}
}
This code writes the file to the file system, but when you try to open it it helpfully says 'This is not a valid bitmap file, or it's format is not currently supported.
We get the byteArray from Flex using:
bitmapData.getPixels(0, 0, bitmapData.width, bitmapData.height);
And the bitmapData comes from the Android camera (via the Distriqt Camera ANE). The image displays fine in the app, but I have no idea where to go next with this, please help.
EDIT:
The code in Flex to get the byteArray is:
<s:BitmapImage id="bitmapImage" width="100%"
maxHeight="{this.height/2}"
source="{profileModel.captureBitmapPreview}"
/>
<s:Button click="{dispatchEvent(new PictureEvent
(PictureEvent.UPLOAD,
0, pictureModel.captureBitmapPreview.getPixels(new Rectangle(0,0,pictureModel.captureBitmapPreview.width,pictureModel.captureBitmapPreview.height))))}"/>
/* this is the alternative which still doesn't work **/
<s:Button click="{dispatchEvent(new PictureEvent
(PictureEvent.UPLOAD,
0, bitmapImage.getPixels(new Rectangle(0,0,bitmapImage.width,bitmapImage.height))))}"/>
PictureEvent takes 3 parameters, type:String, id:int = 0, image:ByteArray = null.
SECOND EDIT: Here is the code that creates the bitmapData:
private function camera_capturedImageHandler(evt:CameraDataEvent):void
{
Camera.instance.setPresetMode(CameraMode.PRESET_MEDIUM);
Camera.instance.addEventListener(CameraEvent.VIDEO_FRAME, camera_videoFrameHandler, false, 0, true);
if (_captureBitmapData.width != evt.data.width || _captureBitmapData.height != evt.data.height)
{
_captureBitmapData = new BitmapData(evt.data.width, evt.data.height, false);
}
_captureBitmapData.draw(evt.data);
var tbd:BitmapData = new BitmapData(FlexGlobals.topLevelApplication.width, FlexGlobals.topLevelApplication.height, false)
tbd = applyOrientation(_bitmapData, "6");
profileModel.captureBitmapPreview.copyPixels(tbd, new Rectangle(0, ((FlexGlobals.topLevelApplication.height/2)-(FlexGlobals.topLevelApplication.height/4)), FlexGlobals.topLevelApplication.width, FlexGlobals.topLevelApplication.height/2), new Point(0, 0));
}
THIRD EDIT:
I has done some testing and discovered that this code should work, it is a variation on the first block above that throws 'Parameter is not valid'
ImageConverter imageConverter = new ImageConverter();
Bitmap bm = (Bitmap)imageConverter.ConvertFrom(image);
if (bm != null && (bm.HorizontalResolution != (int)bm.HorizontalResolution || bm.VerticalResolution != (int)bm.VerticalResolution))
{
bm.SetResolution((int)(bm.HorizontalResolution + 0.5f),(int)(bm.VerticalResolution + 0.5f));
}
bm.Save(path, ImageFormat.Jpeg);
So now I am sure the error is with the byteArray, but I genuinely cannot see what I am doing wrong. I have the bitmapData and it is valid, and it looks like I am collecting the data I need, the written file is nearly 700Kb in size, but something is wrong.
If someone could tell me what is wrong on the Flex side of this equation I would be forever grateful. I have the BitmapImage on the display list, it is bound to the model correctly and I have tried different ways of pointing to the source and drawing up the rectangle.
Ok, so #Lorek was right, the byteArray just contains pixel data and no formatting information. We don't know why this is the case but based on that knowledge and a little digging in the database I realised that this has been the situation for a while and I needed a solution that would solve it.
So in answer to the question 'How do you convert pixel data from a byteArray into an image?' I came up with this, still need a little help, but basically this solves the issue I am having (more after the code)
Bitmap bitmap = new Bitmap(150, 150, PixelFormat.Format32bppRgb);
BitmapData bmData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
IntPtr pNative = bmData.Scan0;
Marshal.Copy(image, 0, pNative, image.Length);
bitmap.UnlockBits(bmData);
bitmap.Save(path, ImageFormat.Png);
So I change the Rectangle size in Flex so I knew what I was working with, and posted the btyeArray to this method, which beautifully writes out a PNG for me.
It's slightly blue, well it's really blue and I am not quite sure why, take a look:
http://ec2-52-89-85-67.us-west-2.compute.amazonaws.com/imagedata/ppd/3898ae89-e4e0-4d03-97d7-dac4e3b618d5.png
It is supposed to be black:
http://ec2-52-89-85-67.us-west-2.compute.amazonaws.com/imagedata/ppd/capture.png
So any of you .Net people know what happened and can advise me (I heard somewhere that I need to switch the order of the colors so instead of rgb it gets switched around, but I have no idea how or why this would be the issue).
EDIT Here is the solution to that, it's slow, it's pointless and it takes too long: http://www.codeproject.com/Articles/2056/Image-Processing-for-Dummies-with-C-and-GDI-Part-3
Or if maybe we can solve the actual Flex issue that's be great although I have given up all hope of SO being useful for Flex support any more
Thanks you everyone for your patience.

How to create a Bitmap deep copy

I'm dealing with Bitmaps in my application and for some purposes I need to create a deep copy of the Bitmap. Is there an elegant way how to do it?
I tried
Bitmap deepCopy = original.Clone();
,well apparently this doesn't create a deep copy, but shallow one.
My next attempt was to create a new Bitmap
Bitmap deepCopy = new Bitmap(original);
Unfortunately this constructor is Bitmap(Image), not Bitmap(Bitmap) and Bitmap(Image) will convert my nice 8bppIndexed Pixelformat into a different one.
Another attempt was to use of a MemoryStream
public static Bitmap CreateBitmapDeepCopy(Bitmap source)
{
Bitmap result;
using (MemoryStream stream = new MemoryStream())
{
source.Save(stream, ImageFormat.Bmp);
stream.Seek(0, SeekOrigin.Begin);
result = new Bitmap(stream);
}
return result;
}
Well, this doesn't work either, since the MemoryStream has to be opened during the whole lifetime of Bitmap.
So, I've summed up all my deadends and I'd really like to see a nice elegant way of creating a Bitmap deep copy. Thanks for that :)
B.Clone(new Rectangle(0, 0, B.Width, B.Height), B.PixelFormat)
Another way I stumbled on that achieves the same thing is to rotate or flip the image. Under the hood that seems to create a completely new copy of the bitmap. Doing two rotations or flips lets you end up with an exact copy of the original image.
result.RotateFlip(RotateFlipType.Rotate180FlipX);
result.RotateFlip(RotateFlipType.Rotate180FlipX);
My environment:Windows 10, Visual Studio 2015, Framework 4.5.2
It works for me.
Bitmap deepCopy = new Bitmap(original);
You could serialize the bitmap and then deserialize it. Bitmap is serializable.
Suppose you already have a bitmap called original with something in it
Bitmap original = new Bitmap( 200, 200 );
Bitmap copy = new Bitmap(original.Width, original.Height);
using (Graphics graphics = Graphics.FromImage(copy))
{
Rectangle imageRectangle = new Rectangle(0, 0, copy.Width, copy.Height);
graphics.DrawImage( original, imageRectangle, imageRectangle, GraphicsUnit.Pixel);
}
This should create a copy of the same size, and draw the original into the copy.
You can place the following class in your code:
public static class BitmapExtensions
{
public static Bitmap DeepClone(this Bitmap source)
{
IFormatter formatter = new BinaryFormatter();
Stream stream = new MemoryStream();
using (stream)
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (Bitmap)formatter.Deserialize(stream);
}
}
}
This method is adjusted to work with Bitmap. If you want to have a generic method look into the link below. By naming the method DeepClone() we remove the risk of breaking already existing code (in the answer below the method is called Clone(), so it's a override method).
Original from here: https://stackoverflow.com/a/43042865/13574233
This method takes a lot of time though. If you're looking for a good performing method, this probably isn't what you're looking for.

Categories