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

Haiku BBitmapStream: Translation I/O, Header Rules, and Bitmap Ownership

Use Haiku BBitmapStream as a Translation Kit position stream while respecting big-endian headers, short I/O, detach semantics, and bitmap ownership.

BBitmapStream adapts a Haiku BBitmap to the Translation Kit’s bitmap stream format. It derives from BPositionIO, exposing positional reads, writes, seeking, size queries, and an explicit DetachBitmap() operation. It is useful when a translator expects or produces a TranslatorBitmap stream. It is usually unnecessary for ordinary file loading because BTranslationUtils offers higher-level bitmap helpers.

The stream contract includes important byte-order and ownership details. The TranslatorBitmap header at the start of the stream is read and written in big-endian byte order. A stream constructed around an existing bitmap and a stream that creates a bitmap during writing have different lifetimes. The stream owns an attached bitmap until it is detached; after detachment, the caller owns the bitmap and must not continue using the stream.

Use the high-level path unless the stream is needed

For common image loading, prefer BTranslationUtils::GetBitmap() with a path, entry reference, or BPositionIO stream. This delegates translator selection and bitmap creation to the existing utility. Use BBitmapStream directly when an API explicitly requires a BPositionIO, when you need to inspect or construct TranslatorBitmap stream bytes, or when a translation pipeline must hand a bitmap through a stream abstraction.

BBitmapStream bitmapStream;
status_t status = roster->Translate(&source, NULL, NULL,
    &bitmapStream, B_TRANSLATOR_BITMAP);
if (status != B_OK)
    return status;

BBitmap* bitmap = NULL;
status = bitmapStream.DetachBitmap(&bitmap);
if (status != B_OK)
    return status;

// The caller owns bitmap now.
// Do not read from or write to bitmapStream after detaching.
delete bitmap;

This uses the BTranslatorRoster::Translate() overload that accepts source and destination BPositionIO objects. Check the destination type and translator selection required by your application. If translation fails, leave the stream attached so its destructor can release any bitmap it owns. After a successful detach, the caller is responsible for the bitmap’s lifetime and cleanup.

Respect the stream’s header and offsets

The stream begins with a TranslatorBitmap header. The header is always represented in big-endian order in the stream, regardless of the host byte order. When writing raw data, provide a valid big-endian header at the start; a mismatched header is rejected. Do not cast the first bytes of an arbitrary image file to TranslatorBitmap and assume it has the correct dimensions or color-space representation.

ReadAt() and WriteAt() operate at explicit offsets, consistent with BPositionIO. Treat their return values as byte counts or errors, not Boolean success. A short transfer is possible at an interface boundary and must be handled according to the translator’s contract. Do not assume one write fills an entire header or pixel buffer unless the call reports that exact number of bytes.

Validate bitmap dimensions and color space before allocating or consuming pixel storage. Compute row-size and total-size arithmetic with overflow checks. A syntactically valid header can still describe unreasonable dimensions for the application. Apply application-level maximums before trusting a translated bitmap from an untrusted or malformed source.

For a direct stream writer, keep header construction in one helper that sets the exact width, height, row-byte count, color space, and data-size fields for the chosen bitmap. Calculate values from the actual BBitmap layout instead of assuming tightly packed rows. The TranslatorBitmap payload is a specific transport representation; a raw BBitmap buffer can have padding between rows. Convert or copy through the supported stream path rather than emitting the in-memory bitmap bytes as if they were already a file format.

When reading into a preexisting stream position, set the position deliberately and handle any prior content. SetSize() changes the stream’s logical data size, so test whether the desired operation replaces or extends data. Do not reuse one stream for independent images without resetting the size and header. Reopen the resulting stream with a new reader in tests to catch accidental dependence on the writer’s in-memory state.

Understand constructor and detach ownership

The constructor can receive an existing BBitmap*. The official documentation describes the stream as operating on that bitmap, and the destructor destroys the bitmap if it remains attached. If a translator writes into a stream constructed with a null bitmap, the stream can create a bitmap as data is written. In both cases, there must be exactly one clear owner at each point.

DetachBitmap() returns the internal bitmap to the caller. The documentation warns that after detachment, no further stream operations should be performed except destroying the stream. A frequent defect is to detach and then call Size(), Seek(), or another read method as though detachment only changed ownership. Structure the code so translation and validation finish before detach, then use the returned bitmap independently.

On every error path, determine whether the stream still owns the bitmap. Avoid deleting the same pointer both directly and through the stream destructor. If the application wraps the stream in a larger pipeline object, document the transfer point and ensure callbacks cannot retain a pointer after the bitmap is released.

Separate image decoding from presentation

Translation produces pixel data, not a complete UI presentation policy. Validate the BBitmap initialization status, color space, bounds, and row bytes before drawing. If the target view or screen uses a different color space, convert explicitly rather than assuming the translator returned the most efficient or desired format. Keep conversion outside a window lock when it is expensive.

Treat the source stream and destination stream separately. A translator may seek, query size, and perform multiple reads or writes. Do not wrap a non-seekable source in a way that falsely advertises positional I/O. If a network or compressed stream is involved, buffer it in a bounded temporary object before translation and enforce size limits to avoid unbounded memory consumption.

If the application saves an image, verify the output by reopening it through a separate translation path. Check every write, close, and rename status. A successful translator call does not guarantee a user-visible file was durably published if the backing filesystem later rejects the write or the application closes the wrong stream.

Failure-oriented verification

Test valid images, truncated headers, wrong byte order, unsupported color spaces, zero and extreme dimensions, short reads, short writes, invalid seeks, allocation failure, translation failure before and after bitmap creation, and repeated detach attempts. Confirm that the stream destructor frees an attached bitmap, that a detached bitmap remains valid after the stream is destroyed, and that no operation is made on the stream after detach.

Include both big-endian stream fixtures and host-native bitmap tests. If a regression appears only on one architecture, inspect header conversion and pixel layout separately. Do not use a rendered preview as the sole check: compare dimensions, color-space metadata, representative pixel values, and output stream size.

Exercise detach failures explicitly. A null output pointer is invalid, and a stream with no internal bitmap cannot produce a detached result. After a successful detach, immediately clear any local borrowed pointer to the stream-owned object and transfer the returned pointer into a clearly named owner. This makes cleanup review straightforward and avoids double deletion when translation succeeds but later view setup fails.

Acceptance criteria

Accept a BBitmapStream workflow when the translator boundary uses valid big-endian TranslatorBitmap headers, every I/O count and error is handled, dimensions are bounded, and ownership transfers exactly once at DetachBitmap(). Use BTranslationUtils for ordinary file loading unless stream-level control is genuinely needed.

BBitmapStream bridges bitmap data and positional translation I/O. It does not select the application’s presentation policy or make a malformed image safe to allocate without validation.

Related:

Sources:

Comments