Creating Icons and Launchers for Delphi Mobile Applications – Redux Redux (Reloaded?)


Several years back, I created a handy little utility (EXE and source) for generating all of the mobile icons, spotlights, settings and launchers for Delphi Android and iOS applications. When I started, there were 2 different platforms (iOS and Android), 3 different device types (iPhone, iPad, Android), and 7 different ratios (1:1, 1.33:1, etc) of icon/launcher graphic sizes all combining to require a whopping total of 28 different png files to be created. There was an update a year later to get it up to 37 files. Now, with the kind contribution of Gregor Kobler, I am posting a new version that adds even more ratios and pushes the number of files up to 54. It has also been updated to compile with Delphi Rio.

Please read the original blog post for instructions.  You can download the source and exe here.

Happy CodeSmithing!

Black Paths with FMX D2D Canvas

Have you ever used the RiverSoftAVG SVG Component Library (RSCL) and it just shows a black SVG?  Have you ever had a Delphi FMX application on Windows where a TPath just displays as black?  You know that the path works in other applications or with VCL or with other FMX TCanvas but not in Windows?  If so, you have encountered a bug in the TCanvasD2D.DoDrawPath method.

Users of the RSCL are most likely to encounter this bug as SVGs use paths so heavily.  Earlier versions of the RSCL had clumsy and heavyweight hacks to ensure the black path did not occur (since removed).  However, I finally got frustrated enough with the bug to track it down and find a fix.  It is surprisingly easy to cause this bug even with adding your own paths to a TPath.

To recreate this bug:

  1. Create a new FireMonkey application
  2. Drop a TPath on the form
  3. Set the Path1.Data := ‘M 2,3 z m -1,1’; // this is a point path, nothing should draw (But notice how the path bounding rectangle disappears from the form)
  4. Set the Path1.Stroke.Thickness to 2
    (Notice how the path is filled with black)

The bug and fix has been posted to Embarcadero.  I would hope the fix will be in their next release.

This bug occurs when a path is specified with more than one close path and the last segment on the path does not have a close path on it. The TCanvasD2D.DoDrawPath method mistakenly doesn’t close the path when passing it to DirectX, causing DirectX to fill in the space of the current clipping rectangle.

To get around this for now, obviously you can just append a ClosePath to the end of your path.  However, if you do not want to edit the path or cannot guarantee what is in the path, you need to patch the FMX.Canvas.D2D.pas.  The bug fix link above has the whole fix, but the short answer is change the following line at the end of the DoDrawPath method from:

 if not Closed then


 if (APath.Count < 1) or (APath[APath.Count - 1].Kind <> TPathPointKind.Close) then

This is actually exactly what the DoFillPath does in its code but its fix was not copied over to DoDrawPath.

I wanted to document this bug for others.  This is a bug that can occasionally happen especially if you are using lots of paths like the SVG component library does.

Happy Holidays and Happy CodeSmithing!



CodeRage XI Deep Dive (and Sale)

For the first time ever, I will be presenting at Embarcadero’s CodeRage conference!  I will be doing a deep dive session into SVGs and how to use them with the RiverSoftAVG SVG Component Library (RSCL).  The session is Wednesday, November 16th, at 3:00 p.m. Pacific Time in Room 1.  The source code and SVGs used in the session are here.  All SVGs (except for the ExampleSimpleButton.svg) were downloaded from I am very excited to be doing the session.  It should be a good session (even if by the end of recording the presentation my voice was starting to fail 🙂 )  I will be available to answer questions and look forward to “seeing” you there.

In honor of CodeRage XI and my participation in it, the annual Black Friday sale is kicking off early this year!  From now through December 2nd, everything is 10-20% off!  Please go to our order page for more details.

I hope you will be joining me at CodeRage XI!  Happy CodeSmithing!

Post Mortem: Building a Business Class 3D Application in Firemonkey


For my day job, I recently had the chance to build a business class 3D application in Delphi with Firemonkey.  For proprietary reasons, I cannot give you a lot of details about the program, but the purpose was to provide a 3D editor where users could assemble 3D object files into a larger project. I thought my lessons learned for using Firemonkey to build a 3D application were interesting enough to share here).  The basic requirements were:

  • Scan a directory of 3D objects files and XML files (that contained metadata about the object files).
  • Provide a list of these objects that users could drag-and-drop or somehow add to a larger project.
  • Provide a cross-platform editor for assembling 3D object files into a larger project.  The user should be able to move, rotate, and align 3D Objects, as well as be able to change their colors, labels, and opacities.
  • Export some project information in a variety of formats (Excel/csv and PDF were specifically mentioned)
  • Works on Windows and Mac (Linux would have been nice but fortunately was not necessary)

The short answer is that it is just barely possible to build a real business class 3D application with Firemonkey.  You need customers who are flexible in their requirements and willing to work around, or live with, the defects in the 3D part of the Firemonkey framework.  For the long answer, keep reading.

What went right

Delphi.  Delphi is an amazingly mature product with a rich and powerful language.  Its libraries for everything between XML support to visual components like TListBox are incredibly complete and mature.  Between the productivity that Delphi gives a developer (specifically in developing the GUI) and my 20 years of experience of working in Delphi, I managed to build the application in a fraction of the time expected and with tons more features than expected.  I was given 9 months to build a prototype application. Instead, I just released the first feature complete, beta version of the production application.  I had a prototype in 2 months that was so good that it blew my customers and colleagues socks off and was directed to go forward in making this the production application.  I was consistently ahead of schedule and adding more features than expected.

Firemonkey on Windows.  Firemonkey, at least the non-3D portion of it, has matured nicely and was able to give most features I needed out of the box.  It worked well with only small problems and bugs.  (The TWebBrowser that I used for displaying the XML files was one glaring exception.  Until the Delphi Berlin 10.1 Update, I could not use Delphi Berlin at all because of bugs in this component).  Finally, I will say that I would not have wanted to use FMX before Seattle, certainly no earlier than XE8.  The speed of the compiled application was adequate, at least on Windows, and the compile-debug cycle on Windows is, as always with Delphi, amazing (I also work on a large Visual Studio C++ project and I often forget what I was working on by the time the darn thing finishes compiling).  The size of the application exe, while large, was reasonable and did not bother my customers.

For exporting, Delphi TStrings class makes it ridiculously easy to make comma-separated files.  Unfortunately, for PDF, Delphi does not have anything that can create PDFs out of the box.  We ended up purchasing TMS Software’s Rich Editor product.  This worked relatively well for us – we were able to add RTF and HTML export as a bonus easily.  Unfortunately, its PDF export is buggy.  We have a bug report in with them and hopefully that will be fixed soon.

Firemonkey 3D Framework.  The Firemonkey 3D framework is something that belongs in both what went right and in what went wrong.  I had the most problems, whether limitations or bugs, with the FMX 3D framework, but ultimately, it barely, just barely, was good enough for the business class 3D application I had to make.  I would not recommend doing 3D in FMX for better than business class applications.  Even with business class 3D applications, it will not necessarily work for all cases and you definitely need flexible customers.  You need to compare your requirements with what FMX 3D framework provides.  For example, if I had needed to modify and save the 3D object files, the project would have had to been abandoned.

But what it did do, it did just good enough.  And, like all things Delphi, it was easy to put something together quickly that visually looked very professional.

What went wrong

Firemonkey 3D Framework.  As I mentioned in the what went right section, the Firemonkey 3D Framework deserves top-billing in what went wrong.  I had the most problems, whether limitations or bugs, with the FMX 3D framework.

The biggest problem was its limited 3D format support.  The customer wanted to read 3D STEP files, but this is not possible in Delphi.  Thankfully, they were willing to accept converting the files to something the FMX 3D framework can read: OBJs or DAEs.  Unfortunately, reading .OBJs, while good on Windows, is really sllooooooooooooww on Mac.  My tests had the Mac version reading OBJs 5-10 times slower than on a Windows machine.   I also never did figure out how to read textures for the OBJs (thankfully, this was not required).  Loading DAEs is actually fast enough on Windows and Mac.  Unfortunately, Delphi’s DAE importer does not support trifolds or tristrips in the DAE file.  As this is what the converter programs were producing, we could not use DAEs (I managed to hack the importer enough to load trifolds and test the speed of loading DAEs, but the surface normals were all scrambled).

The next biggest problem was the opacity of 3D objects.  As reported by Eric Grange 5 years ago,

FireMonkey (as of now) doesn’t support rendering semi-transparent objects in 3D.

FireMonkey only supports blending of semi-transparent objects (either through the Opacity property or because of their texture, for instance a semi-transparent PNG image), but blending alone is not enough to get it right in 3D with a Z-Buffer(which is what FMX, and most 3D apps are using).

Thankfully, this was not a showstopper with the application, though it easily could have been.  Opacity was a nice-to-have, not required.  My customers were willing to live with the problem.

Finally, the FireMonkey 3D framework is just buggy and limited, probably the most buggy portion of the FMX libraries.  For example, if you load a 3D object into a TModel3D that is parented to the viewport, the 3D object loads correctly.  However, if you do the same thing where the TModel3D.Parent is nil, the scale of the 3D object is all wrong.  Changing the parent later will not correct it.  I also never figured out how to get the size of the mesh from the file and adjust the control.  The mesh would always resize to the dimensions of the control, distorting its appearance..  Luckily, I could impose a requirement on the XML files that they would provide the width, height, and depth of the 3D object.  I could first adjust the control size of the TModel3D based on the XML and then load the 3D object file.

Other problems?  Collision detection is limited in Firemonkey 3D.  You can do bounding box collision detection but nothing more precise.  A final minor gripe is that I wish that there was a 3D manipulator control that I could attach to a 3D model.  For example, arrows for dragging the control along an axis and rings to rotate the object.  Again, luckily, for now, my customers were content with using sliders to change the location and rotation.  Perhaps in the future I will add this myself, but this is, to my mind, a basic control that should be added to the Firemonkey 3D framework.

Firemonkey on Mac.  Firemonkey on Mac does not nearly have the maturity of the Windows side.  Partly, I am sure that this is due to the awkwardness of debugging Mac applications.  It is much slower to deploy and debug applications in Mac.  In general, Firemonkey on Mac worked but there were times when it stalled the project.  It is best to test your cross-platform application early and often.  It will not just work, at least to a professional level  The biggest problem I encountered is the slow loading of OBJ files.  Another minor problem is with shortcuts.  There is no way to set both platforms shortcuts easily.  It would be nice if there was an option to automatically convert Ctrl+ from windows to Cmd+ on Mac.  As it was, I IFDEFed Mac code to override the shortcuts on the actions on form creation.


In conclusion, it is possible to create business class 3D applications with Delphi and Firemonkey.  However, you need to be very aware of the limitations imposed and how it will impact fulfilling your requirements.  Be aware that the 3D framework is the orphaned stepchild of the Firemonkey framework.  It is difficult to see any improvements in the 3D portion of the architecture since Firemonkey was launched 5 years ago.  It is obvious in my mind that 3D was a marketing checkbox and that this part of the framework was allowed to languish afterwards. You cannot count on updates to fix missing features from Embarcadero/Idera.

That said, if you can live with the 3D support as it is now and it fulfills your requirements (or you have flexible customers), the Firemonkey framework has matured greatly in other areas in the past few years.  That, combined with the awesome force multiplier that is the regular Delphi development, means that you can create quickly and easily powerful, cross-platform, business class 3D applications.

Views expressed are my own and do not necessarily reflect  those of my employer, or anyone else.

That is all for today.  Happy CodeSmithing!

Get a Free RSCL license at Delphi Developer Days

RiverSoftAVG is proud to announce that it is a sponsor of the Delphi Developer Days (DDD) 2016 Tour this coming Fall.  The RiverSoftAVG SVG Component Library and IMPACT Bundle will be one of the prizes in the DDD raffles at the end of each tour stop.

Even though this is the first time that RiverSoftAVG is helping sponsor the DDD, this is not the first time I have participated.  I have attended the DDD many times, and I always learn some great stuff.  I like to think of myself as an expert Delphi developer, but Cary, Loy, and their developer partner (whether Marco or Nick or Dr Bob) has always taught me something new.  And even if you think you know everything about Delphi, it is also a great way to get an in-depth dive to new features in the latest Delphi version.  I highly recommend going to the Delphi Developer Days if your company can swing it.  It is worth the money, and this time you may be able to save $99 and get a free license of the RiverSoftAVG SVG Component Library and IMPACT! 🙂

Happy CodeSmithing!

Documenting Components – a mini-review

I have a love/hate relationship with documenting my code and components.  Similar to working out on a treadmill or lifting weights, I hate doing the work but I love the results.  🙂  I am of the firm opinion that documentation is what separates professional code from merely good code.  Your code can be the most well-tested and fully featured code in the world, but, unless your code is extremely small, it will always be an amateur product without documentation.  Documentation (along with demos) is required in order for your users to have even the hope of fully using the features of your classes and components.  (I must admit Embarcadero has been guilty of poor documentation; the only saving grace is that they provide the source code and Delphi was at the time so far ahead of other products.  Still, it can take years to figure out their code without documentation).  All things being relatively equal, I will always choose the package that has the better documentation (sometimes even when it is not so equal).

All that said, I have been documenting Delphi components for almost 20 years.  I do not officially release a component until its documentation is done.  Unfortunately, that is a lot of work, and I have to admit that I have failed to create (or more accurately maintain) proper documentation.  It feels like it must be an exaggeration, but documentation seems to take as much time as actually producing the code.  Anything that can make process faster, more accurate, and better is worth it.

I started out using HelpScribble, which for its time was pretty good and reasonably priced.  It would scan your code and generate a skeleton for you to fill in.  The output looked pretty good.  But maintaining your help documentation was a real problem.  Change your code and that skeleton was out of date, and there was no way to rescan the code and only have changes added.  I spent many years trying to fix or overcome HelpScribble’s issues by making a companion application, HelpScribble Apprentice.  HelpScribble had many shortcomings, and it only got worse as time went on as there were few updates.

Which brings me to Documentation Insight by DevJet Software.  Several years ago, I made the leap and switched to Documentation Insight for all new products.  I am extremely glad I did.  Documentation Insight is the best product for documenting Delphi components I have ever used.

Having the documentation embedded with the code is a game changer.  I know other languages have had this for years (see JavaDoc).  However, for Delphi, this was transformational.  Change the code, and the generated documentation will change too.  The integrated DocInsight in the IDE will show the documentation when you hover over a class property or method (when it is working 🙁 ) .  If you browse to the actual code, you can see the documentation right there.  Often, you do not need to even bring up the help file.  It just makes you a more productive programmer using code documented with Documentation Insight.

Documentation Insight handles most of the messy details for documenting code you write.  When you generate help files, it automatically creates links for class hierarchies and types.  It automatically organizes topics into properties, events, and methods.  Change the name of a method or property, the help topic updates (though not links you created to it unfortunately).  Change names of parameters in a method, it changes (though if you documented the old parameters, you need to change it manually).  Change the types of parameters, ditto.  Change the number of parameters, yup.  Maintaining help documentation for components is so much easier with Documentation Insight.  By having the documentation right there next to the code, it encourages you to change the documentation immediately rather than waiting.  I also like that you can create Help & Manual projects, web documentation, or standard .chm documentation.

That said, just like with creating documentation itself, I have a love/hate relationship with Documentation Insight.  Documentation Insight is the best product for documenting Delphi components, true, but sometimes it can be an incredibly frustrating tool for documenting code too.

For a start, it is buggy.  Thankfully not so much when you are writing your help topics (it would be disastrous if it corrupted source code), but when generating the documentation. (See here for an example.  Scroll down to Options under properties.  Note that even though this output is from Help & Manual, I have verified that this is what is given to H&M by Documentation Insight.  And if you generate directly to web help, this is also what you would get.  Disclaimer, this option was generated with v3.4.10.7, which was current as of when my subscription stopped last December.  Documentation Insight’s Changelog stops even before then so it is hard to know what they fixed).

For a tool that is all about producing documentation, its documentation is pretty poor too.  Too often, it is a matter of trying something, generating the help, and then checking if it worked.  Have an event property that is being classified under properties instead of events?  There is a one-line statement about what to do with no examples.  Scoping your links is a matter of trial and error.  In general, it is best to scope your links as little as possible, e.g.,

<see cref=”Assign”>Assign</see>

instead of

<see cref=”MyUnit|TMyClass.Assign”>Assign</see>

Doing your scoping this way makes copying a help topic to another class easier.  However, it doesn’t always work and the reasons why are not always obvious.  By default, the links that Documentation Insight creates for you are tightly scoped so you need to edit them often.

Finally, by itself, Documentation Insight is not really enough.  You need another help tool, such as Help and Manual, for the generic topics not tied to Delphi code (e.g., Introduction, gallery, etc).  For a product so tightly focused on doing one thing, it is pretty expensive (the Enterprise version which I bought is $319) and the subscription is not worth it at all as the updates don’t justify the price.  Since the v3 release in 2013, there have only been bug fixes, updates to work in the latest versions of Delphi, and very minor improvements.  At almost half the cost of the original product per year, you are paying a high price for what are maintenance releases in other products.

In conclusion, Documentation Insight is the best product for documenting your code in Delphi and you really need to buy it if you are documenting Delphi code.  Sadly, it has some severe flaws and the subscription is overpriced, but ultimately, I think it is worth buying.  It provides the best output and maintenance for Delphi code that I have seen.  Thumbs up!  🙂

What is a good blog (and blogger) to me?

What makes a good blog?  What blogs, and bloggers, do I like and why?  This question gained extra relevancy for me recently as I have seen some examples of what I believe are not good blogs.  To me, a good blog is not:

  • a list of recanned company advertisements designed to push traffic to your web site
  • a series of articles that just republishes common information on the web
  • a retweet of someone else’s blog
  • a pseudo spambot for filling up your RSS feeds with multiple posts per day
  • relentlessly negative about Delphi

To me, a good blog provides articles that either educate or entertain.  They provide new perspectives on issues or new information.  They share the hard-earned wisdom of experienced Delphi coders.

When I started this blog, I gave myself a few goals for the blog:

  • Share information on Delphi issues that may be useful to others
  • Give myself a voice to argue, hopefully respectfully, for Delphi features and code styles
  • Occasionally, provide in-depth articles on compelling features about RiverSoftAVG products where a blog format is more appropriate than a help topic
  • Hopefully blog often enough to make the blog useful

Now, obviously, a blogger won’t always be successful at this, and beauty, as they say, is in the eyes of the beholder.  But those are the goals I set myself and are the blogs I pay attention to. I have failed occasionally (and ironically this post probably fails these goals 🙂 ) but I try to uphold those goals.

Examples of blogs I like are Delphi Code Monkey and David Millington’s Parnassus blog.  There are quite a few Embarcadero blogs I like.  Of course, David I’s Sip from the Firehose is a go-to blog.  Other examples: Sarina DuPont’s blog is always full of in-depth articles about features of Delphi as are Pawel Glowacki’s and Stephan Ball’s.  Closer to the entertainment side, Jim McKeeth’s Podcast at Delphi is good. (Note that not all blogs I like are listed, these are just notable ones to me)

My go-to aggregator’s are Begin End and FMX Express.  Begin End is my favorite Delphi site.  If someone has a Delphi’s blog, it will show up there.  And if a blog shows up there too often or is from someone I dislike, I can quiet that blog.  However, I hate to do that.  I believe a person will probably, eventually write something useful.  Sadly, if a blog is stopping me from seeing useful and good blogs from other people though, it has got to go.  But I would much rather a blogger respect their readers and not have to go that route.

Well, now that I have gotten that off my chest, back to Coding blogs. 🙂  Happy CodeSmithing!

Quick Tip: Fixing “Could not convert variant of type (Null) into type…”

I often use the XML Data Binding wizard in Delphi.  However, it doesn’t seem to have been given a lot of attention from Borland/Inprise/Borland/CodeGear/Embarcadero/Idera.  And unfortunately, out of the box what it generates is often error prone, apparently not supporting optional elements/attributes.

When the generated code tries to read an optional element or attribute, you will get a “Could not convert variant of type (Null) into type” exception.  The offending code usually looks like this:

function TXMLMyType.Get_OptionalElement: Single;
 Result := ChildNodes['OptionalName'].NodeValue;

If you do a little googling, you will see that people are still asking questions about this even pretty recently.  The suggested fix you will often discover is labor intensive if you have a lot of optional elements/attributes and will get wiped out if you rerun the XML Data Binding wizard:

 if VarIsNull(ChildNodes['selected'].NodeValue) then
    Result := 0; // or false or empty string, etc
    Result := ChildNodes['selected'].NodeValue;

Hilariously in my mind, there is still an open ticket from 2002 about this issue:

However, it seems the <insert-company-name-which-owns-Delphi> addressed this issue, probably years ago, and the fix/workaround is easy.  You need to include the Variants unit and set the NullStrictConvert global variable to false:

NullStrictConvert := False

As the documentation states:

<strong class="selflink">NullStrictConvert</strong> determines the outcome of attempts to convert <a title="System.Variants.Null" href="">Null</a> variants to other types. If <strong class="selflink">NullStrictConvert</strong> is true (default), attempting to convert a <a title="System.Variants.Null" href="">Null</a> variant raises a<a title="System.Variants.EVariantTypeCastError" href="">EVariantTypeCastError</a>, unless the conversion is to a custom variant that defines a conversion from <a title="System.Variants.Null" href="">Null</a>. If <strong class="selflink">NullStrictConvert</strong> is false, then conversion from <a title="System.Variants.Null" href="">Null</a> follows the following rules

Now, the XML Data Binding code will silently convert NULL to 0, false, or empty string without a problem.  I wanted to publicize this fix.  I have been bitten by this exception more times than I can count and if I had known of the workaround, it would have made my life much easier.

That’s it for today.  I hope everyone is enjoying their Summer (or Winter in the southern hemisphere).  Happy CodeSmithing!

Use Supersampling for offscreen bitmaps on Delphi Mobile

A common method for painting drawings is to draw to an offscreen bitmap and then draw the bitmap to your canvas (say a TPaintBox) as needed.  This is generally used when you create a drawing that does not change often; drawing once to an offscreen bitmap and then as needed on repaints to the real canvas can be very efficient.  However, if you have ever used offscreen bitmaps with Delphi on iOS or Android, you have quickly realized that the quality of the DrawBitmap method is awful.  There seems to be no anti-aliasing performed at all with DrawBitmap and the typical output looks terrible:

The quality of DrawBitmap from an offscreen bitmap to the screen is terrible on mobile platforms with no anti-aliasing

The quality of DrawBitmap from an offscreen bitmap to the screen is terrible on mobile platforms with no anti-aliasing

As you can see, the curve of the ellipse and the diagonal lines look jaggy, with no smooth transition or blur between the lines and the colors around them.

The image above comes from a sample project where I create an offscreen bitmap the same size as a TPaintBox.  Then, I draw the bitmap to the paintbox on its OnPaint event.

It is easy to not realize how bad the DrawBitmap is on mobile until late in your development as the output looks great on the desktop (Windows and OSX) platforms.  Unfortunately, there seem to be no way to improve the quality of the TCanvas.DrawBitmap function directly.  The best you can do is improve the quality of the entire form by changing its Quality property to HighQuality.  However, even this is often not enough:

High Quality forms do not fix the problem

High Quality forms do not fix the problem

Note that not using an offscreen bitmap drastically improves the quality over drawing to a bitmap and then drawing the bitmap to the final canvas.  If you just draw directly to the TPaintBox in its OnPaint event, the output looks pretty good:

Drawing directly to the final canvas has good quality on Mobile

Drawing directly to the final canvas has good quality on Mobile

However, presumably if you are reading this post, you need to use the Offscreen bitmap for various reasons such as its speed efficiency, so what else can we do?

An old technique, supersampling, can drastically improve the quality of the output and be more targeted than the blanket TForm.Quality property.  Supersampling is a brute force anti-aliasing technique where you draw your image on your offscreen bitmap at a much higher resolution (2x, 4x, 8x) than the one being displayed and then it is shrunk back down when it is drawn to your final canvas. The result is a downsampled image with smooth lines and no jaggies:

Drawing to an offscreen bitmap that is twice the width and height dramatically improves quality

Drawing to an offscreen bitmap that is twice the width and height dramatically improves quality which is arguably even better quality than drawing directly to the final canvas.

To perform supersampling, you create a bitmap that is twice as big in width and height (or 4x, 8x etc):

 OffscreenBitmap := TBitmap.Create;

Then you need to scale all your draw operations to the bigger bitmap.  You might think that this technique requires a more complex drawing routine, as you need to scale every draw and fill operation by the scale factor.  However, that is not the case.  By scaling the matrix of the TCanvas, you do not need to change your drawing routines at all:

procedure TForm1.Draw(aRect: TRectF; aCanvas: TCanvas; ScaleFactor: Integer);
 aMatrix: TMatrix;
 aMatrix := aCanvas.Matrix;
  <strong>aCanvas.SetMatrix(aCanvas.Matrix*TMatrix.CreateScaling(ScaleFactor, ScaleFactor));</strong>
  aCanvas.Fill.Kind := TBrushKind.Solid;
  aCanvas.Fill.Color := TAlphaColorRec.Blue;
  aCanvas.Stroke.Kind := TBrushKind.Solid;
  aCanvas.Stroke.Thickness := 3;
  aCanvas.Stroke.Color := TAlphaColorRec.Green;
  aCanvas.DrawRect(aRect, 10, 10, AllCorners, 1);
  aCanvas.FillEllipse(aRect, 1);
  aCanvas.DrawEllipse(aRect, 1);
  aCanvas.Stroke.Color := TAlphaColorRec.Red;
  aCanvas.Stroke.Thickness := 5;
  aCanvas.DrawLine(aRect.TopLeft, aRect.BottomRight, 0.7);
  aCanvas.DrawLine(PointF(aRect.Right, 0), PointF(0, aRect.Bottom), 0.7);
  aCanvas.Stroke.Color := TAlphaColorRec.Red;
  aCanvas.Stroke.Thickness := 1;
  aCanvas.DrawLine(PointF(aRect.Width / 2, 0), PointF(aRect.Right, aRect.Bottom / 2), 1);
  aCanvas.DrawLine(PointF(aRect.Right, aRect.Bottom / 2), PointF(aRect.Width / 2, aRect.Bottom), 1);
  aCanvas.DrawLine(PointF(aRect.Width / 2, aRect.Bottom), PointF(0, aRect.Bottom / 2), 1);
  aCanvas.DrawLine(PointF(0, aRect.Bottom / 2), PointF(aRect.Width / 2, 0), 1);

The aRect parameter is the size of the final TPaintBox.ClipRect.  The aCanvas parameter is the offscreen bitmaps canvas.  Here is how the routine is called:

 Draw(RectF(0, 0, PaintBox1.Width, PaintBox1.Height),
 (Sender as TRadioButton).Tag);
 PaintBox1.Repaint; // changed the offscreen bitmap, trigger the paint

Finally, in the TPaintBox.OnPaint event, draw the offscreen bitmap to the canvas:

procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
 aRect: TRectF;
 aRect := RectF(0,0,OffscreenBitmap.Width,OffscreenBitmap.Height);
 Canvas.DrawBitmap(OffscreenBitmap, aRect, PaintBox1.ClipRect, 1, False);

The scale factor can be as large as you want but in practice going above 4x or at the most 8x is unneccessary.  Here is the 4x output.

Drawing to a 4x bitmap looks almost perfect

Drawing to a 4x bitmap looks almost perfect

The output looks fantastic, but there are downsides.  The biggest is that this is a memory intensive technique.  Doubling the width and height of your bitmap quadruples the amount of memory you use.   Quadrupling the width and height uses 16x the amount of memory over your original offscreen bitmap!  However, since this can be a targeted technique, you only have to increase the size of the offscreen bitmaps for the important painting.

Another major issue with this technique can be speed.  Since you are drawing 4x or 16x more pixels, the offscreen drawing is naturally going to take longer.  And then, of course, drawing back to the main canvas adds time as well.  However, this is offset by the fact that you generally use offscreen bitmaps for things that don’t change as often and the draw bitmap function can still be much faster than drawing directly to the canvas depending on the complexity of your drawing.

The code for the example is here: Super Sampling Example Project.  Feel free to use as you wish.

Note that the RiverSoftAVG SVG Component Library and RiverSoftAVG IMPACT multimedia instrument package add-on use this technique.  There is a Quality property that provides the scale factor for most controls.  By default in FMX, when drawing the TRSSVGImage control to a buffer (Buffered=True), rendering the SVG to an image list, or drawing an instrument, the Quality property is 2 which provides a good balance between memory, speed, and quality.  Change the Quality property from 1 (no supersampling) to 8 (8x supersampling consuming 64x the memory).  The Quality property can also improve the look for extremely small final bitmaps (16×16, 32×32).  Since the memory usage is minimal at these small sizes, this property can really help with small bitmaps.

That is all for today.  Hopefully this tip is useful to some of you.  Happy CodeSmithing!

Video of IMPACT in Action

So I am trying a new thing with the release of RiverSoftAVG IMPACT multimedia gauges and gadgets. now has a youtube channel!  I have created a short 5 minute video about the product for your information and entertainment.


Please let me know what you think, good and bad, as it turned out to be a lot more work than I expected.  I want to know if people find these useful or not.  If people like them, I might start creating tutorials and other videos.

Thanks and Happy CodeSmithing!