AdaCore Blog

AdaFractal Part1: Ada with a Portable GUI

by Rob Tice

The AdaFractal project is exactly what it sounds like; fractals generated using Ada. The code, which can be found here , calculates a Mandelbrot set using a fairly common calculation. Nothing fancy going on here. However, how are we going to display the fractal once its calculated?

To Graphics Library or not to Graphics Library?

We could use GTKAda, the Ada binding to GTK, to display a window… But that means we are stuck with GTK which really is overkill for displaying our little image, and is only supported on Windows and Linux... And Solaris if you’re into that sort of thing...

What’s more portable and easier to handle than GTK? Well, doing a GUI in web based technology is very easy. With a few lines of HTML, some Javascript, and some simple CSS we can throw together an entire Instagram of fractals... Fractalgram? Does that exist already? If it doesn’t, someone should get on that.

The Server Solution

The last piece of the puzzle we need is to be able to serve our web files and fractal image to a web browser for rendering. Since we want to ensure portability, we should probably leverage the inherent portability of Ada and use AWS. No, it’s not the same aws that you may use for cloud storage. This AWS is the Ada Web Server which is a small and powerful HTTP server that has support for SOAP/WSDL, Server Push, HTTPS/SSL and client HTTP. If you want to know more about the other aws, check out the blog post Amazon Relies on Formal Methods for the Security of AWS. For the rest of this blog series, let’s assume AWS stands for Ada Web Server.

Because AWS is written entirely in Ada, it is incredibly portable. The current build targets include UNIX-like OS’s, Windows, macOS, and VxWorks for operating systems and ARM, x86, and PPC for target architectures. Just by changing the compiler I have been able to recompile and run this application, with no source code differences, for QNX on 64 bit ARM, macOS, Windows, and Linux on 64 bit x86, and VxWorks 6.9 on 32 bit ARM. That’s pretty portable!

For those who have access to GNATPro on Windows, Linux, or macOS, you can find AWS included with the toolsuite installation. For GNAT Community users, or GNATPro users who would like to compile AWS for a different target, you can build the library from the source located on the AdaCore GitHub.

Hooking up the pieces

Hooking up the fractal application to AWS was pretty easy. It only required creating a URI router which takes incoming GET requests and dispatches them to the correct worker function. The full router tree can be found in the router_cb.adb file.

function Router (Request : AWS.Status.Data) return AWS.Response.Data
is
   URI      : constant String := AWS.Status.URI (Request);
   Filename : constant String := "web/" & URI (2 .. URI'Last);
begin
   --  Main server access point
   if URI = "/" then
      --  main page
      return AWS.Response.File (AWS.MIME.Text_HTML, "web/html/index.html");
      
   --  Requests a new image from the server
   elsif URI = "/fractal" then   
      return AWS.Response.Build
        (Content_Type  => AWS.MIME.Application_Octet_Stream,
         Message_Body  => Compute_Image);       
      
   --  Serve basic files
   elsif AWS.Utils.Is_Regular_File (Filename) then
      return AWS.Response.File
        (Content_Type => AWS.MIME.Content_Type (Filename),
         Filename     => Filename);
      
   --  404 not found
   else
      Put_Line ("Could not find file: " & Filename);

      return AWS.Response.Acknowledge
        (AWS.Messages.S404,
         "<p>Page '" & URI & "' Not found.");
   end if;

end Router;

Here is a sample of a simplified router tree. The Router function is registered as a callback with the AWS framework. It is called whenever a new GET request is received by the server. If the request is for the index page (“/”), we send back the index.html page. If we receive “/fractal” we recompute the fractal image and return the pixel buffer. If the URI is a file, like a css or js file, we simply serve the file. If it’s anything else, we respond with a 404 error.

The AdaFractal app uses a slightly more complex router tree in order to handle input from the user, compute server side zoom of the fractal, and handle computing images of different sizes based on the size of the browser. On the front-end, all of these requests and responses are handled by jQuery. The image that is computed on the backend is an array of pixels using the following layout:

type Pixel is record
   Red   : Color;
   Green : Color;
   Blue  : Color;
   Alpha : Color;
end record
  with Size => 32;

for Pixel use record
   Red at 0 range 0 .. 7;
   Green at 1 range 0 .. 7;
   Blue at 2 range 0 .. 7;
   Alpha at 3 range 0 .. 7;
end record;

type Pixel_Array is array
  (Natural range <>) of Pixel
  with Pack;

A Pixel_Array with the computed image is sent as the response to the fractal request. The javascript on the front-end overlays that onto a Canvas object which is rendered on the page. No fancy backend image library required!

AWS serves our application to whichever port we specify (the default port is 8080). We can either point our browser to localhost:8080 (127.0.0.1:8080) or if we have a headless device (such as a Raspberry PI, a QNX board, or a VxWorks solution) we can point the browser from another device to the IP address of the headless device port 8080 to view the fractal.

How about the math and performance?

An interesting side experiment came from this project after trying to increase the performance of the application. After implementing a thread pool to parallelize the math, I decided to try using fixed point types for the math. The implementation of the thread pool and the fixed point versus floating point math performance will be presented in Part 2 of this blog post.

Posted in #AWS    #QNX    #VxWorks    #GUI   

About Rob Tice

Rob Tice joined AdaCore in 2016 as a Technical Account Manager. With a degree in Electrical Engineering from Rensselaer Polytechnic Institute, he has worked in the industrial automation and music technology industries as a hardware engineer and embedded software engineer before joining AdaCore.