2010-12-21

arclight qt (source)

Arclight is a prototype written to explore app development with the Qt framework. The app loads a 2D polygon from a .svg file, extrudes it into 3D with a beveling algorithm, then lights the resulting geometry. It provides a simple chaining mechanism to stack multiple image processing functions.

DOWNLOAD. C++ project written for the Qt framework. Source is included, as well as an executable for OS X.

I was curious to try out app development with Qt, so I grabbed several previous projects (one in Go, one in Java) and mashed them up into a simple Qt project. This app has no value for end users, it's purely a research project. As such, this post is intended for any developers interested in code details.

This app generates an image by processing a graph of nodes. The nodes are called "FX" in a nod to a previous music app, and the graph is currently just a simple list, which makes it a very simple pipelining architecture. Users can add or remove FX, select FX and set various properties on the selected FX. There is basic project file loading and saving, as well as exporting of the generated image. The FX make use of "arcs" to manipulate their parameters. For example, the Glow FX, used to create inner or outer glows on the shape, can use the arc to change how the glow is rendered by remapping each value (for example, with the right arc, the glow could be inverted).

The app design is fairly standard: A UI operates by setting values on a model, which communicates changes via Qt's "signals and slots" mechanism. When the main view receives a change notice, it starts the rendering engine, which then creates an image by running the FX.

The main concern from the start of development was to mitigate as much as possible any complexity that might arise from concurrency and memory management issues.

Concurrency handling is currently too simple: There's a main thread for the UI, and a separate thread where the graph is processed, rendering the image. This is enough for a prototype, since support for additional CPUs will be localized to the thread that performs rendering. For the threading mechanism, see ui/renderthread and its owner, ui/renderview.

More work has been done on memory management, where I've tried to follow two rules: Whenever possible, memory is allocated on the stack. Anything that needs to be allocated on the heap has a containing class that manages that memory, a class that ideally is solely responsible for allocating and deallocating that type. The main classes involved in this memory scheme are the templates collections/managedlist.h and collections/recyclelist.h. Completely absent is any notion of sharing data between threads, which means the FX exist both in the UI thread and rendering thread, and any edits need to be kept in sync. This is not ideal, but then neither is sharing, locking and managing data.

  • arc. This package contains the mapping functions that form the heart of the system. An "arc" is a simple operation that, given a series of inputs (values of 0 to 1) produces a series of outputs (generally values of 0 to 1, but that's not a hard requirement). The simplest kind of arc would be a straight linear function, one that maps 0 to 0, 1 to 1, etc. More interesting arcs produce things like sine waves, or combine other arcs by addition or multiplication operations.
  • collections. This package contains custom collection template classes. This is unfortunate, every modern environment includes numerous well-implemented collection services. The main reason for writing my own is because I often end up writing performance-critical code, where memory allocation and deallocation can have severe consequences. Since there's no way to guarantee that anyone else's collection classes avoid deallocating memory, I wrote my own. So be warned: All these classes are designed to grow, never shrink. It would be easy enough to add facilities to do so if it's ever required, but I currently haven't. The main collection class is ManagedList, which requires clients to supply a ManagedFactory for allocating and deallocating memory. This is a result of one of my design guidelines, to have all memory management extremely localized. The ManagedFactory objects are designed to be stateless, so the system can have a single static instance for each data type. Note that with the current design, ManagedLists are only appropriate for homogenous data.
  • compositing. The compositing package contains the rules for combining color data, i.e. the pixels that make up an image. Internally, the system stores color data as alpha-premultiplied floats.
  • engine. This would probably better have been named "public," since it's seen as the public interface to the various domain objects, but I'll probably never have a public API so that just didn't really happen. This includes the base classes for the main FX and Arc classes, as well as some facilities to help with cached data during the rendering process, with data in Cell and behaviour in fx::WorkArgs.
  • fx. This package contains the FX responsible for the high-level image processing functions. The main FX are Shape, which creates a polygon from a file, and Bevel, which extrudes the polygon into 3D and lights it. There's also a Glow shape for performing inner and outer glows on the polygon.
  • io. This package contains a SVG file reader and a JSON file reader and writer. The SVG reader is extremely simple and will choke on many, many files. The JSON utilities are a little more complete, with a small writer that can generate either formatted or unformatted output (use the json::PrettyFormat object when instantiating the json::Writer to generate more human-readable results). To make use of JSON reading, you'll need domain objects that implement the json::Reader interface, and a root object you pass to the json::Parser. The Parser is stack-based, with the stack controlled by the Parser, and Readers allowed to push a target object (another Reader) onto the stack at any point. (Note: There are facilities in Qt for JSON parsing; the main reason I have my own are because eventually image data will be embedded in the file format, and I'll want to avoid the overhead of reading the entire file at once, something not possible with any Qt JSON parser that I'm aware of.)
  • math. A collection of useful primitives and functions. The majority of the math package is devoted to the polygon boolean function found in poly/ and the various line/point/etc. distance tests found in tests/
  • model. A poorly named package that contains the top-level domain objects, the project that stores FX and implements the Observer mechanism.

2 comments:

  1. Best I could do with this:

    http://acronyms.thefreedictionary.com/FYYFF

    I'm gonna go with the "foreign youth" one

    ReplyDelete