How GIMP Converts Vector Anchors to Screen Pixels

When rendering vector paths, GIMP translates floating-point anchor and control coordinates from internal canvas space into discrete screen-space pixels using an affine transformation pipeline combined with curve evaluation via the Cairo graphics library. The process begins by retrieving the mathematical coordinates of the path strokes, applying the active viewport's view transformation matrix to account for canvas zoom, panning, and rotation, and finally flattening the resulting cubic Bézier curves into rasterized, anti-aliased line segments directly mapped to the user's display buffer.

1. Vector Coordinate Storage in Image Space

Within GIMP’s architecture, paths are managed by data structures such as GimpVectors and GimpBezierStroke. Each path component consists of anchor points flanked by directional handles (control points). These points are stored as floating-point values in image coordinate space, independent of display scaling, window dimensions, or display DPI. Storing coordinates as double-precision floating-point numbers ensures that vector data retains geometric precision regardless of image dimensions or subsequent user edits.

2. The Viewport Transformation Matrix

To map coordinates from image space to screen space, GIMP utilizes its display shell system (GimpDisplayShell). The display shell constructs a 2D affine transformation matrix that captures all current display parameters:

  • Scaling: Adjusts for the current zoom factor (e.g., 50%, 100%, 800%).
  • Translation: Accounts for canvas panning offsets within the application window.
  • Rotation and Flipping: Accommodates arbitrary canvas rotation angles or horizontal/vertical canvas flips.

Mathematically, any anchor coordinate \((x_{\text{image}}, y_{\text{image}})\) is converted to an unrounded screen coordinate \((x_{\text{screen}}, y_{\text{screen}})\) through affine matrix multiplication:

\[\begin{bmatrix} x_{\text{screen}} \\ y_{\text{screen}} \\ 1 \end{bmatrix} = \begin{bmatrix} a & c & t_x \\ b & d & t_y \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x_{\text{image}} \\ y_{\text{image}} \\ 1 \end{bmatrix}\]

Here, \(a\) and \(d\) represent scaling, \(b\) and \(c\) represent shearing/rotation components, and \(t_x\) and \(t_y\) represent viewport panning translations.

3. Curve Evaluation and Flattening

Once anchor points and their corresponding control handles are transformed into the display coordinate frame, GIMP utilizes the Cairo 2D graphics library to process the curves. A segment between two anchors is defined as a cubic Bézier curve:

\[B(t) = (1-t)^3 P_0 + 3(1-t)^2 t P_1 + 3(1-t) t^2 P_2 + t^3 P_3, \quad t \in [0, 1]\]

Where \(P_0\) and \(P_3\) are transformed anchor points, and \(P_1\) and \(P_2\) are transformed control handles.

Because computer screens cannot display pure mathematical curves directly, Cairo performs curve flattening (adaptive subdivision). The algorithm recursively calculates midpoints using de Casteljau's algorithm until the deviation between the true curve and a straight line approximation falls below a specific flatness tolerance—typically a fraction of a screen pixel. This converts the smooth curve into a contiguous sequence of tiny linear segments.

4. Screen-Space Rasterization and Anti-Aliasing

The final phase converts the flattened linear segments into pixels on the GTK drawing surface:

  1. Pixel Alignment and Rounding: The coordinates of the linear segments, while in screen coordinate space, are still floating-point values. GIMP and Cairo align these lines relative to the pixel grid, typically offsetting coordinates by 0.5 pixels when drawing 1-pixel-wide path outlines to ensure sharp, non-blurry line rendering.
  2. Coverage Calculation (Anti-Aliasing): For each pixel intersecting a path line segment, Cairo calculates sub-pixel coverage. Pixels entirely covered by the path are set to the stroke's foreground color, while partially covered boundary pixels receive an alpha-blended value proportional to their coverage area.
  3. Buffer Blitting: The resulting pixel values are written directly to the display shell's Cairo context, compositing the path handles and lines over the rendered image tile cache in the GTK widget viewport.