linebender / resvg

An SVG rendering library.
Apache License 2.0
2.83k stars 228 forks source link

Color channels are premultiplied in sRGB space #839

Closed mahkoh closed 1 week ago

mahkoh commented 1 week ago

Assume that an SVG contains the following CSS color: rgb(255 255 255 0.5).

Then resvg will output the following bytes: [128, 0, 0, 128].

Recall that CSS colors are in sRGB space. This means that resvg emits color channels that are pre-multiplied in sRGB space.

This is not incorrect, but it is incompatible with the vulkan R8G8B8A8_SRGB format. If one uploads the output of resvg to such an image and uses it in a shader, the sampled colors will be incorrect. This is because vulkan performs the sRGB -> linear conversion directly on the electrical values.

This should be documented so that users know that they must manually convert the bytes emitted by resvg if necessary:

  1. un-premultiply
  2. convert to linear
  3. premultiply
  4. convert to srgb
RazrFalcon commented 1 week ago

The tiny_skia::Pixmap docs does mention that premultiplied colors are used.

Docs might be a more clear about it, but I wouldn't call this feature undocumented.

mahkoh commented 1 week ago

I was aware that the colors are both pre-multiplied and sRGB. But the order of operations is undocumented. Vulkan images assume the opposite order of operations.

RazrFalcon commented 1 week ago

What do you mean by "order of operations"? Are you expect me to document how to use resvg output with Vulcan? What's the point?

mahkoh commented 1 week ago

What do you mean by "order of operations"?

Vulkan assumes the first, resvg does the second.

RazrFalcon commented 1 week ago

Sorry, but that's too Vulkan specific. It has nothing to do with resvg. In fact, resvg follows the Skia logic here, so blame them instead.

mahkoh commented 1 week ago

The current behavior is very surprising because it is useless. Premultiplication is only relevant if you have transparency. If you have transparency, then you need to do blending. Blending can only be done correctly in linear space. Therefore you must first convert the sRGB values to linear space. But to get the sRGB values, you must first undo the premultiplication done by resvg. It's a huge waste of time.

But do as you wish.

RazrFalcon commented 1 week ago

There is nothing surprising about the current behavior. Every 2D graphics library I know works in the same way: Skia, CoreGraphics, Qt, Cairo.

Blending can only be done correctly in linear space.

Sort of. But Most 2D graphics libraries do not do this. resvg works "correctly" according to the SVG spec. That's the only thing that matters.