I have a facial recognition library working that gives me an array of rectangle. Right now I'm using this way to draw the rectangle.
foreach (Rectangle box in boxes)
{
for (int x = box.X; x <= box.X + box.Width; x++)
{
for (int y = box.Y; y <= box.Y + box.Height; y++)
{
outputbmp.SetPixel(x, y, Color.FromKnownColor(KnownColor.Red));
}
}
}
I'm looking for something as simple as:
Ellipse ellipse = new Ellipse(box); //cast rect to ellipse
outputbmp.DrawEllipse(ellipse);
which will look something more like:
where the outline of the ellipse touching the rectangle corners.
Based on the approach I used above, it is easy to draw a rectangle but for ellipse, it would require me to know all the points in the ellipse. Just wonder if there's anything to make my life easier.
Don't try to draw directly to the bitmap, there is a higher level object you can create, called a Graphics that gives you all kinds of wonderful drawing tools. It will also be significantly faster than drawing pixel by pixel.
You can create a Graphics for a given Bitmap by calling Graphics.FromImage and passing in the bitmap. You must remember ti call Dispose on the Graphics though, or it will leak resources.
Once you have a Graphics instance for your bitmap you can call DrawEllipse and pass in the bounds exactly as you expect.
From MSDN:
private void DrawEllipseInt(Graphics g)
{
// Create pen.
Pen blackPen = new Pen(Color.Black, 3);
// Create location and size of ellipse.
int x = 0;
int y = 0;
int width = 200;
int height = 100;
// Draw ellipse to screen.
g.DrawEllipse(blackPen, x, y, width, height);
}
Related
I've been trying for hours but can't seem to figure it out. How can I get the bar-graph to sit on the bottom of the picture box without instead of it hanging from the top. Here is a picture of what it looks like so far. I've tried changing the Y position but it just moves the whole graph downwards rather then inverting it.
I want to take the distance walked and display it in a bar graph using the DrawRectangle, DrawLine Method. Here are the two methods I am using to draw the bar graph:
Method to retrieve data from list then display as bar graph in picture box:
private void DrawBarGraph()
{
Graphics canvas = pictureBoxTop.CreateGraphics();
int x = 200;
int y = 0;
foreach (int i in distanceList)
{
int length = SCALE_FACTOR * i;
DrawABar(canvas, x, y, length , Color.Red);
x += BAR_WIDTH + GAP;
}
}
Method to draw bars:
private void DrawABar(Graphics paper, int x, int y, int length, Color colour)
{
//create a brush of specified colour and fill background with this colour
SolidBrush brush = new SolidBrush(colour);
paper.FillRectangle(brush, x, y, BAR_WIDTH, length);
//draw outline in black
paper.DrawRectangle(Pens.Black, x, y, BAR_WIDTH, length);
}
Bar graph drawing with DrawRectangle method.
OP's solution (extracted from question post)
/// <summary>
/// Draw a bar graph in the picture box
/// </summary>
private void DrawBarGraph()
{
Graphics canvas = pictureBoxTop.CreateGraphics();
// Set world transform of graphics object to translate.
canvas.TranslateTransform(1200.0F, 195.0F);
// Then to rotate, prepending rotation matrix.
canvas.RotateTransform(180.0F);
//set row and column length then allocate colors to specific columns
int x = 0;
int y = 0;
foreach (int i in distanceList)
{
int length = SCALE_FACTOR * i;
DrawABar(canvas, x, y,length , Color.Red);
x += BAR_WIDTH + GAP;
}
}
After a couple of google searches and a bit more playing around I came across two methods designed to rotate the images inside of a picture box.
Here is the picture below:
Inverted bar graph using RotateTransfrom and RotateFlip Properties.
It seems that WPF's InkCanvas is only able to provide the points of the stroke (independent of the width and height of the stroke). For an application, I need to know all the points that are drawn by the InkCanvas.
For instance, assume that the width and height of the stroke are 16. Using this stroke size I paint a dot on the InkCanvas. Is there a straightforward way to obtain all 256 pixels in this dot (and not the center point of this giant dot alone)?
Why I care:
In my application, the user uses an InkCanvas to draw on top of a Viewport3D which is displaying a few 3D objects. I want to use all the points of the strokes to perform ray casting and determine which objects in the Viewport3D have been overlaid by the user's strokes.
I found a very dirty way of handling this. If anyone knows of a better method, I'll be more than happy to upvote and accept their response as an answer.
Basically my method involves getting the Geometry of each stroke, traversing all the points inside the boundaries of that geometry and determining whether the point is inside the geometry or not.
Here's the code that I am using now:
foreach (var stroke in inkCanvas.Strokes)
{
List<Point> pointsInside = new List<Point>();
Geometry sketchGeo = stroke.GetGeometry();
Rect strokeBounds = sketchGeo.Bounds;
for (int x = (int)strokeBounds.TopLeft.X; x < (int)strokeBounds.TopRight.X + 1; x++)
for (int y = (int)strokeBounds.TopLeft.Y; y < (int)strokeBounds.BottomLeft.Y + 1; y++)
{
Point p = new Point(x, y);
if (sketchGeo.FillContains(p))
pointsInside.Add(p);
}
}
You can use the StrokeCollection's HitTest method. I've compared the performance of your solution with this implementation and found the HitTest method performs better. Your mileage etc.
// get our position on our parent.
var ul = TranslatePoint(new Point(0, 0), this.Parent as UIElement);
// get our area rect
var controlArea = new Rect(ul, new Point(ul.X + ActualWidth, ul.Y + ActualHeight));
// hit test for any strokes that have at least 5% of their length in our area
var strokes = _formInkCanvas.Strokes.HitTest(controlArea, 5);
if (strokes.Any())
{
// do something with this new knowledge
}
You can find the documentation here:
https://learn.microsoft.com/en-us/dotnet/api/system.windows.ink.strokecollection.hittest?view=netframework-4.7.2
Further, if you only care if any point is in your rect, you can use the code below. It's an order of magnitude faster than StrokeCollection.HitTest because it doesn't care about percentages of strokes so it does a lot less work.
private bool StrokeHitTest(Rect bounds, StrokeCollection strokes)
{
for (int ix = 0; ix < strokes.Count; ix++)
{
var stroke = strokes[ix];
var stylusPoints = stroke.DrawingAttributes.FitToCurve ?
stroke.GetBezierStylusPoints() :
stroke.StylusPoints;
for (int i = 0; i < stylusPoints.Count; i++)
{
if (bounds.Contains((Point)stylusPoints[i]))
{
return true;
}
}
}
return false;
}
Today I am trying to solve problem with a blinking panel, when I draw onto it.
Lots of threads I read, like these:
how to stop flickering C# winforms,
Double buffering with Panel,
How can I draw on Panel so it does not blink?
So I tried to draw onto PictureBox, MyPanel with doubleBuffered, but the best solution I found, when I read, that I can't use g.Clear() every time, after that, even on non-doubleBuffered panel, blinking disappeared.
I even read, that I should free Graphics after draw is done. So I use everywhere using(Graphics g = panel.CreateGraphics()).
So my question, is it a great idea to create graphics for bitmap only when I draw something to it? Because before I created Bitmap, and Graphics (only for this bitmap, not for all components), so I had Graphics available for this bitmap every time
Here is my code:
public void newSizeDrawing()
{
Size size = collector.getLetterSize(selectedName);
Size drawingSize = new Size(size.Width * (pixelSizeArray[pixelSize] + 1),size.Height * (pixelSizeArray[pixelSize] + 1));
bitmapDraw = new Bitmap(drawingSize.Width, drawingSize.Height);
int width = (this.MinimumSize.Width - panelDraw.MinimumSize.Width) + drawingSize.Width + 10;
int height = (this.MinimumSize.Height - panelDraw.MinimumSize.Height) + drawingSize.Height + 10;
this.Size = new Size(
(width > this.MinimumSize.Width) ? width : this.MinimumSize.Width,
(height > this.MinimumSize.Height) ? height : this.MinimumSize.Height);
zeroDrawPosition = new Point((panelDraw.Size.Width - bitmapDraw.Width) / 2 - 1, (panelDraw.Size.Height - bitmapDraw.Height) / 2 - 1);
using (Graphics g = panelDraw.CreateGraphics())
{
g.Clear(panelDraw.BackColor);
}
redrawDrawingLetter();
}
public void redrawDrawingLetter()
{
bool[][] grid = collector.getArray(selectedName);
using (Graphics graphicDraw = Graphics.FromImage(bitmapDraw))
{
graphicDraw.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
graphicDraw.Clear(panelDraw.BackColor);
int pxSize = pixelSizeArray[pixelSize];
for (int y = 0; y < grid.Length; y++)
{
for (int x = 0; x < grid[y].Length; x++)
{
graphicDraw.FillRectangle((grid[y][x] ? Brushes.Black : Brushes.White), x * (pxSize + 1), y * (pxSize + 1), pxSize, pxSize);
}
}
}
redrawDrawingPanel();
}
private void redrawDrawingPanel()
{
using (Graphics g = panelDraw.CreateGraphics())
{
if (bitmapDraw != null)
g.DrawImage(bitmapDraw, zeroDrawPosition);
}
}
private void panelDraw_Paint(object sender, PaintEventArgs e)
{
redrawDrawingPanel();
}
Nobody can explain to me how to draw in C# the best way. So maybe my code isn't good, but that is reason why I asking how to do it correctly.
newSizeDrawing is called by myself only, when user click on + or - button. I have bool double-dimension array if pixel is on or off. This is program for drawing letters for microchips and LED display (often 8px height of letter).
I wrote a method that checks if the mouse moved from one "pixel" to another, so I don't redraw it after every call mouseMove event, because "pixel" can be from 10x10 px to 30x30 px.
private void panelDraw_Paint(object sender, PaintEventArgs e)
{
redrawDrawingPanel();
}
This is fundamentally wrong. The Paint event passes e.Graphics to let you draw whatever you want to paint. When you turn on double-buffering, e.Graphics refers to a bitmap, it is initialized with the BackColor. You then proceed to drawing using another Graphics object you got from CreateGraphics(). That one draws directly to the screen.
The flicker effect you see if very pronounced. For a split second you see what the other Graphics context draws. Then your panelDraw_Paint() method returns and Winforms draws the double-buffered bitmap. There's nothing on it so it immediately erases what you drew.
Modify the redrawDrawingPanel() method and give it an argument of type Graphics. Pass e.Graphics in the call. And only use that Graphics object, remove all calls to CreateGraphics().
I have a small paint program that I am working on. I am using SetPixel on a bitmap to do that drawing of lines. When the brush size gets large, like 25 pixels across there is a noticeable performance drop. I am wondering if there is a faster way to draw to a bitmap. Here is a bit of the background of the project:
I am using bitmaps so that I can utilise layers, like in Photoshop or The GIMP.
Lines are being drawn manually because this will eventually use graphics tablet pressure to alter the size of the line over its length.
The lines should eventually be anti-aliaced/smoothed along the edges.
I'll include my drawing code just in case it is this that is slow and not the Set-Pixel bit.
This is in the windows where the painting happens:
private void canvas_MouseMove(object sender, MouseEventArgs e)
{
m_lastPosition = m_currentPosition;
m_currentPosition = e.Location;
if(m_penDown && m_pointInWindow)
m_currentTool.MouseMove(m_lastPosition, m_currentPosition, m_layer);
canvas.Invalidate();
}
Implementation of MouseMove:
public override void MouseMove(Point lastPos, Point currentPos, Layer currentLayer)
{
DrawLine(lastPos, currentPos, currentLayer);
}
Implementation of DrawLine:
// The primary drawing code for most tools. A line is drawn from the last position to the current position
public override void DrawLine(Point lastPos, Point currentPos, Layer currentLayer)
{
// Creat a line vector
Vector2D vector = new Vector2D(currentPos.X - lastPos.X, currentPos.Y - lastPos.Y);
// Create the point to draw at
PointF drawPoint = new Point(lastPos.X, lastPos.Y);
// Get the amount to step each time
PointF step = vector.GetNormalisedVector();
// Find the length of the line
double length = vector.GetMagnitude();
// For each step along the line...
for (int i = 0; i < length; i++)
{
// Draw a pixel
PaintPoint(currentLayer, new Point((int)drawPoint.X, (int)drawPoint.Y));
drawPoint.X += step.X;
drawPoint.Y += step.Y;
}
}
Implementation of PaintPoint:
public override void PaintPoint(Layer layer, Point position)
{
// Rasterise the pencil tool
// Assume it is square
// Check the pixel to be set is witin the bounds of the layer
// Set the tool size rect to the locate on of the point to be painted
m_toolArea.Location = position;
// Get the area to be painted
Rectangle areaToPaint = new Rectangle();
areaToPaint = Rectangle.Intersect(layer.GetRectangle(), m_toolArea);
// Check this is not a null area
if (!areaToPaint.IsEmpty)
{
// Go through the draw area and set the pixels as they should be
for (int y = areaToPaint.Top; y < areaToPaint.Bottom; y++)
{
for (int x = areaToPaint.Left; x < areaToPaint.Right; x++)
{
layer.GetBitmap().SetPixel(x, y, m_colour);
}
}
}
}
Thanks a lot for any help you can provide.
You can lock the bitmap data and use pointers to manually set the values. It's much faster. Though you'll have to use unsafe code.
public override void PaintPoint(Layer layer, Point position)
{
// Rasterise the pencil tool
// Assume it is square
// Check the pixel to be set is witin the bounds of the layer
// Set the tool size rect to the locate on of the point to be painted
m_toolArea.Location = position;
// Get the area to be painted
Rectangle areaToPaint = new Rectangle();
areaToPaint = Rectangle.Intersect(layer.GetRectangle(), m_toolArea);
Bitmap bmp;
BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int stride = data.Stride;
unsafe
{
byte* ptr = (byte*)data.Scan0;
// Check this is not a null area
if (!areaToPaint.IsEmpty)
{
// Go through the draw area and set the pixels as they should be
for (int y = areaToPaint.Top; y < areaToPaint.Bottom; y++)
{
for (int x = areaToPaint.Left; x < areaToPaint.Right; x++)
{
// layer.GetBitmap().SetPixel(x, y, m_colour);
ptr[(x * 3) + y * stride] = m_colour.B;
ptr[(x * 3) + y * stride + 1] = m_colour.G;
ptr[(x * 3) + y * stride + 2] = m_colour.R;
}
}
}
}
bmp.UnlockBits(data);
}
SetPixel does this:
is locks the whole image, sets the pixel and unlocks it
try to do that: you acquire a lock for the whole memory image with lockbits, process you update and release the lock after.
lockbits
I usually use an array to represent the raw pixel data. And then copy between that array and the bitmap with unsafe code.
Making the array of Color is a bad idea, since the Color struct is relatively large(12 bytes+). So you can either define your own 4 byte struct(that's the one I chose) or simply use an array of int or byte.
You should also reuse your array, since GC on the LOH tends to be expensive.
My code can be found at:
https://github.com/CodesInChaos/ChaosUtil/blob/master/Chaos.Image/
An alternative is writing all your code using pointers into the bitmap directly. That's a bit faster still, but can make the code uglier and more error prone.
Just an idea: Fill an offscreen bitmap with your Brush pixels. You only need to regenerate this bitmap when the brush, size or color is changed. And then just draw this bitmap onto your existing bitmap, where the mouse is located.
If you can modulate a bitmap with a color, you could set the pixels in grayscale and modulate it with the current brush color.
You're calling GetBitmap within your nested for loop. It looks like that's not necessary, you should GetBitmap outside the for loops as the reference isn't going to change.
Also look at #fantasticfix answer, Lockbits nearly always sorts slow performance issues with getting/setting pixels
i draw the circle in c# using directx.i like to draw the circle with same dimensions in c# using GDI.It means i like to convert that circle from directx to GDI. Is any body help for me.plz provide the answer for me.how can i do it.Is any algorithm available for that........
And also i give the input for center of the circle is (x,y)in this point format.but in gdi it is pixel format .so how can i convert the directx points to gdi+ pixels
Here is a link from MSDN that introduces Graphics and Drawing in Windows Forms. And it's likely that you will need something similar to:
public Form1()
{
InitializeComponent();
this.Paint += new PaintEventHandler(Form1_Paint);
// This works too
//this.Paint += (_, args) => DrawCircle(args.Graphics);
}
void Form1_Paint(object sender, PaintEventArgs e)
{
DrawCircle(e.Graphics);
}
private void DrawCircle(Graphics g)
{
int x = 0;
int y = 0;
int radius = 50;
// The x,y coordinates here represent the upper left corner
// so if you have the center coordinates (cenX, cenY), you will have to
// substract radius from both cenX and cenY in order to represent the
// upper left corner.
// The width and height represents that of the bounding rectangle of the circle
g.DrawEllipse(Pens.Black, x, y, radius * 2, radius * 2);
// Use this instead if you need a filled circle
//g.FillEllipse(Brushes.Black, x, y, radius * 2, radius * 2);
}
After that you might want to look into double-buffering techniques, a few links :
[SO] How to double buffer .NET controls on a form?
[MSDN] Double Buffered Graphics
[MSDN] Using Double Buffering