5

奇怪的问题。也许有人可以提供一些见解。

  • 情景 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 “不要弄乱我的对象,即使看起来应用程序花费的时间太长”

或者有谁知道导致这种情况发生的德尔福内部的后端原因。

TMemoryStream 是在执行所有计算的过程中创建的,因此是本地对象。对于位图问题,位图是过程之外的全局变量并且它发生了,所以我认为这不是原因。

这也是在 Windows 7 下,但我注意到 Vista 下的原始位图问题。

更新1:

很抱歉没有使用评论,但是对文本大小有限制...

作为对雷米(以及其他阅读本文的人)的回复……

单线程。对于内存流,如果计算速度很快,它在 5000x5000 分辨率下工作得很好,但如果计算速度很慢,它就会失败。

作为一个基本框架,代码是沿着

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 保存正常时,生成的文件(对于 5000x5000 位图)大小为 75,000,054 字节。

当保存的流损坏时,它似乎是一个随机值(从句柄损坏到流被保存的大小)。示例大小为 22 MB 和 9 MB。

当我查看生成的文件是一个十六进制编辑器时,它表明文件的开头与标题块是正确的,但尾部会以某种方式被截断。

这太奇怪了。无论如何,我绝对可以肯定在 SaveToFile 调用之后和释放它之前刷新一个 TMemoryStream?

4

5 回答 5

1

当您释放用于写入的文件流时,不会检查关闭文件的调用是否存在错误。因此,您的写入可能会失败,可能是在刷新大文件的最后一个块时,并且不会引发异常。

我最近受到了打击:它在 QC 80148 中。但是您对此无能为力,因为 Windows CloseHandle 函数也不太可能返回任何错误。

于 2009-12-15T16:24:27.993 回答
1
  1. 在将每个字节写入内存流之前,将容量设置为比特流的近似大小,这样它就不会经常重新调整内存大小。这将加快速度

  2. 我认为你必须在 for 循环中从高度和宽度中减去 1

干杯

于 2009-12-15T16:44:18.923 回答
1

感谢所有的提示家伙。循环在代码中确实有正确的 0 到 width-1。

问题是宽度和高度的变量是全局变量,当主窗体调整大小时,它们在应用程序的其他地方发生了变化(它们通常跟踪屏幕上显示的图像,但我在此过程中使用了相同的变量来跟踪内存流位图宽度和高度。因此,当它们在循环之外进行更改时,就会搞砸并破坏输出。要追踪的问题真是个麻烦。一旦我本地化了宽度和高度变量,一切都按预期工作。

我应该知道这是我的错误,而不是 Delphi 问题。

于 2009-12-15T23:11:56.533 回答
0

仅仅假设正在发生的事情很难找到这样的错误。因为这个操作太长了,如果发现错误,重复并等待是很昂贵的。

我建议您记录该过程的每个步骤。你可能会得到一个巨大的日志,但它会告诉你哪里出了问题。重复这个过程并改进你的日志,这样你会慢慢地但肯定会找到问题的原因。

于 2009-12-15T07:15:14.053 回答
0

您可以使用 API 调用FlushFileBuffers(filestream.Handle)来刷新 tFileStream,但我的猜测是,首先发生了其他事情会导致损坏,这可能就像您的循环从 0 到宽度而不是 1 到宽度或 0 到宽度一样简单-1 ...如果不分析您的例程正在做什么来填充您的内存流,就很难说。

于 2009-12-15T17:05:09.787 回答