图片在我尝试旋转 PictureBox 时消失。

huangapple go评论75阅读模式
英文:

Image disappears whenever I try to rotate a PictureBox

问题

我尝试在C# WinForms中旋转图像,每当我按键盘时。我尝试使用这篇文章 https://www.codeproject.com/Articles/58815/C-Image-PictureBox-Rotations 以及这个答案 https://stackoverflow.com/a/26455088/1907765 来实现这个目标,但是每当我应用变换时,图像就会消失。我不确定如何正确解决这个问题。

图像是在运行时从Resource.resx文件加载到一个PictureBox中的。PictureBox位于一个面板上。

这里列出了代码的相关部分:

// 这里是你的代码

当我启动代码时,图像会显示:

图片在我尝试旋转 PictureBox 时消失。

然后,如果我按下 "Q" 键,我已将其映射为逆时针旋转图像:

图片在我尝试旋转 PictureBox 时消失。

如果我尝试顺时针旋转,情况也是一样的。

我漏掉了什么,以使旋转后的图像显示在我的PictureBox中?

英文:

I'm trying to rotate an image in a C# winforms whenever I press the keyboard. I've attempted to use this article https://www.codeproject.com/Articles/58815/C-Image-PictureBox-Rotations, and this answer https://stackoverflow.com/a/26455088/1907765 to achieve this, but the image disappears whenever I apply the transform. I'm not exactly sure how to correct it.

The image is loaded from a Resource.resx file at run-time, into a PictureBox. The PictureBox sits on top of a panel.

Listing the relevant portion of the code here:

public partial class Form1 : Form
{
public List<PictureBox> Walls;
public bool MoveUp, MoveDown, MoveLeft, MoveRight;
public bool RotateClockwise, RotateAnticlockwise;
public bool PlayerHasShot;
public bool GameOver = false;
public int PlayerAngle = 0;
public Point PlayerPos = new Point(300, 300);
Bitmap PlayerImage;
// Game defaults
public readonly int BulletSpeed = 20;
public readonly int PlayerSpeed = 5;
public Form1()
{
InitializeComponent();
// <....>
}
private void Form1_Load(object sender, EventArgs e)
{
// Load the player image from bitmap
PbxPlayer.Parent = PnlGameBoard;
Image img = Properties.Resources.tankbase_orig;
PlayerImage = new Bitmap(img);
int dpi = 96;
using (Graphics G = PnlGameBoard.CreateGraphics())
dpi = (int)G.DpiX;
PlayerImage.SetResolution(dpi, dpi);
PbxPlayer.Image = (Bitmap)PlayerImage.Clone();
PbxPlayer.ClientSize = PlayerImage.Size;
}
private void MainTimer_Tick(object sender, EventArgs e)
{
Point newPoint = new Point();
if (MoveUp == true)
{
// Move player up 1 pixel until they've reached 'PlayerSpeed' or collided with something
for (int Y = 1; Y <= PlayerSpeed; Y++)
{
newPoint = new Point(PbxPlayer.Location.X, PbxPlayer.Location.Y - 1);
if (DetectCollision(PbxPlayer, newPoint))
break;
PbxPlayer.Location = newPoint;
}
}
if (MoveLeft == true)
{
// same logic as MoveUp
}
if (MoveDown == true)
{
// same logic as MoveUp
}
if (MoveRight == true)
{                    
// same logic as MoveUp
}
if (RotateAnticlockwise == true)
{
PlayerAngle -= 2;
if (PlayerAngle < 0)
PlayerAngle = 360;
// Debug label to check the angle is set correctly
LblAngle.Text = PlayerAngle.ToString();
// The amount to move the image by so that it rotates around centre point
PointF offsets = new PointF(PlayerImage.Width / 2f, PlayerImage.Height / 2f);
// Save old image
Image oldImage = PbxPlayer.Image;
// Return new image rotated by an angle of -2 degrees.
PbxPlayer.Image = RotateImage(PbxPlayer.Image, offsets, PbxPlayer.Location, -2);
// Force the picturebox to redraw (I think?)
PbxPlayer.Invalidate();
if (oldImage != null)
oldImage.Dispose();
}
if (RotateClockwise == true)
{
PlayerAngle += 2;
PlayerAngle %= 360;
LblAngle.Text = PlayerAngle.ToString();
PointF offsets = new PointF(PlayerImage.Width / 2f, PlayerImage.Height / 2f);
Image oldImage = PbxPlayer.Image;
PbxPlayer.Image = RotateImage(PbxPlayer.Image, offsets, PbxPlayer.Location, 2);
PbxPlayer.Invalidate();
if (oldImage != null)
oldImage.Dispose();
}
foreach (Control ctl in PnlGameBoard.Controls)
{
if (ctl is PictureBox && (string)ctl.Tag == "bullet")
{
ctl.Left += BulletSpeed;
if (ctl.Left > PnlGameBoard.Width || DetectCollision((PictureBox)ctl, ctl.Location))
{
RemoveBullet((PictureBox)ctl);
}
}
}
}
private void KeyIsDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.W || e.KeyCode == Keys.Up)
MoveUp = true;
if (e.KeyCode == Keys.A || e.KeyCode == Keys.Left)
MoveLeft = true;
if (e.KeyCode == Keys.S || e.KeyCode == Keys.Down)
MoveDown = true;
if (e.KeyCode == Keys.D || e.KeyCode == Keys.Right)
MoveRight = true;
if (e.KeyCode == Keys.Q || e.KeyCode == Keys.Oemcomma)
RotateAnticlockwise = true;
if (e.KeyCode == Keys.E || e.KeyCode == Keys.OemPeriod)
RotateClockwise = true;
if (e.KeyCode == Keys.Space && PlayerHasShot == false)
{
MakeBullet();
PlayerHasShot = true;
}
}
private void KeyIsUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.W || e.KeyCode == Keys.Up)
MoveUp = false;
if (e.KeyCode == Keys.A || e.KeyCode == Keys.Left)
MoveLeft = false;
if (e.KeyCode == Keys.S || e.KeyCode == Keys.Down)
MoveDown = false;
if (e.KeyCode == Keys.D || e.KeyCode == Keys.Right)
MoveRight = false;
if (e.KeyCode == Keys.Q || e.KeyCode == Keys.Oemcomma)
RotateAnticlockwise = false;
if (e.KeyCode == Keys.E || e.KeyCode == Keys.OemPeriod)
RotateClockwise = false;
// Stop repetitive shots
if (PlayerHasShot == true)
PlayerHasShot = false;
if (e.KeyCode == Keys.Enter && GameOver == true)
RestartGame();
}
private void PnlGameBoard_Paint(object sender, PaintEventArgs e)
{
// I feel like I should be doing something else in this event to make the image persist
e.Graphics.DrawImage(PbxPlayer.Image, PbxPlayer.Location);
}
public Bitmap RotateImage(Image image, PointF offset, Point imagePos, int angle)
{
// Credit: https://www.codeproject.com/Articles/58815/C-Image-PictureBox-Rotations
if (image == null)
return null;
Bitmap rotatedBitmap = new Bitmap(image.Width, image.Height);
rotatedBitmap.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (Graphics G = Graphics.FromImage(rotatedBitmap))
{
G.TranslateTransform(offset.X, offset.Y);
G.RotateTransform(angle);
G.TranslateTransform(-offset.X, -offset.Y);
G.DrawImage(image, new Point(imagePos.X, imagePos.Y));
}
return rotatedBitmap;
}
public void MakeBullet()
{
PictureBox bullet = new PictureBox();
bullet.BackColor = Color.DarkGray;
bullet.BorderStyle = BorderStyle.FixedSingle;
bullet.Height = 5;
bullet.Width = 10;
bullet.Left = PbxPlayer.Left + PbxPlayer.Width;
bullet.Top = PbxPlayer.Top + (PbxPlayer.Height / 2);
bullet.Tag = "bullet";
PnlGameBoard.Controls.Add(bullet);
}
public void RemoveBullet(PictureBox bullet)
{
Controls.Remove(bullet);
bullet.Dispose();
}
/// <summary>
/// Return true if 'pbx' PictureBox intersects with another PictureBox with the 'wall' tag
/// </summary>
/// <param name="pbx">The PictureBox we are checking for a collision</param>
/// <returns></returns>
public bool DetectCollision(PictureBox pbx, Point newPoint)
{
// Create a temporary PictureBox object to check for boundary collision without moving original pbx.
PictureBox newpbx = new PictureBox();
newpbx.Size = new Size(pbx.Width, pbx.Height);
newpbx.Location = new Point(newPoint.X, newPoint.Y);
foreach (var wall in Walls)
{
if (newpbx.Bounds.IntersectsWith(wall.Bounds))
return true;
}
// Release temporary object
newpbx.Dispose();
return false;
}
}

When I start the code, the image displays:

图片在我尝试旋转 PictureBox 时消失。

Then if I press "Q", which I've mapped to rotate the image anticlockwise:

图片在我尝试旋转 PictureBox 时消失。

It's the same if I try to rotate it clockwise.

What am I missing to make the rotated image appear in my PictureBox?

答案1

得分: 1

因为您将其绘制在传递给RotateImage方法的PbxPlayer.Location位置,所以图像会_消失_。

if (RotateAnticlockwise == true)
{
    // ...
    PbxPlayer.Image = RotateImage(PbxPlayer.Image, offsets, PbxPlayer.Location, -2);
    // ...
}

if (RotateClockwise == true)
{
    // ...
    PbxPlayer.Image = RotateImage(PbxPlayer.Image, offsets, PbxPlayer.Location, 2);
    // ...
}

当您偏移位置时,您将图像绘制在图像边界之外或绘图画布之外。如果您只想围绕中心旋转图像,位置必须等于Point.Empty

public Bitmap RotateImage(Image image, PointF offset, int angle)
{
    if (image == null) return null;

    Bitmap rotatedBitmap = new Bitmap(image.Width, image.Height);
    rotatedBitmap.SetResolution(image.HorizontalResolution, image.VerticalResolution);

    using (Graphics G = Graphics.FromImage(rotatedBitmap))
    {
        G.TranslateTransform(offset.X, offset.Y);
        G.RotateTransform(angle);
        G.TranslateTransform(-offset.X, -offset.Y);
        G.DrawImage(image, Point.Empty);
    }

    return rotatedBitmap;
}

说到旋转图像的_绘制画布_。顺便说一下,这是PbxPlayer,而不是PnlGameBoard。因此,在此处实现PnlGameBoardPaint事件是没有意义的,因为您在PictureBox中显示旋转并在定时器的Tick事件中移动它,其中您设置PbxPlayer.Location = newPoint;。因此,似乎不太可能希望在PnlGameBoard表面上绘制。


请注意,无需以这种方式为每个旋转角度创建图像。您只需要源图像,并将RotateImage例程移动到PictureBox.Paint事件中。

private readonly Image playerImage = Properties.Resources.SomeImage;
private int PlayerAngle = 0;
private bool RotateClockwise = true;

private void MainTimer_Tick(object sender, EventArgs e)
{
    PlayerAngle += RotateClockwise ? 2 : -2;
    PlayerAngle %= 360;
    PbxPlayer.Invalidate();
}

private void PbxPlayer_Paint(object sender, PaintEventArgs e)
{
    var g = e.Graphics;
    var src = (sender as Control).ClientRectangle;

    g.TranslateTransform(src.Width / 2, src.Height / 2);
    g.RotateTransform(PlayerAngle);
    g.TranslateTransform(-src.Width / 2, -src.Height / 2);
    g.DrawImage(playerImage, Point.Empty);
    g.ResetTransform();
}
英文:

The image disappears because you are drawing it at the PbxPlayer.Location that you pass to the RotateImage method.

if (RotateAnticlockwise == true)
{
    // ...
    PbxPlayer.Image = RotateImage(PbxPlayer.Image, offsets, PbxPlayer.Location, -2);
    // ...
}

if (RotateClockwise == true)
{
    // ...
    PbxPlayer.Image = RotateImage(PbxPlayer.Image, offsets, PbxPlayer.Location, 2);
    // ...
}

You are drawing the image outside the image bounds or the drawing canvas when you offset the location. The location must equal Point.Empty if you just want to rotate the image around the center.

public Bitmap RotateImage(Image image, PointF offset, int angle)
{
    if (image == null) return null;

    Bitmap rotatedBitmap = new Bitmap(image.Width, image.Height);
    rotatedBitmap.SetResolution(image.HorizontalResolution, image.VerticalResolution);

    using (Graphics G = Graphics.FromImage(rotatedBitmap))
    {
        G.TranslateTransform(offset.X, offset.Y);
        G.RotateTransform(angle);
        G.TranslateTransform(-offset.X, -offset.Y);
        G.DrawImage(image, Point.Empty);
    }

    return rotatedBitmap;
}

Speaking of the drawing canvas of the rotating image. It's PbxPlayer by the way not PnlGameBoard. So, implementing the Paint event of the latter doesn't make sense here because you are showing the rotation in the PictureBox and moving it in the timer's Tick event where you set PbxPlayer.Location = newPoint;. Hence, it doesn't seem likely that you want to draw on the PnlGameBoard surface.


Note, no need to keep creating images for each rotation angle this way. You just need the source image and move the RotateImage routine to the PictureBox.Paint event.

private readonly Image playerImage = Properties.Resources.SomeImage;
private int PlayerAngle = 0;
private bool RotateClockwise = true;

private void MainTimer_Tick(object sender, EventArgs e)
{
    PlayerAngle += RotateClockwise ? 2 : -2;
    PlayerAngle %= 360;
    PbxPlayer.Invalidate();
}

private void PbxPlayer_Paint(object sender, PaintEventArgs e)
{
    var g = e.Graphics;
    var src = (sender as Control).ClientRectangle;

    g.TranslateTransform(src.Width / 2, src.Height / 2);
    g.RotateTransform(PlayerAngle);
    g.TranslateTransform(-src.Width / 2, -src.Height / 2);
    g.DrawImage(playerImage, Point.Empty);
    g.ResetTransform();
}

huangapple
  • 本文由 发表于 2023年6月8日 15:53:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/76429730.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定