Why doesn't FillPolygon draw after FillRectangle? - c#

I'm drawing directly onto a form with two FillPolygon statements to create two arrows - one black, one white. (The white arrow is slightly smaller and drawn over the black arrow.)
Here's the code in the form's OnPaint.
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
g.FillPolygon(brushBlack, travelArrow);
g.FillPolygon(brushWhite, featureArrow);
}
Works great. Now since the white arrow is going to be drawn several times in different rotations, I decided to use double-buffering to avoid as much flicker as possible.
I first created a DrawFeatureArrow method that I call in OnPaint.
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
g.FillPolygon(brushBlack, travelArrow);
DrawFeatureArrow(this, e);
}
Note: There's no rotation of the white arrow coded yet, I'm just trying to get the double-buffering set up.
DrawFeatureArrow looks like this.
private void DrawFeatureArrow(object sender, PaintEventArgs e)
{
Bitmap buffer = new Bitmap(60, 159);
Graphics gOff = Graphics.FromImage(buffer);
gOff.FillRectangle(brushGreen, 0, 0, buffer.Width, buffer.Height);
gOff.FillPolygon(brushWhite, featureArrow);
ImageAttributes attr = new ImageAttributes();
attr.SetColorKey(Color.Green, Color.Green);
Rectangle srcRect = new Rectangle(0, 0, bugger.Width, buffer.Height);
Rectangle destRect = new Rectangle(90, 66, 60, 159);
Graphics f = e.Graphics;
// Should draw green rectangle and white arrow
f.DrawImage(buffer, 90, 66); // Draws just a green rectangle
// If uncommented, should draw just white arrow (green rectangle hidden by SetColorKey)
// f.DrawImage(buffer, destRect, 0, 0, buffer.Width, buffer.Height, GraphicsUnit.Pixel, attr);
f.Dispose();
gOff.Dispose();
buffer.Dispose();
}
When run, the green rectangle is drawn but not the white arrow.
Strangely enough, in DrawFeatureArrow, if you replace this
gOff.FillPolygon(brushWhite, featureArrow);
With this
gOff.FillRectangle(brushWhite, 10, 10, 20, 20);
You get a tiny white rectangle in the upper left of the green rectangle.
Very strange behavior. Hope someone can point out what I'm doing incorrectly.
Thanks in advance for the help.

You're drawing your feature arrow over buffer that has dimensions of 60, 159. Maybe the location of the feature arrow is outside of those dimensions and therefore did not end in the bitmap.

Related

Using graphics to draw rectangle at top left corner of WinForms control cuts off one pixel of top and left of rectangle

I have a WinForms project and I'm trying to draw a rectangle at (0,0), the top, left corner of the form. For some reason it's cutting off one pixel of height and width of the rectangle. Here's the code:
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawRectangle(new Pen(Color.Red, 5), new Rectangle(0, 0, 50, 50));
}
Here is the result, blown up for clarity:
I understand I could correct for this by drawing the rectangle at (1,1), but that's not how it should work based on what I've seen from controls that are placed at (0,0) and (1,1). For instance, here's what a panel looks like at (1,1), and it clearly has a one-pixel gap:
So my question is: why does drawing a rectangle at (0,0) not behave like placing a control at (0,0)? Why does the rectangle get cut off by one pixel on the top and left?
The default value for Alignment property of Pen is PenAlignment.Center which means the drawn line will be centered over the line. So what you see is expected.
You may want to set the Alignment to PenAlignment.Inset:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawRectangle(
new Pen(Color.Red, 5) { Alignment = PenAlignment.Inset },
new Rectangle(0, 0, 50, 50));
}
You may also want to read Pen.Alignment remarks:
Center is the default value for this property and specifies that the
width of the pen is centered on the outline of the curve or polygon. A
value of Inset for this property specifies that the width of the pen
is inside the outline of the curve or polygon. The other three values,
Right, Left, and Outset, will result in a pen that is centered.

Invert Crop from (Cut hole into) Image

Everywhere I look online, I see people posting on how to successfully crop an image. However, I want to 'crop'/ clear a hole out of an image. I want to keep the original image, but crop out a rectangle
As you can see in the image above, I have "cropped" out the kittens face. I maintained the original image, but removed only part of it. I cannot figure out how to do that.
Assuming you want to replace the original pixel colors with transparency you run into a small problem: You can't draw or fill with transparency in GDI+.
But you can use Graphics.Clear(Color.Transparent).
To do that you restrict the region where the Graphics object will draw. Here we can use the simple cropping rectangle but you can clear more complex shapes using a GraphicsPath..
Example using a bitmap bmp:
using (Graphics g = Graphics.FromImage(bmp))
{
Rectangle crop = new Rectangle(222,222,55,55);
g.SetClip(crop);
g.Clear(Color.Transparent);
}
bmp.Save(somefilename, ImageFormat.Png);
Setting your Graphics object's CompositingMode property to CompositingMode.SourceCopy will allow your drawing operations to replace the alpha value instead of proportionally opacifying it:
public static void TestDrawTransparent()
{
//This code will, successfully, draw something transparent overwriting an opaque area.
//More precisely, it creates a 100*100 fully-opaque red square with a 50*50 semi-transparent center.
using(Bitmap bmp = new Bitmap(100, 100, PixelFormat.Format32bppArgb))
{
using(Graphics g = Graphics.FromImage(bmp))
using(Brush opaqueRedBrush = new SolidBrush(Color.FromArgb(255, 255, 0, 0)))
using(Brush semiRedBrush = new SolidBrush(Color.FromArgb(128, 255, 0, 0)))
{
g.Clear(Color.Transparent);
Rectangle bigRect = new Rectangle(0, 0, 100, 100);
Rectangle smallRect = new Rectangle(25, 25, 50, 50);
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
g.FillRectangle(opaqueRedBrush, bigRect);
g.FillRectangle(semiRedBrush, smallRect);
}
bmp.Save(#"C:\FilePath\TestDrawTransparent.png", ImageFormat.Png);
}
}
In this code, I first draw a fully-opaque red square, then a semi-transparent red square "over" it. The result is a semi-transparent "hole" in the square:
And on a black background:
A zero-opacity brush works just as well, leaving a clear hole through the image (I checked).
With that in mind, you should be able to crop any shapes you want, simply by filling them with a zero-opacity brush.

Invalidating a graphic

I have a got a graphic object of a rectangle this is created when the button on the picturebox is clicked but if the form is moved off the screen the graphic dissapears as well as the whole form it just goes white i have tried to invalidate the picturebox but this stops the button from working can anyone help me keep the object on the form using the invalidate method i know theres a way but i can't seem to grasp it
private void squareButton_Click(object sender, EventArgs e)
{
// Declaring a new graphics object has been assigned null
Graphics objGraphics = null;
// This will create the picture graphics to be drawn in the picturebox
objGraphics = PictureBox1.CreateGraphics();
// This will redraw the picture box with a fill chosen after the systemcolors
objGraphics.Clear(SystemColors.ControlDark);
// This will draw the rectangle with a red pen 10,10 represent position and 50,50 reprsent the width and height
objGraphics.DrawRectangle(Pens.Red, 10, 10, 50, 50);
// This will draw the rectangle
objGraphics.Dispose();
invalidate(PictureBox1);
// This is not redrawing the graphic it just shows a blank form
}
Try this:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Graphics objGraphics = null;
// This will create the picture graphics to be drawn in the picturebox
objGraphics = e.Graphics;
// This will redraw the picture box with a fill chosen after the systemcolors
objGraphics.Clear(SystemColors.ControlDark);
// This will draw the rectangle with a red pen 10,10 represent position and 50,50 reprsent the width and height
objGraphics.DrawRectangle(Pens.Red, 10, 10, 50, 50);
// This will draw the rectangle
//objGrphics.Dispose();
}

Moving a high quality line on a panel c#

I want to draw a line on a panel and then move it as the mouse moves. To do so, I draw the line and when the mouse moves I redraw the line to the new location and remove the previous line by drawing a line with the background color on it. It works fine if I do not use the high quality smoothing mode. But if use high quality smoothing mode, it leave traces on the panel. Does anybody know how to fix this? Thank you.
Here is the code
int x_previous = 0;
int y_previous = 0;
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
Pen pen1 = new System.Drawing.Pen(Color.Black, 3);
Pen pen2 = new System.Drawing.Pen(panel1.BackColor, 3);
Graphics g = panel1.CreateGraphics();
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.DrawLine(pen2, new Point(0, 0), new Point(x_previous, y_previous));
g.DrawLine(pen1, new Point(0, 0), new Point(e.Location.X, e.Location.Y));
x_previous = e.Location.X;
y_previous = e.Location.Y;
}
Here is the snapshot with SmoothingMode
Here is the snapshot without SmoothingMode
Instead of drawing a line over a line, the safest option would be to clear the graphics using g.Clear(panel1.BackColor). This will wipe everything off that has been drawn, so that you can safely draw a new line:
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
Pen pen1 = new System.Drawing.Pen(Color.Black, 3);
Pen pen2 = new System.Drawing.Pen(panel1.BackColor, 3);
Graphics g = panel1.CreateGraphics();
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
// Clear the graphics, creating a blank area to draw on
g.Clear(panel1.BackColor);
g.DrawLine(pen1, new Point(0, 0), new Point(e.Location.X, e.Location.Y));
x_previous = e.Location.X;
y_previous = e.Location.Y;
}
Hope this helps!
Instead of drawing the line in the event handler for the mouse movement you should use it to Invalidate the panel and perform the line drawing in a handler for its Paint event. There will be no need to erase the old line.
I haven't used WinForms in a while, so you'll have to forgive me if this doesn't work.
I assume that anti-aliasing has slightly blurred the edges of the line so that the initial line extends slightly further out than the width of the pen. That also means that the edges of the white pen won't be completely opaque when drawing over the black line.
Try increasing the width of the white pen until it completely covers the black, and maybe see if you can leave the Graphics object with lower quality rendering for the white pen, and use the smoothing only for the black pen.

Graphics.DrawImage produces alpha-channel gradient in C# WinForms 2.0

I'm facing a really perplexing problem..
I have a .Net 2.0 C# WinForms project.
I'm trying to stretch a bitmap onto a drawing area, but for some reason it is not stretched properly - I get alpha channel gradient on the right and bottom margins of my drawing area.
It took me quite a while to isolate this problem. I create a few lines of code that reproduce the problem (see code snippet and screenshot below).
Can anyone please shed some light over this matter?
Thanks in advance.
--
private void Form1_Paint( object sender, PaintEventArgs e )
{
// Create a black bitmap resource sized 10x10
Image resourceImg = new Bitmap( 10, 10 );
Graphics g = Graphics.FromImage( resourceImg );
g.FillRectangle( Brushes.Black, 0, 0, resourceImg.Width, resourceImg.Height );
Rectangle drawingArea = new Rectangle( 0, 0, 200, 200 ); // Set the size of the drawing area
e.Graphics.FillRectangle( Brushes.Aqua, drawingArea ); // Fill an aqua colored rectangle
e.Graphics.DrawImage( resourceImg, drawingArea ); // Stretch the resource image
// Expected result: The resource image should completely cover the aqua rectangle.
// Actual Result: The right and bottom edges become gradiently transparent (revealing the aqua rectangle under it)
}
The behavior has to do with how GDI+ handles edges. In this case, you're scaling a very small image over a large area, and you haven't told GDI+ how to handle the edge. If you use the ImageAttributes class and set the WrapMode appropriately, you can get around this issue.
For example:
private void Form1_Paint(object sender, PaintEventArgs e)
{
using (var resourceImg = new Bitmap(10, 10))
{
using (var g = Graphics.FromImage(resourceImg))
{
g.FillRectangle(Brushes.Black, 0, 0,
resourceImg.Width, resourceImg.Height);
}
var drawingArea = new Rectangle(0, 0, 200, 200);
e.Graphics.FillRectangle(Brushes.Aqua, drawingArea);
using (var attribs = new ImageAttributes())
{
attribs.SetWrapMode(WrapMode.TileFlipXY);
e.Graphics.DrawImage(resourceImg, drawingArea,
0, 0, resourceImg.Width, resourceImg.Height,
GraphicsUnit.Pixel, attribs);
}
}
}
The above code should produce an all black image. If you comment out the attribs.SetWrapMode(WrapMode.TileFlipXY); statement, you should see the blue gradient. With the wrap mode set, you're telling GDI+ to flip the image at the edges, so it will pick up more black and not fade things out at the edge when it scales the image.

Categories

Resources