奇怪的问题。也许有人可以提供一些见解。
场景1 。我在内存中有一个写入的TBitmap,同时进行复杂的计算以计算每个像素的颜色。每隔一段时间(通常在填充位图的每条水平线之后)TBitmap都会被绘制到窗体上的图像(image1.Canvas.Draw(0,0,TBitmap)。大部分时间这都工作正常,但我注意到了如果每个位图行有很多缓慢的复杂计算(比如计算时间超过30秒或一分钟),那么主表单会有一个瞬间“闪烁”,以某种方式擦除位图,因此image.draw调用只绘制最新计算的行和前y行在位图中被消隐。我通过在计算之前锁定位图来解决这个问题。
场景2。这是主要的麻烦。我写的是TMemoryStream而不是位图。同样的交易。进行计算以计算每个像素值,然后在处理过程中使用memstream.Write(bytevalue,1)将每个像素值写入TMemoryStream。在所有计算结束时,我使用memstream.SaveToFile('whatever.bmp')将流保存到位图,然后使用memstream.Free释放流。如果计算速度很快,那么无论大小如何都可以保存流(我正在使用10000x10000尺寸进行测试)。
我甚至可以判断生成的文件是否会损坏,因为主应用程序窗口/窗体确实有轻微的闪烁,就像它被重新绘制一样。当发生这种情况时,就好像每个位图和TMemoryStream句柄都被杀死/刷新,因此现有数据已损坏。
有什么想法吗?这真的很糟糕。特别是当每个单个图像可能需要一个小时来创建时才发现它在后台发生了某些事情并且损坏了位图或TMemoryStream。
有没有什么方法可以像位图一样锁定TMemoryStream句柄?这可能有所帮助。或者一些声明告诉Delphi“不要弄乱我的对象,即使看起来应用程序花了太长时间”
或者有人知道Delphi中导致这种情况发生的后端原因。
TMemoryStream是在执行所有计算的过程内创建的,因此是本地对象。由于位图问题,位图是程序之外的全局变量而且它发生了,所以我认为这不是原因。
这也是在Windows 7下,但我注意到Vista下的原始位图问题。
更新1:
很抱歉没有使用这些评论,但是文字大小已经提交了......
回复雷米(和其他读这篇文章的人)......
单线程。对于内存流,如果计算速度很快,它可以在5000x5000分辨率下正常工作,但如果cals很慢则会失败。
作为基本框架,代码与
一致SetupMemorystream;
for y:=0 to height do
for x:=0 to width do
DoCalcs;
SetByteValue;
end;
end;
SaveStream;
如果DoCalcs相对快速,那么一切都按计划进行。如果它很慢,那么我得到TMemoryStream损坏,并且流保存到的结果位图已损坏。
这与使用内存TBitmap相同,直到我发现我可以锁定位图,这会阻止Delphi和/或Windows在“想要”时重新分配新句柄,这会破坏位图内的数据。
如果不认为TMemoryStream及其句柄没有发生同样的问题,这太巧合了。
更新2:
还有一点可能是有用的信息。
当TMemoryStream保存OK时,生成的文件(对于5000x5000位图)的大小为75,000,054字节。
当保存的流损坏时,它似乎是一个随机值(从句柄损坏到保存流的大小)。示例大小为22 MB和9 MB。
当我看到生成的文件是一个十六进制编辑器时,它显示文件的开头是正确的标题块,但尾部以某种方式被截断。
这太离奇了。无论如何,我可以绝对确保在SaveToFile调用之后和释放之前刷新TMemoryStream吗?
答案 0 :(得分:1)
当您释放用于写入的文件流时,不会检查关闭文件的调用是否有错误。因此,您的写入可能会失败,可能会刷新大文件的最后一个块,并且不会引发任何异常。
我最近遇到了这个问题:它出现在QC 80148中。但是你可以做很多事情,因为Windows CloseHandle函数不太可能返回任何错误。
答案 1 :(得分:1)
在将每个字节写入内存流之前,请将容量设置为比特流的近似大小,以使其不会经常重新调整内存大小。这会加快速度
我认为您必须从高度和宽度中减去1 for for循环
干杯
答案 2 :(得分:1)
感谢所有提示人员。代码中的循环确实有0到width-1。
问题是宽度和高度的变量是在调整主窗体大小时应用程序中其他位置更改的全局变量(它们通常跟踪屏幕上显示的图像,但我在此过程中使用了相同的变量来跟踪内存流位图的宽度和高度。所以当它们在环路外部被改变时,拧紧并损坏输出。跟踪问题的问题是什么。一旦我定位了宽度和高度变量,一切都按预期工作。
我应该知道这是我的错误而不是Delphi问题。
答案 3 :(得分:0)
通过假设正在发生的事情很难找到这样的错误。因为操作是如此长,所以如果发现错误就重复并等待是很昂贵的。
我建议你记录这个过程的每一步。你可能会得到一个巨大的日志,但它会告诉你哪里出了问题。重复这个过程并改进你的日志,这样你就可以慢慢但确定地找到问题的原因。
答案 4 :(得分:0)
您可以使用API调用FlushFileBuffers(filestream.Handle)
来刷新tFileStream,但我的猜测是,首先会发生其他事情导致损坏,这可能就像您的循环从0到宽度一样简单而不是超过1到宽度或0到宽度-1 ...很难说没有分析你的例程正在做什么来填充你的内存流。