4.1Overview

4.1.1Motivation

There are three main reasons to use flomaps:

Precision.
A point in a typical bitmap is represented by a few bytes, each of which can have one of 256 distinct values.
In contrast, a point in a flomap is represented by double-precision floating-point numbers, typically between 0.0 and 1.0 inclusive.
This range contains about 4.6 quintillion (or 4.6×1018) distinct values.
While bytes are fine for many applications, their low precision becomes a problem when images are repeatedly operated on, or when their values are built by adding many small amounts—which are often rounded to zero.

Range.
A floating-point value can also represent about 4.6 quintillion distinct intensities above saturation (1.0).
If distinguishing oversaturated values is important, flomaps have the range for it.
Further, floating-point images are closed under pointwise arithmetic (up to floating-point error).

For these reasons, other parts of the images library use flomaps internally, to represent and operate on
RGB and ARGB images, light maps, shadow maps, height maps, and normal maps.

4.1.2Conceptual Model

A flomap is conceptually infinite in its width and height, but has nonzero values in a finite rectangle starting at coordinate 00
and extending to its width and height (exclusive).
A flomap is not conceptually infinite in its components because there is no natural linear order on component coordinates, as the meaning of components depends on programmer intent.

The following example creates a 10×10 bitmap with RGB components, and indexes its top-left red value and two values outside the finite, nonzero rectangle.
It also attempts to index component 3, which doesn’t exist.
Note that flomap-ref accepts its coordinate arguments in a standard order: kxy (with k for komponent).

Many flomap functions, such as flomap-bilinear-ref and flomap-rotate, treat their arguments as if every realxy coordinate has values.
In all such cases, known values are at half-integer coordinates and others are interpolated.

Notice that the plot’s maximum height is above saturation (1.0).
The tallest peak corresponds to the specular highlight (the shiny part) on the bomb.
Specular highlights are one case where it is important to operate on oversaturated values without truncating them—until it is time to display the image.

If we have a w×h flomap and consider its known values as being at half-integer coordinates, the exact center of the flomap is at (*1/2w)(*1/2h).
When unknown values are estimated using bilinear interpolation, the finite rectangle containing all the known and estimated nonzero values is from -1/2-1/2 to (+w1/2)(+h1/2).

4.1.3Opacity (Alpha Components)

A partially transparent flomap is simply a flomap in which component 0 is assumed to be an alpha (opacity) component.
The other components should be multiplied by their corresponding alpha value;
i.e. an RGB triple 1.00.50.25 with opacity 0.5 should be represented
by 0.50.50.250.125.

This representation generally goes by the unfortunate misnomer “premultiplied alpha,” which makes it seem as if the alpha component is multiplied by something.
We will refer to this representation as alpha-multiplied because the color components are multiplied by the alpha component.
All alpha-aware functions consume alpha-multiplied flomaps and produce alpha-multiplied flomaps.

There are many good reasons to use alpha-multiplied flomaps instead of non-alpha-multiplied flomaps.
Some are:

There is only one transparent point: all zeros.
We could not conceptualize partially transparent flomaps as being infinite in size without a unique transparent point.

Many functions can operate on flomaps without treating the alpha component specially and still be correct.

As an example of the last point, consider blur.
The following example creates an alpha-multiplied flomap using draw-flomap.
It blurs the flomap using a general-purpose (i.e. non-alpha-aware) blur function, then converts the flomap to non-alpha-multiplied and does the same.

Of course, this could be fixed by making flomap-blur operate differently on flomaps with an alpha component.
But the implementation would amount to converting them to alpha-multiplied flomaps anyway.

The only valid reason to not multiply color components by alpha is loss of precision, which is not an issue with flomaps.

4.1.4Data Layout

For most applications, there should be enough flomap functions available that you should not need to access their fields directly.
However, there will always be use cases for direct manipulation, so the fields are public.

The color values in a flomap are stored flattened in a single FlVector, in row-major order with adjacent color components.
For example, a 2×2 RGB flomap can be visualized as

In a flomap, it would be stored as

Mathematically, for a c-component, w-width flomap, the kth color component at position xy is at index