Skip to content
Haiku OSDeep Dive Published Updated 6 min readViews unavailable

Haiku BRegion: Building Exact Clipping and Damage Geometry

Use Haiku's BRegion rectangle-set operations to express unions, exclusions, intersections, and bounded redraw work without fragile rectangle arithmetic.

Haiku’s BRegion represents a two-dimensional area as a set of rectangles. It is useful when a drawing or hit-testing operation needs more than one bounding rectangle: a selection with a hole, disjoint damaged areas, or the intersection of a viewport and a content region. The region operations keep the geometry explicit instead of forcing each caller to hand-maintain a growing list of overlapping rectangles.

#include <Region.h>

BRegion visible;
visible.Set(BRect(0, 0, 319, 199));
visible.Exclude(BRect(100, 40, 219, 159));

BRegion dirty;
dirty.Include(BRect(20, 20, 80, 80));
dirty.Include(BRect(240, 120, 300, 180));
dirty.IntersectWith(&visible);

if (dirty.Intersects(BRect(0, 0, 319, 199))) {
    // Schedule work only for the remaining visible area.
}

The first region describes a rectangular viewport with a rectangular exclusion. The second unions two damage rectangles and clips the result to that visible area. The example demonstrates geometry only; an application still needs to pass clipping state to its drawing code using the appropriate Interface Kit view and coordinate system.

Keep coordinate spaces and lifetimes clear

A region has no knowledge of whether its coordinates are screen, window, view, or document coordinates. Transform or offset it when moving between spaces, and do not reuse a region after the object that defines its coordinate system has changed. Haiku’s Interface Kit provides view-level clipping APIs; query or constrain the view’s clipping region only from code that respects that view’s looper and drawing lifecycle.

The source implementation builds union, subtraction, and intersection results through region operations rather than treating the bounding box as the whole shape. That distinction matters: testing only the bounds can report an intersection inside an excluded hole. Use the actual region predicate when correctness depends on a non-rectangular shape.

A frame is only a bound, not the region

Frame() is useful for invalidation, coarse culling, and choosing a coordinate range, but it does not reconstruct the region. A region made from two distant rectangles can have a frame covering a large empty gap. Conversely, a region with a hole can have the same frame as a fully filled rectangle. Use Intersects() or Contains() for membership questions; use Frame() only when a conservative bounding rectangle is what the next API needs.

When inspecting a region, CountRects() reports its current rectangle decomposition and RectAt() returns one rectangle by index. Treat that decomposition as an implementation representation, not as stable application identity. Regions can be normalized or decomposed differently after unions and exclusions. If an application needs named selection components or independent damage causes, retain that semantic model separately and derive a BRegion for drawing.

#include <Region.h>
#include <stdio.h>

void LogRegion(const BRegion& region)
{
    printf("region has %ld rectangles; frame=(%g,%g)-(%g,%g)\n",
        (long)region.CountRects(), region.Frame().left, region.Frame().top,
        region.Frame().right, region.Frame().bottom);

    for (int32 i = 0; i < region.CountRects(); ++i) {
        BRect rect = region.RectAt(i);
        printf("  rect[%ld]=(%g,%g)-(%g,%g)\n", (long)i,
            rect.left, rect.top, rect.right, rect.bottom);
    }
}

This is diagnostic code, not a serialization format. Do not store rectangle order as a persistent identity or assume CountRects() is proportional to the number of user selections. A series of cuts can make a region more complex, so avoid logging or rebuilding large regions on every pointer-move event without measuring the workload.

For performance, merge redundant rectangles and avoid reconstructing the same geometry in every draw callback. Test empty regions, touching edges, negative offsets, fractional BRect coordinates, and repeated inclusion/exclusion. Keep clipping conservative: an accidentally enlarged clip can paint over unrelated views, while an over-restricted clip can make content appear to vanish.

Integrate with invalidation and drawing

The Interface Kit already tracks update regions for views. If application state changes, invalidate the smallest correct rectangle or region and let the normal draw cycle deliver the update. In Draw(updateRect), respect the supplied update area and the view’s existing clipping; avoid replacing system clipping with a region from another view or coordinate space. A custom BRegion is most valuable when the model contains several disjoint dirty areas, when subtracting occluded work, or when applying a geometric mask before scheduling redraw.

Keep the model region and the view’s invalidation region conceptually separate. A document selection may be persistent application state, while a dirty region is a transient rendering optimization. If a selection changes from A to B, the redraw damage is generally the union of old and new visible selection bounds, not only the new shape. If content moves, invalidate both its old and new area. If a view scrolls, transform document-space geometry and clip it to the new viewport rather than reusing screen-space coordinates.

Coordinate transforms need explicit rounding policy. Haiku’s BRect bounds use floating-point coordinates and its geometric semantics are not the same as an integer pixel width. When rasterizing to pixel-aligned clips, decide whether to floor the minimum and ceil the maximum so the clip remains conservative, then clamp to the target bitmap or view bounds. A too-small rounded region can leave stale pixels at an edge; a too-large one costs drawing work but is usually safer than omitting required damage. Verify those edges at non-integral scale factors and with negative origins.

Bound complexity and verify geometry

Repeatedly including overlapping rectangles can increase bookkeeping even if the visible area changes little. Prefer to accumulate a small set of changes and normalize/coalesce them at a deliberate boundary when appropriate. Do not run expensive geometry work while holding a window lock if it can be computed from immutable model data beforehand. If region construction is on a hot path, measure rectangle count, construction time, and drawing time independently; fewer API calls do not necessarily mean less pixel work.

Build tests around the geometry contract rather than screenshots alone. Check an empty region, a single rectangle, two disjoint rectangles, a fully excluded region, an exclusion that splits one rectangle, an intersection that removes everything, and points/rectangles inside the bounding frame’s empty gaps. Test edge-touching and one-pixel-wide cases using Haiku’s documented BRect conventions. Also verify region behavior after translation and scaling, and confirm that view drawing stays within the intended coordinate space.

For a clipping bug, capture the input rectangles, coordinate transform, resulting Frame(), rectangle count, and the exact Intersects() or Contains() query that disagrees with expectation. That makes off-by-one geometry distinguishable from an incorrect view invalidation or update-loop lifecycle. BRegion solves set operations; it cannot decide which coordinate space, redraw policy, or application selection semantics are correct.

BRegion is deliberately narrower than a general vector path: it is a rectangle-set abstraction. For a curved selection or arbitrary glyph outline, use the appropriate path or rasterization API and convert to a region only at a clipping boundary if that boundary requires it. Keeping the source geometry separate avoids repeated approximation and makes hit testing use the same model that produced the display.

Haiku’s public header also exposes integer clipping rectangles through FrameInt() and RectAtInt() alongside the floating-point BRect accessors. Prefer those explicit conversions when handing region bounds to an API that expects a clipping_rect; do not reinterpret the memory layout or cast each coordinate ad hoc. Test conversions at negative origins, fractional edges, and one-pixel boundaries, because a rounding choice can either omit required pixels or enlarge the conservative clip.

Related:

Sources:

Comments