2010-06-30 6 views
3

J'écris un très bibliothèque graphique simple, et j'essaie de comprendre comment faire alpha blending. Je l'ai essayé plusieurs fois, mais mes résultats étaient moins que satisfaisants. Selon Wikipedia, je dois faire:Quelle est la bonne façon d'effectuer l'alpha-blending? (C)

Valeur = (1-alpha) value0 + alpha valeur1

Mais cela ne fonctionne pas du tout. Peut-être que je fais quelque chose de mal?

Le code que j'ai inclus dessine une image colorée (c'est la fonction 'proximité'), puis tente de dessiner une boîte partiellement transparente à (100,100). Cependant, au lieu d'une boîte translucide blanche, je reçois une distorsion bizarre à l'image (je vais essayer de les avoir au bas de mon post). Aucune suggestion? Voici mon code:

#include "hgl.h" 

void proximity() 
{ 
    int x = 0, y = 0, d1, d2, d3, dcenter; 

    while(x < WIDTH){ 
     while(y < HEIGHT){ 
      d1 = distance(x, y, (WIDTH/2) - 200, (HEIGHT/2) + 200); 
      d2 = distance(x, y, (WIDTH/2) + 200, (HEIGHT/2) + 200); 
      d3 = distance(x, y, (WIDTH/2), (HEIGHT/2) - 150); 
      dcenter = distance(x, y, WIDTH/2, HEIGHT/2); 
      putpixel(x, y, d1, d2, d3); 
      y++; 
     } 
     y = 0; 
     x++; 
    } 
} 

int alpha_transparency(float alpha, float value1, float value2) 
{ 
    return (1-alpha) * value1 + alpha * value2; 
} 

void transparent_box(int pos_x, int pos_y, int width, int height, float alpha, char r, char g, char b) 
{ 
    int x = 0, y = 0; 
    while(x < width) 
    { 
     while(y < height) 
     { 
      int rr, rg, rb; 
      rr = alpha_transparency(alpha, p.bitmap[x+pos_x][y+pos_y].r, r); 
      rg = alpha_transparency(alpha, p.bitmap[x+pos_x][y+pos_y].g, g); 
      rb = alpha_transparency(alpha, p.bitmap[x+pos_x][y+pos_y].b, b); 
      putpixel(pos_x + x, pos_y + y, rr, rg, rb); 
      y++; 
     } 
     x++; 
     y = 0; 
    } 
} 

int main() 
{ 
    fp = fopen("out.bmp","wb"); 

    set_dimensions(1440, 900); 
    insert_header(); 

    white_screen(); 

    proximity(); 
    transparent_box(100, 100, 500, 500, .9, 255, 255, 255); 

    insert_image(); 
    fclose(fp); 
    return 0; 
} 

Désolé, je n'ai pas pu inclure la sortie parce que je suis un nouvel utilisateur. Cependant, voici les liens:

Original Picture

Picture with "transparent" box

+0

Pour commencer, vous mélangez flotteurs et ints sans coulée explicite. En particulier votre fonction alpha_transparency est déclarée comme retournant un int mais retourne un float. Est-ce que C sait convertir automatiquement la valeur de retour? – phkahler

+3

C sait convertir automatiquement la valeur de retour. Cela peut vous causer des problèmes si elle tronque automatiquement une fraction à 0. –

+0

Voir cette question précédente: http://stackoverflow.com/questions/1944095/how-to-mix-two-argb-pixels –

Répondre

2

Votre fonction de mélange alpha est correcte; Une autre façon de penser à l'alpha-blending est qu'il interpole entre deux valeurs de couleur basées sur alpha, donc il devrait être une valeur dans [0, 1].

Toutefois, vous ne devez pas passer les composants couleur comme char, qui est signé par défaut. Vous devez les passer en tant que unsigned char ou en tant que type entier plus large. Qu'est-ce qui se passe est que, au lieu de passer en 255 comme vous vous attendez, vous passez en -1. En d'autres termes, stockez vos composants couleur en tant que unsigned char s pour vous assurer que vous ne disposez pas de manœuvres de signature (voir EDIT2).

EDIT: Notez que si votre alpha est dans [0, 255], vous devez le normaliser à [0, 1] pour effectuer l'opération de fusion alpha.

EDIT2: Aussi, si vous stockez vos pixels comme char au lieu de unsigned char, ce qui expliquerait le serrage bizarre j'ai vu:

alpha_transparency(0.9, (char)255, (char)255) 
== alpha_transparency(0.9f, -1.0f, -1.0f) 
== -1.0f 
== 0xff (cast to char) 

alpha_transparency(0.9, (char)128, (char)255) 
== alpha_transparency(0.9f, -128.0f, -1.0f) 
== -13.7f 
== 0xf3 

alpha_transparency(0.9, (char)127, (char)255) 
== alpha_transparency(0.9f, 127.0f, -1.0f) 
== -11.80f 
== 0x0b 

alpha_transparency(0.9, (char)0, (char)255) 
== alpha_transparency(0.9f, 0.0f, -1.0f) 
== -0.9f 
== 0x00 
0

La question que je pense est la façon dont vous traitez avec des couleurs. La méthode utilisée dans Wikipedia suppose que 0 est noir et 1 est blanc, avec 0,5 étant au milieu. Cependant, votre code utilise ints, donc je suppose que vous définissez 0 comme noir et 255 comme blanc.

donc le bon code est:

return (255-alpha)*value1+alpha*value2; 

Vous souffrez peut-être aussi de l'arrondissement compilateur où vous ne le pense pas. Je changerais le code dans votre fonction à ceci:

float result = (255.0f-alpha)*value1+alpha*value2; 
return (int) result; 

En général, il est très fréquent de travailler avec des images en utilisant des flotteurs au lieu de ints. Beaucoup de programmes convertissent aujourd'hui l'image entière en chars, la traitent puis la convertissent en arrière. Vous pouvez éviter plusieurs bugs de cette façon.

+0

Eh bien, une chose est sûre: vous aviez raison au sujet de l'arrondissement. Je n'avais même pas posé de questions à ce sujet! Merci! Mais à propos de la transparence, cela ne semblait pas très bien fonctionner. Vous aviez raison sur les couleurs, elles vont de 0 à 255 pour moi. Étonnamment cependant, passer de (1-alpha) à (255-alpha) n'a pas fait beaucoup de différence. Une valeur de couleur de 256 est exactement la même chose qu'une valeur de couleur de zéro, donc je pense que la première fois, elle a juste fait une boucle et produit le même effet général. Je vais continuer à jouer avec pour l'instant, et encore merci pour l'aide! Hassan – Hassan

+0

Le calcul de l'alpha est correct; dans ce cas, alpha est une valeur fractionnaire qui doit être interpolée entre value1 et value2. Le fait que la gamme de couleurs soit [0, 255] n'a pas d'importance. – MSN

0

Il est probablement préférable de s'en tenir à un seul type de données: soit tous les flottants, soit tous les entiers; Cela réduit le risque de confusion et évite une catégorie d'embûches.

Pour tous ints, vous devez vous rappeler à nouveau échelle l'entier de sorte que le résultat correspond à nouveau dans la gamme originale:

int alpha_transparency(int alpha, int value1, int value2) { 
    int unscaled= (255-alpha)*value1 + alpha*value2; 
    return unscaled/255; /* integer division */ 
} 

Pour tous-flotteurs, vous devez vous rappeler de normaliser les entrées entières d'un valeur brute dans [0..255] (ou autre) à un flottant dans [0.0 .. 1.0], faire tout le traitement, puis convertir en entier seulement à la fin.

float input_raw_value(unsigned char value) { return value/255.0; } 
... 
float alpha_transparency(float alpha, float value1, float value2) { 
    return (1.0-alpha)*value1 + alpha*value2; 
} 
... 
unsigned char output_raw_value(float value) { return value*255.0; } 

Notez que j'ai ignoré les problèmes d'arrondi dans chaque méthode; Une fois que vous avez les mathématiques de base, vous devriez y prêter attention. En outre, il existe diverses astuces pour remplacer les divisions (qui peuvent être relativement lentes) par multiplication ou par manipulation de bits.

Questions connexes