Delphi 如何在后台从磁盘加载图像(多线程)[AKA:TBitmap不是线程安全的]

Delphi 如何在后台从磁盘加载图像(多线程)[AKA:TBitmap不是线程安全的],delphi,Delphi,我想快速显示一些图像(jpg、png等)作为缩略图。因为解码和调整大小的过程很慢,我只能在一个或多个线程中完成 但是,使用TBitmap和TJpeg的画布不是多线程安全的 在这种情况下,我的问题是: 1.如何在不完全重写GIF/PNG/BMP/JPG库的情况下完成此操作? 2.有人知道Embarcadero的Gif和Png LIB是否也不安全吗? 3.如果我使用Lock来锁定画布,会不会破坏性能,因为resize部分访问画布,并且占用了大部分CPU周期 我发现这个问题困扰着我: 大卫·哈鲁什写

我想快速显示一些图像(jpg、png等)作为缩略图。因为解码和调整大小的过程很慢,我只能在一个或多个线程中完成

但是,使用TBitmap和TJpeg的画布不是多线程安全的

在这种情况下,我的问题是:
1.如何在不完全重写GIF/PNG/BMP/JPG库的情况下完成此操作?
2.有人知道Embarcadero的Gif和Png LIB是否也不安全吗?
3.如果我使用Lock来锁定画布,会不会破坏性能,因为resize部分访问画布,并且占用了大部分CPU周期


我发现这个问题困扰着我:

大卫·哈鲁什写道:这是不对的。真正令人困惑的部分 即使是本地TBitmap也不是线程安全的,除非您锁定它们。 这是因为每个TBitmap都将自己注册到全局 graphics.pas中的BitmapCanvasList列表。当DC垃圾 集合FreeMemoryContext()


将GDI+与CreateCompatibleDC和CreateBitmap一起使用将涵盖多种图像格式,并避免画布线程问题。
这只是一个示例,可能会被修改。 GDI+API将需要三个单元,无需安装,例如可以从


将线程中的图像数据加载到类中,但画布的绘制必须与主线程同步,因为画布也不是线程安全的;o) 另见:。我想我不太明白你在说什么。您是否能够演示您遇到的特定问题。“线程安全”一词本身毫无意义。您必须指定您使用的线程模型。看,我想不需要GDI+了。普通的老GDI就可以了。也许有人可以解释这里的实际问题是什么。你是对的,你可以使用旧的GDI进行threadsave,但在简单使用不同的图像格式时会遇到问题,缩放质量会很差。使用GDI+的努力只是在项目中添加3个免费单元。TLAMA解决方案中的问题可以通过添加到他的线程中的注释进行简单的测试。对于重新缩放,我会考虑使用GraceS32。我还是不明白这个问题。问题没有说清楚。@bummi,我已经下载了,但找不到单位:
EXGDIPAPI,EXGDIPOBJ
。这些单位是否包含在该软件包中?@kobik抱歉,我做了编辑,只是删除了声明中的“ex”,我使用的是扩展单位,但忘记了删除。
unit ScaleImageThread;
// 2013 Thomas Wassermann
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls
  ,GDIPAPI, GDIPOBJ, StdCtrls;
Type
  TScaleImageThread=Class(TThread)
    FBMP:TBitMap;
    FMemDC:HDC;
    FMemBMP:HBitmap;
    Procedure Execute;Override;
  private
    Ffn:String;
    FDestWidth,FDestHeight:Integer;
    procedure SyncFinished;
    Public
    Constructor Create(aBitMap:TBitmap;const fn:String);overload;
    property BMP:TBitmap read FBMP;
    Property FileName:String read Ffn;
  End;
implementation
{ TGDIThread }
Procedure ScaleOneImage(Const source:String;aHDC:HDC;DestWidth,DestHeight:Integer;Qual:Integer=92;WithOutMargins:Boolean=false;BgColor:TColor=ClWhite;DoNotUpScale:Boolean=false);
var
  graphics : TGPGraphics;
  image: TGPImage;
  width, height: UINT;
  faktor:Double;
  destx,desty:Double;
  rct:TGPRectF;
  Ext:String;
begin

  image:= TGPImage.Create(source);
  width  := image.GetWidth;
  height := image.GetHeight;


    if (DestWidth / width) < (DestHeight/Height) then faktor  := (DestWidth / width) else faktor:= (DestHeight/Height);
    destx :=  (DestWidth - faktor * width) / 2;
    desty :=  (DestHeight - faktor * Height) / 2;
    graphics := TGPGraphics.Create(aHDC);
    graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic);

    graphics.DrawImage(
      image,
      MakeRect(destx,  desty , faktor * width, faktor * height),  // destination rectangle
      0, 0,        // upper-left corner of source rectangle
      width,       // width of source rectangle
      height,      // height of source rectangle
      UnitPixel);
    image.Free;
    graphics.Free;
end;

constructor TScaleImageThread.Create(aBitMap: TBitmap;const fn:String);
begin
  inherited create(False);
  Ffn :=fn;
  FreeOnTerminate := true;
  FBmp := aBitMap;
  FMemDC := CreateCompatibleDC(FBmp.Canvas.Handle);
  FMemBMP := CreateBitmap(FBmp.Width ,FBmp.Height ,1,GetDeviceCaps(FBmp.Canvas.Handle, BITSPIXEL),nil);
  SelectObject(FMemDC, FMemBMP);
  FDestWidth :=FBMP.Width;
  FDestHeight:=FBMP.Height;
end;


procedure TScaleImageThread.Execute;
begin
  inherited;
  ScaleOneImage(Ffn,FMemDC,FDestWidth,FDestHeight);
  Synchronize(SyncFinished);
end;

procedure TScaleImageThread.SyncFinished;
begin
 BitBlt(FBmp.Canvas.Handle, 0, 0, FBmp.Width, FBmp.Height, FMemDC, 0, 0, SRCCOPY);
 DeleteObject(FMemBMP);
 DeleteDC (FMemDC);
end;

end.
uses ScaleImageThread;
{$R *.dfm}

procedure TForm1.ThreadTerminate(Sender: TObject);
begin
  Canvas.Draw(FX, FY, TGDIThread(Sender).BMP);
  TGDIThread(Sender).BMP.Free;
  FX := FX + 70;
  if FX > 500 then
    begin
    FX := 0;
    FY := FY + 70;
    end;

end;

procedure TForm1.Button1Click(Sender: TObject);
const
  C_DIM = 64;
var
  i: Integer;
  Function GetNewBitMap: TBitMap;
  begin
    Result := TBitMap.Create;
    Result.Width := C_DIM;
    Result.Height := C_DIM;
  end;

begin
  ReportMemoryLeaksOnShutDown := true;
  for i := 1 to 10 do
    With TGDIThread.Create(GetNewBitMap,
      'C:\temp\bild ' + intToStr(i) + '.png') do
      OnTerminate := ThreadTerminate;

  for i := 1 to 10 do
    With TGDIThread.Create(GetNewBitMap,
      'C:\Bilder\Kids' + intToStr(i) + '.jpg') do
      OnTerminate := ThreadTerminate;


end;