This chapter describes nGI from the architectural and conceptual perspective. This chapter talks both about the overall architecture at a high level, as well as the concepts used within the code.
When you look at nGI from a high level you see a set of hierarchical directories:
| Folder | Purpose |
|---|---|
ngi |
The top level or root director of nGI. |
ngi/bin |
Binary files are grouped here. |
ngi/book |
The source files of this book are contained in this directory and its subdirectories. |
ngi/doc |
This directory contains the files that are used to generate the reference documentation. The reference documentation is built during the build process and is extracted from the source code files. This ensures that the reference documentation is always synchronized to the actual code. |
ngi/images |
This directory contains the images used by various sample and test programs. |
ngi/include |
This directory contains the nGI source code. Most of the code is in this directory, but some optional code components are in subdirectories. |
ngi/modules |
Contains the interface code to cameras and frame grabbers. |
ngi/samples |
This directory contains sample programs that should give you a quick start when using nGI. These samples show functional aspects of nGI and they often also show the intended way to use nGI. |
ngi/tests |
We had testing in mind while devloping nGI. This directory contains the extensive test and benchmarking suite of nGI. Besides automated testing, this is also an invaluable source of education, since again the complete source code of the tests is available. |
ngi/tools |
Contains tools that are needed during the build process. |
ngi/wrappers |
Contains the wrapping code that is needed for automated creation of the .NET wrapper. |
The following chapters tell you a little bit more about these architectural aspects of nGI.
Coding Guidelines
In general I do not believe very much in coding guidelines, since I have seen how they can go overboard. However, they can help to keep a code basis consistent, and so a few guidelines are used within nGI.
My golden rule is that coding guidelines are good if they can be put on one sheet of paper. If you need a book to write down the guidelines, then something is wrong. Therefore, you can expect this chapter to be fairly short.
So here are the guidelines:
- nGI is a header only library. Everything is contained in header files (*.h). There are no implementation files. Period.
- The line length of the nGI header files is 120 characters. Lines that are longer have to be broken to multiple lines.
- If you write code that is to be included in nGI, follow the style of the existing nGI headers. In particular, do not use camelCasing or PascalCasing, but use_underscores to separate words in identifiers.
- Doxygen is used to create the nGI reference documentation. Thus,
everything public should be documented with the necessary doxygen
commands. Don't go overboard with doxygen tags, just use the
triple-slash (
///for brief and//!for detailed) comment style.
Testing
nGI is a big, complex piece of software. Any system this big needs considerable efforts to being tested. Within nGI we have tests that run automatically during a build, and we have sample code that can be used to ad-hoc test certain aspects.
nGI partitions its tests into so called unit tests, non-regression tests and benchmarks.
Unit tests focus on small units in the code and test their workings isolated from the rest of nGI.
Non-regression tests test the most common usage scenarios, and make sure that there are no regressions as the code evolves and changes over time.
Benchmarks test the running time of algorithms in real-world scenarios and make sure that the performance of nGI stays within defined bounds.
Unit Tests
Unit tests are tests written as small code snippets that test a certain 'unit'. Within nGI, these units are classes and their defined operations. nGI uses Boost.Test for the unit tests.
Non-regression Tests
Non-regression tests make sure that regressions are not allowed to creep in while the nGI code evolves and is developed further. It makes sure that those regressions are detected and that measures are taken to correct them. nGI uses Boost.Test for the non-regression testing as well.
The non-regression tests are organized in a way that they store their
results into a result directory with the name now. The test then
compares the result in now with a previously stored result in the
directory previous. If both are the same, the test passes. If there
are regressions, the test fails. This scheme provides very easy setup of
non-regressions tests.
- Write a new test case.
- Run the new test case. It will fail, since it has nothing to compare
to in directory
previous. - Copy the resulting file the test created in directory
nowto directoryprevious. This is now the basis to test further potential regressions against. - Rerun the test. Now the test passes, and it will pass in further runs as long as there are no regressions introduced by errors in the code.
Benchmarking
Benchmarks are used to measure the relative performance of the nGI imaging algorithms. We are not so much interested in absolute performance, since this can depend very much on specific hardware.
The benchmarks are written as macros and the time is measured using nGI timing functions. The measured time is then logged with the output.
The benchmarks do not return their results as a time measure, such as some fraction of a second, but as a value specifying clocks per pixel -CPP. The CPP measure is the number of CPU clocks per pixel, which gives you an indication of how efficient some algorithm is implemented.
If you run the benchmark on your machine, the results might be different because you might be using a different CPU. The clocks per pixel are independent of the CPU clock speed, but they may be affected by the CPU architecture. Intel or AMD, as well as different models within the product lines of these manufacturers differ, so it is likely that you may get different results.
Graphics Engines
nGI provides flexible means to visualize images and associated graphical annotations. All the rendering commands go through a portability layer, which directs the commands to various graphics back ends. The graphics system used in nGI is two-dimensional.

The user can select the graphics engine or back end with a #define.
The back ends have different properties that are explained in the
following table.
| Graphics Engine | #define |
Operating System | Hardware Acceleration | Comment |
|---|---|---|---|---|
| Direct2d | GRAPHICS_ENGINE_DIRECT2D |
Windows | yes | Direct2D is available on the Windows Vista and higher (Windows 7, Windows 8) operating systems. It supports an appropriate graphics card for acceleration. |
| GDI+ | GRAPHICS_ENGINE_GDIPLUS |
Windows | no | - |
| OpenGL | GRAPHICS_ENGINE_OPENGL |
portable | yes | - |
| Qt | GRAPHICS_ENGINE_QT |
portable | no | If Qt is used for graphics, it is also used as the window system. |
The CMake based build system allows selection of these graphics engines in the GUI.
Code Layers
nGI is implemented in three layers that are stacked on top of each other. A fourth layer is constituted by the applications you or other users write.

The template layer or core makes heavy use of C++ templates. Code written against this layer must take the templates into account and may look intimidating to the casual C++ user.
The native layer instantiates specific template parameters and flattens out the class hierarchies. This makes nGI usable with languages that can only consume a flat C interface.
The .NET layer is an implementation of the relevant glue code for .NET and can be programmed in C# or Visual Basic or any other language supporting .NET.
Template Layer (C++)
The template layer provides the most flexibility, but with this flexibility there comes a price. The heavy use of templates and template types may make this layer difficult to understand. Here is a little code sample that shows a small sample that loads an image and displays it in a window.
#include <ngi_image.h>
#include <ngi_rgb.h>
#include <ngi_locator.h>
#include <ngi_view.h>
#include <ngi_widget_image.h>
#include <ngi_display.h>
#include <ngi_demo_main.h>
using namespace ngi;
typedef image<rgb<unsigned char> > image_type;
typedef locator<image_type::value_type> locator_type;
typedef view<locator_type> view_type;
typedef widget_image<view_type> widget_type;
typedef display<widget_type> display_type;
int main(int argc, char* argv[])
{
image_type img = import_image<image_type>(TEXT("fish.png"));
display_type wnd(img.get_view(), TEXT("Fish"));
run_message_loop();
return 0;
}
You see that you need a lot of includes and type definitions that are not really intuitive, unless you know a lot about this layer. The code itself inside the main function looks pretty innocent, though. One line to load the image from a file, another line to create a window, and finally a line with a message loop to make the window operational.
.NET Layer
The same program written in C# using the .NET layer looks considerably simpler.
using System;
using System.Windows.Forms;
using Ngi;
namespace file_access_3
{
class Program
{
static void Main(string[] args)
{
Image img = Image.ImportImage("fish.png");
Display wnd = new Display(new WidgetImage(img.View), "Fish");
Application.Run();
}
}
}
Compared with the template layer version the non-intuitive includes and
type definitions are not needed at all. The code inside the Main
function looks pretty similar, though.
Code Generation
Code for the higher layers (native layer, .NET layer) is generated automatically, using an XML based interface definition and some transformation templates. The interface description file written in XML controls the code generation. It consists of a high-level description of the classes and their behavior.
Native Layer Code Generation
The native layer consists of a Dll with a native C interface. This means that some types used in the nGI source code cannot be used as is, but must be converted into their native C counterparts. Data can flow in two directions, i.e. into the native Dll or out of the native Dll. As a simple example, return values flow out of the native Dll and function parameters usually flow into the native Dll.

C has no notion of classes and object. They must therefore be somehow simulated. The native layer uses handles (implemented via void pointers to the original C++ objects) in order to manage the objects.
.NET Layer Code Generation
The .NET layer is split into a native part (written in C++, but with a flat, C-callable interface) and into a managed part (written in C#). Most of the .NET layer is generated automatically, as explained above, but some small portions of it are written by hand.

The whole business of the native Dll is to provide a means that can be called directly by C#, via the ImportDll mechanism.
Names in the C++ interface start with a lower case letter and words in these names are separated by an underscore, i.e. widget_image.
Names in the .NET layer are in PascalCasing, i.e. WidgetImage. The
words are capitalized and tucked together. As such, the naming
conventions used in the C++ and .NET layers are incompatible. A common
naming convention would have been inconvenient, because the C++
community expects the naming convention used in the C++ Standard
Library, while the .NET community expects the naming convention used in
.NET. C++ names look alien to a .NET programmer, and vice versa.
Namespaces
The template layer of nGI is contained completely within namespace
ngi. In order to get at definitions from namespace ngi you have some
possibilities. For example:
Use the scoping operator ::
ngi::buffer buffer;
Use a using declaration:
using namespace ngi;
buffer buffer;
You should always use the scoping operator :: in header files, whereas
it is acceptable to use the using declaration in implementation files.
If you use a using declaration in a header file that is included in
other files, you inject the using declaration into these other files,
and you might get name clashes down the road that are really hard to
track down.