parasyte / pixels

A tiny hardware-accelerated pixel frame buffer. 🦀
https://docs.rs/pixels
MIT License
1.81k stars 123 forks source link

Premultiplication with `tiny-skia` #362

Closed truchi closed 1 year ago

truchi commented 1 year ago

Hello,

I am looking at your tiny-skia example, but I don't understand what is going on.

First, why do you pixels.frame_mut().copy_from_slice(drawing.data()); when there is PixmapMut?

Second, Pixmap states A container that owns premultiplied RGBA pixels.. If I understand correctly, Pixels::new has a Vec<u8> that are Rgba8UnormSrgb. What about premultiplication? How does this work? I guess you use premultiplied values in shape.rs. But you don't "demultiply" for wgpu...

I'm confused.

Thank you!

parasyte commented 1 year ago

I can't really answer the questions related to the tiny-skia crate, since I do not use it. This was a contribution from another user in #317.

I can however provide some info about premultiplied alpha, since it is commonly used for blending. The TL;DR is that premultiplied alpha will do nothing for you with pixels if you are not actually blending anything. (The example doesn't.)

If I understand correctly, Pixels::new has a Vec<u8> that are Rgba8UnormSrgb.

It's a byte vector, yes, but the texture format is configured by the builder method render_texture_format.

What about premultiplication? How does this work?

Premultiplied alpha has nothing to do with the texture format. It's just a method of preprocessing the data. You can use it with any texture format!

The example itself doesn't do any blending. Though there should be some implied blending internal to tiny-skia. (I have no idea how it behaves with non-premultiplied color samples.) Presumably it uses saturating addition, and everything has mathematically incorrect blending. But as far as wgpu is concerned, it's just given a fully opaque texture and that's that.

If you want to use premultiplied alpha, you can change the blending function with the blend_state builder method. And all of the color samples given to tiny-skia would need to be premultiplied. This will blend the premultiplied texture data with the clear color (which defaults to opaque black, but can also be configured in the builder). Whatever blending is done internally by tiny-skia is outside of the scope of pixels and wgpu.

I guess you use premultiplied values in shape.rs.

No, that is not the case. For instance, this color is not premultiplied: https://github.com/parasyte/pixels/blob/cbda7df53b54be03b9b40bacbf46bc6b2dbaa13a/examples/tiny-skia-winit/src/shape.rs#L9

But you don't "demultiply" for wgpu...

There is no need to "demultiply" the result. Just ask wgpu to blend it with the right blending function!

truchi commented 1 year ago

Hello,

Thanks a lot for the detailed answer! I ended up diving into the WebGPU rabbit hole 😊, but your answer still helps me understand these things.

Keep up the good work!