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

Haiku BPicture: Record and Replay Interface Kit Drawing

Use Haiku BPicture as a recorded drawing-command stream, understanding attachment, child-view limits, data replacement, replay, and lifetime.

BPicture records a sequence of Interface Kit drawing instructions so they can be played later. Unlike a BBitmap, it stores drawing operations rather than pixels and is therefore independent of the display resolution at record time. That distinction makes it useful for caching or replaying a stable graphic, but it does not turn the object into a complete retained-mode scene graph, a screenshot, or a serialization of the entire view hierarchy.

The current Haiku documentation is precise about what is captured: drawing instructions performed directly on the view are sent to the picture; child views are not captured automatically. The view must be attached to a window for recording, even though instructions are recorded when the view is hidden, outside the clipping region, or in an off-screen window. This has practical consequences for initialization and testing: creating a BView and issuing drawing calls before it is attached is not a reliable way to produce a picture.

Record through a view

To record, create a BPicture, call BView::BeginPicture(), perform the desired primitive drawing operations on that view, and finish with EndPicture(). The method returns the resulting picture pointer. AppendToPicture() is the explicit alternative when the intent is to append to an existing picture. BeginPicture() erases the picture’s existing data; it is not an append operation.

The basic shape is:

BPicture* picture = new(std::nothrow) BPicture;
if (picture == NULL)
	return;

view->BeginPicture(picture);
view->SetHighColor(0, 120, 210);
view->FillEllipse(BRect(8, 8, 56, 56));
picture = view->EndPicture();

This illustrates only the recording boundary; include <new> for std::nothrow. The view must be attached to a window before recording; the code must also manage the returned object according to ownership. If the window or view is destroyed before recording finishes, the picture cannot be assumed complete. Keep recording short and never hold the view in a partially initialized state while unrelated UI events run.

Only operations sent directly to the recorded view are captured. A child button, nested view, or separate layer is not implicitly flattened into the picture. If the desired output includes child content, either record those drawing operations through an explicit common view or use another documented capture/export strategy. Do not rely on the on-screen composition as proof that BPicture contains the same complete content.

Replay is a command stream, not a pixel copy

DrawPicture() replays the drawing instructions. The output depends on the destination view’s coordinate system, drawing state, clipping, color space, and supported primitive operations. A picture that looks correct on the recording view may appear at an unexpected scale or position when replayed elsewhere unless the caller establishes a deliberate origin and transform.

Because a picture contains drawing commands, it is often smaller than storing a large raster for simple shapes and text. It can also be replayed at different resolutions. But if the commands depend on mutable external state, text fonts, or system colors, their appearance may vary when replayed later. A BPicture does not freeze a referenced font file or all global display settings into an immutable visual result.

For content that must be pixel-identical, such as a captured diagnostic image or a specific export artifact, a bitmap or a defined export format may be more appropriate. For a reusable resolution-independent drawing sequence, a picture can be effective. Choose based on fidelity, editability, memory cost, and the application’s need to preserve the original model.

Picture bounds and invalidation

BPicture does not know when the application’s semantic model changes. If the graphic depends on a selected item, current value, or theme color, the application must invalidate and rebuild the recording when that input changes. Keep a version or dirty flag beside the model; do not reuse a picture indefinitely after its data source changes.

Recorded drawings are still rendered through the Interface Kit and app_server pipeline. DrawPicture() should be called in the correct drawing context and within normal view redraw rules. It does not replace BView::Invalidate() or permit drawing into a window from an arbitrary worker thread. A view should reconstruct the expected output during Draw() from current model state and a valid cached picture.

When the destination area changes, test clipping and the drawing origin explicitly. A cached picture may have been generated for a different geometry. If the recording contains fixed coordinates, resizing does not magically reflow it like a layout. Either rebuild the picture from a new logical layout or intentionally scale the result and accept that text and fine strokes may not match native UI metrics.

Composition and lifetime

Use one BPicture for a stable unit of drawing, such as a graph background or a repeated decorative primitive sequence. Separate volatile overlays, cursors, selection state, and active controls from the cached portion so changing one small value does not force every command to be regenerated. Conversely, avoid creating dozens of tiny pictures when one simple redraw would be cheaper and easier to maintain.

The application owns the BPicture object returned from recording and must retain it for as long as replay is possible. Replacing a cached pointer should release the old object only after no draw path can still use it. This is particularly important when a worker prepares a new model while the window thread paints the old picture. Exchange immutable references through a synchronized message or other clear ownership transfer, not a naked shared pointer.

BPicture derives from BArchivable and has Archive()/Instantiate() as well as stream flatten/unflatten methods. These APIs serialize picture data, but they do not make arbitrary app-specific meaning portable. If the picture is persisted, version the enclosing data and retain the source model where future regeneration is required. Do not assume the binary representation is a permanent interchange format across unrelated implementations or revisions unless the official format contract says so.

Performance and measurement

Recording can reduce repeated CPU-side submission of many drawing operations, but it is not automatically faster. It adds allocation, serialization or upload, cache invalidation, and potentially extra app_server work. Benchmark the actual window and target machine. Compare rebuilding the drawing on every update, caching a picture, and rasterizing a bitmap when the output is mostly static.

Keep the recording path deterministic: set every color, font, pen size, and drawing mode that affects the result rather than depending on the view’s previous state. If a state change is omitted, the picture can capture a value left by an earlier handler. Recording should be a pure projection of model state, not a hidden side effect of the user navigating through a particular screen sequence.

Validation matrix

Test attached and unattached recording, hidden views, clipping, one child view, append versus replacement, repeated BeginPicture(), and redraw after model changes. Render into a different-sized view and confirm the expected scaling and origin. Test destruction while a replacement picture is pending and verify there is no use-after-free.

Compare picture replay with a direct draw of the same primitives. Check each result at multiple display scales, after changing font settings or theme colors if relevant, and after resizing the destination. If an archived picture is part of saved state, verify that loading old state fails gracefully and that the app can rebuild from its own document model.

BPicture is a recording of primitive drawing instructions, not a snapshot of a full view tree. Its value comes from replaying stable commands at useful sizes, while its constraints come from attachment, captured-view scope, mutable drawing state, and object lifetime. Treating those as explicit design choices makes recorded graphics predictable instead of an opaque rendering cache.

Related:

Sources:

Comments