I started studying SharpGL on WPF, and I wanted to learn how to output a picture, I found a tutorial where it was shown how to output a picture, but after rewriting the code to myself, the picture is not output, but output a red image, what could be wrong?
Wpf screen
Link to Download Project
Texture texture = new Texture();
private void openGLControl1_OpenGLInitialized(object sender, EventArgs e)
{
// Get the OpenGL object, for quick access.
SharpGL.OpenGL gl = this.openGLControl1.OpenGL;
// Get the OpenGL object, for quick access.
// A bit of extra initialisation here, we have to enable textures.
gl.Enable(OpenGL.GL_TEXTURE_2D);
// Create our texture object from a file. This creates the texture for OpenGL.
texture.Create(gl, #"C:\Users\user\Desktop\Crate.bmp");
}
private void OpenGLControl_OpenGLDraw(object sender, OpenGLRoutedEventArgs args)
{
// System.Threading.Thread.Sleep(1000);
SharpGL.OpenGL gl = this.openGLControl1.OpenGL;
gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
texture.Bind(gl);
gl.LoadIdentity();
gl.Translate(0, 0, -1f);
gl.Begin(OpenGL.GL_QUADS);
var startX = 0 - 0 / 2;
var startY = 0 - 0 / 2;
float width = 100;
float height = 100;
gl.TexCoord(0, 1); gl.Vertex(new[] { startX, startY });
gl.TexCoord(1, 1); gl.Vertex(new[] { startX + width, startY });
gl.TexCoord(1, 0); gl.Vertex(new[] { startX + width, startY + height });
gl.TexCoord(0, 0); gl.Vertex(new[] { startX, startY + height });
gl.End();
gl.Flush();
}
private void OpenGLControl_Resized(object sender, OpenGLRoutedEventArgs args)
{
SharpGL.OpenGL gl = this.openGLControl1.OpenGL;
gl.MatrixMode(OpenGL.GL_PROJECTION);
gl.LoadIdentity();
float w = 450;
float h = w * 0.5625f;
gl.Viewport(0, 0, (int)w, (int)h);
gl.MatrixMode(OpenGL.GL_MODELVIEW);
}
Related
I'm trying to use SharpGL to render in a WPF application using gl.VertexPointer() and gl.DrawArrays(). But I cannot get it to render a square. The background clears to green and I can see the FPS drawing at the bottom left of the WPF panel. When adding the code for the square, the FPS text disappears as well and I just have a blank screen.
I'm doing the same exact thing that I have in a C++ project which works just fine. I don't know what I'm missing or doing incorrectly.
XAML
<Window x:Class="NodePlusPlus.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:NodePlusPlus"
xmlns:gl="clr-namespace:SharpGL.WPF;assembly=SharpGL.WPF"
mc:Ignorable="d"
Title="MainWindow" Height="900" Width="1600" Background="#FF202020">
<Grid x:Name="MainGrid">
<gl:OpenGLControl x:Name="glPanel" DrawFPS="True" />
</Grid>
</Window>
C#
public MainWindow()
{
InitializeComponent();
glPanel.OpenGLInitialized += glPanel_OpenGLInitialized;
glPanel.OpenGLDraw += glPanel_OpenGLDraw;
glPanel.Resized += glPanel_Resized;
}
private void glPanel_Resized(object sender, SharpGL.WPF.OpenGLRoutedEventArgs args)
{
OpenGL gl = args.OpenGL;
gl.Ortho2D(0, Width, 0, Height);
}
private void glPanel_OpenGLInitialized(object sender, SharpGL.WPF.OpenGLRoutedEventArgs args)
{
OpenGL gl = args.OpenGL;
gl.Hint(OpenGL.GL_PERSPECTIVE_CORRECTION_HINT, OpenGL.GL_FASTEST);
gl.ShadeModel(OpenGL.GL_SMOOTH);
gl.Enable(OpenGL.GL_CULL_FACE);
gl.Enable(OpenGL.GL_TEXTURE_2D);
gl.Enable(OpenGL.GL_BLEND);
gl.Enable(OpenGL.GL_DEPTH_TEST);
gl.BlendFunc(OpenGL.GL_SRC_ALPHA, OpenGL.GL_ONE_MINUS_SRC_ALPHA);
gl.EnableClientState(OpenGL.GL_VERTEX_ARRAY);
gl.EnableClientState(OpenGL.GL_TEXTURE_COORD_ARRAY);
gl.ClearColor(0.0f, 1.0f, 0.0f, 0.1f);
}
private void glPanel_OpenGLDraw(object sender, SharpGL.WPF.OpenGLRoutedEventArgs args)
{
OpenGL gl = args.OpenGL;
// Clear the color and depth buffers
gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
gl.LoadIdentity();
// Build a rectangle
float Width = 100.0f;
float Height = 100.0f;
float[] vertices = new float[18];
vertices[0] = 0; vertices[1] = 0; vertices[2] = 0.0f;
vertices[3] = 0; vertices[4] = Height; vertices[5] = 0.0f;
vertices[6] = Width; vertices[7] = Height; vertices[8] = 0.0f;
vertices[9] = 0; vertices[10] = 0; vertices[11] = 0.0f;
vertices[12] = Width; vertices[13] = Height; vertices[14] = 0.0f;
vertices[15] = Width; vertices[16] = 0; vertices[17] = 0.0f;
// If I remove this whole Begin()-End() section, it will render the Open GL FPS. With this draw code here, it does not render the FPS.
gl.Begin(OpenGL.GL_TRIANGLES);
gl.Translate(0.0f, 0.0f, 0.0f);
gl.Color(0.0f, 0.0f, 0.0f, 1.0f);
gl.VertexPointer(3, 0, vertices);
gl.DrawArrays(OpenGL.GL_TRIANGLES, 0, 6);
gl.End();
gl.Flush();
}
With Draw code...
Without Draw code...
As mentioned by #BDL, glBegin() and glEnd() are not used in this case.
As mentioned by #Rabbid76, glEnableClientState(OpenGL.GL_TEXTURE_COORD_ARRAY) should be removed since I am not using texture coordinates.
Thanks for the help guys!
Also removing gl.Enable(OpenGL.GL_CULL_FACE) was necessary in the specific example to draw my square. As #Rabbid76 mentioned, the default winding order is counter-clockwise. My vertices are winding clockwise.
End result
private void glPanel_OpenGLInitialized(object sender, SharpGL.WPF.OpenGLRoutedEventArgs args)
{
OpenGL gl = args.OpenGL;
gl.Hint(OpenGL.GL_PERSPECTIVE_CORRECTION_HINT, OpenGL.GL_FASTEST);
gl.ShadeModel(OpenGL.GL_SMOOTH);
gl.Enable(OpenGL.GL_TEXTURE_2D);
gl.Enable(OpenGL.GL_BLEND);
gl.Enable(OpenGL.GL_DEPTH_TEST);
gl.BlendFunc(OpenGL.GL_SRC_ALPHA, OpenGL.GL_ONE_MINUS_SRC_ALPHA);
gl.EnableClientState(OpenGL.GL_VERTEX_ARRAY);
gl.ClearColor(0.0f, 1.0f, 0.0f, 0.1f);
}
private void glPanel_OpenGLDraw(object sender, SharpGL.WPF.OpenGLRoutedEventArgs args)
{
OpenGL gl = args.OpenGL;
// Clear the color and depth buffers
gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
gl.LoadIdentity();
// Build a rectangle
float Width = 100.0f;
float Height = 100.0f;
float[] vertices = new float[18];
vertices[0] = 0; vertices[1] = 0; vertices[2] = 0.0f;
vertices[3] = 0; vertices[4] = Height; vertices[5] = 0.0f;
vertices[6] = Width; vertices[7] = Height; vertices[8] = 0.0f;
vertices[9] = 0; vertices[10] = 0; vertices[11] = 0.0f;
vertices[12] = Width; vertices[13] = Height; vertices[14] = 0.0f;
vertices[15] = Width; vertices[16] = 0; vertices[17] = 0.0f;
gl.Translate(0.0f, 0.0f, 0.0f);
gl.Color(0.0f, 0.0f, 0.0f, 1.0f);
gl.VertexPointer(3, 0, vertices);
gl.DrawArrays(OpenGL.GL_TRIANGLES, 0, 6);
gl.Flush();
}
I am trying to implement a zooming-in option on a graph by allowing the user to select the area he wants (shown in pic). The graph doesn't take up the whole screen. It ranges from -0.8 to around +1.4 on Y-axis.
After Zooming-in:
I do the zooming in by scaling and translating the projection matrix in the OnRenderFrame event.
Xscale= 1, Yscale=1, Xtrans =0, Ytrans =0;
protected override void OnRenderFrame(FrameEventArgs e)
{
GL.ClearColor(Color4.Black);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
for (int j = 0; j < lines.Count(); j++)
{
projection = Matrix4.CreateOrthographicOffCenter(-1.0f, 1.0f, -1.0f, 1.0f, -2f, 2.0f);
Matrix4 Translate = Matrix4.CreateTranslation(Xtrans, Ytrans, 0);
Matrix4 Scale = Matrix4.CreateScale(1/Xscale, 1/Yscale, 1);
projection = Translate* Scale* projection;
view = Matrix4.LookAt(CameraPos,
CameraPos + CameraFront, CameraUp);
shader.Use();
shader.SetMatrix4("model", Matrix4.Identity);
shader.SetMatrix4("view", view);
shader.SetMatrix4("projection", projection);
shader.SetFloat("color", new Vector4(colors.names[i]));
GL.BindVertexArray(VAO[i]);
GL.DrawArrays(PrimitiveType.LineStrip, 0, Y1Vertices[0].Count());
shader.Unbind();
GL.BindVertexArray(0);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
}
SwapBuffers();
base.OnRenderFrame(e);
}
I get the scaling factors by finding the ratio between the rectangle dimensions and screen dimensions. For translating, I am trying to find the difference between the rectangle origin and the screen origin then translate the object by this amount. The scaling is working correctly but not the translation. I think maybe the issue is that my object doesn't fill the screen initially, as in doesn't go from -1 to 1 on Y-axis? I am not sure how to account for this offset especially that it changes depending on the graph data. I would also prefer if the zooming in doesn't take up the whole screen and just stays within the initial graph dimensions. Could you please help me out?
protected override void OnMouseDown(MouseButtonEventArgs e)
{
if(e.Button == MouseButton.Right)
{
initialPos = new Vector2(e.X, e.Y);
}
base.OnMouseDown(e);
}
protected override void OnMouseMove(MouseMoveEventArgs e)
{
MouseState mstate = Mouse.GetCursorState();
if (e.Mouse[MouseButton.Right])
{
mouseDown = true;
newPos = new Vector2(e.X, e.Y);
}
base.OnMouseMove(e);
}
protected override void OnMouseUp(MouseButtonEventArgs e)
{
if (e.Button == MouseButton.Right)
{
Xscale *= (Math.Abs(newPos.X - initialPos.X)) / this.Width;
Yscale *= (Math.Abs(newPos.Y - initialPos.Y) / this.Height);
Xtrans = (((newPos.X + initialPos.X) / 2)-(this.Width/2))/this.Width;
Ytrans = (((newPos.Y + initialPos.Y) / 2) - (this.Height / 2)) / this.Height;
}
base.OnMouseUp(e);
}
Edit:
After changing to projection = Scale*Translate* projection;
I am working on a project for school, we need to make a basic top down race game in C# without using XNA.
First of all let me tell you that the stuff we have learned about programming so far has little to do with making something that even remotely looks like a racegame. It didn't get any more difficult than array's, loops etc.
So we didn't learn about graphics or anything like that.
Having said all that I am having the following problem.
We have created a Graphics object, and then use DrawImage and use a bitmap from a car.jpg.
graphics = e.Graphics;
graphics.RotateTransform(angle);
graphics.DrawImage(car, xPos, yPos, car.Width, car.Height);
Then we wait for a key press e.g Right
case Keys.Right:
if (angle != 360)
{
angle += 10;
}
else
{
angle = 0;
}
this.Refresh();
break;
The problem we have is that the pivot point for the rotation is in the top left corner. So as soon as we move the car to something like (20,25) and start to rotate it, it will use (0,0) as the center of rotation. What we want to achieve is to have the center point of rotation at the center of our car.
We have tried looking for ways to change the centerX and centerY of the RotateTransform but have come to the conclusion that this isn't possible with the bitmap.
We have been struggling with this problem for over 2 days and can't seem to find any solution for achieving the thing we want.
Is there something we are doing wrong creating the Graphics object, or is there a totally different way to change centerX and centerY for the car?
To draw a rotated Bitmap you need to do a few steps to prepare the Graphics object:
first you move its origin onto the midpoint of the rotation
then you rotate by the desired angle
next you move it back
now you can draw the Bitmap
finally you reset the Graphics
This needs to be done for each bitmap.
Here are the steps in code to draw a Bitmap bmp at position (xPos, yPos):
float moveX = bmp.Width / 2f + xPos;
float moveY = bmp.Height / 2f+ xPosf;
e.Graphics.TranslateTransform(moveX , moveY );
e.Graphics.RotateTransform(angle);
e.Graphics.TranslateTransform(-moveX , -moveY );
e.Graphics.DrawImage(bmp, xPos, yPos);
e.Graphics.ResetTransform();
There is one possible complication: If your Bitmap has different dpi resolution than the screen i.e. than the Graphics you must first adapt the Bitmap's dpi setting!
To adapt the Bitmapto the usual 96dpi you can simply do a
bmp.SetResolution(96,96);
To be prepared for future retina-like displays you can create a class variable you set at startup:
int ScreenDpi = 96;
private void Form1_Load(object sender, EventArgs e)
{
using (Graphics G = this.CreateGraphics()) ScreenDpi = (int)G.DpiX;
}
and use it after loading the Bitmap:
bmp.SetResolution(ScreenDpi , ScreenDpi );
As usual the DrawImage method uses the top left corner of the Bitmap. You may need to use different Points for the rotation point and possibly also for the virtual position of your car, maybe in the middle of its front..
Here is static class which will paint the image in desired location within desired area. Change the rotationangle value to rotate the image. And you can also pan and zoom the image.
Add this class in your Project and call the static functions from Win Form.
public static class FullImage
{
public static Image image;
public static RectangleF DisplayRect, SourceRect;
public static Size ParentBoundry;
public static float rotationangle=0;
internal static void Paint(Graphics graphics)
{
if (image == null)
return;
float hw = DisplayRect.X + DisplayRect.Width / 2f;
float hh = DisplayRect.Y + DisplayRect.Height / 2f;
System.Drawing.Drawing2D.Matrix m = graphics.Transform;
m.RotateAt(rotationangle, new PointF(hw, hh), System.Drawing.Drawing2D.MatrixOrder.Append);
graphics.Transform = m;
graphics.DrawImage(image, new RectangleF(DisplayRect.X, DisplayRect.Y, DisplayRect.Width, DisplayRect.Height), SourceRect, GraphicsUnit.Pixel);
graphics.ResetTransform();
}
public static void LoadImage(Image img)
{
image = img;
SizeF s = GetResizedSize(image, ParentBoundry);
SourceRect = new RectangleF(0, 0, image.Width, image.Height);
DisplayRect = new RectangleF(ParentBoundry.Width / 2 - s.Width / 2, ParentBoundry.Height / 2 - s.Height / 2, s.Width, s.Height);
}
public static Size GetResizedSize(Image ImageToResize, Size size)
{
int sourceWidth = ImageToResize.Width;
int sourceHeight = ImageToResize.Height;
float nPercent = 0;
float nPercentW = 0;
float nPercentH = 0;
nPercentW = ((float)size.Width / (float)sourceWidth);
nPercentH = ((float)size.Height / (float)sourceHeight);
if (nPercentH < nPercentW)
nPercent = nPercentH;
else
nPercent = nPercentW;
int destWidth = (int)(sourceWidth * nPercent);
int destHeight = (int)(sourceHeight * nPercent);
return new Size(destWidth, destHeight);
}
internal static void MouseWheel(int delta)
{
if (delta > 0)
DisplayRect = ZoomImage(DisplayRect,CurrentMouse, .1f);
else
DisplayRect = ZoomImage(DisplayRect, CurrentMouse, -.1f);
}
private RectangleF ZoomImage(RectangleF ImageRectangle, PointF MouseLocation, float ScaleFactor)
{
/// Original Size and Location
SizeF OriginalSize = ImageRectangle.Size;
PointF OriginalPoint = ImageRectangle.Location;
///Mouse cursor location -located in width% and height% of totaloriginal image
float mouse_widthpercent = System.Math.Abs(OriginalPoint.X - MouseLocation.X) / OriginalSize.Width * 100;
float mouse_heightpercent = System.Math.Abs(OriginalPoint.Y - MouseLocation.Y) / OriginalSize.Height * 100;
///Zoomed Image by scalefactor
SizeF FinalSize = new SizeF(OriginalSize.Width + OriginalSize.Width * ScaleFactor, OriginalSize.Height + OriginalSize.Height * ScaleFactor);
if (FinalSize.Width < 15 || FinalSize.Height < 15)
return ImageRectangle;
if (FinalSize.Width > 60000 || FinalSize.Height > 60000)
return ImageRectangle;
/// How much width increases and height increases
float widhtincrease = FinalSize.Width - OriginalSize.Width;
float heightincrease = FinalSize.Height - OriginalSize.Height;
/// Adjusting Image location after zooming the image
PointF FinalLocation = new System.Drawing.PointF(OriginalPoint.X - widhtincrease * mouse_widthpercent / 100,
OriginalPoint.Y - heightincrease * mouse_heightpercent / 100);
ImageRectangle = new RectangleF(FinalLocation.X, FinalLocation.Y, FinalSize.Width, FinalSize.Height);
return ImageRectangle;
}
static bool drag = false;
static Point Initial, CurrentMouse;
internal static void MouseMove(Point location)
{
CurrentMouse = location;
if (drag)
{
DisplayRect = new RectangleF(DisplayRect.X + location.X - Initial.X, DisplayRect.Y + location.Y - Initial.Y, DisplayRect.Width, DisplayRect.Height);
Initial = location;
}
}
internal static void MouseDown(Point location)
{
Initial = location;
drag = true;
}
internal static void MouseUp(Point location)
{
drag = false;
}
}
After Adding this code in your project (Better add in separate cs file), Call the functions from Win Form class (Form1.cs).
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
FullImage.ParentBoundry = new Size(this.Width, this.Height);
// Enter the image path
FullImage.LoadImage(Image.FromFile(#"D:\a.jpg"));
}
//Create a paint event
private void Form1_Paint(object sender, PaintEventArgs e)
{
FullImage.Paint(e.Graphics);
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
Vault.FullImage.MouseDown(e.Location);
this.Invalidate();
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
Vault.FullImage.MouseMove(e.Location);
this.Invalidate();
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
Vault.FullImage.MouseUp(e.Location);
this.Invalidate();
}
protected override void OnMouseWheel(MouseEventArgs e)
{
Vault.FullImage.MouseWheel(e.Delta);
this.Invalidate();
}
Now, if you want to rotate the image, just set the value however you want (with slider, button or add some more functions to detect the mouse movement and then rotate)
Example: add a button and each time the button clicked increase the value by 1.
private void button1_clicked(object sender, EventArgs e)
{
FullImage.rotationangle++;
this.invalidate();
}
To rotate the top left from the center you first need to know the angle of it then adjust it by the angle you want and re-calculate the new top left by the new angle:
var newXPos = (int)(xPos + car.Width / 2.0 + Math.Cos(Math.Atan2(-car.Height / 2, -car.Width / 2)
+ angle / 180.0 * Math.PI) * -car.Width / 2.0);
var newYPos = (int)(yPos + car.Height / 2.0 + Math.Sin(Math.Atan2(-car.Height / 2, -car.Width / 2)
+ angle / 180.0 * Math.PI) * -car.Height / 2.0);
graphics = e.Graphics;
graphics.RotateTransform(angle);
graphics.DrawImage(car, newXPos, newYPos, car.Width, car.Height);
I am scratching my head to figure out a way to scale a signal on a 2D graphic pane. The story is: I connect my application to a microcontroller and on fixed intervals I read a data value (A voltage point). Now I want to draw this on my graphic pane. Example:
So up in the picture you see at time 0, the voltage is also 0 and this goes on and after 6 data points I will clear the pane and redo the whole stuff.
The question is, how can I translate this voltage into pixel values, having in mind I want the middle of the graphic pane to be my signals 0, just like a normal cartesian graph. Can someone please help me to figure out the scaling algorithm in this case?
Seems like simple math: just add the width/2 to all X coordinates which you are passing into drawing functions. Suppose you have an array of 6 points you can do the following:
var g = this.CreateGraphics();
var points = new Point[6]{new Point(0, 0), new Point(10, 10), new Point(30, 0), new Point(40,20), new Point(50, 0), new Point(60,30)};
for (int i = 0; i < points.Length-1; i++)
{
g.DrawLine(Pens.Black, points[i].X + Width / 2, Height / 2 - points[i].Y, points[i + 1].X + Width / 2, Height / 2 - points[i + 1].Y);
}
Alternatively you can invoke TranslateTransform function to move all further drawing to some amount by X and Y axes. Example:
var g = this.CreateGraphics();
var points = new Point[6]{new Point(0, 0), new Point(10, 10), new Point(30, 0), new Point(40,20), new Point(50, 0), new Point(60,30)};
g.TranslateTransform(Width / 2, 0, System.Drawing.Drawing2D.MatrixOrder.Append);
for (int i = 0; i < points.Length-1; i++)
{
g.DrawLine(Pens.Black, points[i].X, Height / 2 - points[i].Y, points[i + 1].X, Height / 2 - points[i + 1].Y);
}
Maybe this will be useful (remember that scale and translate functions are changing points in array):
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
var points = new PointF[6] { new PointF(0, 0), new PointF(30, 3), new PointF(90, 0), new PointF(190, 3.1f), new PointF(270, -0.5f), new PointF(360, 3.5f) };
float maxX = (from p in points select p).Max(t => t.X);
float maxY = (from p in points select p).Max(t => t.Y);
float xSizeToFit = pictureBox1.Width;
float ySizeToFit = pictureBox1.Height/2;
float scaleX = xSizeToFit / maxX;
float scaleY = ySizeToFit / maxY;
// scale to fit to given size
ScalePoints(points, scaleX, scaleY);
// translate to center
TranslatePoints(points, this.pictureBox1.Width / 2 - 0.5f * xSizeToFit, this.pictureBox1.Height / 2 + 0.5f * ySizeToFit);
DrawAxis(e.Graphics, this.pictureBox1.Size);
e.Graphics.DrawLines(Pens.Black, points);
}
private void TranslatePoints(PointF[] points, float transX, float transY)
{
for (int i = 0; i < points.Length; i++)
{
points[i].X += transX;
points[i].Y = transY - points[i].Y;
}
}
private void ScalePoints(PointF[] points, float scaleX, float scaleY)
{
for (int i = 0; i < points.Length; i++)
{
points[i].X *= scaleX;
points[i].Y *= scaleY;
}
}
public void DrawAxis(Graphics g, Size size)
{
//x
g.DrawLine(Pens.Black, 0, size.Height / 2, size.Width, size.Height / 2);
//y
g.DrawLine(Pens.Black, size.Width / 2, size.Height, size.Width / 2, 0);
}
private void pictureBox1_Resize(object sender, EventArgs e)
{
pictureBox1.Invalidate();
}
I'm writing an extended progress bar control at present, 100% open source and I've created some basic styles with gradients and solid colours.
One of the options I wanted to add was animation to the bar, prety much like the windows 7 and vista green progress bar. So I need to add a moving "Glow" to the % bar, however my attempt at this looks terrible.
My method is to draw an ellipse with set size and move it's x position until it reaches the end were the animation starts again.
First of does anyone have nay links or code to help me achieve the current windows 7 glow effect using GDI or some similar method?
I have several other animations that will also be added the the bar hence the GDI.
private void renderAnimation(PaintEventArgs e)
{
if (this.AnimType == animoptions.Halo)
{
Rectangle rec = e.ClipRectangle;
Rectangle glow = new Rectangle();
//SolidBrush brush = new SolidBrush(Color.FromArgb(100, Color.White));
//int offset = (int)(rec.Width * ((double)Value / Maximum)) - 4;
int offset = (int)(rec.Width / Maximum) * Value;
if (this.animxoffset > offset)
{
this.animxoffset = 0;
}
glow.Height = rec.Height - 4;
if (this.animxoffset + glow.X > offset)
{
glow.Width = offset - (this.animxoffset + 50);
}
else
{
glow.Width = 50;
}
glow.X = this.animxoffset;
LinearGradientBrush brush = new LinearGradientBrush(glow, Color.FromArgb(0, Color.White), Color.FromArgb(100, Color.White), LinearGradientMode.Horizontal);
e.Graphics.FillEllipse(brush, this.animxoffset, 2, glow.Width, glow.Height);
brush.Dispose();
string temp = offset.ToString();
e.Graphics.DrawString(temp + " : " + glow.X.ToString(), DefaultFont, Brushes.Black, 2, 2);
animTimer = new System.Timers.Timer();
animTimer.Interval = 10;
animTimer.Elapsed += new System.Timers.ElapsedEventHandler(t_Elapsed);
animTimer.Start();
}
}
void t_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
this.animTimer.Stop();
this.animxoffset += 2;
Invalidate();
}
This is just an example glow iterating through a pen array.
You could also use a transparent image (although it could have a performance impact).
Pen[] gradient = { new Pen(Color.FromArgb(255, 200, 200, 255)), new Pen(Color.FromArgb(150, 200, 200, 255)), new Pen(Color.FromArgb(100, 200, 200, 255)) };
int x = 20;
int y = 20;
int sizex = 200;
int sizey = 10;
int value = 25;
//draw progress bar basic outline (position - 1 to compensate for the outline)
e.Graphics.DrawRectangle(Pens.Black, new Rectangle(x-1, y-1, sizex, sizey));
//draw the percentage done
e.Graphics.FillRectangle(Brushes.AliceBlue, new Rectangle(x, y, (sizex/100)*value, sizey));
//to add the glow effect just add lines around the area you want to glow.
for (int i = 0; i < gradient.Length; i++)
{
e.Graphics.DrawRectangle(gradient[i], new Rectangle(x - (i + 1), y - (i + 1), (sizex / 100) * value + (2 * i), sizey + (2 * i)));
}