Insert series of smaller bitmaps on top of background bitmap - c#

this may be an amateur question but I'm still stuck...
I have a background bitmap image, and then need to super-impose a few smaller bitmaps (mostly qr codes). Things work for the 1st insert, and then it breaks. It compiles OK, but it fails on the new Bitmap line with a Exception Unhandled message System.ArgumentException: 'Parameter is not valid.'
The code is something like
Bitmap Background_bmp= new Bitmap(File_name);
Graphics Background_gfx = Graphics.FromImage(Background_bmp);
for (i=1;i<=4;i++)
{
Bitmap Insert_image = new Bitmap(File_name[i]);
Print_doc_gfx.DrawImage(Insert_image, blablabla (scaling and positioning);
Insert_image.Dispose();
}
Background_bmp.Save("C:\\Total image.bmp");
Background_gfx.Dispose();
Background_bmp.Dispose();
Simple enough, and yet it doesn't work. I'm pretty sure the breakage is over the repeated "new" in the "new Bitmap" piece, but I don't know how to declare once and use many times when it comes to bitmaps... Like I said, amateur question...

The parts you posted from your code do not appear to be causing the problem. It's most likely caused by other parts of the code, such as the coordinates of the image insertion or something totally different.
I tested using the following code with one large image and 4 small images and it worked without problems:
string File_name = "background.png";
string[] File_names = new string[] { "img1.png", "img2.png", "img3.png", "img4.png" };
Bitmap Background_bmp = new Bitmap(File_name);
// can also use empty all-black image like the following line:
// Bitmap Background_bmp = new Bitmap(800, 600, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
Graphics Background_gfx = Graphics.FromImage(Background_bmp);
for (int i = 1; i <= File_names.Length; i++)
{
Bitmap Insert_image = new Bitmap(File_names[i - 1]);
Background_gfx.DrawImage(Insert_image, i * 150, i * 100);
Insert_image.Dispose();
}
Background_gfx.Dispose();
Background_bmp.Save("Total_image.png", System.Drawing.Imaging.ImageFormat.Png);
Background_bmp.Dispose();

Related

Bitmap.Clone(Rectangle, PixelFormat) - OutOfMemoryException [duplicate]

Why am I getting an out of memory exception?
So this dies in C# on the first time through:
splitBitmaps.Add(neededImage.Clone(rectDimensions, neededImage.PixelFormat));
Where splitBitmaps is a List<BitMap> BUT this works in VB for at least 4 iterations:
arlSplitBitmaps.Add(Image.Clone(rectDimensions, Image.PixelFormat))
Where arlSplitBitmaps is a simple array list. (And yes I've tried arraylist in c#)
This is the fullsection:
for (Int32 splitIndex = 0; splitIndex <= numberOfResultingImages - 1; splitIndex++)
{
Rectangle rectDimensions;
if (splitIndex < numberOfResultingImages - 1)
{
rectDimensions = new Rectangle(splitImageWidth * splitIndex, 0,
splitImageWidth, splitImageHeight);
}
else
{
rectDimensions = new Rectangle(splitImageWidth * splitIndex, 0,
sourceImageWidth - (splitImageWidth * splitIndex), splitImageHeight);
}
splitBitmaps.Add(neededImage.Clone(rectDimensions, neededImage.PixelFormat));
}
neededImage is a Bitmap by the way.
I can't find any useful answers on the intarweb, especially not why it works just fine in VB.
Update:
I actually found a reason (sort of) for this working but forgot to post it. It has to do with converting the image to a bitmap instead of just trying to clone the raw image if I remember.
Clone() may also throw an Out of memory exception when the coordinates specified in the Rectangle are outside the bounds of the bitmap. It will not clip them automatically for you.
I found that I was using Image.Clone to crop a bitmap and the width took the crop outside the bounds of the original image. This causes an Out of Memory error. Seems a bit strange but can beworth knowing.
I got this too when I tried to use the Clone() method to change the pixel format of a bitmap. If memory serves, I was trying to convert a 24 bpp bitmap to an 8 bit indexed format, naively hoping that the Bitmap class would magically handle the palette creation and so on. Obviously not :-/
This is a reach, but I've often found that if pulling images directly from disk that it's better to copy them to a new bitmap and dispose of the disk-bound image. I've seen great improvement in memory consumption when doing so.
Dave M. is on the money too... make sure to dispose when finished.
I struggled to figure this out recently - the answers above are correct. Key to solving this issue is to ensure the rectangle is actually within the boundaries of the image. See example of how I solved this.
In a nutshell, checked to if the area that was being cloned was outside the area of the image.
int totalWidth = rect.Left + rect.Width; //think -the same as Right property
int allowableWidth = localImage.Width - rect.Left;
int finalWidth = 0;
if (totalWidth > allowableWidth){
finalWidth = allowableWidth;
} else {
finalWidth = totalWidth;
}
rect.Width = finalWidth;
int totalHeight = rect.Top + rect.Height; //think same as Bottom property
int allowableHeight = localImage.Height - rect.Top;
int finalHeight = 0;
if (totalHeight > allowableHeight){
finalHeight = allowableHeight;
} else {
finalHeight = totalHeight;
}
rect.Height = finalHeight;
cropped = ((Bitmap)localImage).Clone(rect, System.Drawing.Imaging.PixelFormat.DontCare);
Make sure that you're calling .Dispose() properly on your images, otherwise unmanaged resources won't be freed up. I wonder how many images are you actually creating here -- hundreds? Thousands?

How can i add two animated gif's on one bitmap?

I have two animated gifs i created.
I want to add them to a new bitmap side by side so i will see both animated gifs animation.
Not as stil image but two animation side by side.
This code is in the top of form1 im using now:
public static class BitmapExtensions
{
public static Bitmap DoubleBitmap(this Bitmap bm)
{
Bitmap bitmap = new Bitmap(bm.Width * 2, bm.Height);
using (Graphics g = Graphics.FromImage(bitmap))
{
g.DrawImage(bm, Point.Empty);
g.DrawImage(bm, new Point(bm.Width, 0));
return bitmap;
}
}
public static Bitmap AppendBitmap(this Bitmap bm, Bitmap rightBitmap)
{
Bitmap bitmap = new Bitmap(bm.Width + rightBitmap.Width, Math.Max(bm.Height, rightBitmap.Height));
using (Graphics g = Graphics.FromImage(bitmap))
{
g.DrawImage(bm, Point.Empty);
g.DrawImage(rightBitmap, new Point(bm.Width, 0));
return bitmap;
}
}
}
Then i use it like this:
private void CreateNewImage(string DirOfUrls)
{
List<string> files = Directory.GetFiles(DirOfUrls, "RainImage*.*").ToList();
List<string> files1 = Directory.GetFiles(DirOfUrls, "SatelliteImage*.*").ToList();
Bitmap bmp = new Bitmap(#"d:\localpath\RainMapGif");//files1[i]);
Bitmap bmp1 = new Bitmap(#"d:\localpath\SatelliteMapGif");//files[i]);
//Use it
//Double the same image
Bitmap doubledBitmap = bmp1.DoubleBitmap();
//Append new image
Bitmap appendedBitmap = bmp1.AppendBitmap(bmp);
appendedBitmap.Save(#"d:\localpath\newbitmapGif", System.Drawing.Imaging.ImageFormat.Gif);
}
RainMapGif and SatelliteMapGif are animated gif's.
But when i tried to do it this way i get one new bitmap with two stills images and not two animtions of the two animated gifs.
How can i add both animated gifs to one bitmap and when i open the bitmap on internet explorer for example i will see both animations moving of the two gifs side by side ?
EDIT**
This is how i used it before :
private void CreateNewImage(string DirOfUrls)
{
int newImageCounter = 0;
List<string> files = Directory.GetFiles(DirOfUrls, "RainImage*.*").ToList();
List<string> files1 = Directory.GetFiles(DirOfUrls, "SatelliteImage*.*").ToList();
for (int i = 0; i < files.Count; i++)
{
if (newImageCounter == 9)
{
CreateNewGif(DirOfUrls);
//break;
}
Bitmap bmp = new Bitmap(files1[i]);
Bitmap bmp1 = new Bitmap(files[i]);
//Use it
//Double the same image
Bitmap doubledBitmap = bmp1.DoubleBitmap();
//Append new image
Bitmap appendedBitmap = bmp1.AppendBitmap(bmp);
appendedBitmap.Save(#"d:\localpath\newbitmap" + newImageCounter.ToString("D6"), System.Drawing.Imaging.ImageFormat.Gif);
newImageCounter++;
}
So i have 9 images of both of them the RainMap images and the SatelliteMap images.
And the rest of the SatelliteMap images are singles.
Then im using CreateNewGif:
private void CreateNewGif(string urlsdirs)
{
List<string> files = Directory.GetFiles(urlsdirs, "RainImage*.*").ToList();
List<string> files1 = Directory.GetFiles(urlsdirs, "SatelliteImage*.*").ToList();
List<string> test = files;
test.RemoveRange(0, files1.Count);
List<string> newbitmap = Directory.GetFiles(localdir, "newbitmap*.*").ToList();
for (int i = 0; i < test.Count; i++)
{
newbitmap.Add(test[i]);
}
uf.MakeGIF(newbitmap, localdir + "newbitmapGif", 50, true);
}
And make new animated gif:
uf.MakeGIF(newbitmap, localdir + "newbitmapGif", 50, true);
But the new animated gif is not good since the rain images ending before the satellite images so the new animated gif show both animation and after 9 frames only one is continue.
How can i make that the one with the 9 images will keep continue animated over and over again and the second one will keep animated untill the end ?
This is the problem veljkoz wrote about before. One animation count is shorter then the other one. But how can i solve it ?
Only gif can have animation. Bitmap doesn't and never will.
Furthermore, there's the issue of extracting all of the gif slides (see this answer), putting them side by side in another gif (see this answer), and deciding what will you do if the slide count isn't a match (which would most probably be the case).
Personally, I would go with what #Mike W is suggesting in the comment...
Edit:
You have to have in mind that the resulting gif will have a single number of frames after which it repeats itself. So, both gifs you're trying to add have to somehow fit there. There's only a few options here:
Repeat inserting all frames of both gifs until they reach at the end at the same time (e.g. if one has 6 frames and the other 9 - the result will have 18 frames. The the first one would repeat itself 3 times, the other 2). This is (obviously) prolonging the length & size of the gif, which is probably not something you want, but it would give you best results. When the numbers are prime numbers you'd get the worst count - e.g. 53 & 59 frame gifs when joined will last for 3127 frames.
Change the timing of the frames so they end at the same time. This would of course speed-up / slow down parts of the gifs. (e.g. 6&9 frames gifs: the result has 9 slides - you could repeat the first 3 slides of 1st gif to last two frames - which means 1st slide will last 2 frames (#1 & #2), second one also (#3 & #4) as well as third (#5 & #6). Then copy the rest of the slides - frame 4 displayed at #7, 5->#8, 6->#9 and that's the end).
Remove some of the slides - shorten the gifs by removing some of the slides so the count matches...

gdi+ generic error when drawing image on Graphics object

Consider the following code running in a windows service.
Graphics g = Graphics.FromImage(printImage);
for (int rows = 1; rows <= thumbRows; rows++) {
for (int cols = 1; cols <= thumbColumns; cols++) {
using (Image thumbImage = new Bitmap(resourceQueue.Peek()))
{
g.DrawImage(thumbImage, targetRect, sourceRect, GraphicsUnit.Pixel);
resourceQueue.Dequeue();
if (resourceQueue.Count == 0) break;
}
}
}
The code draws a list of images after making them smaller onto another image.
It works fine most of the time but sometimes an exception is thrown in the middle of the loop on the Image thumbImage = new Bitmap. The exception is "A generic error occurred in GDI+". It always happens after the 13th image is created no matter what images are used.
After googling it seems that this a common problem when saving files but the difference here is that no file is saved. An image is drawn on a Graphics object.
Does anyone know how to fix this?
Most likely the stream used to create the Bitmap (if that is what it is) is closed. They have to stay open for the life of the Bitmap. You can copy the stream into a new MemoryStream, then close the original.

MagickNet PNG with Transparency to JPEG

I am using MagickNet for image manipulation in my ASP.NET C# project. My issue is that I am uploading a PNG image with transparency and when I convert it to JPEG, I get a black background with some white spots instead of a white background for the transparent part.
Stream su = upload.FileContent;
MagickNet.Image testimage = new MagickNet.Image(su);
testimage.Filter = FilterType.LanczosFilter;
testimage.Compression = CompressionType.JPEGCompression;
testimage.QuantizeDither = false;
testimage.BackgroundColor = new Color(System.Drawing.Color.White);
testimage.Resize( new System.Drawing.Size(Convert.ToInt32(testimage.Size.Width * 0.4), Convert.ToInt32(testimage.Size.Height * 0.4)));
testimage.Write(System.Web.HttpContext.Current.Server.MapPath(".") + "\\temp\\" + DateTime.Now.Hour + "-" +DateTime.Now.Minute + "-" + DateTime.Now.Second + ".jpg");
su.Close();
su.Dispose();
testimage.Dispose();
Magick.Term();
I played with it and always get the wrong result that I am after. Sometimes I get a transparent background but some parts of the image at the outer region have white dots. I also resize the image to be smaller than what it is. I think the re-sizing it causing the issue.
update: this is caused because of the resizing for some reason. Without resizing it works. Having said that, I need to resize, so I need it to work with it.
Thanks.
Try to composite onto a white background image.
Image bg = new Image(testimage.Size, new ColorRGB(255, 255, 255));
testimage = bg.Composite(testimage, 0, 0);
First of all, it is better to create your MagickImage object with the desirable size the speed of reading the file/stream with the required size can in some situation 100 times faster. You may not have that error.
using(var testimage = new MagickImage(yourstream/yourFileAddress, width, height)
{
....
}
But if you convert the MagickImage to Bitmap and then save the bitmap as jpg you can see that the image has a white background
using (var testBitmap = testimage.ToBitmap())
{
testBitmap.Save(#"d:\temp.jpg");
}
also using is much better that calling dispose member function. Because if your code throw exception before it reaches the dispose call your object will remain in the memory. But with using, if program jump out of the block the object will be disposed.

Graphics DrawImage causes error with big red X

I have a collection of bitmap images that I am looping through and writing them all to one new bitmap. Basically I am taking a loose collection of bitmaps and writing them all to one bitmap one after another so that they are visible as one image.
When i call dc.DrawImage from one of the bitmaps in the collection onto the new bitmap my winform is showing a big red X in the form. When I set a PictureBox.Image to the newly drawn bitmap I am getting a big red X.
For some reason i cannot find the error anywhere. I am not able to locate the error with debugging.
Now, if I just set the PictureBox.Image to one of the images in the collection of images with-out looping and drawning onto a new bitmap everything works fine.
To make everything ease I am only working with one bitmap that is in the collection and drawing the one bitmap to the new bitmap. So I know I have only one bitmap to get working then i can add the other ones.
In the images below is what the form looks like if i just set the picturebox.image of the image in the collection.
The second image is the error that shows after I loop and drawing the bitmap in the collection to another bitmap.
The code below is what needs to work, but throws an error.
Notice where I am setting the property of the PictureBox.Image like so:
this.picBx.Image = schedule; this causes the error.
But if i set the picturebox.image like so:
this.picBx.Image = schedules[0].Door; it works just fine.
DoorSchedules schedules = GetDoorDrawing(elev, projInfo.ProjectName);
int prevWidth = 0;
//
using (Bitmap schedule = new Bitmap(schedules.Width + 50, schedules.Height + 50))
{
using (Graphics dc = Graphics.FromImage(schedule))
{
using (Pen pen = new Pen(LINE_COLOR))
{
pen.Width = 4;
pen.Color =
Color.FromArgb(50, LINE_COLOR.R, LINE_COLOR.G, LINE_COLOR.B);
//
for (byte i = 0; i < schedules.Count; i++)
{
if (i > 0)
{
dc.DrawLine(pen, prevWidth - 25, 0,
prevWidth - 25, schedule.Height);
};
dc.DrawImage(schedules[i].Door, prevWidth, 0);
prevWidth += schedules[i].Door.Width;
};
};
};
this.picBx.Image = schedule;
this.picBx.BackColor = BACK_COLOR;
this.Size = new System.Drawing.Size(schedule.Width, schedule.Height);
};
You have Bitmap schedule defined in a using statement.
When that using block ends, the bitmap is disposed.

Categories

Resources