linebender / linebender/tiny-skia
Support stride in `Pixmap`/`PixmapMut`?
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 1.6k
- Forks
- 99
- Avg merge
- 2h 52m
- Merged PRs (30d)
- 1
Description
Hey! I'm working on [improving Softbuffer](https://github.com/rust-windowing/softbuffer/milestone/2), 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 https://github.com/rust-windowing/softbuffer/pull/325.
For us to be zero-copy on Android and macOS, we need to expose stride, see https://github.com/rust-windowing/softbuffer/pull/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:
```rust
// 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`).
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading the Pixmap and PixmapMut creation methods, especially from_bytes, and trace how width, height, and pixel rows are represented. Determine the API and invariants needed to accept a byte stride different from width * 4 while preserving the existing pixel format. Done means callers can construct Pixmap or PixmapMut over strided data without an intermediate buffer.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- computer-graphics
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100