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 by GimpAnchor structures.
  • Flags indicating whether the sub-path is open or closed (closed boolean).
  • 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: A GimpVector2 holding double-precision floating-point x and y coordinates 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:

  1. Anchor point (\(P_0\)): Starting on-curve knot (GIMP_ANCHOR_ANCHOR).
  2. Outgoing control point (\(P_1\)): Handle influencing departure from \(P_0\) (GIMP_ANCHOR_CONTROL).
  3. Incoming control point (\(P_2\)): Handle influencing arrival at \(P_3\) (GIMP_ANCHOR_CONTROL).
  4. 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.