Discussion:
[Pixman] Op. when compositing
Eric Nicolas
2014-06-11 08:16:46 UTC
Permalink
Hello,

I have a question about the OP modes when compositing with Pixman. I had a look at the source code, but this part of the code is quite complex, so I couldn?t find the answer there.

What I would like to do is :
- Use a plain color via a pixman_image_create_solid_fill on a RGBA color ;
- Use a PIXMAN_a8 mask ;
- Modify a target PIXMAN_r8g8b8a8 image.
So I do a pixman_image_composite(OP, fill, mask, target, ?).

My problem is that I cannot find an OP which would allow me to blend fill and mask together into target. I mean if ?fill? color has an alpha (for instance 0x80), mask is also an alpha (for instance 0x80), I expect the target to be blent with the color modified with an alpha of 0x40 (full fill color x mask alpha => blent to target).

The closest I found is PIXMAN_OP_ATOP, but it always use the RGB from the fill color and the A from the mask, it never blends the color with the mask before compositing onto the target.

Any help would be appreciated.

Regards.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/pixman/attachments/20140611/150d6617/attachment.html>
Søren Sandmann
2014-06-11 19:49:34 UTC
Permalink
Post by Eric Nicolas
Hello,
I have a question about the OP modes when compositing with Pixman. I had a look at the source code, but this part of the code is quite complex, so I couldn?t find the answer there.
- Use a plain color via a pixman_image_create_solid_fill on a RGBA color ;
- Use a PIXMAN_a8 mask ;
- Modify a target PIXMAN_r8g8b8a8 image.
So I do a pixman_image_composite(OP, fill, mask, target, ?).
My problem is that I cannot find an OP which would allow me to blend fill and mask together into target. I mean if ?fill? color has an alpha (for instance 0x80), mask is also an alpha (for instance 0x80), I expect the target to be blent with the color modified with an alpha of 0x40 (full fill color x mask alpha => blent to target).
The closest I found is PIXMAN_OP_ATOP, but it always use the RGB from
the fill color and the A from the mask, it never blends the color with
the mask before compositing onto the target.
The PIXMAN_OP_OVER operator used with a solid color, a PIXMAN_a8 mask
and a PIXMAN_r8g8b8a8 target will multiply the fill color with the pixel
from the a8 mask and then blend the result onto the target image.

But maybe this is not what you want to do. If not, it might be helpful
if you could post images showing what you would like to get, and what
you are getting.


S?ren
Eric Nicolas
2014-06-11 20:53:05 UTC
Permalink
Hello, Thanks for your answer.

Below is a small test program (works on little endian machines).

As is the result is already strange to me :
in: r=20 g=20 b=80 a=FF
out: r=90 g=90 b=C0 a=FF
Where as I expected the fill color #FFFFFFFF x the mask 0x80 = #FFFFFF80
and so an output of #9090FFFF.

However, if I set the fill color to #FFFFFF80, the result seems really
wrong :
in: r=20 g=20 b=80 a=FF
out: r=98 g=98 b=E0 a=FF

I would expect fill x mask => #FFFFFF40 and thus out: #606060C0FF.

I especially do not understand how a lower alpha input color can end up in
a lighter output on the target image.

What I am doing wrong here ?

Thanks.


#include <stdlib.h>

#include <stdio.h>


union C {

uint32_t value;

struct RGBA8888 {

uint8_t a;

uint8_t b;

uint8_t g;

uint8_t r;

} rgba;

};


void testPixmapOps()

{

// create target image full with r=0x20 g=0x20 b=0x80 a=0xFF

size_t w = 100; // multiple of 4 for alignment

size_t h = 100;

C *target = (C*)malloc(w * h * sizeof(C));

for(size_t i = 0; i < w * h; ++i)

target[i].value = 0x202080FF;

printf("in: r=%02X g=%02X b=%02X a=%02X\n",

target[0].rgba.r, target[0].rgba.g, target[0].rgba.b, target[0].rgba
.a);


// connect target to pixman image

pixman_image_t *ptarget = pixman_image_create_bits(PIXMAN_r8g8b8a8, w,
h, (uint32_t*)target, w * sizeof(uint32_t));


// create fill

pixman_color_t cfill;

cfill.red = uint16_t(0xFF) << 8;

cfill.green = uint16_t(0xFF) << 8;

cfill.blue = uint16_t(0xFF) << 8;

cfill.alpha = uint16_t(0xFF) << 8;

pixman_image_t *pfill = pixman_image_create_solid_fill(&cfill);



// create mask with a=0x80

uint8_t *mask = (uint8_t*)malloc(w * h);

for(size_t i = 0; i < w * h; ++i)

mask[i] = 0x80;

pixman_image_t *pmask = pixman_image_create_bits(PIXMAN_a8, w, h, (
uint32_t*)mask, w);



// do compositing

pixman_image_composite(

PIXMAN_OP_OVER,

pfill, pmask, ptarget,

// src_x, src_y

0, 0,

// mask_x, mask_y

0, 0,

// dest_x, dest_y, width, height

0, 0, w, h);



// display one pixel of target

printf("out: r=%02X g=%02X b=%02X a=%02X\n",

target[0].rgba.r, target[0].rgba.g, target[0].rgba.b, target[0].rgba
.a);

}
Post by Eric Nicolas
Post by Eric Nicolas
Hello,
I have a question about the OP modes when compositing with Pixman. I had
a look at the source code, but this part of the code is quite complex, so I
couldn?t find the answer there.
Post by Eric Nicolas
- Use a plain color via a pixman_image_create_solid_fill on a
RGBA color ;
Post by Eric Nicolas
- Use a PIXMAN_a8 mask ;
- Modify a target PIXMAN_r8g8b8a8 image.
So I do a pixman_image_composite(OP, fill, mask, target, ?).
My problem is that I cannot find an OP which would allow me to blend
fill and mask together into target. I mean if ?fill? color has an alpha
(for instance 0x80), mask is also an alpha (for instance 0x80), I expect
the target to be blent with the color modified with an alpha of 0x40 (full
fill color x mask alpha => blent to target).
Post by Eric Nicolas
The closest I found is PIXMAN_OP_ATOP, but it always use the RGB from
the fill color and the A from the mask, it never blends the color with
the mask before compositing onto the target.
The PIXMAN_OP_OVER operator used with a solid color, a PIXMAN_a8 mask
and a PIXMAN_r8g8b8a8 target will multiply the fill color with the pixel
from the a8 mask and then blend the result onto the target image.
But maybe this is not what you want to do. If not, it might be helpful
if you could post images showing what you would like to get, and what
you are getting.
S?ren
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/pixman/attachments/20140611/4b9b7665/attachment.html>
Søren Sandmann
2014-06-12 15:01:19 UTC
Permalink
Post by Eric Nicolas
Hello, Thanks for your answer.
Below is a small test program (works on little endian machines).
in: r=20 g=20 b=80 a=FF
out: r=90 g=90 b=C0 a=FF
Where as I expected the fill color #FFFFFFFF x the mask 0x80 = #FFFFFF80
and so an output of #9090FFFF.
However, if I set the fill color to #FFFFFF80, the result seems really
in: r=20 g=20 b=80 a=FF
out: r=98 g=98 b=E0 a=FF
I would expect fill x mask => #FFFFFF40 and thus out: #606060C0FF.
I especially do not understand how a lower alpha input color can end up in
a lighter output on the target image.
What I am doing wrong here ?
The thing you are missing is that Pixman works with premultiplied
alpha. That is, if the rgb values are R, G, and B, and the alpha value
is A, then pixman will assume that pixels are stored as

R * A, G * A, B * A, A

and it will also generate pixels in this format. The reason for doing
this is that it avoids divisions when compositing.

When you set the fill value to 0xffffff80 pixman will interprete it as

(1.0 / 0.5), (1.0 / 0.5), (1.0 / 0.5), 0.5

= (2, 2, 2, 0.5)

ie., you get out-of-range color values, which is why the result will be
lighter. Normally, the alpha value in premultiplied pixels should not be
bigger than the color values, though it can occasionally be useful.

Premultiplied alpha also means that when applying an alpha mask, it must
be applied to all four channels, so in this case you get

0xffffff80 x 0x80 = 0x80808040

when applying the mask.


S?ren
Eric Nicolas
2014-06-13 06:08:50 UTC
Permalink
Thanks so much. It works perfectly now.
May I suggest the following modification in pixman.h so that it is clear for all users ?

/*
* Important note: pixman colors are alpha-premultiplied
* and must conform: red <= alpha ; green <= alpha ; blue <= alpha
* So that for instance pure white with 0x80 alpha should
* be r=0x80, g=0x80, b=0x80, a=0x80
*/
struct pixman_color
{
uint16_t red;
uint16_t green;
uint16_t blue;
uint16_t alpha;
};

Thanks
Post by Søren Sandmann
Post by Eric Nicolas
Hello, Thanks for your answer.
Below is a small test program (works on little endian machines).
in: r=20 g=20 b=80 a=FF
out: r=90 g=90 b=C0 a=FF
Where as I expected the fill color #FFFFFFFF x the mask 0x80 = #FFFFFF80
and so an output of #9090FFFF.
However, if I set the fill color to #FFFFFF80, the result seems really
in: r=20 g=20 b=80 a=FF
out: r=98 g=98 b=E0 a=FF
I would expect fill x mask => #FFFFFF40 and thus out: #606060C0FF.
I especially do not understand how a lower alpha input color can end up in
a lighter output on the target image.
What I am doing wrong here ?
The thing you are missing is that Pixman works with premultiplied
alpha. That is, if the rgb values are R, G, and B, and the alpha value
is A, then pixman will assume that pixels are stored as
R * A, G * A, B * A, A
and it will also generate pixels in this format. The reason for doing
this is that it avoids divisions when compositing.
When you set the fill value to 0xffffff80 pixman will interprete it as
(1.0 / 0.5), (1.0 / 0.5), (1.0 / 0.5), 0.5
= (2, 2, 2, 0.5)
ie., you get out-of-range color values, which is why the result will be
lighter. Normally, the alpha value in premultiplied pixels should not be
bigger than the color values, though it can occasionally be useful.
Premultiplied alpha also means that when applying an alpha mask, it must
be applied to all four channels, so in this case you get
0xffffff80 x 0x80 = 0x80808040
when applying the mask.
S?ren
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/pixman/attachments/20140613/34fa54bb/attachment.html>
Loading...