如何在后台加载图像(多线程)[AKA:TBitmap不是线程安全的] [英] How to load images from disk in background (multiple threads) [AKA: TBitmap is not thread-safe]

查看:167
本文介绍了如何在后台加载图像(多线程)[AKA:TBitmap不是线程安全的]的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想快速显示一些图像(jpg,png等)作为缩略图。因为解码和调整大小的过程是sloooow我要在一个或多个线程中执行。



但是,它看起来像使用TBitmap的画布,TJpeg不是多线程安全的。



在这种情况下,我的问题是:

1.如果没有完全重写GIF / PNG / BMP / JPG库,怎么办?

2.有人知道Embarcadero的Gif和Png库也不安全?

3.如果我使用Lock来锁定画布,那么调整大小部分访问画布并且占用大部分CPU周期,就不会破坏性能? p>




我发现这个麻烦我:

$ b $ David HARUCHE写道:这是不正确的。真正令人困惑的部分是
,即使是局部TBitmap也不是线程安全的,除非你锁定它们。
这是因为每个TBitmap注册到graphics.pas中的全局
BitmapCanvasList列表。当DC垃圾
收集FreeMemoryContexts()


http://www.codenewsfast.com/cnf/thread/0/permalink.thr-ng1908q2024

解决方案

使用GDI +与CreateCompatibleDC和CreateBitmap将覆盖许多图像格式,并避免画布线程问题。

这只是一个示例实现和可能会被修改。
GDI + API将需要三个单元,无需安装,例如可以从 http://www.progdigy获取。 com /

  unit ScaleImageThread; 
// 2013 Thomas Wassermann
界面
使用
Windows,消息,SysUtils,变体,类,图形,控件
,GDIPAPI,GDIPOBJ,StdCtrls;
键入
TScaleImageThread = Class(TThread)
FBMP:TBitMap;
FMemDC:HDC;
FMemBMP:HBitmap;
过程执行;覆盖;
private
Ffn:String;
FDestWidth,FDestHeight:Integer;
程序SyncFinished;
public
构造函数Create(aBitMap:TBitmap; const fn:String); overload;
属性BMP:TBitmap读取FBMP;
属性FileName:String读取Ffn;
结束;
实现
{TGDIThread}
过程ScaleOneImage(Const source:String; aHDC:HDC; DestWidth,DestHeight:Integer; Qual:Integer = 92; WithOutMargins:Boolean = false; BgColor:TColor = ClWhite; DoNotUpScale:Boolean = false);
var
图形: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)然后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),//目标矩形
0,0,/ /源矩形左上角
width,//源矩形宽度
height,//源矩形高度
UnitPixel);
image.Free;
graphics.Free;
结束

构造函数TScaleImageThread.Create(aBitMap:TBitmap; const fn:String);
begin
继承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;
结束


程序TScaleImageThread.Execute;
开始
继承;
ScaleOneImage(Ffn,FMemDC,FDestWidth,FDestHeight);
同步(SyncFinished);
结束

程序TScaleImageThread.SyncFinished;
begin
BitBlt(FBmp.Canvas.Handle,0,0,FBmp.Width,FBmp.Height,FMemDC,0,0,SRCCOPY);
DeleteObject(FMemBMP);
DeleteDC(FMemDC);
结束

结束。

实施测试

 使用ScaleImageThread; 
{$ R * .dfm}

程序TForm1.ThreadTerminate(发件人:TObject);
begin
Canvas.Draw(FX,FY,TGDIThread(Sender).BMP);
TGDIThread(Sender).BMP.Free;
FX:= FX + 70;
如果FX> 500然后
开始
FX:= 0;
FY:= FY + 70;
结束

end;

procedure TForm1.Button1Click(Sender:TObject);
const
C_DIM = 64;
var
i:整数;
函数GetNewBitMap:TBitMap;
begin
结果:= TBitMap.Create;
Result.Width:= C_DIM;
Result.Height:= C_DIM;
结束

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

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


end;


I want to quickly show some images (jpg, png, etc) as thumbnails. Because the decoding and resizing process is sloooow I to do it in one or more threads.

However, it looks like using the canvas of TBitmap and TJpeg is not multithreading-safe.

In this case, my question are:
1. How can this be done without fully rewriting the GIF/PNG/BMP/JPG library?
2. Does anybody know if Embarcadero's Gif and Png libs are also unsafe?
3. If I use Lock to lock the canvas wouldn't it ruin the performance since the resize part accesses the canvas and it takes most of the CPU cycles?


I have found this that troubles me:

David HAROUCHE wrote: That is not correct. The really confusing part is that even local TBitmap are not thread safe unless you lock them. This is because every TBitmap registers itself to the global BitmapCanvasList list in graphics.pas. And when the DC garbage collection FreeMemoryContexts()

http://www.codenewsfast.com/cnf/thread/0/permalink.thr-ng1908q2024

解决方案

Using GDI+ with CreateCompatibleDC and CreateBitmap will cover many image formats and avoid canvas thread problems.
This is only a sample implemetaion and might be modified. GDI+ API will need three units, no installation and can be got for example from http://www.progdigy.com/

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.

Test of Implementation

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;

这篇关于如何在后台加载图像(多线程)[AKA:TBitmap不是线程安全的]的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆