Skip to content

Support stride in Pixmap/PixmapMut? #168

@madsmtm

Description

@madsmtm

Hey! I'm working on improving Softbuffer, a library for creating a buffer that can be rendered on the CPU and presented to a window. I believe that tiny-skia and vello_cpu are good complements for it, and I'll be recommending them in our documentation in rust-windowing/softbuffer#325.

For us to be zero-copy on Android and macOS, we need to expose stride, see rust-windowing/softbuffer#315. This is unfortunate though, because it means that integration with tiny-skia when stride != width requires rendering into a separate buffer first.

Would it be possible (and desirable) for tiny-skia to support passing a byte stride != width * 4 in their creation methods?


Note that this might not be that useful yet, since a lot of platforms still only support BGRA pixel formats, so users of tiny-skia would still have to copy in any case then. I plan to improve that, such that in the future an integration between these two libraries would look like:

// Set surface as transparent and try to use an RGBA pixel format.
surface.set_alpha_mode(AlphaMode::Premultiplied);
let needs_rgba_conversion = surface.supported_pixel_formats().contains(PixelFormat::Rgba8) {
    surface.set_alpha_mode(PixelFormat::Rgba8);
    false
} else {
    // Ideally supported on most platforms, this would only be a fallback in edge cases.
    // (e.g. Wayland compositors aren't guaranteed to support RGBA, but most do).
    surface.set_pixel_format(PixelFormat::Bgra8);
    true
};

// Get next buffer.
let buffer = surface.next_buffer().unwrap();

// Create `PixmapMut<'_>` from `Buffer<'_>`.
let width = buffer.width();
let height = buffer.height();
let byte_stride = buffer.byte_stride();
let data = buffer.data();
let pixmap = PixmapMut::from_bytes(data, byte_stride, width, height);

draw(pixmap); // User draws here.

if needs_rgba_conversion {
    // Swap red and blue channel to convert RGBA to BGRA.
    for row in buffer.rows() {
        for [r, _g, b, _a] in row.as_chunks::<4>() {
            std::mem::swap(r, b);
        }
    }
}

// Present the image.
buffer.present();

That would fully avoid allocating unnecessary buffers, and would only add an extra processing step (swapping the r and b channels, which could probably be vectorized quite a bit) if absolutely necessary. (If you wanted to avoid that too, you'd have to make the order of PremultipliedColorU8 platform-dependent. This is what Softbuffer is gonna do in its default mode, but it is probably gonna be too annoying for tiny-skia).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions