I hope that question was not asked before. I could not find anything specific in this regard. I have a complicated custom drawn form that I would lake to save as a vector graphic (emf) using the MetaFile class in C#.
Somehow I just can't get it to work. I always end up with a 1KB empty file and maybe someone can tell me what I am doing wrong.
This is my code so far. Am I missing something?
Graphics g = Graphics.FromImage(new Bitmap(this.Width, this.Height));
IntPtr hdc = g.GetHdc();
Metafile imageMetafile = new Metafile(filepath, hdc);
using (Graphics imageGraphics = Graphics.FromImage(imageMetafile))
{
imageGraphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
imageGraphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
this.InvokePaint(this, new PaintEventArgs(imageGraphics, new Rectangle(0, 0, this.Width, this.Height)));
}
g.ReleaseHdc();
g.Dispose();
I got parts of this code structure from the source code of ZedGraph. However ZedGraph then feeds everything through its own Draw() methods that stanard winforms don't have. This is where I thought I might be able to use InvokePaint to accomplish the same result but without any success.
As a side note: The control element I try to paint is already on screen at this moment and I am calling this code from the UI thread.
How would one get a Control element use a custom graphics object instead of the regular "paint on screen" graphics object?
EDIT: Just to further clarify. I don't have an issue with generating vector graphics in general. The MetaFile graphics route works fine if I just start drawing shapes or even bitmaps into it. The only issue I face is that I can't get my UI control to use THAT graphics object instead of the default "print-to-screen" graphics object.
var imageMetafile = new Bitmap(this.Width, this.Height);
using (Graphics imageGraphics = Graphics.FromImage(imageMetafile))
{
imageGraphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
imageGraphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
imageGraphics.CopyFromScreen(this.Location.X, this.Location.Y, 0, 0, this.Size);
imageMetafile.Save(filepath);
}
Related
I'm trying to render some text onscreen in Monogame by drawing a string to a Texture2D
This is the output I currently have
this is what i want it to look like
and here is a snippet of the code
Bitmap b = new Bitmap(width, height, PixelFormat.Format32bppArgb);
System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(b);
g.Clear(System.Drawing.Color.Transparent);
//Quality settings
g.SmoothingMode = SmoothingMode.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
Brush brush = new SolidBrush(Color.White);
g.DrawString(text, f, brush, 0, 0);
After reading some of the other posts about the same issue I tried clearing with the colour white, didn't change anything, then I tried adding a bunch of graphics settings like this post said to and that only gave me
Which has a bunch of unwanted black dots around it, which is arguably better then the first result but still not the crisp text I need, and clearing it with a single colour wont work because this text could potentially be on an image or something that moves
EDIT: i was able to get it slightly better with TextRenderingHint.SingleBitPerPixelGridFit
Still isnt what i want but at least closer
I was finally able to fix it with the help of some people in the monogame discord by settings BlendState to Nonpremultiplied in the sprite batch where the text is drawn to
Here is some pseudocode of what I am doing. Everything worked fine but then I tried to save my results. The saving also works but the image turns out to be transparent. Any idea what could cause this odd behavior?
static Graphics G = Panel.CreateGraphics();
//some painting -> shows up correctly on the panel
Bitmap bitmap = new Bitmap(500, 500, G);//bitmap is transparent!
bitmap.Save("path/test1.png", System.Drawing.Imaging.ImageFormat.Png);
The documentation for the Bitmap constructor you are using says:
Initializes a new instance of the Bitmap class with the specified size and with the resolution of the specified Graphics object.
Which means it just gets the resolution from the Bitmap. It does not paint anything to the bitmap. Either use Graphics.FromImage or, as Hans Passant mentions, the Control.DrawToBitmap method.
My personal preference, have I a need to paint to both the screen and a bitmap, would be to create a method that does the painting (taking a Graphics object as an argument). I can then call this either in a Paint event handler, or from other code to generate a bitmap.
Also, in general, never use Control.CreateGraphics. The proper way of drawing is in the Paint event of a control.
This will draw the Bitmap but wont show up in the panel. If showing is mandatory, then you have to implement it on the Paint event.
Bitmap bmp = new Bitmap(Panel.Width, Panel.Height);
Panel.DrawToBitmap(bmp, new Rectangle(0, 0, Panel.Width, Panel.Height));
Graphics grp = Graphics.FromImage(bmp);
Pen selPen = new Pen(Color.Blue);
grp.DrawRectangle(selPen, 10, 10, 50, 50);
bmp.Save("d:\\check3.png", System.Drawing.Imaging.ImageFormat.Png);
I'm working on a windows forms application and when I'm using the System.Drawing.Graphics on top of a picturebox the graphics either don't appear or appear only momentarily before disappearing.
This is the code that I'm using to set the picturebox (it's a simplified version and still exhibits the behavior)
private void showGraphic()
{
pictureBox1.Invalidate();
System.Drawing.Graphics graphics = this.pictureBox1.CreateGraphics();
SolidBrush semiTransBrush = new SolidBrush(Color.FromArgb(128, 0, 0, 255));
System.Drawing.Rectangle rect = new System.Drawing.Rectangle(100,100, 50, 50);
graphics.FillEllipse(semiTransBrush, rect);
}
private void button1_Click(object sender, EventArgs e)
{
showGraphic();
}
The settings for the picturebox are just the default settings with a picture from a file declared in the properties pane.
I was able to solve this problem by using a timer which was started by the button and then executed the graphic drawing before stopping itself, however this seemed a terrible solution and I wanted to do this a better way, if one exists as that could result in lack of portability to older computers.
Thanks in advance
You need to register a handler for the PictureBox's Paint method and do your drawing in that method. (Note: use the Graphics object passed in via the PaintEventArgs parameter.) That will guarantee that any time the PictureBox gets redrawn, your drawing code will run as well. Otherwise, you're just drawing over the top of something that will get refreshed for any of a number of reasons.
Once you've registered for the Paint event, anytime you want to repaint, call Invalidate() on the PictureBox and your painting code will run. You can keep track of whether or not your overlay graphics should be drawn via a private boolean member variable.
When you call pictureBox1.Invalidate() it queues up a message that the picture box needs to be drawn. Before that message gets processed you are drawing an ellipse on top of the current picture. Then message loop then processes the paint message from the invalidate and then repaints itself (which erases your image)
To make this (old and answered) question more complete I'm adding an alternative solution: sometimes you don't want to redraw your picture every refresh, for example if drawing is complicated and takes time. For these cases, you can try the following approach:
Create a new bitmap in the size of the picturebox.
Draw on bitmap.
Set bitmap as the picturebox Image.
For example:
// create a new bitmap and create graphics from it
var bitmap = new Bitmap(pictureBox1.Width, pictureBox1.Height);
var graphics = System.Drawing.Graphics.FromImage(bitmap);
// draw on bitmap
SolidBrush semiTransBrush = new SolidBrush(Color.FromArgb(128, 0, 0, 255));
System.Drawing.Rectangle rect = new System.Drawing.Rectangle(100,100, 50, 50);
graphics.FillEllipse(semiTransBrush, rect);
// set bitmap as the picturebox's image
pictureBox1.Image = bitmap;
With the code above you can draw everything once and it will survive redraw events.
I have been working on creating a program similar to MS Paint. I have several of the features it has down but the one which is currently giving me trouble is the rectangular selection tool. My program currently draws everything on the panel and saves it all in an ArrayList so each shape can be redrawn in Paint().
Like MS paint I would like the user to be able to select a section of the drawing on the panel and either copy it, move it, re-size it, or even delete it. I was thinking about having the user draw a rectangle & saving the information for it. Then taking that information for the rectangle, passing them to create a new Bitmap. I would then paint a new rectangle in the background color to give the appearance that the selected area was "removed" when the selected portion is moved. It sounded okay until I realized that I couldn't use the Graphics.FromImage() on the PaintEventArgs variable passed to Paint() which made my idea useless. Not sure if that makes sense so my apologies if it's a confusing mess.
I've been searching the internet for some assistance and I haven't found much to help so either this is very easy to do, very difficult, or "rectangle selection tool" is not the proper term. Any help or pointers would be greatly appreciated!!! Thank you for your time! :)
I understand that you actually have the Rectangle and now would like to copy an area from your painted Panel.
This is possible, assuming you have, as you should, placed all the painting in the Paint event of the Panel.
Then you can, use DrawToBitmap to ask the Panel to draw itself onto a new Bitmap; from there you can DrawImage the Rectangle onto your Panel.
Note: For this to integrate with your list of 'Paint-Actions' you will have to either now store that Bitmap or store the Rectangle and redo the whole operation.
using (Graphics G = panelCanvas.CreateGraphics() )
{
Rectangle R0 = new Rectangle(22,22,55,55); // your Rectangle!
using (Bitmap bmp = new
Bitmap(panelCanvas.ClientSize.Width, panelCanvas.ClientSize.Height))
{ panelCanvas.DrawToBitmap(bmp, panelCanvas.ClientRectangle);
G.DrawImage(bmp, 111f, 111f, R0, GraphicsUnit.Pixel);
}
}
Aside: Please do replace the ArrayList, which is depracated by the new List<T>, e.g. a List<PaintAction> or whatever name your class has!
If you simply want to extract a rectanglular area from the Panel Control you can use thsi function:
public Bitmap getAreaFrom(Control ctl, Rectangle area)
{
Bitmap bmp2 = new Bitmap(area.Width, area.Height);
using (Graphics G = ctl.CreateGraphics())
using (Bitmap bmp = new Bitmap(ctl.ClientSize.Width, ctl.ClientSize.Height))
{
ctl.DrawToBitmap(bmp, ctl.ClientRectangle);
using (Graphics G2 = Graphics.FromImage(bmp2))
G2.DrawImage(bmp, 0f, 0f, area, GraphicsUnit.Pixel);
}
return bmp2;
}
I'm developing a program that makes use of Windows 7 (and Vista) taskbar features. Right now I have a custom bitmap that will be shown in the taskbar thumbnail. The bitmap is created programmatic and shows up successfully. The only 'problem' I'm having is that want to use transparent in that image, which should show transparent in the thumbnail too. But without any success, resulting in the standard Light-Gray color.
I've seen evidence that programs successfully get transparent in the their images:
http://blog.miranda.or.at/wp-content/uploads/2010/05/taskbar.png
http://www.addictivetips.com/wp-content/uploads/2010/04/TaskbarRSS.png
http://www.addictivetips.com/wp-content/uploads/2009/10/Thumbnail1.png
Now is my question: how do I get transparent in my thumbnail image?
I'll be filling the image with the Graphics class, so anything is allowed.
I should mention that I use Windows® API Code Pack, which uses GetHbitmap to
set the image as thumbnail.
EDIT:
Just to make it complete, this is the code I'm using atm:
Bitmap bmp = new Bitmap(197, 119);
Graphics g = Graphics.FromImage(bmp);
g.FillRectangle(new SolidBrush(Color.Red), new Rectangle(0, 0, bmp.Width, bmp.Height)); // Transparent is actually light-gray;
g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
g.DrawString("Information:", fontHeader, brush, new PointF(5, 5));
bmp.MakeTransparent(Color.Red);
return bmp;
What pixel format is your Bitmap? If it has no alpha channel, you won't be able to store transparency information in your image.
Here is how to create a bitmap with an alpha channel and make it transparent by default:
Bitmap image = new Bitmap(width, height, PixelFormat.Format32bppArgb);
using(Graphics graphics = Graphics.FromImage(image))
{
graphics.Clear(Color.Transparent);
// Draw your stuff
}
You can then draw anything you want, including semi transparent stuff using the alpha channel.
Also note that if you are trying to paint transparency over existing opaque stuff (say to make a hole), you need to change compositing mode:
graphics.CompositingMode = CompositingMode.SourceCopy;
This will make whatever color you use to overwrite the one in the image rather than blending with it.
System.Drawing.Bitmap supports an alpha level. So the simplest way is
Graphics g = Graphics.FromImage(bmp);
g.FillRectangle(Brushes.Transparent, new Rectangle(0, 0, bmp.Width, bmp.Height)); // Transparent is actually light-gray;
g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
g.DrawString("Information:", fontHeader, brush, new PointF(5, 5));
however you can also have partial transparency by replacing Brushes.Transparent with
new SolidBrush(Color.FromArgb(150, 255, 255, 255));