Graphics DrawImage causes error with big red X - c#

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.

Related

Insert series of smaller bitmaps on top of background bitmap

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();

how to extend draw area in Graphics.DrawImage c#

I have a Rectangle (rec) that contains the area in which a smaller image is contained within a larger image. I want to display this smaller image on a Picturebox. However, what I really am doing is using the smaller image as a picture detector for a larger image that is 333x324. So what I want to do is use the coordinates of the smaller image rectangle, and then draw to the Picturebox, starting from lefthand side of the rectangle, going outwards by 333 width and 324 height.
Currently my code works but it only displays the small image that was being used for detection purposes. I want it to display the smaller image + 300 width and + 300 height.
I fiddled with this code for hours and I must be doing something extremely basic wrong. If anyone can help me I would appreciate it so much!
My code for the class:
public static class Worker
{
public static void doWork(object myForm)
{
//infinitely search for maps
for (;;)
{
//match type signature for Threading
var myForm1 = (Form1)myForm;
//capture screen
Bitmap currentBitmap = new Bitmap(CaptureScreen.capture());
//detect map
Detector detector = new Detector();
Rectangle rec = detector.searchBitmap(currentBitmap, 0.1);
//if it actually found something
if(rec.Width != 0)
{
// Create the new bitmap and associated graphics object
Bitmap bmp = new Bitmap(rec.X, rec.Y);
Graphics g = Graphics.FromImage(bmp);
// Draw the specified section of the source bitmap to the new one
g.DrawImage(currentBitmap, 0,0, rec, GraphicsUnit.Pixel);
// send to the picture box &refresh;
myForm1.Invoke(new Action(() =>
{
myForm1.getPicturebox().Image = bmp;
myForm1.getPicturebox().Refresh();
myForm1.Update();
}));
// Clean up
g.Dispose();
bmp.Dispose();
}
//kill
currentBitmap.Dispose();
//do 10 times per second
System.Threading.Thread.Sleep(100);
}
}
}
If I understand correctly, the rec variable contains a rectangle with correct X and Y which identifies a rectangle with Width=333 and Height=324.
So inside the if statement, start by setting the desired size:
rec.Width = 333;
rec.Height = 324;
Then, note that the Bitmap constructor expects the width and height, so change
Bitmap bmp = new Bitmap(rec.X, rec.Y);
to
Bitmap bmp = new Bitmap(rec.Width, rec.Height);
and that's it - the rest of the code can stay the way it is now.

Where does this quality loss on Images come from?

in my Winforms application which is connected to a database via Linq to SQL I am saving images (always *.png) to a table which looks like this:
CREATE TABLE [dbo].[Images] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Bild] IMAGE NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);
Before I can store a picture I have to convert it to byte[] and this is how I do it:
public static byte[] ImageToByteArray(System.Drawing.Image imageIn)
{
using (MemoryStream ms = new MemoryStream())
{
imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
return ms.ToArray();
}
}
Afterwards if I want to load this very same image to a PictureBox in my application I convert it back with this method:
public static Image ByteArrayToImage(byte[] byteArrayIn)
{
using (MemoryStream ms = new MemoryStream(byteArrayIn))
{
Image returnImage = Image.FromStream(ms);
return returnImage;
}
}
It actually works, the only problem appears when I try to display an Image from the database in a Picturebox.
So when I load this Image to the database:
and later I try to display it. It suddenly looks like this:
I already tried all the possible SizeMode settings for the PictureBox (Normal, Stretchimage, AutoSize,CenterImage,Zoom) and it still looks like this.
Also here is how I load the Images from the database to the pictureBox:
First I retrieve the all the Images belonging to a set via id:
public static ImageList GetRezeptImages(int rezeptId)
{
using (CookBookDataContext ctx = new CookBookDataContext(ResourceFile.DBConnection))
{
IEnumerable<RezeptBilder> bilder = from b in ctx.RezeptBilders where b.FKRezept == rezeptId select b;
ImageList imageList = new ImageList();
foreach(RezeptBilder b in bilder)
{
imageList.Images.Add(Helper.ByteArrayToImage(b.Bild.ToArray()));
}
return imageList;
}
}
Also in my application I have a datagridview where Id's are stored in the first column. So when I want to retrieve any Images belonging to the set I do it like this:
private void dgvRezeptListe_CellClick(object sender, DataGridViewCellEventArgs e)
{
pbRezeptBild.Image = DBManager.GetRezeptImages(Int32.Parse(dgvRezeptListe.SelectedRows[0].Cells[0].Value.ToString())).Images[0];
}
When loaded from a local directory the Image looks fine in the pictureBox. I also tried to convert the initial picture to binary and back (without loading it to the database), it still looked fine when displayed in the pictureBox.
There is something else I realized while debugging the Image which came from the database. When looking into the ImageSize, width and height had both the value 16. This is weird because the original image has totally different dimensions.
Any ideas?
It appears that ImageList is converting your images to MemoryBmp's when you are adding them in GetRezeptImages to the list.
I am not aware as to why it would do that, but that is the reason for your loss of quality in the image. As you also realised, the image is converted to a 16x16 image and then when that is resized in your PictureBox to the original size, it just looks more cheesy.
Edit:
From TaW's comment: The ImageList collection cannot handle varying sized images, so it is converting all the images to a common size. Since there is no size set, it seems it defaults to 16x16.
I would recommend that you change the GetRezeptImages method to return a List<Image> instead of an ImageList and use that accordingly to show the images.
Alternatively, if you will always use the GetRezeptImages method in the same way that you showed in your question, you can change it to always just return the first image in an Image object and entirely throw away all the lists.
ImageList is nice for what it can do: Store lots of Images without losing GDI resources.
But what it can't do is what you probably need: Store images of varying sizes & proportions.
You can and should set it Imagesize and ColorDepth properties (look at the defaults, that are clearly meant for use as a StateImageList; 16x16px and 8bit depth is even bad for a LargeImageList..) but you can't use it if your images need to have different sizes & proportions..
If they don't, that is if they can share at least their proportions, just fix the ImageList by picking a nice size and you're set! All Images you add to the ImageList will automatically be scaled to that Size and converted to the ColorDepth.
Otherwise replace it by a List<Image> or List<Bitmap>..!
If you know the common size and ColorDepth of your Images you can do that:
using (CookBookDataContext ctx = new CookBookDataContext(ResourceFile.DBConnection))
{
IEnumerable<RezeptBilder> bilder =
from b in ctx.RezeptBilders where b.FKRezept == rezeptId select b;
ImageList imageList = new ImageList();
imageList.ColorDepth = ColorDepth.Depth24Bit; //
imageList.ImageSize = yourImageSize; //
foreach(RezeptBilder b in bilder)
{
imageList.Images.Add(Helper.ByteArrayToImage(b.Bild.ToArray()));
}
return imageList;
}
You can either not use an ImageList but a List<Image> or List<Bitmap> instead or make
them - that is: Trim them to the same proportion, basically not much more than a few extra lines..:
Bitmap expandCanvas(Bitmap bmp, Size size)
{
float f1 = 1f * bmp.Width / bmp.Height;
float f2 = 1f * size.Width / size.Height;
Size newSize = size;
if (f1 > f2) newSize = new Size(bmp.Width, (int)(bmp.Height * f1));
else if (f1 < f2) newSize = new Size((int)(bmp.Width / f1), bmp.Height);
Bitmap bmp2 = new Bitmap(newSize.Width, newSize.Height);
bmp2.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);
Rectangle RDest = new Rectangle(Point.Empty, bmp.Size);
Rectangle RTgt = new Rectangle(Point.Empty, newSize);
using (Graphics G = Graphics.FromImage(bmp2))
{
G.DrawImage(bmp, RDest, RDest, GraphicsUnit.Pixel);
}
return bmp2;
}
This routine expands the image canvas size with transparent pixels to the right or bottom without scaling the pixels, so it should stay losslessly crisp.
Also You might think about converting it to a string64 instead of byte[]. The code is a little cleaner and once it's a string it can be dropped in just about any file because it's just text.

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...

How do I display an image within a region defined in a Picturebox?

As a follow on to my previous question I have gotten my region but spent the last two hours trying to display tiny pictures wihing that region alone; with the end goal being to be able to arbitarily display any number of images I choose.
so far this is my code:
void OnPaintRadar(Object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Bitmap blip = new Bitmap(tst_Graphics.Properties.Resources.dogtag);
Rectangle radar_rect = new Rectangle(myRadarBox.Left + 80, myRadarBox.Left + 7, myRadarBox.Width - 200, myRadarBox.Height + 200);
using (Pen drw_pen = new Pen(Color.White, 1) )
{
using (GraphicsPath path = new GraphicsPath())
{
path.AddPie(radar_rect, 180, 180);
path.CloseFigure();
using (Region rdRegion = new Region(path) )
{
g.DrawPath(drw_pen, path);
g.Clip = rdRegion;
PictureBox pb = new PictureBox();
pb.Image = (blip);
pb.Size = blip.Size;
g.DrawImage(blip, radar_rect);
}
}
}
}// end paint method
I have tried the DrawImageUnscaled method also but I either get a my tiny picture blown to fill the pie region or nothing is displayed.
Click here to run a sample application that demonstrates the basics of how to do radar (or one way, at least). Note: this application does not do double-buffering or transparency of the tiny image.
Source code for the project is here.
This line:
pb.Image = (blip);
is what's causing the tiny image to appear large. Basically, you pulling a tiny bitmap out of resources, and then setting the PictureBox's Image property to that bitmap (I'm assuming "pb" is a PictureBox on your form). Try commenting out that line and the line below it.

Categories

Resources