2016-05-30 2 views
0

J'ai donc, à partir d'une bibliothèque native externe (C++), un tampon de pixels qui semble être en 16Bit RGB (l'équivalent SlimDX est B5G6R5_UNorm).Convertir le tampon de pixels en B8G8R8A8_UNorm depuis 16Bit

Je souhaite afficher l'image représentée par ce tampon à l'aide de Direct2D. Mais Direct2D ne prend pas en charge B5G6R5_UNorm.

donc je dois convertir ce tampon de pixel à B8G8R8A8_UNorm

J'ai vu plusieurs extraits de code d'une telle tâche à l'aide des méthodes décalage de bits, mais aucun d'entre eux étaient spécifiques pour mes besoins ou formats. Ça ne m'aide pas d'avoir zéro, nada, aucun, zilch aucune idée sur le décalage de bits, ou comment cela est fait.

Ce que je suis après est un exemple de code C♯ d'une telle tâche ou tout construit dans la méthode pour effectuer la conversion - je ne me dérange pas utiliser d'autres

de bibliothèque

S'il vous plaît Note: Je sais que cela peut être fait en utilisant les classes bitmap C♯, mais j'essaie de ne pas compter sur ces classes intégrées (il y a quelque chose à propos de GDI que je n'aime pas), les images (sous la forme de tampons de pixels) arriveront en profondeur et rapidement, et Je choisis SlimDX pour sa facilité d'utilisation et ses performances. Si je dessine l'image en utilisant B8G8R8A8_UNorm l'image a une couverture verte et les pixels sont juste partout, donc pourquoi je crois que je dois d'abord convertir ou 'mettre à jour' le tampon de pixels au format requis. Juste pour ajouter: Lorsque je fais ce qui précède sans convertir le tampon, l'image ne remplit pas toute la géométrie.

Les tampons pixels sont fournis par byte[] objets

Répondre

1

décalage de bits et les opérateurs logiques sont vraiment utiles lorsqu'ils traitent avec les formats d'image, de sorte que vous le devez à vous-même pour en lire davantage. Cependant, je peux vous donner un aperçu rapide de ce que ce format de pixel représente, et comment convertir de l'un à l'autre. Je devrais préface ma réponse avec un avertissement que je ne connais pas très bien C# et ses bibliothèques de support, donc il peut y avoir une solution dans-boîte pour vous. Tout d'abord, votre tampon de pixel a le format B5G6R5_UNORM. Nous avons donc 16 bits (5 rouges, 6 verts et 5 bleus) assignés à chaque pixel. Nous pouvons visualiser la disposition des bits de ce format de pixel comme "RRRRRGGGGGGBBBBB", où "R" représente les bits qui appartiennent au canal rouge, "G" pour les bits qui appartiennent au canal vert, et "B" pour les bits qui appartiennent à le canal bleu.

Maintenant, disons que les 16 premiers bits (deux octets) de votre mémoire tampon de pixels sont 1111110100101111. ligne qui avec la mise en page de bits de format de pixel ...

RRRRRGGGGGGBBBBB 
1111110100101111 

Cela signifie que le canal rouge a bits 11111, vert a 101001 et bleu a 01111. Conversion de binaire en décimal: rouge = 31, vert = 41 et bleu = 15. Vous remarquerez que le canal rouge a tous les bits mis 1, mais sa valeur (31) est en fait plus petit que le canal vert (41). Cependant, cela ne signifie pas que la couleur est plus verte que le rouge lorsqu'il est affiché; le canal vert a un bit supplémentaire, donc il peut représenter plus de valeurs que les canaux rouges et bleus, mais dans cet exemple particulier il y a effectivement plus de rouge dans la couleur de sortie! C'est là qu'intervient la partie UNORM ...

UNORM est l'abréviation d'un entier normalisé non signé; Cela signifie que les valeurs des canaux de couleur doivent être interprétées comme des nombres à virgule flottante espacés uniformément de 0,0 à 1,0.Les valeurs sont normalisées par le nombre de bits alloués. Qu'est-ce que cela signifie exactement? Disons que vous avez un format avec seulement 3 bits pour stocker une chaîne. Cela signifie que le canal peut avoir 2^3 = 8 valeurs différentes, qui sont représentées ci-dessous avec les représentations respectives décimales, binaires et normalisées. La valeur normalisée est juste la valeur décimale divisée par la plus grande valeur décimale possible qui peut être représentée avec N bits.

Decimal | Binary | Normalized 
----------------------------- 
0  | 000 | 0/7 = 0.000 
1  | 001 | 1/7 =~ 0.142 
2  | 010 | 2/7 =~ 0.285 
3  | 011 | 3/7 =~ 0.428 
4  | 100 | 4/7 =~ 0.571 
5  | 101 | 5/7 =~ 0.714 
6  | 110 | 6/7 =~ 0.857 
7  | 111 | 7/7 = 1.000 

Pour revenir à l'exemple précédent, où le pixel avait des bits 1111110100101111, nous savons déjà nos valeurs décimales pour les trois canaux de couleur: RVB = {31, 41, 15}. Nous voulons plutôt les valeurs normalisées, car les valeurs décimales sont trompeuses et ne nous en disent pas beaucoup sans connaître le nombre de bits dans lesquels elles ont été stockées. Les canaux rouge et bleu sont stockés avec 5 bits, donc la plus grande valeur décimale est 2^5 -1 = 31; Cependant, la plus grande valeur décimale du canal vert est 2^6-1 = 63. Sachant cela, les canaux de couleur normalisés sont les suivants:

// NormalizedValue = DecimalValue/MaxDecimalValue 
R = 31/31 = 1.000 
G = 41/63 =~ 0.650 
B = 15/31 =~ 0.483 

Réitérer, les valeurs normalisées sont utiles, car ils représentent la contribution relative de chaque canal de couleur dans la sortie. Ajouter plus de bits à un canal donné n'affecte pas la gamme de couleur possible, il améliore simplement la précision des couleurs (plus de nuances de ce canal de couleur, fondamentalement). Sachant tout ce qui précède, vous devriez pouvoir convertir n'importe quel format RGB (A), quel que soit le nombre de bits stockés dans chaque canal, à tout autre format RGB (A). Par exemple, convertissons les valeurs normalisées que nous venons de calculer en B8G8R8A8_UNORM. C'est facile une fois que vous avez calculé des valeurs normalisées, parce que vous vous contentez de mettre à l'échelle la valeur maximale dans le nouveau format. Chaque canal utilise 8 bits, donc la valeur maximale est 2^8-1 = 255. Étant donné que le format d'origine ne possédait pas de canal alpha, vous stockiez généralement la valeur maximale (c'est-à-dire totalement opaque).

// OutputValue = InputValueNormalized * MaxOutputValue 
B = 0.483 * 255 = 123.165 
G = 0.650 * 255 = 165.75 
R = 1.000 * 255 = 255 
A = 1.000 * 255 = 255 

Il ne manque plus qu'une seule chose avant de pouvoir le coder. Bien au-dessus, j'étais capable de sortir les morceaux pour chaque canal juste en les alignant et en les copiant. C'est ainsi que j'ai obtenu les bits verts 101001. Dans le code, cela peut être fait en "masquant" les bits dont nous ne nous soucions pas. Le décalage fait exactement ce que cela ressemble: il déplace des bits vers la droite ou vers la gauche. Lorsque vous déplacez des bits vers la droite, le bit le plus à droite est ignoré et le nouveau bit le plus à gauche est affecté à 0. Visualisation ci-dessous en utilisant l'exemple 16 bits ci-dessus.

1111110100101111 // original 16 bits 
0111111010010111 // shift right 1x 
0011111101001011 // shift right 2x 
0001111110100101 // shift right 3x 
0000111111010010 // shift right 4x 
0000011111101001 // shift right 5x 

Vous pouvez continuer à changer de direction et vous finirez avec seize 0. Cependant, je me suis arrêté à cinq quarts de travail pour une raison. Notez maintenant que les 6 bits les plus à droite sont les bits verts (j'ai décalé/mis au rebut les 5 bits bleus). Nous avons presque extrait les bits exacts dont nous avons besoin, mais il reste encore 5 bits rouges supplémentaires à gauche des bits verts. Pour les supprimer, nous utilisons une opération "logique et" pour ne masquer que les 6 bits les plus à droite. Le masque, en binaire, est 0000000000111111; 1 signifie que nous voulons le bit, et 0 signifie que nous ne le voulons pas. Le masque est tous les 0, sauf pour les 6 dernières positions, parce que nous voulons seulement les 6 derniers bits. Ligne ce masque avec le nombre 5x décalé, et la sortie est 1 lorsque les deux bits sont 1 et 0 pour tous les autres bits:

0000011111101001 // original 16 bits shifted 5x to the right 
0000000000111111 // bit mask to extract the rightmost 6 bits 
------------------------------------------------------------ 
0000000000101001 // result of the 'logical and' of the two above numbers 

Le résultat est exactement le nombre que nous recherchons: le 6 vert bits et rien d'autre. Rappelons que les 0 premiers n'ont aucun effet sur la valeur décimale (c'est toujours 41). Il est très simple de faire les opérations 'shift right' (>>) et 'logical and' (&) en C# ou tout autre langage de type C. Voici à quoi il ressemble en C#:

// 0xFD2F is 1111110100101111 in binary 
uint pixel = 0xFD2F; 

// 0x1F is 00011111 in binary (5 rightmost bits are 1) 
uint mask5bits = 0x1F; 

// 0x3F is 00111111 in binary (6 rightmost bits are 1) 
uint mask6bits = 0x3F; 

// shift right 11x (discard 5 blue + 6 green bits), then mask 5 bits 
uint red = (pixel >> 11) & mask5bits; 

// shift right 5x (discard 5 blue bits), then mask 6 bits 
uint green = (pixel >> 5) & mask6bits; 

// mask 5 rightmost bits 
uint blue = pixel & mask5bits; 

Mettre le tout ensemble, vous pourriez vous retrouver avec une routine qui ressemble à cela.Faites attention à lire sur l'endianness, cependant, pour vous assurer que les octets sont ordonnés de la manière que vous attendez. Dans ce cas, le paramètre est un entier non signé de 32 bits (les 16 premiers bits sont ignorés)

byte[] R5G6B5toR8G8B8A8(UInt16 input) 
{ 
    return new byte[] 
    { 
     (byte)((input & 0x1F)/31.0f * 255),   // blue 
     (byte)(((input >> 5) & 0x3F)/63.0f * 255), // green 
     (byte)(((input >> 11) & 0x1F)/31.0f * 255), // red 
     255           // alpha 
    }; 
} 
+0

Justin. Merci pour l'info. Je ferai référence à cela assez souvent au cours des deux prochaines semaines. et mettra vos exemples et détails à bon escient! C'est détaillé, informé et répondu! Je vous remercie –