Greeting!
I've been working on a project where you got a form where the user asks for a certain amount of dot (between 1 and 10). Then it draws them on a picturebox with a buttonclick action and then the user could click on any dot and a line between them is drawn.
So far it looks like this:
private void button1_Click(object sender, EventArgs e)
{
pictureBox1.CreateGraphics().Clear(this.BackColor);
Random r = new Random();
int db = new int();
Graphics rl = pictureBox1.CreateGraphics();
db = Convert.ToInt32(textBox1.Text);
if (db > 0 && db < 11)
{
for (int i = 0; i < db; i++)
{
rl.FillRectangle(new SolidBrush(Color.Black), r.Next(4, pictureBox1.Width - 4), r.Next(4, pictureBox1.Height - 4), 5, 5);
}
}
else
{
MessageBox.Show("Wrong number!");
}
}
This only draws the point on the form and i want to make them linkable. My idea is that i put it on a bitmap then i check the x,y coordinates and if the color is black (which means there is a point) I store in dot1 and dot2 and then simply make a drawline command like this:
private bool first = false;
private void Picturebox_Mouse(object sender, MouseEventArgs e)
{
Bitmap bm = new Bitmap(pictureBox1.Image);
Color color = bm.GetPixel(e.X, e.Y);
if (color == Color.Black & first==false)
{
dot1.X = e.X;
dot1.Y = e.Y;
first = true;
}
if (color == Color.Black & first == true)
{
dot2.X = e.X;
dot2.Y = e.X;
first = false;
}
rl.DrawLine(new Pen(Color.Black), dot1, dot2);
}
My question is that is my approach fine or i should do a different way? Also if this can work how do i put the content of the picturebox into a bitmap? I tried to drawing the point into the bitmap diractly then setting the pictureboxs image to the bitmap:
pictureBox1.DrawToBitmap(bm, new Rectangle(r.Next(0, pictureBox1.Width - 4), r.Next(0, pictureBox1.Height - 4), 5, 5));
pictureBox1.BackgroundImage = bm;
and also i tried to reverse it so putting the content to the bitmap:
Bitmap bm = new Bitmap(pictureBox1.Image);
but it doesn't seem to work. So can i get some ideas how to do it?
Any Kind of Idea is Appriciated!Thanks you.
I have done something similar, but as I was doing it on a web page I used an ImageButton instead of a PictureBox, so my code doesn't do exactly what you need, unfortunately. However, to use a bitmap with your image, you can do something like this:
Bitmap flag = new Bitmap(200, 100);
using(Graphics flagGraphics = Graphics.FromImage(flag))
{
// draw something on flagGraphics
pictureBox1.Image = flag;
}
(I actually based this on https://msdn.microsoft.com/en-us/library/ms404307(v=vs.110).aspx)
Note, you will need to change your test for black to something like if(color.ToArgb() == Color.Black.ToArgb()) or it won't work. Also you are setting a Y to an X value when I think it should be a Y!
Related
Sorry for my eng. Im quite new to C#, and i need this:
After clicking a button, a rectangle form with pre-determined size will appear;
This rectangle must move all around the form + move on screen even if the form is minimized;
The retangle must follow the mouse movements;
When i make a mouse click on the place of screen i want, i need that all coordinates of the area of the rectangle are stored in variables.
This coordinates of rectangle will later be checked by a read-pixel code that i already have, so i really need that the rectangle area of where i clicked really be stored in variables.
EDIT:
Im really new to c# and what i have done so far is:
private void button5_Click(object sender, EventArgs e)
{
Graphics dc = this.CreateGraphics();
Pen Bluepen = new Pen(Color.Blue, 3);
dc.DrawRectangle(Bluepen, 0, 0, 50, 50);
}
And:
private void button5_MouseMove(object sender, MouseEventArgs e)
{
if (isMouseDown == true)
{
rect.Location = e.Location;
if (rect.Right > pictureBox1.Width)
{
rect.X = pictureBox1.Width - rect.Width;
}
if (rect.Top < 0)
{
rect.Y = 0;
}
if (rect.Left < 0 )
{
rect.X = 0;
}
if (rect.Bottom > pictureBox1.Height)
{
rect.Y = pictureBox1.Height - rect.Height;
}
Refresh();
}
}
Thanks in advance!!
Update:
Updated code added.
However for some reason it still doesn't work properly. If the coordinates are Bitmap coordinates, what could be the reason? The first code sample I put here doesn't work properly and the second gives me an OutOfMemoryException.
I've ran into a problem trying to crop an image between two points. In my project I have a pictureBox (named AP), and the general idea is that the user clicks on two points and the program crops the image between these two corners. I've tried two methods, one with Bitmap.Crop and the other one with Graphics.DrawImage, but both seemed to fail for the same reason and didn't work at all (cropped a much smaller portion of the image).
Code:
private void AP_Click(object sender, EventArgs e)
{
// Setting the corners
else if (mark_shape == 0)
{
var mouseEventArgs = e as MouseEventArgs;
if (picture_corners_set == 0)
{
northEast = AP.PointToClient(new Point(mouseEventArgs.X, mouseEventArgs.Y));
picture_corners_set = 1;
}
else if (picture_corners_set == 1)
{
southWest = AP.PointToClient(new Point(mouseEventArgs.X, mouseEventArgs.Y));
Rectangle imageRectangle = new Rectangle(southWest.X, northEast.Y, (northEast.X - southWest.X), (southWest.Y - northEast.Y));
var bmp = new Bitmap(imageRectangle.Width, imageRectangle.Height);
using (var gr = Graphics.FromImage(bmp))
{
gr.DrawImage(AP.Image, 0, 0, imageRectangle, GraphicsUnit.Pixel);
}
AP.Image = bmp;
enableAllButtons();
}
}
}
Since your cropped Bitmap image size is the same as the width/height of the user selection, I'm guessing you want that cropped image to be in the top/left of the new Bitmap and fill it. As it is, you're telling the DrawImage() method to draw that portion of the Bitmap in the same location, albeit in a smaller size Bitmap.
The correct way to do this is to draw the source rectangle image at (0, 0):
private Point pt1, pt2;
private void AP_Click(object sender, EventArgs e)
{
// ... obviously other code here ...
else if (mark_shape == 0) // Setting the corners
{
Point pt = AP.PointToClient(Cursor.Position);
if (picture_corners_set == 0)
{
pt1 = new Point(pt.X, pt.Y);
picture_corners_set = 1;
}
else if (picture_corners_set == 1)
{
pt2 = new Point(pt.X, pt.Y);
picture_corners_set = 0;
Rectangle imageRectangle = new Rectangle(new Point(Math.Min(pt1.X, pt2.X), Math.Min(pt1.Y, pt2.Y)), new Size(Math.Abs(pt2.X - pt1.X) + 1, Math.Abs(pt2.Y - pt1.Y) + 1));
var bmp = new Bitmap(imageRectangle.Width, imageRectangle.Height);
using (var gr = Graphics.FromImage(bmp))
{
gr.DrawImage(AP.Image, 0, 0, imageRectangle, GraphicsUnit.Pixel);
}
AP.Image = bmp;
enableAllButtons();
}
}
}
There are several other overloads you could use to do this, but the one above makes it pretty clear that imageRectangle is being drawn at (0, 0).
Let me preface this with a real life product; You may remember in Elementary school, they had scratch paper which basically consisted of a rainbow-colored sheet of paper with a black film on top. You would take a sharp object and peel away the black film to expose the colored paper.
I am attempting to do the same thing using images in a picture box.
My idea consists of these things:
A textured image.
A black rectangle the size of the picture box.
A circle image.
What I am trying to achieve is to open a program, have an image drawn to a picture box with the black rectangle on top of it. Upon clicking the picture box it uses the circle to invert the alpha of the rectangle where I click using the circle as a reference.
My Problem-
I cannot figure out any way to erase (set the transparency of) a part of the black rectangle where I click.
For the life of me, I do not know of any method to cut a window in an image. It is almost like a reverse crop, where I keep the exterior elements rather than the interior, exposing the textured image below.
Can WinForms not do this? Am I crazy? Should I just give up?
I should mention that I prefer not to have to change alpha on a pixel per pixel basis. It would slow the program down far too much to be used as a pseudo-painter. If that is the only way, however, feel free to show.
Here is an image of what I'm trying to achieve:
This is not really hard:
Set the colored image as a PictureBox's BackgroundImage.
Set a black image as its Image.
And draw into the image using the normal mouse events and a transparent Pen..
We need a point list to use DrawCurve:
List<Point> currentLine = new List<Point>();
We need to prepare and clear the the black layer:
private void ClearSheet()
{
if (pictureBox1.Image != null) pictureBox1.Image.Dispose();
Bitmap bmp = new Bitmap(pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height);
using (Graphics G = Graphics.FromImage(bmp)) G.Clear(Color.Black);
pictureBox1.Image = bmp;
currentLine.Clear();
}
private void cb_clear_Click(object sender, EventArgs e)
{
ClearSheet();
}
To draw into the Image we need to use an associated Graphics object..:
void drawIntoImage()
{
using (Graphics G = Graphics.FromImage(pictureBox1.Image))
{
// we want the tranparency to copy over the black pixels
G.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
G.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
G.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
using (Pen somePen = new Pen(Color.Transparent, penWidth))
{
somePen.MiterLimit = penWidth / 2;
somePen.EndCap = System.Drawing.Drawing2D.LineCap.Round;
somePen.LineJoin = System.Drawing.Drawing2D.LineJoin.Round;
somePen.StartCap = System.Drawing.Drawing2D.LineCap.Round;
if (currentLine.Count > 1)
G.DrawCurve(somePen, currentLine.ToArray());
}
}
// enforce the display:
pictureBox1.Image = pictureBox1.Image;
}
The usual mouse events:
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
currentLine.Add(e.Location);
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
currentLine.Add(e.Location);
drawIntoImage();
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
currentLine.Clear();
}
That's all that's needed. Make sure to keep the PB's SizeMode = Normal or else the pixels won't match..!
Note that there are a few challenges when you want to get soft edges, more painting tools, letting a simple click paint a dot or an undo or other finer details to work. But the basics are not hard at all..
Btw, changing Alpha is not any different from changing the color channels.
As an alternative you may want to play with a TextureBrush:
TextureBrush brush = new TextureBrush(pictureBox1.BackgroundImage);
using (Pen somePen = new Pen(brush) )
{
// basically
// the same drawing code..
}
But I found this to be rather slow.
Update:
Using a png-file as a custom tip is a little harder; the main reason is that the drawing is reversed: We don't want to draw the pixels, we want to clear them. GDI+ doesn't support any such composition modes, so we need to do it in code.
To be fast we use two tricks: LockBits will be as fast as it gets and restricting the area to our custom brush tip will prevent wasting time.
Let's assume you have a file to use and load it into a bitmap:
string stampFile = #"yourStampFile.png";
Bitmap stamp = null;
private void Form1_Load(object sender, EventArgs e)
{
stamp = (Bitmap) Bitmap.FromFile(stampFile);
}
Now we need a new function to draw it into our Image; instead of DrawCurve we need to use DrawImage:
void stampIntoImage(Point pt)
{
Point point = new Point(pt.X - stamp.Width / 2, pt.Y - stamp.Height / 2);
using (Bitmap stamped = new Bitmap(stamp.Width, stamp.Height) )
{
using (Graphics G = Graphics.FromImage(stamped))
{
stamp.SetResolution(stamped.HorizontalResolution, stamped.VerticalResolution);
G.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
G.DrawImage(pictureBox1.Image, 0, 0,
new Rectangle(point, stamped.Size), GraphicsUnit.Pixel);
writeAlpha(stamped, stamp);
}
using (Graphics G = Graphics.FromImage(pictureBox1.Image))
{
G.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
G.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
G.CompositingQuality =
System.Drawing.Drawing2D.CompositingQuality.HighQuality;
G.DrawImage(stamped, point);
}
}
pictureBox1.Image = pictureBox1.Image;
}
A few notes: I found that I hat to do an explicit SetResolution since the stamp file I photoshopped was 72dpi and the default Bitmaps in my program were 120dpi. Watch out for these differences!
I start the Bitmap to be drawn by copying the right part of the current Image.
Then I call a fast routine that applies the alpha of the stamp to it:
void writeAlpha(Bitmap target, Bitmap source)
{
// this method assumes the bitmaps both are 32bpp and have the same size
int Bpp = 4;
var bmpData0 = target.LockBits(
new Rectangle(0, 0, target.Width, target.Height),
ImageLockMode.ReadWrite, target.PixelFormat);
var bmpData1 = source.LockBits(
new Rectangle(0, 0, source.Width, source.Height),
ImageLockMode.ReadOnly, source.PixelFormat);
int len = bmpData0.Height * bmpData0.Stride;
byte[] data0 = new byte[len];
byte[] data1 = new byte[len];
Marshal.Copy(bmpData0.Scan0, data0, 0, len);
Marshal.Copy(bmpData1.Scan0, data1, 0, len);
for (int i = 0; i < len; i += Bpp)
{
int tgtA = data0[i+3]; // opacity
int srcA = 255 - data1[i+3]; // transparency
if (srcA > 0) data0[i + 3] = (byte)(tgtA < srcA ? 0 : tgtA - srcA);
}
Marshal.Copy(data0, 0, bmpData0.Scan0, len);
target.UnlockBits(bmpData0);
source.UnlockBits(bmpData1);
}
I use a simple rule: Reduce target opacity by the source transparency and make sure we don't get negative.. You may want to play around with it.
Now all we need is to adapt the MouseMove; for my tests I have added two RadioButtons to switch between the original round pen and the custom stamp tip:
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
if (rb_pen.Checked)
{
currentLine.Add(e.Location);
drawIntoImage();
}
else if (rb_stamp.Checked) { stampIntoImage(e.Location); };
}
}
I didn't use a fish but you can see the soft edges:
Update 2: Here is a MouseDown that allows for simple clicks:
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (rb_pen.Checked) currentLine.Add(e.Location);
else if (rb_stamp.Checked)
{
{ stampIntoImage(e.Location); };
}
}
I followed the following in post on"Creating a screen magnifier".
Therefore I have this code.
It is not copy & pasted from the post. I have also added a timer so the form is not blank.
However I have found some problems.
It doesn't zoom in very much. I would like to have a larger zoom. An adjustable zoom setting will be optimal, but I can make that myself if I know how to zoom in more.
The center of the form is not always the tip of the cursor like I want it would be. Is there anyway I can Fix this?
Here is the code I have got now.
Graphics g;
Bitmap bmp;
private void Timer1_Tick(object sender, EventArgs e)
{
bmp = new Bitmap(250, 200);
g = this.CreateGraphics();
g = Graphics.FromImage(bmp);
g.CopyFromScreen(MousePosition.X , MousePosition.Y , 0, 0, new Size(300, 300));
pictureBox1.Image = bmp;
}
The results seem to be exactly the same to this software that I found during my research.the link, It takes you to a Japanese webpage.
You're going to have to play around with the various numbers in the example in order to see what effect they have on the output. It'll help to turn them into variables so you can play with them more easily. Here is a good start, no promises that it works, but it'll give you a good place to start experimenting until you get what you want.
Graphics g;
Bitmap bmp;
private void Timer1_Tick(object sender, EventArgs e)
{
var endWidth = 300;
var endHeight = 300;
var scaleFactor = 2; //perhaps get this value from a const, or an on screen slider
var startWidth = endWidth / scaleFactor;
var startHeight = endHeight / scaleFactor;
bmp = new Bitmap(startWidth, startHeight);
g = this.CreateGraphics();
g = Graphics.FromImage(bmp);
var xPos = Math.Max(0, MousePosition.X - (startWidth/2)); // divide by two in order to center
var yPos = Math.Max(0, MousePosition.Y - (startHeight/2));
g.CopyFromScreen(xPos, yPos, 0, 0, new Size(endWidth, endWidth));
pictureBox1.Image = bmp;
}
I want to rotate an image in the picture box. Here is my code.
public static Bitmap RotateImage(Image image, PointF offset, float angle)
{
if (image == null)
{
throw new ArgumentNullException("image");
}
var rotatedBmp = new Bitmap(image.Width, image.Height);
rotatedBmp.SetResolution(image.HorizontalResolution, image.VerticalResolution);
var g = Graphics.FromImage(rotatedBmp);
g.TranslateTransform(offset.X, offset.Y);
g.RotateTransform(angle);
g.TranslateTransform(-offset.X, -offset.Y);
g.DrawImage(image, new PointF(0, 0));
return rotatedBmp;
}
private void button1_Click(object sender, EventArgs e)
{
Image image = new Bitmap(pictureBox1.Image);
pictureBox1.Image = (Bitmap)image.Clone();
var oldImage = pictureBox1.Image;
var p = new Point(image.Width / 2, image.Height);
pictureBox1.Image = null;
pictureBox1.Image = RotateImage(image, p, 1);
pictureBox1.SizeMode = PictureBoxSizeMode.CenterImage;
pictureBox1.Refresh();
if (oldImage != null)
{
oldImage.Dispose();
}
}
private void button2_Click(object sender, EventArgs e)
{
Image image = new Bitmap(pictureBox1.Image);
pictureBox1.Image = (Bitmap)image.Clone();
var oldImage = pictureBox1.Image;
var p = new Point(image.Width / 2, image.Height);
pictureBox1.Image = null;
pictureBox1.Image = RotateImage(image, p, -1);
pictureBox1.SizeMode = PictureBoxSizeMode.CenterImage;
pictureBox1.Refresh();
if (oldImage != null)
{
oldImage.Dispose();
}
}
Now the problem is that when I rotate the image it gets cut. Here is the situation.
I have stretched the picture box and changed the colour of form just for clear picture.
My question is when I have used the statement
pictureBox1.Image = RotateImage(image, p, 1);
Then why is the picture not getting right after postion as this is the same statement used for any situation where we have to assign some image to groupbox. Why is not it working here? I have searched it before but the most of the searches seem irrelevant to me because they use filip function which rotate through 90,180,270. But I want to rotate by some degree maximum upto 10 degree.
Rotating Controls is not something supported by default (links talking about this: link1, link2). The reason why the picture gets cut is because, after the rotation, its width is bigger than the pictureBox1 one; thus a quick solution would be updating its size after the rotation:
pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize; //Adapts the size automatically
or
pictureBox1.Width = image.Width;
pictureBox1.Height = image.Height;
This should be an acceptable solution (there has to be enough free space to account for the new dimensions of the image after being rotated anyway). The other option would be affecting the PictureBox control directly (by affecting the rectangle defining its boundaries, for example) what would be much more difficult.
Well i have come to know that win Forms are not meant for any transformations and rotations.Changing the mode to AutoSize does not make a difference. The best thing for rotation and transformation is WPF.
WPF has a good transformation classes which rotate and transform objects without affecting the object. The object does not get blurred.
You can use This for rotations and transformations.