Show Frames of a Spritesheet - c#

I don't have much C# experience, however I am writing a program to assist in modding another Game. I wish to make a Form that allows you to preview frames of a Spritesheet. Each frame is 16x32 pixels and the sheet can be no wider than 64pixels but can be as long as 4096px. I have asked around with people I know and they have not been able to give me any place to start. I have no code as I am unsure how to even begin. It requires no Animation just static images.
EDIT: I am putting this on a WinForm. The small rectangle is all I need.

I appreciate everyone who answered quickly. I was pointed in the right direction and I managed to build this code:
First I made this:
public Rectangle GetTileArea(int tileIndex, int tileWidth, int tileHeight, int sheetWidthInTiles)
{
int x = tileIndex % sheetWidthInTiles;
int y = tileIndex / sheetWidthInTiles;
return new Rectangle(x * tileWidth, y * tileHeight, tileWidth, tileHeight);
}
Then all I had to do was:
private void seeFrame_Click(object sender, EventArgs e)
{
try
{
//Get Spritesheet
image1 = new Bitmap(Environment.CurrentDirectory + $"\\Export\\[CP]{systemName}\\assets\\img\\spritesheet.png");
Rectangle pixelAreaForTile = this.GetTileArea(tileIndex: (int)frameCount.Value, tileWidth: 16, tileHeight: 32, sheetWidthInTiles: 4); // Rectangle rect = new Rectangle(xmin, ymin, xmaximum, ymaximum);
Bitmap spriteFrame = image1.Clone(pixelAreaForTile, PixelFormat.DontCare);
pictureBox1.Image = spriteFrame;
}
catch (Exception ex)
{
}
}

Related

2d display objects rotating 360 degrees in circular (elipse) motion

I am new at stackoverflow so I hope I am not opening a question that has already been answered somewhere already.
I am trying to achieve this effect of displaying some objects exactly as seen on this video:
visual example
This example is from the game Binding of Isaac.
In order to do so I figured as much as I would have to define a ellipse or circle in c# so that I can place objects on the circle's periferi. I would do this by simply dividing my amount of display objects that we will call n with 360* from here I would simply create n points on the circle and make the display objects rotate along the circles periferi until they reach their destination point, in the meantime I just resize the scale as they move.
My question I need answered to start this though is how I should define the ellipse. I've found tutorials on drawing it but I don't really need the visual representation of the ellipse but rather the code that defines the ellipse so I can move these display objects in a circular/ellipse motion.
Thank you.
What you want is the perspective projection of a circle. This is achieved below by defining a circle on the XZ plane (rotation about Y) and viewing it from a vantage point translated in the Y axis a bit.
I was able to create a quick sample that mimics this behavior. I had to create a continuously updating form, although my approach was heavy-handed, I could have done it with a Timer.
I am using System.Numerics to generate the 3D geometry, and apply the rotation and view transformations.
Code
public partial class RunningForm1 : Form
{
float posAngle;
Queue<PointF> tail;
#region Windows API - User32.dll
[StructLayout(LayoutKind.Sequential)]
public struct WinMessage
{
public IntPtr hWnd;
public Message msg;
public IntPtr wParam;
public IntPtr lParam;
public uint time;
public System.Drawing.Point p;
}
[System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
[DllImport("User32.dll", CharSet = CharSet.Auto)]
static extern bool PeekMessage(out WinMessage msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);
#endregion
public RunningForm1()
{
InitializeComponent();
//Initialize the machine
posAngle = 0f;
tail = new Queue<PointF>();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
pictureBox1.Paint += pic_Paint;
pictureBox1.SizeChanged += pic_SizeChanged;
MainLoop();
}
void UpdateMachine()
{
posAngle += 0.002f;
pictureBox1.Refresh();
}
#region Main Loop
public void MainLoop()
{
// Hook the application's idle event
System.Windows.Forms.Application.Idle += new EventHandler(OnApplicationIdle);
//System.Windows.Forms.Application.Run(TrackForm);
}
private void OnApplicationIdle(object sender, EventArgs e)
{
while (AppStillIdle)
{
// Render a frame during idle time (no messages are waiting)
UpdateMachine();
}
}
private bool AppStillIdle
{
get
{
WinMessage msg;
return !PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
}
}
#endregion
private void pic_SizeChanged(object sender, EventArgs e)
{
pictureBox1.Refresh();
}
private void pic_Paint(object sender, PaintEventArgs e)
{
// Show FPS counter
// Draw the machine
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
var s = Math.Min(ClientSize.Width, ClientSize.Height) / 2f;
var view = Matrix4x4.CreatePerspective(1f, 1f, 1f, 10f);
view = Matrix4x4.Multiply(view, Matrix4x4.CreateTranslation(0, 0.24f, 0));
var q = Quaternion.CreateFromAxisAngle(Vector3.UnitY, posAngle);
var pts = new Vector3[] {
new Vector3(s/2, 0, 0),
new Vector3(s/2, -20f, 0),
new Vector3(s/2, -20f, -18f),
new Vector3(s/2, -20f, 18f),
new Vector3(s/2, -40f, 18f),
new Vector3(s/2, -40f, -18f),
new Vector3(s/2, -20f, -18f),
new Vector3(s/2, -20f, 0f),
};
for (int i = 0; i < pts.Length; i++)
{
pts[i] = Vector3.Transform(pts[i], q);
pts[i] = Vector3.Transform(pts[i], view);
}
e.Graphics.TranslateTransform(ClientSize.Width / 2f, ClientSize.Height / 2f);
using (var fill = new SolidBrush(Color.Gray))
{
foreach (var pt in tail.Reverse())
{
e.Graphics.FillEllipse(fill, pt.X - 4, pt.Y - 4, 8, 8);
fill.Color = Color.FromArgb(Math.Max(0, fill.Color.A-1), fill.Color);
}
}
var px = pts.Select((p) => new PointF(p.X, p.Y)).ToArray();
e.Graphics.FillPolygon(SystemBrushes.ActiveCaption, px);
e.Graphics.DrawPolygon(Pens.Black, px);
e.Graphics.FillEllipse(Brushes.Black, px[0].X - 4, px[0].Y - 4, 8, 8);
tail.Enqueue(px[0]);
while (tail.Count >= 255)
{
tail.Dequeue();
}
}
}
I added a tail object that draws a tail for a better motion effect.
Let center coordinate is (CX, CY), scene half-width is R, and vertical contraction (ratio of vertical elipse axis and horizontal one) is V. Number of objects is N
So you can calculate coordinates of object centers at the circle and make vertical contraction.
Angle = 2 * Math.Pi / N + Shift
X[i] = CX + R * Cos(Angle)
Y[i] = CY + V * R * Sin(Angle)
where i is object number and Shift is parameter of carousel rotation (in range 0..2*Pi)
Perhaps for exact positioning you have to add -ObjWidth/2, -ObjHeight/2 (if you drawing dunction takes left top corner rather than center).
Also to provide proper Z-order you have to draw "back" objects first
according to angle range.

C# Update bitmap in picturebox

I'm working on a screen sharing project ,and i recieve a small blocks of image from a Socket constantly and need to update them on a certain initial dekstop bitmap i have.
Basically i constantly read data from socket(data which is stored as jpeg image) ,using Image.FromStream() to retrieve the image and copying the recieved block pixels to the full primary bitmap(at a specific position X and Y which i also get from the socket)- that's how the initial image gets updated. But then comes the part where i need to display it on a Picturebox
I handle the Paint event and redrawing it all again-the entire inital image,which is pretty big(1920X1080 in my case).
This is my code:
private void MainScreenThread()
{
ReadData();//reading data from socket.
initial = bufferToJpeg();//first intial full screen image.
pictureBox1.Paint += pictureBox1_Paint;//activating the paint event.
while (true)
{
int pos = ReadData();
x = BlockX();//where to draw :X
y = BlockY();//where to draw :Y
Bitmap block = bufferToJpeg();//constantly reciving blocks.
Draw(block, new Point(x, y));//applying the changes-drawing the block on the big initial image.using native memcpy.
this.Invoke(new Action(() =>
{
pictureBox1.Refresh();//updaing the picturebox for seeing results.
// this.Text = ((pos / 1000).ToString() + "KB");
}));
}
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
lock (initial)
{
e.Graphics.DrawImage(initial, pictureBox1.ClientRectangle); //draws at picturebox's bounds
}
}
Because i'm aiming at high speed performance(it's kind of a real-time project) , i would like to know if there isn't any method to draw current recieved the block itself on the picturebox instead of drawing the whole initial bitmap again-which seems very inefficient to me...
This is my drawing method(works extremly fast, copying block with memcpy):
private unsafe void Draw(Bitmap bmp2, Point point)
{
lock (initial)
{
BitmapData bmData = initial.LockBits(new Rectangle(0, 0, initial.Width, initial.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, initial.PixelFormat);
BitmapData bmData2 = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width, bmp2.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp2.PixelFormat);
IntPtr scan0 = bmData.Scan0;
IntPtr scan02 = bmData2.Scan0;
int stride = bmData.Stride;
int stride2 = bmData2.Stride;
int Width = bmp2.Width;
int Height = bmp2.Height;
int X = point.X;
int Y = point.Y;
scan0 = IntPtr.Add(scan0, stride * Y + X * 3);//setting the pointer to the requested line
for (int y = 0; y < Height; y++)
{
memcpy(scan0, scan02 ,(UIntPtr)(Width * 3));//copy one line
scan02 = IntPtr.Add(scan02, stride2);//advance pointers
scan0 = IntPtr.Add(scan0, stride);//advance pointers//
}
initial.UnlockBits(bmData);
bmp2.UnlockBits(bmData2);
}
}
Here are some examples of a full primary bitmap,and other small blocks i'm getting and need to draw over the full one.
Full bitmap:
small block:
small block:
small block:
I'm getting large amount of small blocks per second(30~40) somtimes their bounds are really small(rectangle of 100X80 pixels for example) so redrawing the entire bitmap again is not necessary...Rapidly Refreshing a full screen image would kill the performance...
I hope my explaination was clear.
Looking forward for an answer.
Thanks.
It would be shame to leave that question without some answer. The following is about 10 times faster in my tests when updating small portions of the picture box. What it does basically is smart invalidating (invalidates just the updated portion of the bitmap, considering the scaling) and smart painting (draws only the invalidated portion of the picture box, taken from e.ClipRectangle and considering the scaling):
private Rectangle GetViewRect() { return pictureBox1.ClientRectangle; }
private void MainScreenThread()
{
ReadData();//reading data from socket.
initial = bufferToJpeg();//first intial full screen image.
pictureBox1.Paint += pictureBox1_Paint;//activating the paint event.
// The update action
Action<Rectangle> updateAction = imageRect =>
{
var viewRect = GetViewRect();
var scaleX = (float)viewRect.Width / initial.Width;
var scaleY = (float)viewRect.Height / initial.Height;
// Make sure the target rectangle includes the new block
var targetRect = Rectangle.FromLTRB(
(int)Math.Truncate(imageRect.X * scaleX),
(int)Math.Truncate(imageRect.Y * scaleY),
(int)Math.Ceiling(imageRect.Right * scaleX),
(int)Math.Ceiling(imageRect.Bottom * scaleY));
pictureBox1.Invalidate(targetRect);
pictureBox1.Update();
};
while (true)
{
int pos = ReadData();
x = BlockX();//where to draw :X
y = BlockY();//where to draw :Y
Bitmap block = bufferToJpeg();//constantly reciving blocks.
Draw(block, new Point(x, y));//applying the changes-drawing the block on the big initial image.using native memcpy.
// Invoke the update action, passing the updated block rectangle
this.Invoke(updateAction, new Rectangle(x, y, block.Width, block.Height));
}
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
lock (initial)
{
var viewRect = GetViewRect();
var scaleX = (float)initial.Width / viewRect.Width;
var scaleY = (float)initial.Height / viewRect.Height;
var targetRect = e.ClipRectangle;
var imageRect = new RectangleF(targetRect.X * scaleX, targetRect.Y * scaleY, targetRect.Width * scaleX, targetRect.Height * scaleY);
e.Graphics.DrawImage(initial, targetRect, imageRect, GraphicsUnit.Pixel);
}
}
The only kind of tricky part is determining the scaled rectangles, especially the one for invalidating, due to floating point to int conversions required, so we make sure it's eventually a little bigger than needed, but not less.
If you just need to draw on top of the canvas, you can draw the initial image just once and then use CreateGraphics() and DrawImage to update the content:
ReadData();
initial = bufferToJpeg();
pictureBox1.Image = initial;
var graphics = pictureBox1.CreateGraphics();
while (true)
{
int pos = ReadData();
Bitmap block = bufferToJpeg();
graphics.DrawImage(block, BlockX(), BlockY());
}
I'll update the answer with a performance comparison as I'm not convinced this will give any major benefit; it will, at least, avoid a double DrawImage though.

Draw fractal Fit inside panel using Winform?

i am trying to build a windows application in .net which draw fractal image inside the panel.It take end points of line as starting point of next line.But problem is, diagram is going outside of the panel.How do i fix drawing inside the panel
static int start_x, start_Y;
static int end_x, end_Y;
static int my_angle = 0;
static int my_length = 0;
private void Canvas_Paint(object sender, PaintEventArgs e)
{
start_x = Canvas.Width / 2;
start_Y = Canvas.Height / 2;
for (int i = 0; i < 400; i++)
{
draw_T();
}
}
public void draw_T()
{
Pen mypen = new Pen(Color.Green, 2F);
my_angle = my_angle + (45);
my_length = 100 + (1);
end_x = (int)(start_x + Math.Cos(my_angle * .0174539676) * my_length);
end_Y = (int)(start_Y + Math.Sin(my_angle * .0174539676) * my_length);
Point[] points =
{
new Point (start_x,start_Y),
new Point (end_x,end_Y)
};
Point[] points1 =
{
new Point ((end_x+start_x)/2,(end_Y+start_Y)/2),
new Point (end_x+50,end_Y-100)
};
start_x = end_x;
start_Y = end_Y;
Graphics g = Canvas.CreateGraphics();
g.DrawLines(mypen, points);
g.DrawLines(mypen, points1);
}
I'm not sure how you graphic is supposed to look but I can give you a couple of hints.
At general one first: Do make use of e.Graphics parameter! Change
public void draw_T()
To
public void draw_T(Graphics g)
and delete the line.
Graphics g = Canvas.CreateGraphics();
Change the call to
draw_T(e.Graphics);
You are leaking GDI resource by creating all those Graphcs with disposing of them and and lose time by creating them when you already have the one from the Paint event.
Next you should add a NumericUpDown for testing your algorithm and script it like this:
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
Canvas.Invalidate();
}
To work you now change the loop to
for (int i = 0; i < numericUpDown1.Value; i++)
And watch your graphics develop.
Another test could be to introduce a second pen color for the second series of point.
To play around further you could add another NumericUpDown and tie my_lengthto it..
In the end you'll see that length needs to be smaller than 101 or the Canvas needs to be as large as 700 pixels.
BTW: Neither my_angle nor my_length need to be declared at class level since they are always set in the method and used nowhere else and no other variable needs to static either, at least from what you show us..

How to make a rectangle move when PictureBox is resized

I have a PictureBox with a picture as a background of an application, having all the Anchors set, so it can resize with the form. On this PictureBox, I am creating many other things, for now only rectangles. I am creating them on some X and Y coordinates, that is fine. Adding a picture to show what I am trying to do. Created rectangle is actually the little light blue square.
But, when i resize the form, for example I maximize it, the rectangle stays at the same coordinates, which of course ar somewhere else at the moment (including only part of image to save space):
My question is - how can i make the rectangle "stick" with the same place as it is, during the resize? Note - they will have to move later, like every 2 seconds or so, so it cant be absolutely static.
EDIT:
here is some of the code creating the rectangle
private void button1_Click(object sender, EventArgs e)
{
spawn = "aircraft";
pictureBox1.Invalidate();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
switch (spawn)
{
case "aircraft":
Point[] points = new Point[2];
Point bod = new Point(750, 280);
points[0] = bod;
aircraft letadlo = new aircraft(605, 180, "KLM886", 180, e.Graphics);
aircrafts[0] = letadlo;
letadlo.points = points;
break;
...
public aircraft(int x, int y, string csign, int spd, Graphics g)
{
Pen p = new Pen(Color.Turquoise, 2);
Rectangle r = new Rectangle(x, y, 5, 5);
g.DrawRectangle(p, r);
p.Dispose();
One option could be to redraw the rectangle in new coordinates which are proportional to the PictureBox changed size.
For example:
oldX, oldY // old coordinates of the rectangle should be saved
oldPictureBoxWidth, oldPictureBoxHeight // should be saved too
//and on the PictureBox Paint event You have the new:
newPictureBoxWidth and newPictureBoxHeight
//the new coordinates of rectangle: (resize ratio is in brackets)
newX = oldX * (newPictureBoxWidth / oldPictureBoxWidth)
newY = oldY * (newPictureBoxHeight / oldPictureBoxHeight)
i think you have to calculate the % between the distance of your x and y from the top and the bottom , and if the form re-sized just use your % and draw again your rect !
for ex :
x = 100 the width is 200 so 100 is 1/2 so it 50% So if the form resized just calculate the new size and (newsize * 50 ) / 100
Hope that can help you .

How to draw the points( with floating value) over a bitmap picture?

I captured a video and took out a frame of that, converted it to Bitmap and now I can show it on picture box.
I have some float points which is the return values of GoodFeaturesToTrack() function from image class.
Now I want to draw/show those points/marks on different Xi,Yi over my picture;
How is it possible to do it? which command I have to use?
You could use builtin OpenCV functions to render around the feature points found, before you convert your image to a normal bitmap. This is also going to be much faster, as the image class will work with the raw memory rather than issue graphics calls.
Here's an (incomplete) example to illustrate the point. Note: you might need to adjust the calls to the CV signatures declared by your wrapper:
private int maxPointCount = 16;
private CvPoint2D32f[] points = new CvPoint2D32f[maxPointCount];
private CvImage grayImage = new CvImage(size, CvColorDepth.U8, CvChannels.One);
private CvImage eigenValues = new CvImage(size, CvColorDepth.F32, CvChannels.One);
private CvImage tempImage = new CvImage(size, CvColorDepth.F32, CvChannels.One);
public int FeatureRadius { get; set; }
private CvScalar featureColor;
public Color FeatureColor
{
get
{
return Color.FromArgb((byte)featureColor.Value2, (byte)featureColor.Value1, (byte)featureColor.Value0);
}
set
{
featureColor.Value0 = value.B;
featureColor.Value1 = value.G;
featureColor.Value2 = value.R;
}
}
public void Process(CvImage input, CvImage output)
{
CV.ConvertImage(input, grayImage);
CV.GoodFeaturesToTrack(grayImage, eigenValues, tempImage, points, ref maxPointCount, 0.01, 10, IntPtr.Zero, 3, 0, 0.04);
CV.Copy(input, output);
// This draws a circle around the feature points found
for (int i = 0; i < pointCount; i++)
CV.Circle(output, new CvPoint((int)points[i].X, (int)points[i].Y), FeatureRadius, featureColor);
}
Add a handler for the PictureBox.Paint event and do your drawing there. If you need to refresh the drawing call Invalidate() on your PictureBox control to redraw.
void PictureBox_Paint(object sender, PaintEventArgs e) {
// draw points from var pointsList = List<Point>
foreach (Point p in pointsList) {
e.Graphics.DrawEllipse(Pens.Yellow, p.X - 2, p.Y - 2, 4, 4);
}
}

Categories

Resources