I am currently making a tile-based game in c#, but every time I draw the tiles it uses a lot of CPU, and as the tiles get bigger(if i make the game full screen) it consumes even more.
This is my Tile class:
public class Tiles
{
//PRIVATE :
//variabiles
private int XPosition, YPosition;
private Image Texture;
private bool Colidable;
private int SizeW = 32;
private int SizeH = 32;
private Resizer resizer = new Resizer();
//methods
//PUBLIC :
//variabiles
//methods
//CONSTRUCTOR
public Tiles(int _x,int _y,Image _i, int _sW = 32, int _sH = 32, bool _c = false)
{
XPosition = _x;//set position X
YPosition = _y;//set position Y
SizeW = _sW;
SizeH = _sH;
Texture = resizer.ResizeImage(_i, SizeW, SizeH) ;// set texture
Colidable = _c;//set if the tile is colidable,default : false
resizer = null;
}
//DRAW METHOD
//gets graphics object to draw on, adn draws at the position of the tile
public void Draw(Graphics _g)
{
_g.DrawImage(this.Texture, this.XPosition, this.YPosition);
}
//GET PRIVATE MEBERS
//returns if the tile is colidable
public bool getColidable()
{
return this.Colidable;
}
}
and this is how I draw the tiles:
private void DrawMap(Graphics _g)
{
//CALLS THE DRAW METHOD OF EACH TILE
for (int i = 0; i < MAP_WIDTH; i++)
{
for (int j = 0; j < MAP_HEIGHT; j++)
{
Tile[i, j].Draw(_g);
}
}
}
bool TilesUpdate = false;
private void _Window_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(Color.Black);
if (isGameRunning)
{
DrawMap(e.Graphics);
}
else
{
FullRezolutionBtn.Draw(e.Graphics);
BigRezolutionBtn.Draw(e.Graphics);
NormalRezolutionBtn.Draw(e.Graphics);
}
}
private void Update_Tick(object sender, EventArgs e)
{
Invalidate();
}
I want to mention that the map is 20 x 20 tiles and it's cosuming around 50% of the cpu when it's fullscreen.
As I mentioned in the comments, the direction should be to do less painting. One way is to invalidate and paint portions of the drawing canvas only when something related to that portion changes. Windows itself does such optimization for controls/windows.
Here is an example. Look how Gadget class invalidates it's rectangle when some property changes. Then during the paint, only rectangles that intersect with e.ClipRectange are drawn. This highly reduces the number of the drawing operations.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Samples
{
class Gadget
{
public readonly Control Canvas;
public Gadget(Control canvas) { Canvas = canvas; }
private Rectangle bounds;
public Rectangle Bounds
{
get { return bounds; }
set
{
if (bounds == value) return;
// NOTE: Invalidate both old and new rectangle
Invalidate();
bounds = value;
Invalidate();
}
}
private Color color;
public Color Color
{
get { return color; }
set
{
if (color == value) return;
color = value;
Invalidate();
}
}
public void Invalidate()
{
Canvas.Invalidate(bounds);
}
public void Draw(Graphics g)
{
using (var brush = new SolidBrush(color))
g.FillRectangle(brush, bounds);
}
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var form = new Form { WindowState = FormWindowState.Maximized };
int rows = 9, cols = 9;
var gadgets = new Gadget[rows, cols];
var rg = new Random();
Color[] colors = { Color.Yellow, Color.Blue, Color.Red, Color.Green, Color.Magenta };
int size = 64;
var canvas = form;
for (int r = 0, y = 8; r < rows; r++, y += size)
for (int c = 0, x = 8; c < cols; c++, x += size)
gadgets[r, c] = new Gadget(canvas) { Color = colors[rg.Next(colors.Length)], Bounds = new Rectangle(x, y, size, size) };
int paintCount = 0, drawCount = 0;
canvas.Paint += (sender, e) =>
{
paintCount++;
for (int r = 0; r < rows; r++)
{
for (int c = 0; c < cols; c++)
{
if (e.ClipRectangle.IntersectsWith(gadgets[r, c].Bounds))
{
gadgets[r, c].Draw(e.Graphics);
drawCount++;
}
}
}
form.Text = $"Paint:{paintCount} Draw:{drawCount} of {(long)paintCount * rows * cols}";
};
var timer = new Timer { Interval = 100 };
timer.Tick += (sender, e) =>
{
gadgets[rg.Next(rows), rg.Next(cols)].Color = colors[rg.Next(colors.Length)];
};
timer.Start();
Application.Run(form);
}
}
}
Not sure how your resizer class works. i think there is problem when you re-size every image every time.
Texture = resizer.ResizeImage(_i, SizeW, SizeH) ;// set texture
i would replace above line like this
Texture = _i;// set texture but do not resize image now
at the same time update the Draw Function of Tile like below.
public void Draw(Graphics _g)
{
//now specify the location and size of the image.
_g.DrawImage(Texture , new Rectangle(this.XPosition, this.YPosition, SizeW, SizeH));
}
hopefully it should improve the performance.
if it flicker then you can use Double Buffer
Related
I am trying to make a program that will draw waves with any given parameters and I want to have them oscillate in real time. I have a checkbox in my form and in the CheckedChanged event that will toggle the drawing of the wave and when toggled it is supposed to start constantly drawing and redrawing the given wave using my DrawWave function until I untick the box. Below is the code for the DrawWave function and the event for ticking the checkbox.
private void DrawWave(object waveinput)
{
var wave = (Wave)waveinput;
Graphics graphicsobj = this.CreateGraphics();
Point[] graphPoints = new Point[waveSizeConstant];
myPen = new Pen(Color.Red, 3f);
for (int i = 0; i < waveSizeConstant; i++)
{
int displacement = Convert.ToInt32(Math.Round(wave.getDisplacementDegrees(i, timer.Elapsed.TotalSeconds)));
graphPoints[i] = new Point(i + 30, displacement + 250);
}
for (int i = 0; i < waveSizeConstant - 1; i++)
{
graphicsobj.DrawLine(myPen, graphPoints[i], graphPoints[i + 1]);
}
graphicsobj.Dispose();
}
private void ToggleWave_CheckedChanged(object sender, EventArgs e)
{
var graphicsThread = new Thread(new ParameterizedThreadStart(DrawWave));
if (ToggleWave.Checked)
{
while (true)
{
graphicsThread.Start(wave1);
Thread.Sleep(1000);
graphicsThread.Abort();
Invalidate();
}
}
else
{
graphicsThread.Abort();
Invalidate();
}
}
I'm trying to make sorting visualization algorithm in c# but I got a problem with the canvas refreshing.
I tried to refresh the canvas every time I redraw but its not looks good. I'm sure there is another way to do it and I hope someone can help me.
In this picture you can see the black rectangles that I want to delete from the canvas
This is my code :
private void GenerateArrayButton_Click(object sender, EventArgs e)
{
MyCanvas.Refresh();
Random random = new Random();
int xPosition = 0 , yPosition = MyCanvas.Height/2;
const int k_RectangleWight = 2;
for(int i = 0; i < k_SizeOfArray; i++)
{
int rectangleHeight = random.Next(MyCanvas.Height / 2);
m_UnsortedArray[i] = new Rectangle(xPosition,yPosition, k_RectangleWight, rectangleHeight);
xPosition += 5;
}
draw(m_UnsortedArray, Pens.Black);
}
private void draw(Rectangle[] i_ArrayToDraw, Pen i_PenColor)
{
var graphics = MyCanvas.CreateGraphics();
graphics.DrawRectangles(i_PenColor, i_ArrayToDraw);
graphics.Dispose();
}
private void SortingButton_Click(object sender, EventArgs e)
{
bubbleSort();
draw(m_UnsortedArray, Pens.Green);
}
private void bubbleSort()
{
for(int i = 0; i < m_UnsortedArray.Length; i++)
{
for(int j = 0; j < m_UnsortedArray.Length - 1; j++)
{
if(m_UnsortedArray[j].Height > m_UnsortedArray[j + 1].Height)
{
swap(ref m_UnsortedArray[j], ref m_UnsortedArray[j+1]);
}
}
draw(m_UnsortedArray,Pens.Black);
}
}
private void swap(ref Rectangle i_Rectangle1, ref Rectangle i_Rectangle2)
{
// Swap the position of the rectangle
var location = i_Rectangle1.Location;
i_Rectangle1.Location = i_Rectangle2.Location;
i_Rectangle2.Location = location;
// Swap the position of the current rectangle in the array
var copyRect = i_Rectangle1;
i_Rectangle1 = i_Rectangle2;
i_Rectangle2 = copyRect;
}
}
The drawing canvas in question MyCanvas whether its a PictureBox or a Panel or the Form itself, provides specific events for the painting routines, particularly the Paint event in this context. The event has a PaintEventArgs which provides a free Graphics object to do your drawings. Meaning, you don't need to create extra Graphics objects like in your draw method. Now let's draw those rectangles.
Class level fields:
using System.Threading.Tasks;
//...
public partial class Form1 : Form
{
private const int k_RectangleWight = 2;
private const int k_SizeOfArray = 100; //assign the right value.
private Rectangle[] m_UnsortedArray;
Random rand = new Random();
private Pen MyPen;
Handle the Paint event of the MyCanvas control.
public Form1()
{
InitializeComponent();
//You can add normal event handler instead if you prefer so.
MyCanvas.Paint += (s, e) =>
{
if (MyPen != null)
e.Graphics.DrawRectangles(MyPen, m_UnsortedArray);
};
}
In the GenerateArrayButton_Click event, create the rectangles, assign the drawing pen, and call the Invalidate() method of the drawing canvas.
private void GenerateArrayButton_Click(object sender, EventArgs e)
{
m_UnsortedArray = new Rectangle[k_SizeOfArray];
var xPosition = 0;
var yPosition = MyCanvas.Height / 2;
for(var i = 0; i < k_SizeOfArray; i++)
{
var rectangleHeight = rand.Next(MyCanvas.Height / 2);
m_UnsortedArray[i] = new Rectangle(
xPosition,
yPosition,
k_RectangleWight,
rectangleHeight);
xPosition += 5;
}
MyPen = Pens.Black;
MyCanvas.Invalidate();
}
At this point, you will get something drawn like this:
Now the second part. Your methods for swapping the rectangles:
private async void bubbleSort()
{
for (int i = 0; i < m_UnsortedArray.Length; i++)
{
for (int j = 0; j < m_UnsortedArray.Length - 1; j++)
if (m_UnsortedArray[j].Height > m_UnsortedArray[j + 1].Height)
swap(ref m_UnsortedArray[j], ref m_UnsortedArray[j + 1]);
await Task.Delay(30);
MyCanvas.Invalidate();
}
}
private void swap(ref Rectangle i_Rectangle1, ref Rectangle i_Rectangle2)
{
var location = i_Rectangle1.Location;
i_Rectangle1.Location = i_Rectangle2.Location;
i_Rectangle2.Location = location;
var copyRect = i_Rectangle1;
i_Rectangle1 = i_Rectangle2;
i_Rectangle2 = copyRect;
}
In the click event of the SortingButton, you just need to:
private void SortingButton_Click(object sender, EventArgs e)
{
MyPen = Pens.Green;
bubbleSort();
}
}
... and you will get:
I am trying to simulate a LED display board with c# . I need a control which contains 1536 clickable controls to simulate LEDs (96 in width and 16 in Height). I used a panel named pnlContainer for this and user will add 1536 tiny customized panels at runtime. These customized panels should change their color by click event at runtime. Everything works . But adding this number of tiny panels to the container takes long time ( about 10 secs). What is your suggestion to solve this issue? Any tips are appreciated.
this is my custome panel:
public partial class LedPanel : Panel
{
public LedPanel()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (this.BackColor == Color.Black)
{
this.BackColor = Color.Red;
}
else
{
this.BackColor = Color.Black;
}
}
}
}
and this is piece of code which adds tiny panels to the pnlContainer :
private void getPixels(Bitmap img2)
{
pnlContainer.Controls.Clear();
for (int i = 0; i < 96; i++)
{
for (int j = 0; j < 16; j++)
{
Custom_Controls.LedPanel led = new Custom_Controls.LedPanel();
led.Name = i.ToString() + j.ToString();
int lWidth = (int)(pnlContainer.Width / 96);
led.Left = i * lWidth;
led.Top = j * lWidth;
led.Width = led.Height = lWidth;
if (img2.GetPixel(i, j).R>numClear.Value)
{
led.BackColor = Color.Red;
}
else
{
led.BackColor = Color.Black;
}
led.BorderStyle = BorderStyle.FixedSingle;
pnlContainer.Controls.Add(led);
}
}
}
Is there any better approach or better control instead of panelto do this?
I agree with what #TaW recommends. Don't put 1000+ controls on a form. Use some sort of data structure, like an array to keep track of which LEDs need to be lit and then draw them in the Paint event of a Panel.
Here's an example. Put a Panel on a form and name it ledPanel. Then use code similar to the following. I just randomly set the values of the boolean array. You would need to set them appropriately in response to a click of the mouse. I didn't include that code, but basically you need to take the location of the mouse click, determine which array entry needs to be set (or unset) and then invalidate the panel so it will redraw itself.
public partial class Form1 : Form
{
//set these variables appropriately
int matrixWidth = 96;
int matrixHeight = 16;
//An array to hold which LEDs must be lit
bool[,] ledMatrix = null;
//Used to randomly populate the LED array
Random rnd = new Random();
public Form1()
{
InitializeComponent();
ledPanel.BackColor = Color.Black;
ledPanel.Resize += LedPanel_Resize;
//clear the array by initializing a new one
ledMatrix = new bool[matrixWidth, matrixHeight];
//Force the panel to repaint itself
ledPanel.Invalidate();
}
private void LedPanel_Resize(object sender, EventArgs e)
{
//If the panel resizes, then repaint.
ledPanel.Invalidate();
}
private void button1_Click(object sender, EventArgs e)
{
//clear the array by initializing a new one
ledMatrix = new bool[matrixWidth, matrixHeight];
//Randomly set 250 of the 'LEDs';
for (int i = 0; i < 250; i++)
{
ledMatrix[rnd.Next(0, matrixWidth), rnd.Next(0, matrixHeight)] = true;
}
//Make the panel repaint itself
ledPanel.Invalidate();
}
private void ledPanel_Paint(object sender, PaintEventArgs e)
{
//Calculate the width and height of each LED based on the panel width
//and height and allowing for a line between each LED
int cellWidth = (ledPanel.Width - 1) / (matrixWidth + 1);
int cellHeight = (ledPanel.Height - 1) / (matrixHeight + 1);
//Loop through the boolean array and draw a filled rectangle
//for each one that is set to true
for (int i = 0; i < matrixWidth; i++)
{
for (int j = 0; j < matrixHeight; j++)
{
if (ledMatrix != null)
{
//I created a custom brush here for the 'off' LEDs because none
//of the built in colors were dark enough for me. I created it
//in a using block because custom brushes need to be disposed.
using (var b = new SolidBrush(Color.FromArgb(64, 0, 0)))
{
//Determine which brush to use depending on if the LED is lit
Brush ledBrush = ledMatrix[i, j] ? Brushes.Red : b;
//Calculate the top left corner of the rectangle to draw
var x = (i * (cellWidth + 1)) + 1;
var y = (j * (cellHeight + 1) + 1);
//Draw a filled rectangle
e.Graphics.FillRectangle(ledBrush, x, y, cellWidth, cellHeight);
}
}
}
}
}
private void ledPanel_MouseUp(object sender, MouseEventArgs e)
{
//Get the cell width and height
int cellWidth = (ledPanel.Width - 1) / (matrixWidth + 1);
int cellHeight = (ledPanel.Height - 1) / (matrixHeight + 1);
//Calculate which LED needs to be turned on or off
int x = e.Location.X / (cellWidth + 1);
int y = e.Location.Y / (cellHeight + 1);
//Toggle that LED. If it's off, then turn it on and if it's on,
//turn it off
ledMatrix[x, y] = !ledMatrix[x, y];
//Force the panel to update itself.
ledPanel.Invalidate();
}
}
I'm sure there can be many improvements to this code, but it should give you an idea on how to do it.
#Chris and #user10112654 are right.
here is a code similar to #Chris but isolates the displaying logic in a separate class. (#Chris answered your question when I was writing the code :))))
just create a 2D array to initialize the class and pass it to the Initialize method.
public class LedDisplayer
{
public LedDisplayer(Control control)
{
_control = control;
_control.MouseDown += MouseDown;
_control.Paint += Control_Paint;
// width and height of your tiny boxes
_width = 5;
_height = 5;
// margin between tiny boxes
_margin = 1;
}
private readonly Control _control;
private readonly int _width;
private readonly int _height;
private readonly int _margin;
private bool[,] _values;
// call this method first of all to initialize the Displayer
public void Initialize(bool[,] values)
{
_values = values;
_control.Invalidate();
}
private void MouseDown(object sender, MouseEventArgs e)
{
var firstIndex = e.X / OuterWidth();
var secondIndex = e.Y / OuterHeight();
_values[firstIndex, secondIndex] = !_values[firstIndex, secondIndex];
_control.Invalidate(); // you can use other overloads of Invalidate method for the blink problem
}
private void Control_Paint(object sender, PaintEventArgs e)
{
if (_values == null)
return;
e.Graphics.Clear(_control.BackColor);
for (int i = 0; i < _values.GetLength(0); i++)
for (int j = 0; j < _values.GetLength(1); j++)
Rectangle(i, j).Paint(e.Graphics);
}
private RectangleInfo Rectangle(int firstIndex, int secondIndex)
{
var x = firstIndex * OuterWidth();
var y = secondIndex * OuterHeight();
var rectangle = new Rectangle(x, y, _width, _height);
if (_values[firstIndex, secondIndex])
return new RectangleInfo(rectangle, Brushes.Red);
return new RectangleInfo(rectangle, Brushes.Black);
}
private int OuterWidth()
{
return _width + _margin;
}
private int OuterHeight()
{
return _height + _margin;
}
}
public class RectangleInfo
{
public RectangleInfo(Rectangle rectangle, Brush brush)
{
Rectangle = rectangle;
Brush = brush;
}
public Rectangle Rectangle { get; }
public Brush Brush { get; }
public void Paint(Graphics graphics)
{
graphics.FillRectangle(Brush, Rectangle);
}
}
this is how it's used in the form:
private void button2_Click(object sender, EventArgs e)
{
// define the displayer class
var displayer = new LedDisplayer(panel1);
// define the array to initilize the displayer
var display = new bool[,]
{
{true, false, false, true },
{false, true, false, false },
{false, false, true, false },
{true, false, false, false }
};
// and finally
displayer.Initialize(display);
}
namespace ConwayGameofLife
{
public partial class GameController : Window
{
public int row;
public int col;
public SolidColorBrush brush;
public Rectangle[,] rectangle;
public GridCell cell;
public string[,] cellPosition;
public int cellColumn;
public int cellRow;
public GameController()
{
InitializeComponent();
brush = new SolidColorBrush(Colors.Gray);
}
private void GridSlider_Button(object sender, RoutedPropertyChangedEventArgs<double> e)
{
row = (int)gridSlider.Value;
col = (int)gridSlider.Value;
resizeGrid.DataContext = gridSlider;
}
private void BuildGrid_Button(object sender, RoutedEventArgs e)
{
gameUniformGrid.Children.Clear();
gameUniformGrid.Rows = row;
gameUniformGrid.Columns = col;
rectangle = new Rectangle[row, col];
for (int x = 0; x < row; x++)
{
for (int y = 0; y < col; y++)
{
rectangle[y, x] = new Rectangle { Stroke = Brushes.Black, Fill = brush };
gameUniformGrid.Children.Add(rectangle[y, x]);
}
}
//for (int i = 0; i < rectangle.Length; i++)
//{
// rectangle[i] = new Rectangle { Stroke = Brushes.Black, Fill = brush };
// gameUniformGrid.Children.Add(rectangle[i]);
//}
}
On the ToggleGrid function, I wanted to set cellColumn and cellRow variable to the value of the column and row of where the clicked rectangle is on a uniform grid and be able to change the rectangle's color using the index value of those variables. I did some research on ways I can go about this and couldn't find anything useful.
private void ToggleGrid(object sender, MouseButtonEventArgs e) //changes color
{
if (e.OriginalSource is Shape s)
{
cellColumn = (int)GetBoundingBox(s, gameUniformGrid).X;
cellRow = (int)GetBoundingBox(s, gameUniformGrid).Y;
rectangle[cellColumn, cellRow].Fill = new SolidColorBrush(Colors.Green);
//s.Fill = new SolidColorBrush(Colors.Green);
//cellPosition[y, x] = "Dead";
}
MessageBox.Show($"Column Clicked: {cellColumn}, Row Clicked: {cellRow}");
}
private static Rect GetBoundingBox(FrameworkElement element, FrameworkElement parent)
{
GeneralTransform transform = element.TransformToAncestor(parent);
Point topLeft = transform.Transform(new Point(0, 0));
Point bottomRight = transform.Transform(new Point(element.ActualWidth, element.ActualHeight));
return new Rect(topLeft, bottomRight);
}
}
though rectangles are placed into UniformGrid, it is possible to assign Grid.Row and Grid.Column properties for them:
rectangle[x, y] = new Rectangle { Stroke = Brushes.Black, Fill = brush };
rectangle[x, y].SetValue(Grid.RowProperty, x);
rectangle[x, y].SetValue(Grid.ColumnProperty, y);
gameUniformGrid.Children.Add(rectangle[x, y]);
then read them:
if (e.OriginalSource is Shape s)
{
cellColumn = (int)s.GetValue(Grid.ColumnProperty);
cellRow = (int)s.GetValue(Grid.RowProperty);
rectangle[cellRow, cellColumn].Fill = Brushes.Green;
}
I want to draw a loading circle (.gif) on the screen, while the applications is loading something big. But I can't run the circle as fast as I want, because I get memory issues. Does anyone know how to solve these (on 75 ms instead of 1000ms)? And how to remove the circle when done (it doesn't disappear any more).
EDIT: The load function is executed when the window size changes.
public Main()
{
InitializeComponent();
StartUp();
WindowState = FormWindowState.Maximized;
}
bool onrun;
bool done;
System.Threading.Timer timer;
GifImage Circle;
Point center;
void StartUp()
{
onrun = true;
done = false;
timer = new System.Threading.Timer(new System.Threading.TimerCallback(Animate));
timer.Change(0, 500);
}
void Animate(object sender)
{
if (onrun == true)
{
Circle = new GifImage("circleAnim.gif");
Circle.ReverseAtEnd = false;
int width = Screen.PrimaryScreen.Bounds.Width;
int height = Screen.PrimaryScreen.Bounds.Height;
center = new Point((width / 2) - 150, (height / 2) - 150);
onrun = false;
}
else if (done == true)
{
timer.Dispose();
}
Image i = Circle.GetNextFrame();
System.Drawing.Graphics GraphicsObject = Graphics.FromHwnd(IntPtr.Zero);
try
{
GraphicsObject.DrawImage(i, center);
i.Dispose();
GraphicsObject.Dispose();
}
catch { }
}
private void Load(object sender, EventArgs e)
{
int hduizend = 100000;
for (int i = 1; i <= 100000000; i++)
{
hduizend /= 2;
hduizend *= 2;
}
done = true;
}
EDIT2 (error):
"Attempted to read or write protected memory. This is often an indication that other memory is corrupt".
Also the gif image itself is not the problem:
public class GifImage
{
private Image gifImage;
private FrameDimension dimension;
private int frameCount;
private int currentFrame = -1;
private bool reverse;
private int step = 1;
public GifImage(string path)
{
gifImage = Image.FromFile(path);
dimension = new FrameDimension(gifImage.FrameDimensionsList[0]);
frameCount = gifImage.GetFrameCount(dimension);
}
public bool ReverseAtEnd
{
get { return reverse; }
set { reverse = value; }
}
public Image GetNextFrame()
{
currentFrame += step;
if (currentFrame >= frameCount || currentFrame < 1)
{
if (reverse)
{
step *= -1;
currentFrame += step;
}
else
currentFrame = 0;
}
return GetFrame(currentFrame);
}
public Image GetFrame(int index)
{
gifImage.SelectActiveFrame(dimension, index);
return (Image)gifImage.Clone();
}
}
assign the gif to a PictureBox and just show the Picturebox when needed. It will take care of the animation. You just need to take care of when it needs to be shown and of positioning it then.
private PictureBox pictureBox = new PictureBox();
Image animatedPicture = Image.FromFile(path);
...
int width = Screen.PrimaryScreen.Bounds.
int height = Screen.PrimaryScreen.Bounds.Height;
center = new Point((width / 2) - 150, (height / 2) - 150);
pictureBox.Location = center;
pictureBox.Visible = true;