【问题标题】:Jpeg save to base64 in TThreadJpeg 在 TThread 中保存到 base64
【发布时间】:2014-11-23 12:46:48
【问题描述】:

我对 Delphi 有一些问题。

我编写了两个简单的函数来制作屏幕截图,将其转换为 jpeg 并解码为 base64 流。 如果我在主流节目中制作它,它的效果很好。但是如果我创建一个 TThread 类并在 Execute 上启动这个函数,windows 会冻结,我只能重新启动我的电脑。

通过多次尝试,我发现通过程序JpegImg.SaveToStream(Input);挂起PC 如果我不将 Bitmap 转换为 jpeg,它的效果很好,我会得到图像字符串。

请帮忙。

这里有代码

procedure TEvReader.ScreenShot(DestBitmap : TBitmap) ;
var   DC : HDC;
begin   DC := GetDC (GetDesktopWindow) ;
  try
    DestBitmap.Width := GetDeviceCaps (DC, HORZRES) ;
    DestBitmap.Height := GetDeviceCaps (DC, VERTRES) ;
    BitBlt(DestBitmap.Canvas.Handle, 0, 0, DestBitmap.Width, DestBitmap.Height, DC, 0, 0, SRCCOPY) ;
  finally
    ReleaseDC (GetDesktopWindow, DC) ;
  end;
end;


function TEvReader.Base64FromBitmap(Bitmap: TBitmap): string;
var
  Input: TBytesStream;
  Output: TStringStream;
  JpegImg:TJPEGImage;
begin
  Input := TBytesStream.Create;
  try
    JpegImg:=TJPEGImage.Create;
    JpegImg.Assign(Bitmap);


    JpegImg.SaveToStream(Input); {here a problem.When i replace "JpegImg" to "Bitmap" all works good }
    Input.Position := 0;
    Output := TStringStream.Create('', TEncoding.ASCII);
    try
      Soap.EncdDecd.EncodeStream(Input, Output);
      Result := Output.DataString;
    finally
      Output.Free;
    end;
  finally
    Input.Free;
  end;
end;


procedure TOutThread.Execute;
var

bmp:TBitmap;
strrr:String;
begin

  bmp:=TBitmap.Create;
  mObj.ScreenShot(bmp);

  strrr := mObj.Base64FromBitmap(bmp);

  Form2.Memo4.Text := strrr;

end;

【问题讨论】:

  • 我猜,但这可能会有所帮助:qc.embarcadero.com/wc/qcmain.aspx?d=55871。位图不是线程安全的。你需要锁定/解锁它的画布。
  • @kobik 听起来好像他从多个线程访问同一个位图对象。或者 VCL 位图是否有一些疯狂的实现?
  • @DavidHeffernan TJPEGImage 搞砸了,它的 Bitmap.Canvas DC 存在问题,有时由于 graphics.pas 中的 GDI 对象缓存机制而被清除
  • @David,问题是从与主 UI 不同的线程访问位图 DC。除非被锁定,否则主 UI WndProc 将销毁 DC。
  • @kobik 这太奇怪了。我无法想象他们脑子里在想什么。有时我想知道 Embarcadero 的开发人员是否知道线程存在。我从来没有克服过Set8087CW 不是线程安全的事实,仍然不是线程安全的。

标签: delphi bitmap base64 tthread


【解决方案1】:

TJPEGImage 不是线程安全的。虽然http://qc.embarcadero.com/wc/qcmain.aspx?d=55871 中提到的线程安全绘图问题在 Delphi XE6 中得到了一定程度的修复(通过公开 Canvas 属性,您必须锁定自己),但在您的情况下,它可能无济于事。

您必须将 TJPEGImage 处理与主线程同步。

由于您从未发布过 JpgImg 和 Bmp 对象,因此在您的代码中也造成了一些内存泄漏。

试试下面的代码:

procedure TEvReader.ScreenShot(DestBitmap: TBitmap);
var
  DC: HDC;
begin
  DC := GetDC(GetDesktopWindow);
  DestBitmap.Canvas.Lock;
  try
    DestBitmap.Width := GetDeviceCaps(DC, HORZRES);
    DestBitmap.Height := GetDeviceCaps(DC, VERTRES);
    BitBlt(DestBitmap.Canvas.Handle, 0, 0, DestBitmap.Width, DestBitmap.Height, DC, 0, 0, SRCCOPY);
  finally
    DestBitmap.Canvas.Unlock;
    ReleaseDC(GetDesktopWindow, DC);
  end;
end;

function TEvReader.Base64FromBitmap(Bitmap: TBitmap): string;
var
  Input: TBytesStream;
  Output: TStringStream;
  JpegImg: TJPEGImage;
begin
  Input := TBytesStream.Create;
  try
    JpegImg := TJPEGImage.Create;
    try
      TThread.Synchronize(nil,
        procedure
        begin
          JpegImg.Assign(Bitmap);
          JpegImg.SaveToStream(Input);
        end);
    finally
      JpegImg.Free;
    end;
    Input.Position := 0;
    Output := TStringStream.Create('', TEncoding.ASCII);
    try
      Soap.EncdDecd.EncodeStream(Input, Output);
      Result := Output.DataString;
    finally
      Output.Free;
    end;
  finally
    Input.Free;
  end;
end;

procedure TOutThread.Execute;
var
  mObj: TEvReader;
  bmp: TBitmap;
  strrr: string;
begin
  mObj := TEvReader.Create;
  bmp := TBitmap.Create;
  try
    mObj.ScreenShot(bmp);
    strrr := mObj.Base64FromBitmap(bmp);
  finally
    bmp.Free;
    mObj.Free;
  end;

  Synchronize(nil,
    procedure
    begin
      Form2.Memo4.Text := strrr;
    end);
end;

【讨论】:

  • 必须有合适的第三方jpeg库
  • @DavidHeffernan 我曾经自己修补 TJPEGImage,但我只需要在线程中绘制 JPEG 并且该部分修补没有问题(当然,除非 Embarcadero 忘记包含重新编译所需的所有源文件,经常发生的事情)。
  • 最安全的方法是使用 GDI+ / CreateCompatibleDCCreateBitmap,如 here 所述,我也不确定是否同步 JpegImg.Assign 部分。但是在ScreenShot 中锁定/解锁DestBitmap.Canvas.Handle 是必不可少的。 +1
  • @kobik 谢谢,我已经更新了代码。 JpegImg.Assign 和 JpegImg.SaveToStream 都必须访问内部位图,这在不锁定其画布的情况下无法安全使用。分配可能在没有同步的情况下工作,但我将把这部分测试留给 OP
  • 只是我想写没有任何作用,但已经阅读了关于“锁定”和“解锁”的评论,并意识到我在您的代码中的“屏幕截图”过程中错过了这一点)现在我明白了问题出在哪里)一切正常)谢谢)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-20
  • 2020-02-22
  • 2014-03-15
  • 1970-01-01
相关资源
最近更新 更多