Delphi FMX正确加载图片最大限度减少内存占用(之一TBitmapSurface)
国庆前,无意间发现App内存占用陡增,发现是几张4K图片(7680x4320像素)加载引发的(TImage.Bitmap.LoadFromStream 或 TImage.Bitmap.LoadFromFile ,加载大图片时(>=6M),D10.4下吃内存大的惊人,共23M的3个图片文件,用上面的句法吃了接近400M内存),由研究图像处理的群友提醒:图片是压缩格式,显示的时候要解压缩,占用内存当然就大了;所有静态图片在显示的时候,都相当于是bmp位图。
仔细阅读了一下子Delphi的源码,小结了一下图片的正确加载方式,供大伙参考:
procedure TfmxMain.FormShow(Sender: TObject);
begin
{$IF DEFINED(IOS) or DEFINED(ANDROID) or DEFINED(MACOS)}
//每个窗口必须有的,否则打不开新窗口:
self.BorderStyle := TFmxFormBorderStyle.Sizeable;
{$ENDIF}
//创建主窗体并立即执行未来数据的异步下载:Main:
if not Assigned(fmxMainUI) then
fmxMainUI := TfmxMainUI.Create(Application);
Application.MainForm := fmxMainUI;//fmxMainUI;
FThread:=
TThread.CreateAnonymousThread( //:创建一个单线程完成ATask
procedure
var
LBitmapSurface1,LBitmapSurface2,LBitmapSurface3 :TBitmapSurface;
LBitmapCodec :TBitmapCodecManager;
LMaxSizeLimit: Cardinal;
LLoadedBitmapSurface1,LLoadedBitmapSurface2,LLoadedBitmapSurface3 :Boolean;
LmyFile1,LmyFile2,LmyFile3 :string;
begin
FEvent :=TEvent.Create;
//sleep(4000);//:模拟临时区主界面显示多长时间:
//:必须足够长顺便展示品牌,否则特别是MACOS反应不过来,
//:因为fmxMainUI产生时动态生成大量图片菜单及远程下载数据需要时间:
//if Image_grid01.Bitmap = nil then Image_grid01.Bitmap:=Image_grid01.MultiResBitmap.Items[0].Bitmap;
// .\图标\牵引官网巨幕背景图4k_470x320Android_Icons_天蓝1.png
//: 4320 x 7680 = 4k :设计时 + 美工1:1
LBitmapSurface1 :=TBitmapSurface.Create;//:创建客制化位图界面
LBitmapSurface2 :=TBitmapSurface.Create;
LBitmapSurface3 :=TBitmapSurface.Create;
LBitmapCodec :=TBitmapCodecManager.Create;
{$IFDEF POSIX}
{$IFDEF IOS}
LMaxSizeLimit :=FDisplayMetrics.PhysicalScreenSize.cx;
//:像素值:设备物理分辨率的短边值1080
if LMaxSizeLimit<=1080*1 then //1K
begin//:主流:
LmyFile1 :=SJGY.GetADeploymentFile('Product001_1k.png');
LmyFile2 :=SJGY.GetADeploymentFile('Product002_1k.png');
LmyFile3 :=SJGY.GetADeploymentFile('Product003_1k.png');
end;
{$ELSE}
{$IFDEF MACOS}
LMaxSizeLimit :=FDisplayMetrics.PhysicalScreenSize.cx;//:像素值:像素值:设备物理分辨率的短边值1920
//:即Screen.Size.cx;//dp像素密度值:1440;
if LMaxSizeLimit<=1920*1 then //1K:我的MAC:1440
begin//:主流:
LmyFile1 :=SJGY.GetADeploymentFile('Product001_1k.png');
LmyFile2 :=SJGY.GetADeploymentFile('Product002_1k.png');
LmyFile3 :=SJGY.GetADeploymentFile('Product003_1k.png');
end;
{$ENDIF MACOS}
{$ENDIF IOS}
{$IF Defined(LINUX) or Defined(ANDROID)}
LMaxSizeLimit :=FDisplayMetrics.PhysicalScreenSize.cx;
//:像素值:设备物理分辨率的短边值1080
if LMaxSizeLimit<=1080*1 then //1K
begin//:主流:
LmyFile1 :=SJGY.GetADeploymentFile('Product001_1k.png');
LmyFile2 :=SJGY.GetADeploymentFile('Product002_1k.png');
LmyFile3 :=SJGY.GetADeploymentFile('Product003_1k.png');
end;
{$ENDIF LINUX or ANDROID} //:LINUX或ANDROID结束
{$ENDIF POSIX} //:POSIX结束
{$IFDEF MSWINDOWS}//:MSWINDOWS要独立写:
LMaxSizeLimit :=FDisplayMetrics.PhysicalScreenSize.cx;
//:像素值:设备物理分辨率的短边值1920
//:MSWINDOWS下即Screen.Size.cx;//dp像素密度值:1920;
if (LMaxSizeLimit<=1920*1) then //1K
begin//:主流:
LmyFile1 :=SJGY.GetADeploymentFile('Product001_1k.png');
LmyFile2 :=SJGY.GetADeploymentFile('Product002_1k.png');
LmyFile3 :=SJGY.GetADeploymentFile('Product003_1k.png');
end;
if (LMaxSizeLimit<=1920*2) and (LMaxSizeLimit>1920*1) then //2K
begin//:已面世正在普及:
LmyFile1 :=SJGY.GetADeploymentFile('Product001_2k.png');
LmyFile2 :=SJGY.GetADeploymentFile('Product002_2k.png');
LmyFile3 :=SJGY.GetADeploymentFile('Product003_2k.png');
end;
if (LMaxSizeLimit<=1920*4) and (LMaxSizeLimit>1920*2) then //4K
begin//:截止2020年底实际中很少:
LmyFile1 :=SJGY.GetADeploymentFile('Product001_4k.png');
LmyFile2 :=SJGY.GetADeploymentFile('Product002_4k.png');
LmyFile3 :=SJGY.GetADeploymentFile('Product003_4k.png');
end;
{$ENDIF MSWINDOWS}
try
LLoadedBitmapSurface1:=
LBitmapCodec.LoadFromFile( LmyFile1,
LBitmapSurface1, LMaxSizeLimit );//Ceil(LayoutTab.Width)
LLoadedBitmapSurface2:=
LBitmapCodec.LoadFromFile( LmyFile2,
LBitmapSurface2, LMaxSizeLimit );//: LBitmapCodec :TBitmapCodecManager;
LLoadedBitmapSurface3:=
LBitmapCodec.LoadFromFile( LmyFile3,
LBitmapSurface3, LMaxSizeLimit );//:像素值:以设备物理分辨率的短边值自适应适配
while not (LLoadedBitmapSurface1 and LLoadedBitmapSurface2 and LLoadedBitmapSurface3) do sleep(0);
//:宽屏设备才需要deployment图片并调出图片显示出来
//:这样加载图片才是正确的最低限度占用内存的方式(23M的3个图片只占用1M左右内存):
//:错误加载图片的方式:
//Image01.Bitmap.LoadFromFile( SJGY.GetADeploymentFile('Product001_4k.png') );
//Image02.Bitmap.LoadFromStream( TFileStream.Create(
//SJGY.GetADeploymentFile('Product002_4k.png'),fmOpenRead) );
//:共23M的3个图片文件,上面这三句造成控件加载Bitmap时吃了接近400M内存:
//:另:在过去在长条度生成器实践中证实:Bitmap.LoadFromFile能加载的图片的最大像素尺寸:
//:取决于内存大小:通常PC端8G内存大约共能加载不超过30张1M左右的图片;手机端4G内存<10张类似图片
FEvent.SetEvent;
TThread.Synchronize(nil,
procedure
begin
if LLoadedBitmapSurface1 and LLoadedBitmapSurface2 and LLoadedBitmapSurface3 then
begin
Application.ProcessMessages;//:UI不阻塞
fmxMainUI.Image01.Bitmap.Assign( LBitmapSurface1 );
//:控件加载Bitmap到内存大小取决于BitmapSurface及其MaxSizeLimit像素尺寸:
Application.ProcessMessages;
fmxMainUI.Image02.Bitmap.Assign( LBitmapSurface2 );
Application.ProcessMessages;
fmxMainUI.Image03.Bitmap.Assign( LBitmapSurface3 );
//fmxMainUI.myTitleLabel.Text:='测试最大尺寸限制:'+LMaxSizeLimit.ToString;
//===========其实就取决于位图及设定它显示所需的LMaxSizeLimit:=1920(关键),
//===========要想图片显示精度越大,图片就要设计像素尺寸越大,内存所需的分配空间就越大,就越耗内存
//===========下面不能设置而取的是位图自身默认的设计时像素尺寸所以大得惊人MaxSizeLimit:
//fmxMainUI.Image01.Bitmap.LoadFromFile(LmyFile1);
Application.ProcessMessages;
end;
Application.ProcessMessages;
if not Application.HandleMessage then
fmxMainUI.Show;
if FEvent<>nil then
begin
FEvent.DisposeOf; if FEvent<>nil then FEvent:=nil;
end;
fmxMain.Close;
end);
finally
LBitmapSurface1.DisPoseOf;//:释放客制化位图界面
LBitmapSurface2.DisPoseOf;
LBitmapSurface3.DisPoseOf;
end;
end );//.Start;
FThread.Start;
end;
//:美工在设计图片时,
//:可将图片核心要表达的内容居中,周边用背景图或背景色表达,
//:以此来进行响应式的图片设计,
//:可将图片设计为4k(PNG24)输出,即满足了像素尺寸精度又减少了内存字节数,
//:4k: 1080*4=4320,1920*4=7680,
//:美工设计图片居中的核心内容的临界高度,需要控制好:
//: =TImage.Height=432, 768(:临界宽度,但实际宽度方向可以多设计内容,
// :超出窗体ClientWidth的部分被自动遮掩也无所谓)
//:二图片在容器中自动TAlignLayout.Fit; WrapMode:=TImageWrapMode.Center布局
{if LayoutTab.Visible=true then
begin //:FormCreate定义LayoutTab的可见性及图片是否加载:
//:宽屏设备才需要deployment图片并调出图片显示出来:
if self.ClientWidth <= 768 then
begin
LayoutTab.Width:=self.ClientWidth;
LayoutTab.Height:= //0.5625:高宽比、设备屏幕缩放比ScreenScale;
(FDisplayMetrics.RawScreenSize.cy * LayoutTab.Width)
/FDisplayMetrics.RawScreenSize.cx;
end;
end; //}
本博客相关博文:
1、《delphi XE关于Android四大组件之ContentProvider:案例delphi XE加载Android手机图片的效率》
https://blog.csdn.net/pulledup/article/details/105642380
https://blog.csdn.net/pulledup/article/details/108660481
3、《Delphi FMX正确加载图片最大限度减少内存占用(之二TImageList)》
https://blog.csdn.net/pulledup/article/details/108979086
喜欢的话,就在下面点个赞、收藏就好了,方便看下次的分享: