Sunday, 23 February 2014

"Hidden Features of the Delphi IDE" on Stack Overflow - a call for useful answers

Recently Jeroen Pluimers and Jim McKeeth posted about three "Hidden Features of..." questions on Stack Overflow that were either deleted or nominated for deletion.  They are:
The third one was deleted some time ago - this was not the first time I have followed a link to that question and found it deleted.  So I asked it be undeleted, and now it has.  (Turns out it was reopened, but reopening a deleted question doesn't undelete it.)

However, the discussion on my please-reopen question makes a good point: this question does not have many high-quality answers.  The vast majority seem to be sourced from a single keyboard-shortcuts page. Many aren't well formatted. Some are useful, eg little-known timesavers. Some are not. I now feel slightly embarrassed for having asked it be undeleted. As is, in its current state I'd agree it should be closed, although I'd still disagree with the deletion because I don't believe in deleting any useful content.

The same question for other IDEs, such as this one for XCode, shows the level of quality such a question can have and the useful resource answers can be for users of an IDE. I personally find the good "Hidden Features of..." threads amazingly interesting.  So this is a call to action: can we, Delphi users, improve the answers and show that such a question can be worth keeping alive?

(One useful possibility: the question is from 2010.  There have been five versions of Delphi since then.  I think Embarcadero will have added some useful stuff in that time. Let's get it visible!)

Update a couple of hours later: Hidden Features of the Delphi Language (the first link, and a really cool set of answers) has been deleted.  Hidden Features of Oxygene is on hold.  Hidden Features of XCode, a page I used to demonstrate that the Hidden features of IDE X questions can produce high-quality resources, is now locked.  All these have occurred since I asked for the initial question to be reopened.  I am sorry to think that by asking for one question to be undeleted, I may have inadvertently caused great damage to others.

For the IDE Features page: I have edited some posts in the initial question, but the question is now locked and I cannot edit it to provide links to answers, for example, to follow the way this question about Python is organised, suggested in the comments to my undelete request. This means I can't improve the question, and no-one can add new answers.

Sunday, 26 January 2014

DWS Mandelbrot Explorer Mark II, and random notes about FireMonkey and threads

The DWS Mandelbrot Explorer, which renders tiles generated by Eric Grange's tile server, has been updated.

The following is a braindump of information about the app, about FireMonkey, about threading, and about how they all interrelate. I think some points will be interesting to you.

Miscellaneous things I've learned

  • It turns out that you cannot reliably use two FireMonkey Direct2D TBitmaps in two different threads at the same time. The code had a background thread to download the fractal data and create a tile bitmap, which was then passed to the main thread to draw. (Only one thread accessed each TBitmap at a time, but several threads used independent TBitmaps concurrently.) Sometimes this results in silent failures to update the texture object backing the TBitmap data. The DirectX methods CopyResource and DrawBitmap, and possibly others, can fail.
    I spent a long time investigating this, seeing what I might be able to do to hack in enough thread-awareness into the library that using two theoretically-independent bitmaps at the same time would work. The code is strongly designed around shared objects, used for all operations. I've looked into:
    • Direct3D factories and using the ID2D1MultiThread interface to synchronize (only Windows 7 and up though);
    • the (different) ID3D10Multithread interface for synchronization, which works brilliantly right up until it deadlocks;
    • surface sharing between APIs;
    • DXGI shared surfaces;
    • per-thread instances of render targets and textures;
    • hand-rolled synchronization around specific areas; 
    • name it. Several things have been almost successful but nothing is reliable, and the more I read, especially when there are caveats about certain functionality not working on Vista but only on 7, or only on hardware and not on WARP, the more I understand why it makes sense for the FireMonkey Direct2D canvas implementation not to have even tried to implement it.
      Because of this, there's a lot more processing in the app's main thread, generating the bitmaps as well as just drawing everything onscreen. This is not ideal. It seems FMX apps will unfortunately have to stay away from second-thread graphics processing but this is due to the underlying graphics libraries, specifically here Direct2D. Ie, it's not really FireMonkey's fault. If you really need to, you can use a specific graphics library in your other threads, just don't use lots of plain TCanvases and TBitmaps and expect it to work - keep them in one thread. Graphics32 and VPR might be worth investigating.
  • Also, hacking thread support into a non-threaded library you're not overly familiar with is a hard task. Just saying. Fun though.
  • There seems to be no cross-platform Delphi RTL equivalent of WaitForMultipleObjects. The TEvent class wraps an event, but what happens when you want to wait for two of them? This would make a good open source class, I think (a list of events, with wait methods?) but right now this app's code re-uses one event for several things making it a bit complicated.
  • If you want to interrupt a TIdHTTP downloading (via blocking call TidHTTP.Get), call TIdHTTP.Disconnect. The object seems to be reusable afterwards for a subsequent Get call. I use Disconnect to achieve fast termination of thread downloading when I want to stop the background thread objects and need to wait on them, so need them to stop quickly.
  • The Quartz canvas on OSX is noticeably slower than Direct2D.  I think, without measuring, it might even be slower than GDI+. I am curious why this is and if anyone else has seen the same thing in their FireMonkey apps.
  • TImageControl on OSX does not render correctly when you write to its Bitmap. I traced through the code and am not sure what it's doing - the code looks valid - but I can state that after drawing onto the Bitmap what was displayed onscreen, a blank solid color, was wrong. Since I just needed a canvas to draw on and a control to give mouse events, I ended up just using a TPanel.
  • A FireMonkey TForm is missing some seemingly obvious events: there is no OnClick or OnDblClick.  I shouldn't need a client-aligned panel to give mouse click events, but I did.
  • The look of FireMonkey on Windows has improved greatly since XE2, and is quite close to how native Windows controls / the VCL looks. The following image (click to expand) has exactly the same controls placed in the same position with the same dimensions on a XE2 FMX form, a XE4 FMX form and VCL FMX form.  The XE2 one doesn't look very native; the XE4 one is quite similar to the VCL.

    In some cases I think FireMonkey is better. Look how the TTrackBar's edges align nicely in FireMonkey, for example, but don't in the genuine native control - something that bugs me every time I use the real one.

Notes about the app itself

FireMonkey in practice

  • The point of the app was a write a non-trivial FireMonkey app and see, in practice, what issues arose. I can confidently state I have learned a lot about FireMonkey building this app. That was the goal.
    Has there been anything particularly bad? I don't think so. There were three areas:
    • Bugs: none serious. Graphics performance is easily fixable.
    • Cross-platform: some code is slightly less clear than it could be, because I've stuck to using the cross-platform RTL and FMX only.  (For example, had I been able to drop back to using WaitForMultipleObjects(Ex) some code might have been clearer.)
    • Framework: FireMonkey is different to the VCL, and I limited myself to sticking to it and the Delphi RTL / libraries only rather than using, say, Windows API code. This is no different to learning and using any other framework: the same problems occur learning the .Net libraries, Cocoa, etc.
  • On the whole, FireMonkey is a good framework the details of which you have to learn, like any other. It's backed up by the Delphi RTL which is fairly comprehensive. Put it this way, if you can write a VCL app using Delphi, you can write a FireMonkey one.


  • Finally, thanks to François Piette, who submitted some changes to port from XE2 to XE5. I integrated his changes which improved the code - especially bitmap generation from the downloaded data - greatly. Thanks François!
  • Download version 1.1 or find the source code here. Windows 32, 64 and OSX 32.

Next up will be something completely different.

Saturday, 25 January 2014

A unit to enable Direct2D in FireMonkey where possible

A few days ago I posted about FireMonkey's choice of canvas classes, where it would choose to render via GDI+ instead of via Direct2D.  There were two fixes: one (untested and possibly dangerous) enabled hardware rendering on DirectX9-class hardware, but required editing the FireMonkey source; the second (known safe and tested) enables optimised software rendering via WARP when DirectX10 hardware support is not available.  This last isn't as good when you have DX9 hardware, but it doesn't required editing any FireMonkey source and WARP renders surprisingly fast - it's certainly good enough for 2D/HD applications.

This code is now available as a unit you can include in your FireMonkey apps.  It is ifdef-ed so it will only function on Windows when compiled with XE4 and XE5. (Thanks to commenter Skamradt in the original post for suggesting this.) That means you can include and use it when compiling for OSX or Android without having to worry about it not compiling on those platforms, and that it will also only apply for known IDE / RTL versions that require this patch.  I have only briefly tested it on XE4 (where it works) and XE2 (where of course it doesn't, but compiles anyway.)  Suggestions / changes are welcome.

To use it, add the unit to your .dpr file and then add a call to TryUseDirect2D before Application.Initialize, like so:
program Project1;

  Unit1 in 'Unit1.pas' {Form1},
  FMXDirect2DFix in 'FMXDirect2DFix\FMXDirect2DFix.pas';

{$R *.res}

  FMXDirect2DFix.TryUseDirect2D; // <-- The key method

  Application.CreateForm(TForm1, Form1);

The code is currently checked in to the source of my DWS MandelbrotExplorer app - the rest of the code of which is in a halfway state, so no point looking at it right now :)

  • You can find the unit here.
  • It's MPL licensed, so useable in both commercial and open-source software.
  • It makes a big speed difference for FireMonkey apps on my Windows 7, non-DirectX-10-hardware.  It should make a noticeable difference for anyone on a recently patched (with the Platform Update) Vista or 7 without DirectX10 hardware, which includes those running Windows in a virtual machine like Fusion.

Monday, 20 January 2014

FireMonkey canvas classes and a bugfix to speed up your apps

Everything you need to know about FireMonkey canvases - and a performance boost bugfix for some people as well!

I recently posted my first real-world FireMonkey app, which gave a zoomable, scrollable, very interactive view of the Mandelbrot fractal using the precomputed DWS Mandelbrot tiles. It worked fine on my computer.

Those are famous last words.

Soon the comments on that page were filled with people saying it didn't work: the UI said tiles were downloading etc, but it drew only a blank solid colour where the fractal should have been. I made an educated guess that the problem only happened when using the Direct2D canvas, and put out a "fix" that restricted it to drawing using GDI+. This fix worked - it draws - but GDI+ is slow, and the app as it's currently available is not of a quality I feel personally comfortable having publicly available with my name attached. Clearly I need to fix it. But how?

This is a perfect example of why being aware of the different canvases in FireMonkey matters. You need to test with each one that your app could possibly end up using on an end-user's machine, which means you need to know what they are, when they're chosen by FMX to be used, and how to force a specific choice in order to test each case. Moreover, there is (IMO) a bug in Firemonkey's logic about which class to choose when, which results in your apps rendering much more slowly than they need to in some use cases, and you may want to tweak some code in order to fix this and make your app render faster.

What's in this article?

  • The role of canvases in FireMonkey rendering
  • Overview of each possible Windows canvas class: GDI+, Direct2D, and GPU
  • How does FireMonkey choose which canvas class to use?
    • Investigating when Direct2D is chosen vs GDI+, and we find a bug
    • Fixing the bug - three possible solutions
  • For testing: how to force the selection of a specific class
    • Checking what class you are using
  • Summary
This is a long article - two and a half thousand words - so let's get going.

The role of canvases in FireMonkey rendering

FireMonkey is a cross-platform UI toolkit. As such it needs to be able to render everything onscreen independent of the underlying graphics framework - it needs one API you and I can code against that runs on Windows and OSX and iOS and Android.

It achieves this by using a variety of different canvas classes.  That is, when you access a TCanvas such as Form.Canvas or TBitmap.Canvas, due to the wonderfulness of polymorphism the actual class you are using can vary widely.  Here are the possibilities:
  • TCanvasGDIPlus (Windows)
  • TCanvasD2D (Windows)
  • TCanvasGpu (Windows)
  • TCanvasQuartz (OSX)
  • TCanvasQuartz (iOS, implementation appears independent of the OSX class with the same name)
  • At least one more for Android in XE5+. 
Let's write off the platforms that only have a single canvas implementation - OSX, iOS, and probably Android. (I don't have XE5 and googling didn't show much about the underlying code.) If you're using one of those platforms, you are by default testing using the only canvas class and this is a non-issue. But that leaves three possible canvas classes that your app could end up using on Windows. (Even if you know about the Direct2D and GDI+ canvases, I bet you didn't know about the 'GPU' canvas. I sure didn't.)

Each Windows canvas class


TCanvasGDIPlus is the default, fallback canvas. It uses GDI+, a software-only, fairly slow, API provided by Microsoft in the Windows XP days. It will run on anything, but your rendering performance may not be great. For example, in my fractal app which draws anywhere from four to a few dozen 256x256 tiles at various scales on the window with every paint, at the default small window size click-dragging to navigate is fast. But if you maximise the window, and the rendering area becomes much larger, scrolling around - which invalidates with every mouse movement, effectively drawing as fast as possible - is painfully laggy. This is not FireMonkey's fault. It is one of the problems with using GDI+, and I have experienced the same problem drawing complex interactive UIs with GDI+ before.

If your app is rendering using this class - I show how to find out which class later - I strongly recommend you find out why and do what you can to fix it. In general, avoid using this class if possible.

You will always end up using the class on Windows XP, since it's the only one supported. On all other versions of Windows, Vista and above, 99% of the time you will be able to use TCanvas2D instead (once you fix a problem with when it's chosen) and I highly recommend you do this.


TCanvasD2D uses Direct2D, a 2D API implemented over Direct3D, which is available on Vista SP2+ and above. It is hardware-accelerated and fast, and theoretically the default. The quick answer is that you want to use this class if at all possible, but you may need to make some code changes to do it. Without some very small tweaks, there are cases where FireMonkey will choose a GDI+ canvas instead of a D2D one on hardware where D2D would run faster - much, much faster. This is rare, but my setup is one where it occurs.


TCanvasGpu is turned off by default, and is only used if the global FMX.Types.GlobalUseGPUCanvas is true. (Set this in your project file before Application.Initialize.) It's quite neat in that it uses a base class TContext3D to do its work, which has a very similar system for choosing which subclass is appropriate to instantiate as the canvas system. There are context classes for D3D9, D3D10, GLES and Quartz.

The first time I tried this out, it crashed immediately - FillText ends up calling TCharHelper.ConvertToUtf32 with an empty string, which raises an exception. Reading the preceding code, which seems to implement text wrapping, I don't understand why it's trying to do what it is.

TCanvasGpu running on Windows 7 on DX9
hardware. Yes, there is a whole TTrackBar
between those two buttons. (See it? Me either.)
This class is turned off by default and I do not
 recommend manually enabling it.
On my machine, it uses a TDX9Context to draw. There are noticeable severe rendering bugs. In my fractal app one control, a TTrackBar, doesn't draw at all. Text draws 'bold', which looks similar to the effect you get drawing antialiased text over itself many times. Buttons had one-pixel-wide edges missing.

I don't know how much of this is due to the TDX9Context it was using, and how much is due to TCanvasGPU itself. Since on DX10-class hardware FireMonkey will use Direct2D, DirectX-9 class hardware is the only use case on Windows for this class. (As it turns out, we should normally use Direct2D even for DirectX9 hardware. More information below.) The severity of the bugs are, I suspect, why it is turned off by default. I do not recommend manually turning it on.

How does FireMonkey choose which canvas class to use?

In FMX.Types.pas is a method TCanvasManager.GetDefaultCanvas. This returns a metaclass which is used to instantiate the actual canvas class. The first time this method is called, it assembles a list of possible, valid canvases which the current platform supports and then from that list it chooses which one is best to instantiate. There are some complex if statements about whether a class is the default and whether to try to use a software canvas, but in my testing these didn't make any practical difference.

The key is in the TPlatformWin.RegisterCanvasClasses method, which out of the GPU, D2D and GDI+ canvas classess tests which can be used and where possible adds them to this list. It only 'registers' (adds to the list) the GPU canvas if GlobalUseGPUCanvas is true, and by default it is false (see above.) That leaves D2D and GDI+.

Investigating when Direct2D is chosen over GDI+, and a FMX bugfix

First off, the easy case: GDI+ is the fallback, and is always available on machines that meet the FireMonkey requirements. It is always registered. This means that if the Direct2D class is not registered, your app will end up using GDI+.

Direct2D is trickier. And remember, any bug or quirk here that invalidly thinks D2D is not the right choice will cause the GDI+ canvas to be chosen instead, and that's bad.

Fmx.Canvas.D2D.pas's RegisterCanvasClasses method checks the Direct3D 10 capabilities reported by DirectX, and registers the D2D canvas if the D3D10 driver type is either hardware or WARP. This latter is interesting: the Windows Advanced Rasterization Platform is a software rasterizer supporting Direct3D 9.1 through 10.1 feature levels, and by all accounts is a very good one.  It is part of the DX11 runtime which you need to have installed, which is part of the platform update for Vista or Windows 7. You should already have these automatically through Windows Update.
Direct2D applications benefit from hardware-accelerated rendering on modern mainstream GPUs. Hardware acceleration is also achieved on earlier Direct3D 9 hardware by using Direct3D 10-level-9 rendering. This combination provides excellent performance on graphics hardware on existing Windows PCs.
When rendering in software, applications that use Direct2D experience substantially better rendering performance than with GDI+ and with similar visual quality. 

- MSDN Direct2D page
In other words, on DirectX 9.1 hardware there is a high-performance hardware rasterizer available and on lesser hardware there is still a high-performance software rasterizer available. Now, for DirectX 10 and above, it's simple: Direct2D will be chosen. But for DirectX9-class hardware, there is a choice between two software renderers: GDI+, an old and slow API, or WARP, a speedy, very technically impressive API. Clearly, where possible, FireMonkey should choose to use it, falling back to GDI+ only if nothing else whatsoever is possible. As you've no doubt guessed if you've read this far, it doesn't, and this is what we need to investigate and fix.

The problem lies in TCustomDX10Context.CheckDevice. An edited version of the problematic portion of code is:
if ...{can create a D3D hardware device} then
end else if
  not TCustomDX9Context.HardwareSupported and
  Succeeded(D3D10CreateDevice1Ex(D3D10_DRIVER_TYPE_WARP, D3D10_CREATE_DEVICE_BGRA_SUPPORT, g_pd3dDevice)) then
  // Switch to software mode
  FDriverType := D3D10_DRIVER_TYPE_WARP;
It's this else statement that is problematic. It basically says to use WARP if it's supported (fine) but only if Direct3D9-class hardware is not supported (not fine.) Almost all computers since about 2005 will support D3D9, and this API is available on Vista and above. The only reason I can think of for this is that TCanvasGpu with D3D9 support is expected to be the fallback here before GDI+. However, as we've seen, not only is that class buggy but it is disabled by default (probably because it's buggy.) This means that anyone with D3D9 hardware (but not D3D10+ hardware), and that includes people running on virtual machines like VMWare Fusion, which only supports D3D9 emulation, will end up using GDI+ when they could be using the much faster WARP.

How can we fix it?

A global switch

Normally the best way to force FireMonkey to choose a particular graphics path is via one of the globals at the top of FMX.Types.pas. There is a potentially suitable one: GlobalUseDX10Software. (Remember you need to set these in your project file before you call Application.Initialize.) It's false by default but if you set it to true, you will get WARP. Unfortunately, this means you will always get WARP when possible, even when hardware DX10 support is available. No matter how good WARP is we should choose the hardware-accelerated option when possible, and so this is a no go.

Edit the FireMonkey source

The second option is to edit the FMX source. To do this, make a local copy of FMX.Context.DX10.pas in your program's source folder. (I do not recommend editing RTL source directly and trying to recompile FMX - leave it alone and make your changes separately. If you add your local file to the project it will be used in preference to the RTL version. Just make sure you document what you've changed for future you.)

Add this local file to your project, and remove the 'not' from the else if statement above. It should look something like this:
end else if {not TCustomDX9Context.HardwareSupported and} ...
Recompile and you should get a Direct2D canvas. If you were using the GDI+ canvas before, you should notice a significant difference.

Manual code

The final - and probably best - option is to add some code to try to create a D3D10 context and check the driver type, and if it returns WARP then turn on the above switch. The following slightly ugly method (it's 1AM...) does the trick; call it before Application.Initialize in the project file. This method depends on  FMX.Types, Winapi.D3D10_1, Winapi.D3D10, and WinAPI.Windows.

Because of the method's dependencies and for code cleanliness, I would suggest putting this in a separate unit from the main project source, and ifdef both the unit being included, and the method being called, out completely if you are not compiling for Windows (the MSWINDOWS constant.) As suggested by a commenter below, it is probably also a good idea to ifdef for your specific Delphi version in case this is fixed in future.

The below code doesn't check for D3D9 hardware support, assuming that if it can create a WARP device that's enough. Feel free to add back in additional checks.
procedure TryUseWARPCanvas;
  DX10Library : THandle;
  TestDevice : ID3D10Device1;
  DX10Library := LoadLibrary(Winapi.D3D10_1.D3D10_1_dll);
  if DX10Library = 0 then Exit;

    SaveClearFPUState; // Copy from FMX.Context.DX10
      if GetProcAddress(DX10Library, 'D3D10CreateDevice1') = nil then Exit;

      // If there's no hardware D3D10 support, but there /is/ WARP (software support)
      // force that to be used. Don't bother checking DX9 support, just go for WARP.
      if not Succeeded(D3D10CreateDevice1(nil, D3D10_DRIVER_TYPE_HARDWARE, 0, D3D10_CREATE_DEVICE_BGRA_SUPPORT, D3D10_FEATURE_LEVEL_10_1, D3D10_1_SDK_VERSION, TestDevice)) and
        Succeeded(D3D10CreateDevice1(nil, D3D10_DRIVER_TYPE_WARP, 0, D3D10_CREATE_DEVICE_BGRA_SUPPORT, D3D10_FEATURE_LEVEL_10_1, D3D10_1_SDK_VERSION, TestDevice))
        then begin
          FMX.Types.GlobalUseDX10Software := true;
      TestDevice := nil;
      RestoreFPUState; // Copy from FMX.Context.DX10

Tweaks to this code

You might want to change a few things about this code:
  • Editing the FMX code: to match the manual code, I changed it to remove the DX9 check entirely. It either sees if it can create a D3D10 hardware device, or otherwise tries to create a WARP device. Thanks Remy for the suggestion.
  • Untested useful tweak: Direct2D is still hardware-accelerated on DX9-class hardware. Try changing the feature level to D3D10_FEATURE_LEVEL_9_1 to see if it has this level hardware support on your computer. You will need to change both the manual code test (if you use it) and the FireMonkey code creating the devices in the same area as above to match. I haven't tested this and it's just an idea for further investigation; the current code goes either either with D3D10-hardware or WARP, which I know for sure will work and are good, safe modifications to make. Changing this will always require editing the FMX source.
  • XP support: with Microsoft dropping XP support on April 8, 2014, it's quite possible the next version of Delphi will not need to support XP at all - or at least, will only do so as a legacy option. I would suggest that Embarcadero make some changes requiring the Platform Update be installed as prerequisite for FireMonkey apps, and then using one of the D3D10, D3D9-feature-level, or software WARP Direct2D canvases as the only option on Vista and above, and only using GDI+ on XP. There should never be a case on Vista or Windows 7 where GDI+ is the chosen canvas.

For testing: how to force the selection of a specific class

I stated at the beginning that you should test with each possible canvas type, in order to catch code tht works with one and doesn't with another.  How?
  • To force GDI+, set FMX.Types.GlobalUseDirect2D to false.
  • To force Direct2D (using the WARP software rasterizer even with hardware support - so only for testing) set FMX.Types.GlobalUseDX10Software to true.
  • To force the GPU canvas (unnecessary for testing, since it's off by default) set FMX.Types.GlobalUseGPUCanvas to true

How to check what class you are actually using

This is fairly simple. Find a valid normal canvas (such as Form.Canvas) and check its ClassName. It will be one of the above classes.


  • FireMonkey has several underlying graphics classes depending on the platform and, on Windows, on the capabilities of the platform
  • You need to test each one, because code that works on one can fail on another
  • On Windows, if you (or a user) have D3D9 hardware (but not D3D10 or higher hardware) FireMonkey will use GDI+ to render where it probably shouldn't, which will make your program noticeably slower when run on (a) D3D9-class hardware, or (b) in a virtual machine like VMWare Fusion. It should use Direct2D's software rasterizer instead. Fix this with one of the three ways above; I recommend with the sample code I showed above.

Monday, 30 December 2013

A FireMonkey client for the DWS Terapixel Mandelbrot data set

You may have seen Eric Grange's precomputed Mandelbrot set data - read the Christmas announcement and the followup. He has generated and is hosting the fractal as a multi-level tiled data set: that is, as a structure of varying levels of detail, where each level of detail is twice as large as the one before. His tiles are not images, but rather a table of the number of iterations required for each point.  These values can then be mapped to colour as the client application decides.

Quick overview of tiled level-of-detail data

You can picture this as a pyramid, or as an infinitely divided square. The first level, level 0, has one tile. Level 1 subdivides level 0's tile into two on each axis, so four total.  Level 2 subdivides each of these tiles, as so forth.  Since each tile has the same dimensions in pixels, but represents a smaller area of the data, you get higher and higher resolution data in each tile.
This is a common method of storing similar data - I've previously used it for geographic maps, for example. Often tiles will be images, but in this case they are data that can be converted to images.
As of Dec 27, there are fourteen levels (0-13.)  At level 13, there are 213 tiles in each dimension making a Mandelbrot set 8192 x 8192 tiles on a side.  At 256 pixels square, this is 2097152 pixels a side, or 4398046511104 pixels in total.  That's quite a lot.  Add in the other smaller levels and you have an enormous of data, many gigabytes.  For comparison, zooming in from the whole earth to a few meters is only a few more orders of magnitude; at 1 pixel/meter, zoomed in to the maximum zoom on this set makes the whole Mandelbrot set, in pixel space on your screen, the size of a large country.

I thought it would be a fun project to write a small viewer app for this dataset, so here it is: a cross-platform (Windows and OS X) tiled data viewer written in Delphi and FireMonkey.

Why write it?

  • To find out first-hand what it's like writing a non-trival (although admittedly small) app using FireMonkey.  Some people rave, some people complain: who's right?  There's only one good way to find out. I will write about this in a followup post.
  • To provide an example of using a tile API.  If you end up having to implement one of the common tile APIs out there and you've never had to deal with tiles, navigation, zooming and pixel-perfect dragging/scrolling at different zoom levels, this code is simple and worth reading. It's also open-source (MPL.) You're welcome to use it.
  • Because everyone like knowing people are using code they've written, and I hope Eric finds it interesting someone's written an app to use his server fractal API & data.
  • For fun!


  • Smooth scaling: the zoom level does not have to match a tile level. High-res tiles are loaded and scaled down.
  • Smooth UI: grab and drag to scroll, animated scaling as you zoom in or out
  • Asynchronous loading: tiles are downloaded and converted to image data in another thread.
  • Cross-platform, from the one project and source code (only one $ifdef required for low-level pixel color handling)

Random notes:

  • Despite reviewing FireMonkey when it was released with XE2, this is actually my first non-trivial FireMonkey app.  It was written with Delphi XE2, and I will upgrade it to XE4.  My aim here is to see what the experience is truly like using both the first FMX and upgrading to a new FMX version.  I will write an article about my impressions soon.
  • In addition, unlike my other open-source projects (which are hopefully useful libraries, not apps) I've hosted this one on a Mercurial server. I had read about but hadn't used a distributed version control system before, so starting a new project was a good opportunity to try one out.  It's an interesting paradigm, although for a sole developer not much different to a standard system like Subversion.


  • While the DWS Mandelbrot set is cool, there's lots of other tiled data out there too.  With some refactoring, it would be easy to change this app to support a wide variety of tiled data sources and turn it into a generic tiled data viewer.  One immediate candidate is Bing Maps's tile API.  Nokia / HERE maps also has a tile API, and Google can have one with a bit of hacking / hope - its map APIs are not as extensive for non-web platforms.  I would be interested to hear of any data sources you are interested in, and if I expand the product to support each API as a plugin then anyone could implement support for a specific type of data.
Meanwhile, it's been a fun way to spend a day or two!


Wednesday, 24 July 2013

TTransparentCanvas: changing the background color of glowing text

You have probably already seen how to use DrawThemeTextEx on Vista and above to draw text with a white blurry 'glow' effect behind it. It's commonly used when drawing on glass, to ensure that text has enough background contrast to be easily readable. But the API only draws a white glow. What if you want another color?

The glow effect over a coloured background. What
if you want non-white glowing background?
I asked a question on Stack Overflow to see if it was possible to change the background glow colour - after all, the text colour and glow size are both options in the DTTOPTS structure; perhaps there was a way to make the border or shadow options affect the glow. But no-one answered, and from my own research / experiments it appears it's not possible through the API.

Instead, I've coded a method to do this into my TTransparentCanvas open-source library.

If you haven't seen the previous blog posts about it, TTransparentCanvas is a class (or set of classes) to draw transparent, blended shapes and text onto a bitmap or device context using TCanvas-like functions and normal VCL objects like TPen, TBrush, TFont etc. Shapes and text can be composed and blended over each other, and the final result then blended over an existing image.  The code aims to be easy to pick up and use if you are familiar with drawing with TCanvas, and is implemented with pure GDI - that is, no GDI+ or other external libraries are required.

How it's done

The following assumes you have read my previous articles about drawing transparent graphics with pure GDI (Part 1, Part 1½, and Part 2.) Specifically, you should know what premultiplied alpha is and be familiar with the record I use to represent a single transparent pixel, TQuadColor.
A quick explanatory note if you didn't go and meticulously pore over those links - which of course you did :) - is that premultiplied alpha changes the RGB representations of a pixel from 0-255 to that value scaled, or pre-multiplied, by the alpha channel, so with an alpha of 64, a full-white (normally (255, 255, 255)) pixel will have RGB values of (64, 64, 64). A grey pixel of (128, 128, 128) with an alpha value of 64 would have premultiplied RGB values of (32, 32, 32), which still represent the same colourIt's one of the steps done when blending transparent images together, and storing bitmaps in this format saves some calculations. TQuadColor is a simple record representing a 32-bit pixel as union or variant record of four bytes in ABGR order or a 32-bit Cardinal, along with some helpful methods.

TTransparentCanvas draws every shape or text onto an intermediate bitmap. This allows processing of the alpha of that shape before the result is blended onto the 'result' bitmap. This design allows every item drawn to have different transparencies, but also allows other intermediate processing before the drawn item is composed onto the final alpha-aware bitmap result.

It's this intermediate step we can take advantage of. If there's no way to change the background colour in the API, we can do it, effectively, as a post-processing stage by tinting the glowing pixels.

Examining the bitmap

Examining the glowing pixels in the debugger shows that the white transparent pixels are represented as pure white until they are completely transparent, but are of course in premultiplied alpha form. Thus, a pixel right at the edge of the glow may have the ABGR value (2, 2, 2, 2): that is, an alpha of 2, with a full-100%-white value (normally (255, 255, 255) premultiplied by the alpha to give each channel a value of 2.

What about the text? If drawn as black text, they will have full-alpha but black pixels, e.g. ABGR (255, 0, 0, 0.) However, text is anti-aliased: most parts of the text will not be fully black, but a colour between black and white, and it's not even guaranteed the text pixels will have full alpha.

Tinting these pixels

I initially considered tinting the pixels by drawing black text on the white glow, figuring out the relative "whiteness" and "blackness" of each pixel, and using that to interpolate between the user-specified glow and text colours. However, not only is this probably more processing than is required but sub-pixel antialiasing might result in non-uniformly-grey pixels.

A simpler solution is to tint all pixels, both glow and text, to the one colour, and then draw the text over again. Visually, this gives almost the same result with less per-pixel processing - a win.

(Note: the following quotes heavily from my Stack Overflow answer explaining the same material.)

To tint, one can ignore the existing colour of the pixel and instead set any non-zero-alpha pixels to a premultiplied alpha value using the user-specified background colour, based on the existing alpha of the pixel. Loop through your temporary bitmap and set the colour using the existing alpha as an intensity:

// PQuad is a pointer to the first pixel, a TQuadColor (see link, basically a packed struct of ABGR bytes)
for Loop := 0 to FWidth * FHeight - 1 do begin
  if PQuad.Alpha <> 0 then begin
    PQuad.SetFromColorMultAlpha(Color); // Sets the colour, and multiplies the alphas together

The key is PQuad.SetFromColorMultAlpha:

procedure TQuadColor.SetFromColorMultAlpha(const Color: TQuadColor);
  MultAlpha : Byte;
  Red := Color.Red;
  Green := Color.Green;
  Blue := Color.Blue;
  MultAlpha := Round(Integer(Alpha) * Integer(Color.Alpha) / 255.0);
  SetAlpha(MultAlpha, MultAlpha / 255.0);

This takes a quad colour (that is, alpha with RGB) and multiplies the two alphas together to get a resulting alpha. This lets you tint by a transparent colour to have the glow effect lessened by being partially transparent at its strongest point if you pass in a non-full-alpha color to tint with. The example application has a slider allowing you to see this in action.

SetAlpha is an existing method that converts to premultiplied alpha:

procedure TQuadColor.SetAlpha(const Transparency: Byte; const PreMult: Single);
  Alpha := Transparency;
  Blue := Trunc(Blue * PreMult);
  Green := Trunc(Green * PreMult);
  Red := Trunc(Red * PreMult);

Example of the result

What does this give you?

This image is the text 'Test glowing text with background color' tinted clLime:
A clLime-tinted glow
The final step is to draw the text over the top again, this time without any glow effect, giving this result:
...and with text drawn over the glow.
This means you can now draw text with any font color and any glow color:
clRed text with a clSkyBlue glow

But why stop there? Naturally, the next step is to experiment with custom-drawn title bars. The source demos include a small project heavily based on Chris Rolliston's excellent code to let you draw on a Vista+ title bar. (If you want to custom-draw on a title bar, please go read his article - it's very good - rather than basing your code on my quick-and-dirty hacked-in functionality, which was written only for the purpose of demonstrating using this code with the title bar.) That gives you results like this:



And of course, since TTransparentCanvas can draw to glass as easily as it can to a normal bitmap or DC, you can draw anything else on the title bar too.


TTransparentCanvas is a MPL-licensed open source project hosted on Google Code. Get the source here. If you use it, have feature requests (such as drawing more shapes than are implemented so far) or if you have patches, please let me know. It has been tested with Delphi 2010, XE2 and XE4 and works when compiled to either 32 and 64-bit. Have fun!

Sunday, 21 July 2013

TFireMonkeyContainer update: bugs fixed, features added

The example application showing a 3D FireMonkey form.
On the other tab is a standard 2D form.
On Wednesday I announced TFireMonkeyContainer, a VCL control that can host a FireMonkey form, allowing you to mix FireMonkey elements into your VCL app. It was (and is) a new project, and the announcement page listed several known bugs and possible future design changes.

Bugs: gone! Design changes: made! The current state is not quite perfect but is bug-free enough I feel happy about you using it in real applications, or at least trying it out. (With the caveat that I'm the only one who has tested it so far, and my entire QA department is me :) If you try it, please let me know how it goes.

 Bugs fixed

  • Focus now correctly follows the host VCL and hosted FMX forms
  • Window activation now correctly follows the host VCL and hosted FMX forms, when switching by clicking on the host VCL form, the hosted FMX form, via alt-tab, via the taskbar, and when there are several VCL forms and several hosted FMX forms in the one application.
  • The title bar of host VCL forms correctly changes when the form is active and inactive.
  • An exception is now thrown when two containers both try to host the same form.

Design changes

    This FireMonkey form embedded in the VCL form can't be
    edited in the VCL designer - it's a static preview. Switch
    to the FMX form tab to edit it.
  • Design-time (in the IDE): When a FireMonkey form is hosted in a VCL form in designtime, it shows an uneditable view of the hosted form. (This used to be an editable FireMonkey form, ie you could invoke the FireMonkey designer. This only partially worked and caused a lot of problems.) Switch tabs to the FireMonkey form's IDE design tab to edit the FireMonkey form. Changes will be reflected immediately when you switch back to the VCL form's design tab.
  • There are two new events you can use to control hosting the FireMonkey form. This means there are three ways to embed a FireMonkey form:
    1. Set the FireMonkeyForm property at design-time. (Not recommended, see 'Known problems' below.)
    2. Set the FireMonkeyForm property at runtime.
    3. Use the OnCreateFMXForm and OnDestroyFMXForm events.
      • OnCreateFMXForm fires when the component is first created. It has a var Form parameter which you can use to set the hosted FireMonkey form. If a form was specified at designtime, Form refers to it. If you change it, OnDestroyFMXForm will be called for the old value.
      • OnDestroyFMXForm fires when the component is destroyed. Much like a TForm's OnClose event, you can choose an action to apply to the hosted FMX form, eg to free it. The default is to do nothing, since it was probably created with an owner. The Destroy event also occurs when you change the Form parameter in the Create event (eg, when there was a FMX form set at design-time, but in the Create event you change it to another form.)

Known problems

  • When you set the FireMonkeyForm property at designtime, you can only set it to a FMX form that is open in a tab in the IDE - otherwise the IDE doesn't see it and won't show it in the dropdown in the Object Inspector. When the IDE tab is closed, the reference is invalid - and this means the FireMonkeyForm property is cleared. This might be due to problems mixing VCL and FMX forms in the one app, or (more likely) it might be due to a mistake I've made in how to correctly reference another form in a component. Suggested workaround: set the property at runtime, or use the OnCreateFMXForm and OnDestroyFMXForm events instead.

Notes, and getting the code

The component subclasses both the VCL form and the FMX form in order to catch the WM_NCACTIVATE (VCL form) and WM_ACTIVATE and WM_MOUSEACTIVATE (FMX form) messages. I'm not an expert in window activation and there may be bugs (or I might have just plain written bad code) in the  handlers. I'm also not certain I have hit on completely the right design for the events. I'd quite appreciate feedback if you use the component and feel that the events that occur are a bad design or force you to write ugly code. The same goes for any aspects of the component, really.

You can download the latest version from the Google code homepage and Subversion source checkout.