GIMP Bezier Curve Internal Data Structures
This article provides a technical overview of how GNU Image
Manipulation Program (GIMP) internally models and stores Bezier curves.
In GIMP's vector architecture, paths are maintained through an
object-oriented hierarchy built on GLib and GObject. Understanding this
subsystem requires examining the relationship between the top-level path
container (GimpVectors), the curve component implementation
(GimpBezierStroke), and the fundamental point structure
(GimpAnchor) that defines cubic Bezier curves in
memory.
The Path Hierarchy: GimpVectors and GimpStroke
At the highest level of GIMP's vector system resides
GimpVectors, a subclass of GimpItem. A
GimpVectors object represents a complete path layer visible
in the GIMP Paths dockable dialog. However, GimpVectors
does not store raw coordinate data directly. Instead, it serves as a
container for one or more stroke components, represented by the abstract
base class GimpStroke.
A path can contain multiple disconnected sub-paths (for example, the
distinct inner and outer loops of a letter "O"). Each of these discrete
sub-paths is an instance of a GimpStroke subclass.
The GimpBezierStroke Structure
For vector paths containing Bezier curves, GIMP implements
GimpBezierStroke, a derived class of
GimpStroke. Defined within GIMP's internal core source
(app/vectors/gimpbezierstroke.h), this structure manages
the mathematical representation and manipulation of cubic Bezier
splines.
Internally, GimpBezierStroke maintains:
- A dynamic array (
GArray) of coordinate knots and control handles represented byGimpAnchorstructures. - Flags indicating whether the sub-path is open or closed
(
closedboolean). - Methods for subdivision, transformation, point insertion, and interpolation.
The Fundamental Unit: GimpAnchor
The actual geometry of a Bezier curve is stored as a sequential list
of GimpAnchor instances. The GimpAnchor
structure defines both the on-curve endpoints (knots) and the off-curve
directional control handles.
A simplified C representation of GimpAnchor
includes:
struct _GimpAnchor
{
GimpVector2 position;
GimpAnchorType type;
gboolean selected;
};position: AGimpVector2holding double-precision floating-pointxandycoordinates representing the point in canvas space.type: An enumeration (GimpAnchorType) that identifies the functional role of the point:GIMP_ANCHOR_ANCHOR: An on-curve knot through which the path strictly passes.GIMP_ANCHOR_CONTROL: An off-curve control handle that dictates the tangent and curvature entering or leaving an anchor.
selected: A boolean flag indicating whether the point is currently active or selected by the user in the Path Tool UI.
Sequence and Cubic Bezier Segment Representation
GIMP structures cubic Bezier curves through a rigid ordering
convention within the GArray of GimpAnchor
structures. Because every cubic Bezier segment requires four points—two
endpoints and two control handles—the array organizes them in
groups:
- Anchor point (\(P_0\)): Starting on-curve knot
(
GIMP_ANCHOR_ANCHOR). - Outgoing control point (\(P_1\)): Handle influencing
departure from \(P_0\)
(
GIMP_ANCHOR_CONTROL). - Incoming control point (\(P_2\)): Handle influencing arrival
at \(P_3\)
(
GIMP_ANCHOR_CONTROL). - Anchor point (\(P_3\)): Ending on-curve knot
(
GIMP_ANCHOR_ANCHOR).
When multiple segments are connected, the ending anchor of the preceding segment serves as the starting anchor of the succeeding segment. Thus, a continuous open path of \(n\) cubic segments consists of \(3n + 1\) elements stored sequentially:
\[\text{Anchor}_0, \text{Handle}_{\text{out}, 0}, \text{Handle}_{\text{in}, 1}, \text{Anchor}_1, \text{Handle}_{\text{out}, 1}, \text{Handle}_{\text{in}, 2}, \text{Anchor}_2, \dots\]
If the path is closed, the final incoming handle loops back to influence the initial anchor, eliminating the trailing terminal anchor and bringing the total count to \(3n\) anchors.
Conversion and Rendering Pipeline
For rendering, rasterization, and selection conversion, GIMP does not
draw cubic curves directly from these structures onto the pixel canvas.
Instead, GimpBezierStroke evaluates the curve segments
using De Casteljau's algorithm or adaptive forward differencing to
flatten the parametric curves into dense polylines composed of
GimpCoords. These coordinate arrays are then passed to
Cairo or GEGL to generate antialiased scanlines, stroke masks, or vector
selections.