2016-02-20 3 views
4

J'ai un problème avec l'affichage de la transparence alpha en utilisant GTK et Cairo. J'essaie d'afficher cette image 1Transparence alpha au Caire

Si je fais l'alpha me fondre, tout fonctionne.

Manual alpha blending

Si je passe les valeurs alpha directement au Caire, l'ombre semble rendre bien, mais l'effet de lueur est corrompu.

Cairo alpha rendering

Est-ce un bug au Caire 1.14.2, ou suis-je manque quelque chose?

//Need deprecated API to get background color 
GdkColor color = gtk_widget_get_style(widget)->bg[GTK_STATE_NORMAL]; 
Pixel color_blend 
    { 
    uint8_t(255*color.red/65535.0f) 
    ,uint8_t(255*color.green/65535.0f) 
    ,uint8_t(255*color.blue/65535.0f) 
    ,255 
    }; 
while(ptr!=ptr_end) 
    { 
// TODO: Interpolate 
    auto row_src=size_t(row*factor); 
    auto col_src=size_t(col*factor); 

    auto alpha=ptr_src[row_src*width_in + col_src].v3/255.0f; 
    *ptr= 
     { 
    // Using manual alpha blend works 
     uint8_t(alpha*ptr_src[row_src*width_in + col_src].v2 + (1-alpha)*color_blend.v2) 
     ,uint8_t(alpha*ptr_src[row_src*width_in + col_src].v1 + (1-alpha)*color_blend.v1) 
     ,uint8_t(alpha*ptr_src[row_src*width_in + col_src].v0 + (1-alpha)*color_blend.v0) 
     ,255 
    /* This appears to be broken 
     ptr_src[row_src*width_in + col_src].v2 
     ,ptr_src[row_src*width_in + col_src].v1 
     ,ptr_src[row_src*width_in + col_src].v0 
     ,ptr_src[row_src*width_in + col_src].v3*/ 
     }; 

    ++col; 
    if(col==width_out) 
     { 
     col=0; 
     ++row; 
     } 
    ++ptr; 
    } 

Je pousse les pixels à l'aide

auto surface=cairo_image_surface_create_for_data((uint8_t*)pixels.begin(),CAIRO_FORMAT_ARGB32,width_out,height_out,width_out*sizeof(Pixel)); 

cairo_set_source_surface(cr, surface, 0.5*(width-width_out), 0.0); 
cairo_paint(cr); 

cairo_surface_destroy(surface); 

mise Explicitement l'opérateur de CAIRO_OPERATOR_OVER ne fonctionne pas, le résultat est toujours le même.

+0

Utilisez-vous l'opérateur correct, c'est-à-dire CAIRO_OPERATOR_OVER au lieu de CAIRO_OPERATOR_SOURCE? –

+0

@H. Guijt CAIRO_OPERATOR_OVER est par défaut, et en le spécifiant explicitement donne le même résultat. Passer à CAIRO_OPERATOR_SOURCE a cependant le comportement correct, c'est-à-dire que j'obtiens une fenêtre semi-transparente. – user877329

+0

Cela n'a aucun sens ... Uhm, qu'en est-il du format de la surface de destination: est-ce RVB ou ARGB? –

Répondre

3

Comme vous l'avez mentionné dans votre commentaire ci-dessus, les valeurs de vos pixels sont erronées. Vous devez utiliser l'alpha pré-multiplié. Pour en revenir à mon exemple de la question (et en ignorant l'endianness), entièrement rouge avec 50% de transparence est 0x7f << 24 | 0x7f au Caire. Les pixels dont les valeurs sont incorrectes (certains composants de couleur sont plus grands que la valeur alpha) produisent des résultats indéfinis et votre 0xff << 24 | 0x7f appartient à cette catégorie.

Voir http://www.cairographics.org/manual/cairo-Image-Surfaces.html#cairo-format-t:

alpha prémultipliés est utilisé. (Qui est, rouge transparent de 50% est 0x80800000, pas 0x80ff0000.)

P.S .: Pour moi, la manière correcte d'accéder à des données de pixels se fait par un uint32_t et le déplacement, par exemple, uint32_t pixel = (r << 24) | (g << 16) | (b << 8) | a;. De cette façon, vous n'avez pas à vous soucier de l'endianisme.

P.P.S .: Pour OVER et une cible totalement opaque, la formule Cairo simplifie à source_color + target_color * (1 - source_alpha) tandis que votre code utilise source_color * source_alpha + target_color * (1 - source_alpha). Voir http://www.cairographics.org/operators/. Ces deux formules ne sont manifestement pas équivalentes.

Editer: Ok, peut-être qu'ils sont équivalents lors de l'utilisation pré-multiplie alpha. Désolé pour la confusion là-bas.

+0

Ils * sont * équivalents lors de l'utilisation d'alpha pré-multiplié. Et la formule utilisée par Cairo n'est correcte que dans ce cas. Donc, vous pouvez supprimer le * peut-être. – user877329