gpt4 book ai didi

c# - 为什么位图中的数据超出范围?

转载 作者:可可西里 更新时间:2023-11-01 13:34:34 25 4
gpt4 key购买 nike

我正在为 Skyrim 创建一个保存游戏管理器,但遇到了一个问题。当我自己创建一个 SaveGame 对象时,保存的位图部分工作正常。但是,当我在循环中调用该方法时,位图会呈现一个错误值,主要是类似于另一个保存游戏的值。

TL;DR - 为什么我的表单的列表框显示除了嵌入的图片之外的字符保存的正确信息?它似乎没有选择正确的图片,而是选择了最后处理的图片。该过程与通过打开的文件对话框选择时有何不同?


编辑:更新 - 我查看了与每个 SaveGame 对象一起存储的位图,发现在 scanDirectoryForSaves 中创建 SaveGames 的过程中不知何故弄乱了它。位图和使用字节指针是否存在我不知道的对象范围问题?


这是我保存游戏对象的静态工厂的代码:

public string Name { get; private set; }
public int SaveNumber { get; private set; }
public int PictureWidth { get; private set; }
public int PictureHeight { get; private set; }
public Bitmap Picture { get; private set; }
public DateTime SaveDate { get; private set; }
public string FileName { get; private set; }

public static SaveGame ReadSaveGame(string Filename)
{

SaveGame save = new SaveGame();
save.FileName = Filename;

byte[] file = File.ReadAllBytes(Filename);

int headerWidth = BitConverter.ToInt32(file, 13);
save.SaveNumber = BitConverter.ToInt32(file, 21);
short nameWidth = BitConverter.ToInt16(file, 25);

save.Name = System.Text.Encoding.UTF8.GetString(file, 27, nameWidth);
save.PictureWidth = BitConverter.ToInt32(file, 13 + headerWidth - 4);
save.PictureHeight = BitConverter.ToInt32(file, 13 + headerWidth);
save.readPictureData(file, 13 + headerWidth + 4, save.PictureWidth, save.PictureHeight);

save.SaveDate = DateTime.FromFileTime((long)BitConverter.ToUInt64(file, 13 + headerWidth - 12));

return save;
}

private void readPictureData(byte[] file, int startIndex, int width, int height)
{
IntPtr pointer = Marshal.UnsafeAddrOfPinnedArrayElement(file, startIndex);
Picture = new Bitmap(width, height, 3 * width, System.Drawing.Imaging.PixelFormat.Format24bppRgb, pointer);
}

在我的表单上,我使用一种方法读取特定目录中的所有保存文件,从中创建 SaveGame 对象,并根据角色名称将它们存储在字典中。

private Dictionary<string, List<SaveGame>> scanDirectoryForSaves(string directory)
{
Dictionary<string, List<SaveGame>> saves = new Dictionary<string, List<SaveGame>>();
DirectoryInfo info = new DirectoryInfo(directory);

foreach (FileInfo file in info.GetFiles())
{
if (file.Name.ToLower().EndsWith(".ess") || file.Name.ToLower().EndsWith(".bak"))
{
string filepath = String.Format(@"{0}\{1}", directory, file.Name);
SaveGame save = SaveGame.ReadSaveGame(filepath);

if (!saves.ContainsKey(save.Name))
{
saves.Add(save.Name, new List<SaveGame>());
}
saves[save.Name].Add(save);
}
}

foreach (List<SaveGame> saveList in saves.Values)
{
saveList.Sort();
}

return saves;
}

我将键添加到列表框中。当在列表框中选择一个名称时,该角色的最新保存将显示在表单上。每个角色的名称、日期和其他字段都是正确的,但位图是特定角色保存游戏图片的变体。

我在从打开的文件对话框和列表框中选择保存时调用相同的方法来更新表单字段。

private void updateLabels(SaveGame save)
{
nameLabel.Text = "Name: " + save.Name;
filenameLabel.Text = "File: " + save.FileName;
saveNumberLabel.Text = "Save Number: " + save.SaveNumber;

saveDateLabel.Text = "Save Date: " + save.SaveDate;

saveGamePictureBox.Image = save.Picture;
saveGamePictureBox.Image = ScaleImage(
saveGamePictureBox.Image, saveGamePictureBox.Width, saveGamePictureBox.Height);
saveGamePictureBox.Invalidate();
}

最佳答案

当您使用 constructor 创建一个 Bitmap 时采用 IntPtr 时,IntPtr 必须指向在 Bitmap 对象的生命周期内保持有效的内存块。您有责任确保内存块不会被移动或释放。

但是,您的代码正在传递一个指向 fileIntPtr,它是一个托管字节数组。因为在 ReadSaveGame 返回后没有引用 file,所以垃圾收集器可以自由回收内存并将其重新用于下一个文件。结果:损坏的位图。

尽管您可以通过使用 GCHandle 将数组固定在内存中来解决此问题。 , 让 Bitmap 管理它自己的内存可能更容易也更安全。首先创建一个空的 Bitmap,然后设置它的位:

private void readPictureData(byte[] file, int startIndex, int width, int height)
{
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
BitmapData data = bitmap.LockBits(
new Rectangle(0, 0, width, height),
ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
Marshal.Copy(file, startIndex, data.Scan0, width * height * 3);
bitmap.UnlockBits(data);
Picture = bitmap;
}

关于c# - 为什么位图中的数据超出范围?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14790645/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com