I have been doing mobile development with Adobe AIR and one of the features that it has is the ability to cache/export/convert an object as a bitmap which greatly increases it’s rendering speed on mobile. I wanted to bring this same functionality over to Delphi XE5 Firemonkey and apply it to my Android and IOS apps. It should also now work in Delphi XE6 Firemonkey and appMethod. I had a developer on oDesk create the TImageCacheLayout component for this purpose. I designed the component, he built it, and then I added some additional functionality. It cost me $600 to have this component built via oDesk. I’d like to see this type of functionality included in Delphi by Embarcadero. Below is the information I wrote up explaining the component and it’s usage. This text is included with the component and you can download and use the component for free at the end of this post.
The purpose of the TImageCacheLayout component is to cache the visible display of a TLayout and it’s child controls as a bitmap to increase rendering speed.
Usage examples:
- Use TRectable to create objects in Delphi and then animate them at runtime. TImageCacheLayout will automatically turn these objects into a bitmap.
- Put 5 different TImage components within a TImageCacheLayout. It will combine them into a single bitmap at design time or runtime.
- Have a group components that are off of the screen inside of a TVertScrollBox. You could use ctCacheAsBitmap to only display them when they become visible.
- A TTabControl with three tabs and you want to animate the transition between tabs. Set ctCacheAsBitmap to true on all of the non active tabs and only set each one to ctNone after that tab becomes visible.
- Combine this component with the Flappy Bird Clone Source for increased speed.
Warnings:
- May not work with components like TWebBrowser or native wrappers of MapView and WebView on IOS and Android.
- The trade off with this component is that you will probably using more memory because the bitmap will exist in memory.
- On IOS you could turn all your ctCacheAsBitmap settings to ctNone if you receive a Low Memory warning.
Benchmarks:
- The demo projects showed a rendering speed increase of 66% on an Android device when using the ctCacheAsBitmap setting over ctNone.
- ctNone (normal rendering) took 30 ms.
- ctCacheAsBitmap (cached bitmap) took 10ms.
- Try out the control and see what settings work best for the best performance in each situation. Have a rendering bottleneck? Try using this control to fix it.
Design time:
There are two different design time functionalities implemented in the right click menu of TImageCacheLayout. They are “Update Cache Image” and “Convert to TImage Bitmap”.
Update Cache Image:
- You can store a screenshot of the TImageCacheLayout at design time to increase speed at runtime.
- It will not have to do the initial paint if you use ctCacheAsBitmap or ctExportAsBitmap.
- The screen ratio is different on different devices. You can experience image stretching with this option.
- Your deployment file size will increase with this option.
- You can use ctCacheAsBitmap and ctExportAsBitmap without caching an image at design time.
Convert to TImage Bitmap:
- Will take a screenshot and convert your TImageCacheLayout item into a bitmap at design time.
- You will object contents of your TImageCacheLayout and it will be replace by a TImage. You can no longer edit the contents.
- Your deployment file size will increase with this option.
Runtime:
- There are three different image cache types (TImageCacheType) which are ctNone, ctCacheAsBitmap, and ctExportAsBitmap.
- There are four different ways in which the cache (TImageCacheUpdate) will automatically update itself which are cuMouse, cuTouch, cuTouchDelay, and cuManual.
- There are three different post processing effects (TImageCacheEffect) which an be applied after the bitmap has been created which are cqNormal, cqNoAlphaEdge, and cqSharpen.
TImageCacheType:
- ctNone:
-Operates as a normal TLayout. - ctCacheAsBitmap:
– The contents of TImageCacheLayout layout are stored in the CacheImage property the first time it is drawn.
– The controls contained within the TImageCacheLayout are Visible:=False when this setting is in effect.
– The controls still exist and can be accessed and updated using the TImageCacheUpdate option. - ctExportAsBitmap:
– The contents of TImageCacheLayout layout are stored in the CacheImage property the first time it is drawn.
– The controls contained within the TImageCacheLayout removed once it is cached.
– The controls no longer exist and can not be accessed.
TImageCacheUpdate:
- cuMouse:
– TImageCacheType.ctCacheAsBitmap is required.
– The MouseDown, MouseMove, MouseLeave, and MouseUp events are hooked and allow controls to be interacted with.
– Once the interaction of the mouse with the controls contained within the TImageCacheLayout is complete it will automatically re-cache itself.
– Suitable for use on devices with a mouse cursor. - cuTouch:
– TImageCacheType.ctCacheAsBitmap is required.
– The MouseDown, MouseMove, and MouseUp events are hooked and allow controls to be interacted with.
– TImageCacheLayout will automatically re-cache itself on the MouseUp event.
– More suitable for using with graphic only or single interaction controls.
– Not suitable for interacting with TEdit or TMemo controls.
– Suitable on touch capable devices with no mouse cursor. - cuTouchDelay:
– TImageCacheType.ctCacheAsBitmap is required.
– The MouseDown, MouseMove, and MouseUp events are hooked and allow controls to be interacted with.
– TImageCacheLayout will automatically re-cache itself once a delay has elapsed (default 5 seconds) after the MouseUp event.
– More suitable for interacting with interface components like TEdit and TMemo than cuTouch.
– Not suitable for use with child components where no interaction is required.
– Suitable on touch capable devices with no mouse cursor. - cuManual:
– TImageCacheType.ctCacheAsBitmap is required.
– No automatic re-caching is done.
– Suitable if no re-caching is needed.
– Suitable for implementing your own re-caching system based on keyboard visibility or the visibility of the TImageCacheLayout within the viewable screen.
– Call ClearCachedImage; to clear the cache image when you need to.
TImageCacheEffect:
- cqNormal:
– No post processing.
– Suitable for graphical child components.
– Less suitable for text child components.
– Faster than the other options. - cqNoAlphaEdge:
– Experimental.
– Each pixel is accessed and checked to see if it is below the opacity threshold of 63 [0..255]. If it is then it is set to completely transparent.
– Suitable for text child components.
– Less suitable for graphical child components.
– Slower than cqNormal. - cqSharpen:
– Experimental.
– A TSharpenEffect is applied to the bitmap in post processing with an amount of 0.01.
– Causes artifacts on some components like TPanel.
– Suitable for text child components.
– Less suitable for graphical child components.
– Slower than cqNormal.
Need more functionality? Dive into the CacheLayout.pas file and build what you need.
Build something cool with this component? Share the source with other developers.
Can’t get it work on Delphi XE6… when trying to build Design Time project, getting an error:
[dcc32 Fatal Error] CacheLayout.pas(7): F1026 File not found: ‘…\CacheLayout\Source\FMX.PixelFormats.dcu’
In XE6 FMX.PixelFormats is gone and is now in FMX.Types. So remove FMX.PixelFormats from the uses clause.
Tried that, but there are some functions missing… For example GetPixelFormatBytes() …
[dcc32 Error] CacheLayout.pas(136): E2003 Undeclared identifier: ‘GetPixelFormatBytes’
Could you please fix it for XE6? Would be grateful.
Updated to support XE6 and AppMethod.
http://www.fmxexpress.com/wp-content/uploads/2014/03/CacheLayout_v102.7z
Thank you! You’re great!
Doesn’t work on Rad Studio XE6. I Receive the following errors:
[dcc32 Error] CacheLayout.pas(136): E2003 Undeclared identifier: ‘GetPixelFormatBytes’
Updated to support XE6 and AppMethod.
http://www.fmxexpress.com/wp-content/uploads/2014/03/CacheLayout_v102.7z
Failed to Install in XE7:
[21CAA19D]{delphicoreide210.bpl} PasCppPakMgr.TIDEDesignPackage.Load (Line 2363, “PasCppPakMgr.pas” + 75) + $59
[50066A9C]{rtl210.bpl } System.@IntfClear (Line 36046, “System.pas” + 10) + $0
[5014ADC6]{rtl210.bpl } System.Classes.TRegGroup.BestClass (Line 2887, “System.Classes.pas” + 5) + $2
[5014AE07]{rtl210.bpl } System.Classes.TRegGroup.BestGroup (Line 2900, “System.Classes.pas” + 2) + $0
[5014B659]{rtl210.bpl } System.Classes.TRegGroups.FindGroup (Line 3195, “System.Classes.pas” + 5) + $9
[5014B90D]{rtl210.bpl } System.Classes.TRegGroups.RegisterClass (Line 3279, “System.Classes.pas” + 0) + $1
[5014BDCE]{rtl210.bpl } System.Classes.RegisterClass (Line 3468, “System.Classes.pas” + 5) + $3
[21CA4E2F]{delphicoreide210.bpl} PasCppPakMgr.TIDERegClass.Create (Line 806, “PasCppPakMgr.pas” + 8) + $3
[21CA3EF5]{delphicoreide210.bpl} PasCppPakMgr.RegisterComponents (Line 365, “PasCppPakMgr.pas” + 6) + $10
[5014C539]{rtl210.bpl } System.Classes.RegisterComponents (Line 3632, “System.Classes.pas” + 1) + $5
[31E53F20]{CacheLayoutDesigntime.bpl} Cachelayouteditors.Register + $58
[21CAA0F7]{delphicoreide210.bpl} PasCppPakMgr.TIDEDesignPackage.Load (Line 2356, “PasCppPakMgr.pas” + 68) + $0
[21CA9C6B]{delphicoreide210.bpl} PasCppPakMgr.TIDEDesignPackage.DelayLoad (Line 2217, “PasCppPakMgr.pas” + 11) + $4
[21D90058]{delphicoreide210.bpl} PakList.TPackageListItem.LoadWait (Line 939, “PakList.pas” + 3) + $4
[21D8FF7A]{delphicoreide210.bpl} PakList.TPackageListItem.LoadDesignPackage (Line 905, “PakList.pas” + 6) + $5
[21D8EFE7]{delphicoreide210.bpl} PakList.TPackageListItem.SetIsInstalled (Line 582, “PakList.pas” + 7) + $3
[21D8EDB7]{delphicoreide210.bpl} PakList.TPackageList.AddPackage (Line 497, “PakList.pas” + 13) + $5
[21DA645E]{delphicoreide210.bpl} BasePasProjOpts.TProjOptsManager.InstallPackage (Line 1784, “BasePasProjOpts.pas” + 9) + $5
[21DA76B7]{delphicoreide210.bpl} BasePasProjOpts.TProjectOptions.InstallPackage (Line 2407, “BasePasProjOpts.pas” + 0) + $3
[21D6E174]{delphicoreide210.bpl} PasMgr.TPascalPackageCodeUpdater.InstallPackage (Line 12767, “PasMgr.pas” + 18) + $19
[21CB4C11]{delphicoreide210.bpl} PkgContainers.TStdPackageProjectContainer.CommandHandler (Line 178, “PkgContainers.pas” + 8) + $7
[204A4763]{coreide210.bpl} ContainerIntf.TIDEProjectManagerMenuObject.Execute (Line 862, “ContainerIntf.pas” + 26) + $17
[204A647E]{coreide210.bpl} ContainerIntf.TProjectManagerMenuItem.Click (Line 1110, “ContainerIntf.pas” + 18) + $22
[506FBE94]{vcl210.bpl } Vcl.Menus.TMenu.DispatchCommand (Line 3436, “Vcl.Menus.pas” + 5) + $4
[506FD106]{vcl210.bpl } Vcl.Menus.TPopupList.WndProc (Line 4597, “Vcl.Menus.pas” + 4) + $E
[506FD055]{vcl210.bpl } Vcl.Menus.TPopupList.MainWndProc (Line 4572, “Vcl.Menus.pas” + 2) + $5
[5016E214]{rtl210.bpl } System.Classes.StdWndProc (Line 16598, “System.Classes.pas” + 6) + $1
[50716333]{vcl210.bpl } Vcl.Forms.TApplication.ProcessMessage (Line 10352, “Vcl.Forms.pas” + 23) + $1
[50716376]{vcl210.bpl } Vcl.Forms.TApplication.HandleMessage (Line 10382, “Vcl.Forms.pas” + 1) + $4
[507166A9]{vcl210.bpl } Vcl.Forms.TApplication.Run (Line 10520, “Vcl.Forms.pas” + 26) + $3
Those are some odd errors there. Is that C++Builder? It installed fine in my Delphi XE7 (and even Appmethod). However, it was built for XE5 and XE6. In XE7 it has a drawing issue at least that I have someone working on fixing.
Thanks,
This is Delphi. I got install using CacheLayoutRuntime.dpk as Desingtime, but with CacheLayoutDesingtime.dpk I got a acess violation in the register procedure.