my first question here. And I'm sure it's an easy one, but I can't find a solution anywhere, I've read lots and just can't comprehend how to what i need to do:
I have a *.gif file, that I want to store in System.Drawing.Bitmap, so that I have access to GetPixel() method
I want to have my drawing on some control (currently trying out PictureBox)
So far what I've got:
I create a template, Bitmap from file - and that works (map_t in code)
I create a bitmap that will actually be drawn (map)
I can set my PictureBox image to a bitmap
I can't change this bitmap. Or I can, but the result doesn't show
I thought I'd have to create Graphic from that bitmap, then change it. I have no idea what i'm missing.
Here's the code: MapCanvas is PictureBox on a form, everything is in MouseMove, because I was trying to make some circles around the pointer - didn't work so I ended up with this code, which doesn't work either. I have read every question I could find, nothing helps... Here's the code:
namespace Projekt_innowacje
{
public partial class MapForm : Form
{
Bitmap map_t;
Bitmap map;
public MapForm()
{
InitializeComponent();
map_t = new Bitmap("map.gif", true);
map = new Bitmap(map_t.Width, map_t.Height);
MapCanvas.Image = map;
}
private void MapCanvas_MouseMove(object sender, MouseEventArgs e)
{
Graphics canvas = Graphics.FromImage(map);
for (int i = 0; i <= map.Width; i++)
for (int j = 0; j <= map.Height; j++)
if (map.GetPixel(i, j) == Color.Red)
canvas.DrawLine(new Pen(Color.Blue), i, j, i + 1, j + 1);
// map.SetPixel(i, j, map.GetPixel(i, j)); // also doesn't work
//MapCanvas.Refresh();
MapCanvas.Invalidate();
canvas.Dispose();
}
}
}
The code you are showing will never modify the canvas/map image unless you are making some other changes to the map image that you're not sharing. In your loop you test map.GetPixel, however map is created as a blank image in the constructor, so none of its pixels will ever be Color.Red. Did you intend to test against your template instead?
Also, you may want to have a look at this question which talks about comparing colors. It suggests that for basic color comparisons to use the .ToArgb() method on the color structure since the == and Equals do more than just compare the color value.
Related
I want to get access to pixels colors from an image, but as far as I know Image cannot be used in that way. In my code there is an event Modify where I call a method MakeDarker, but I want to know how can I modify pixels there.
I tried to convert an Image to a bitmap or to byte array, but none of this works for me. Visual Studio doesn't even recognize Bitmap, when I'm typing it despite using Xamarin.Forms.Image.
public void Modify(object sender, EventArgs args)
{
ImageModify modify = new ImageModify();
picture = modify.MakeDarker(picture);
}
class ImageModify
{
public Image MakeDarker(Image image)
{
Image output = new Image();
for (int i = 0; i < image.Height; i++)
{
for (int j = 0; j < image.Width; j++)
{
}
}
return output;
}
}
Have you tried with the Color struct from the System.Drawing namespace (don't know if you can use it in Xamarin or if it's specific for Windows based projects) ?
I used it in my own C# projet to collect RGB values of a pixel and it worked well.
I'm doing an application with a splash screen.
I've an image an I'd like to put below a progress bar like :
Example
I've succeeded to make the bitmap transparent.
But, now, the image is behind the progress bar
Now
Is there a way to get the image in front of the progress bar ?
Thank you.
F.
Code :
public partial class Form1 : Form
{
Bitmap m_l;
public Form1()
{
InitializeComponent();
m_l = Properties.Resources.LU;
m_l.MakeTransparent(Color.Transparent);
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(m_l, new Rectangle(new Point(0, -40), new Size(200, 264))); progressBar1.Refresh();
}
}
(sorry, for missuing the answer function but the answer is to long for a comment)
#TaW
seems like you didnt quite understand the approach, so I will try to explain it in more detail
OP asked if he can make a transparant Image over another control (a progressbar)
I assumed this transparent Image is inside a PictureBox, you seem to assume some other control
to position the control, if my assumption is correct the picturebox, infront of the progress bar all he has to do is right click and click "Bring to Front" on the PictureBox
and there you have it a "transparent" PictureBox infront of a progressbar - but as you mentioned in your answer we cannot stop there since the "transparent" isnt what I expected, but obviously you knew - its this "parent background color picking" that WinForms does and we end up with a not fully transparent image infront of the ProgressBar but instead one with a gray Background
Now the posted url comes in place:
http://www.richardhyland.com/diary/2009/05/26/how-to-truely-make-a-picturebox-background-transparent/
This is the code provided, and explained in that url:
public static System.Drawing.Drawing2D.GraphicsPath Transparent(Image im)
{
int x;
int y;
Bitmap bmp = new Bitmap(im);
System.Drawing.Drawing2D.GraphicsPath gp = new System.Drawing.Drawing2D.GraphicsPath();
Color mask = bmp.GetPixel(0, 0);
for (x = 0; x <= bmp.Width - 1; x++)
{
for (y = 0; y <= bmp.Height - 1; y++)
{
if (!bmp.GetPixel(x, y).Equals(mask))
{
gp.AddRectangle(new Rectangle(x, y, 1, 1));
}
}
}
bmp.Dispose();
return gp;
}
With this we can achieve a fully transparent Picture box infront of a Progress bar.
So without this Code, we have this:
But with that Code:
Notice, this approach has some downsides:
doesn't work perfectly - as you can see gray pixels around the edges of the image
performs poorly on big Images - since getting each pixel with GetPixel is "challange"
(Please, ignore the fact that the image shows "JPG" and I am talking about transparent Images - this was just the first image Google search presented me and yes, the file is a transparent png)
You can accomplish this using a PictureBox with a Region.
Add a PictureBox to your form. This will hold the image. Position it to overlap the ProgressBar as you would like. Set the Image property to your overlay image.
In the form constructor we're then going to set the Region of this PictureBox. The region defines the shape of the control. We're going to set the Region equal to the non-transparent parts of the image.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
pictureBox1.Region = CreateRegion(Properties.Resources.LU);
}
private static Region CreateRegion(Bitmap maskImage)
{
// We're using pixel 0,0 as the "transparent" color.
Color mask = maskImage.GetPixel(0, 0);
GraphicsPath graphicsPath = new GraphicsPath();
for (int x = 0; x < maskImage.Width; x++)
{
for (int y = 0; y < maskImage.Height; y++)
{
if (!maskImage.GetPixel(x, y).Equals(mask))
{
graphicsPath.AddRectangle(new Rectangle(x, y, 1, 1));
}
}
}
return new Region(graphicsPath);
}
}
Much of this code came from here
There are 5 6 options:
You could set the ProgressBar's BackColor to Transparent. Result:
Translation:
Invalid property value. This control does not support a transparent backcolor.
This will hold true for a subclass as well.
You could nest a transparent Panel with the image as its BackgroundImage. Result:
As you can see, despite the panel and most of the image being transparent, the progressbar still underlays it with a rectangle in its own BackColor; which, see above, can't be transparent.
You could overlay with the panel.
Result:
Looks similar. But this time the backcolor is the original background of wherever the panel was before overlaying it. This is the way winforms fakes transparency; this faked transpareny will only work with nested controls, but not with all..
You could draw your image in the progressbar's Paint event. Problem: It doesn't have one. And if you subclass it it will not work for you.
To sum it up: all those attempts fail; the conclusion is simple: ProgressBar is an animated control that won't support any messing with it.
Last option: Write your own. You can subclass a Panel or Label and write your own progressbar. Many folks who wanted to have a custom look, have done this and you can find many ready made examples.
Upate: Looks like you can have a 6th option, which will work if and only if you don't need semi-transparency, like anti-aliasing etc..: You can create a GraphicsPath to create a Region which will mask some control with the image.. So while my example will not work, OP's image may look quite OK.
as ways, people are developers but seams to have a really weak logic, post code and link the rest to a site that can die at any time, here the 2 missing line of the answer
public static System.Drawing.Drawing2D.GraphicsPath Transparent(Image im)
{
int x;
int y;
Bitmap bmp = new Bitmap(im);
System.Drawing.Drawing2D.GraphicsPath gp = new System.Drawing.Drawing2D.GraphicsPath();
Color mask = bmp.GetPixel(0, 0);
for (x = 0; x <= bmp.Width - 1; x++)
{
for (y = 0; y <= bmp.Height - 1; y++)
{
if (!bmp.GetPixel(x, y).Equals(mask))
{
gp.AddRectangle(new Rectangle(x, y, 1, 1));
}
}
}
bmp.Dispose();
return gp;
use:
System.Drawing.Drawing2D.GraphicsPath gp = Resources.Images.Transparent(pictureBox1.Image);
pictureBox1.Region = new System.Drawing.Region(gp);
In my code, the output is an image each pixel of which is determined using nested loops.
1) How can I force a window to open and show the output image as it is being constructed in the loop? (The window shows up when everything is finished. I don't want this.)
2) How can I have the output be displayed line by line (or even pixel by pixel) as the loop goes on. User must have the sense of getting the output in real-time.
outImage = new Image<Hsv, Byte>(numberOfColumns, numberOfRows);
byte[,,] pixelValue = outImage.Data;
for (int i = 0; i < numberOfRows - 1; i++)
{
for (int j = 0; j < numberOfColumns - 1; j++)
{
//pixelValue[i, j, k] is determined here using some other functions
imageBox1.Image = outImage; //too slow and impossible
}
}
You can display an image pixel by pixel in real time by putting it on a separate thread and using GetPixel and SetPixel. Keep in mind though that these methods are slow and if you are displaying high resolution pictures, it will take a while.
What you'll want to do is create a form with a picture box control on it. Next, create a Bitmap object containing the image you'll want to display. You can do this using a line like this:
Bitmap _bmp = new Bitmap(Image.FromFile(#"C:\MyImage.jpg"));
Next, in your form's shown event, spin off a new task to do the work, so the GUI doesn't lock up:
private void Form1_Shown(object sender, EventArgs e)
{
Task.Factory.StartNew(ShowImage);
}
This line will spin off a new thread every time the form is displayed. The thread will fork off and call ShowImage(), which should look like this:
private void ShowImage()
{
Graphics g = pbImage.CreateGraphics();
for (int x = 0; x < _bmp.Width; x++)
{
for (int y = 0; y < _bmp.Height; y++)
{
Color c = _bmp.GetPixel(x, y);
if (pbImage.InvokeRequired)
{
var x1 = x;
var y1 = y;
pbImage.BeginInvoke((Action) (() =>
{
g.FillRectangle(new SolidBrush(c), x1, y1, 1, 1);
}));
}
else
{
g.FillRectangle(new SolidBrush(c), x, y, 1, 1);
}
System.Threading.Thread.Sleep(1);
}
}
}
If you wanted to speed this up a bit, you could spin up two or more tasks, each task working in parallel (e.g. one thread starts at the beginning, another at the end, another in the middle maybe, etc). Just make sure your threads don't "overlap".
Another way to speed this up is to use pointers instead of GetPixel() and SetPixel(). You'd have to mark your code as unsafe though.
put your code in background Worker => Do Work
A separate thread would be initiated
I am not a WinForms expert I am more of a WPF type. But I have an application running a solid 30fps and that is faster than humans can detect. I really do not quite understand what you want to do here. You have to blit each pixel individually but have display in real time? An ImageBox derives from the Windows Forms PictureBox, that won't work I do not think.
You could try moving to WPF, and use a WriteableBitmap for a ImageSource for an Image object or the background of a Canvas Object. A WriteableBitmap will let you access each pixel, but the refresh rate is controlled by WPF and the monitor refresh rate is controlled by the AC current frequency.
Doug
I am making a program where you bassicly move from tile to tile in windows forms.
So in order to do that, I wanted to use panels each panel has a tag. To detect collision.
So I have an image of my map. and I divided into multiple tiles. However now I have to drag 900 tiles onto panels.
This isn't very effective in 2 ways. First loading 900 textures isn't really a smart idea. Also it would take ages. So i wanted to use a spritesheet or tilemap. But how would I do that in winforms. I believe I have seen some people use a grid view or whatever. However im not sure how to do what I want to do.
What would be the best solution?
Thanks in advance!
For any serious gaming project WinForms is not the best platform. Either WPF or XNA or Unity are able to deliver high performance use of DirectX.
But since you want to do it in Winforms here is a way to do it.
It creates a whopping number of 900 PictureBoxes and loads each with a fraction of an source image:
private void Form1_Load(object sender, EventArgs e)
{
int tileWidth = 30;
int tileHeight = 30;
int tileRows = 30;
int tileCols = 30;
using (Bitmap sourceBmp = new Bitmap("D:\\900x900.jpg"))
{
Size s = new Size(tileWidth, tileHeight);
Rectangle destRect = new Rectangle(Point.Empty, s);
for (int row = 0; row < tileRows; row++)
for (int col = 0; col < tileCols; col++)
{
PictureBox p = new PictureBox();
p.Size = s;
Point loc = new Point(tileWidth * col, tileHeight * row);
Rectangle srcRect = new Rectangle(loc, s);
Bitmap tile = new Bitmap(tileWidth, tileHeight);
Graphics G = Graphics.FromImage(tile);
G.DrawImage(sourceBmp, destRect, srcRect, GraphicsUnit.Pixel);
p.Image = tile;
p.Location = loc;
p.Tag = loc;
p.Name = String.Format("Col={0:00}-Row={1:00}", col, row);
// p.MouseDown += p_MouseDown;
// p.MouseUp += p_MouseUp;
// p.MouseMove += p_MouseMove;
this.Controls.Add(p);
}
}
}
When I tried it I was a bit worried about perfomance, but..
This takes under 1 second to load on my machine.
Starting the programm adds 10MB to VS memory usage. That is like nothing.
For a fun project this will do; for best performance one might use Panels but these will have to be filled and refilled in the Paint event. This solution saves you the hassle and since you don't change the tile picture all the time this works well enough.
Pleae note: I have added a Name and a Tag to each PictureBox, so you can later refer to it. These both contain info about the original position of the Picturebox. The Name looks like this: Col=23-Row=02 and the Tag is the original Location object.
Also: Dynamically added controls take a little extra to script since you can't create their method bodies in the designer. Instead you add them like above. In doing so Intellisense and the Tab key are your best friends..
I have added three event handlers for a few mouse events. When you uncomment them you will have to add the methods like e.g. this:
void p_MouseMove(object sender, MouseEventArgs e)
{
throw new NotImplementedException();
}
But maybe you want to use other events to play like Drag&Drop or keyboard events..
There are two ways to refer to these tiles. Maybe you want to try and/or use both of them: You can loop over the form's controls with a
foreach (Control ctl in this.Controls)
{ if (ctl is PictureBox ) this.Text = ((PictureBox)ctl).Name ; }
It tests for the right type and then casts to PictureBox. As an example it displays the name of the tile in the window title.
Or you can have a variable and set it in the MouseDown event:
PictureBox currentTile;
void p_MouseDown(object sender, MouseEventArgs e)
{
currentTile = (PictureBox ) sender;
}
I've modified the SuperContextMenuStrip found at CodeProject to meet some of my projects needs. I'm using it as a tooltip for map markers on a GMap.NET Map Control. Here is a sample of what it looks like:
What I would like to do is pretty this up a little by making it look more like a bubble. Similar to an old Google Maps stytle tooltip:
I've spent some time searching on control transparency and I know this isn't an easy thing. This SO question in particular illustrates that.
I have considered overriding the OnPaint method of the SuperContextMenuStrip to draw a background of the GMap.NET control that is underneath the SuperContextMenuStrip, but even that would fail in cases where the marker is hanging off the GMap.NET control:
What is the correct way to create the type of transparency I am looking for?
In Windows Forms, you achieve transparency (or draw irregularly shaped windows) by defining a region. To quote MSDN
The window region is a collection of pixels within the window where
the operating system permits drawing.
In your case, you should have a bitmap that you will use as a mask. The bitmap should have at least two distinct colors. One of these colors should represent the part of the control that you want to be transparent.
You would then create a region like this:
// this code assumes that the pixel 0, 0 (the pixel at the top, left corner)
// of the bitmap passed contains the color you wish to make transparent.
private static Region CreateRegion(Bitmap maskImage) {
Color mask = maskImage.GetPixel(0, 0);
GraphicsPath grapicsPath = new GraphicsPath();
for (int x = 0; x < maskImage.Width; x++) {
for (int y = 0; y < maskImage.Height; y++) {
if (!maskImage.GetPixel(x, y).Equals(mask)) {
grapicsPath.AddRectangle(new Rectangle(x, y, 1, 1));
}
}
}
return new Region(grapicsPath);
}
You would then set the control’s Region to the Region returned by the CreateRegion method.
this.Region = CreateRegion(YourMaskBitmap);
to remove the transparency:
this.Region = new Region();
As you can probably tell from the code above, creating regions is expensive resource-wise. I'd advice saving regions in variables should you need to use them multiple times. If you use cached regions this way, you'd soon experience another problem. The assignment would work the first time but you would get an ObjectDisposedException on subsequent calls.
A little investigation with refrector would reveal the following code within the set accessor of the Region Property:
this.Properties.SetObject(PropRegion, value);
if (region != null)
{
region.Dispose();
}
The Region object is disposed after use!
Luckily, the Region is clonable and all you need to do to preserve your Region object is to assign a clone:
private Region _myRegion = null;
private void SomeMethod() {
_myRegion = CreateRegion(YourMaskBitmap);
}
private void SomeOtherMethod() {
this.Region = _myRegion.Clone();
}