View Full Version : [C] Convertire RGB 32 bit a RGB 16 bit. Mi aiutate a capire il problema?
Devo fare questo esercizio ma non credo di averlo capito bene (indipendentemente dal linguaggio che comunque è il C):
Implementare la funzione:
unsigned short ConvertRGB(unsigned int colorRGB);
- colorRGB è il colore a 32 bit che si vuole convertire.
- colorRGB è così formato: 8 bit di empty padding, 8 bit rosso, 8 bit verde, 8 bit blu
- il colore restituito deve essere: 5 bit rosso, 6 bit verde, 5 bit blu
Poi mi indica nei suggerimenti che in esadecimale RED:0x00FF0000, GREEN 0x0000FF00, BLUE 0x000000FF, WHITE 0x00FFFFFF, BLACK 0X00000000.
Ora, il problema in cosa consiste?
Io ho in memoria un unsigned int che contiene all'interno nei diversi byte i valori r,g,b e siccome sono blocchi di 8 bit saranno valori tra 0 e 255.
Io devo convertirli in 5 bit per rosso e blu, cioè in valori da 0 a 31 ed in 6 bit il verde, cioè da 0 a 63.
Supponiamo che esegua semplicemente delle proporzioni trasformando i numeri nel nuovo formato, poi come li memorizzo nel blocco di memoria corretto lavorando su un unico unsigned short?
esempio, supponi di avere 0xFF, 255. devi rappresentarlo su 5 bit, per cui devi tagliare informazione dal tuo numero originale. puoi tagliare il range (MSBs) o la precisione (LSBs). Tagliare il range non e' una bella idea, in quanto riuscirai a rappresentare solo un sottoinsieme dei tuoi valori di ingresso, seppur con una bella precisione. per cui devi tagliare la precisione,ovvero rappresenti tutto il tuo range ma perdi in dettaglio.
se X e' il nuo numero (0xFF), Y sara' floor(X/8). in operazioni bitwise, sara' semplicemente X>>3, oppure and and shift del tuo numero originale rappresentato su tutti e 32 i bit. Oppure i bitfields, sono ancora meglio... ma quello lo lascio a te per esercizio.
Grazie, stavo ragionando proprio su questo e mi sono accorto che effettivamente il numero finale corrisponde al numero iniziale esclusi i primi 8 bit di padding shiftato a destra di 3.
Ci sto ancora lavorando su ma l'and per cosa dovrebbe servire?
Ho scritto questa porzione di codice:
//0x (padding) (red) (green) (blue)
unsigned int colorRGB = 0x00010203;
colorRGB <<= 8;
_Uint32t red = colorRGB >> 24;
_Uint32t green = (colorRGB >> 16) & 0xff;
_Uint32t blue = (colorRGB >> 8) & 0x0ff;
cout << "Red: " << red << endl << endl;
cout << "Green: " << green << endl << endl;
cout << "Blue: " << blue << endl << endl;
funziona, posso così ottenere i valori di r, g, b estraendoli dall'unsigned int.
Però questo codice l'ho trovato su internet e non capisco esattamente l'and a cosa serva.
Mi dovrebbe annullare il valore dei bit inutili ma l'and non mi pare possa fare questo (eppure funziona quindi non ho capito qualcosa).
Se io ho un int iniziale di 32 bit e voglio prendere i bit da 8 a 16 eseguo uno switch a destra di 16 in modo da ritrovarmi i miei 8 bit a destra e poi i bit da 0 a 8 subito a sinistra. Questi bit da 0 a 8 poi li voglio annullare perchè non fanno parte dei bit che mi interessano e qui mi pare che si esegua l'and ma l'and con 0xff (cioè otto 1) da quello che so lascerebbe a 1 i bit già ad 1 ed a 0 i bit già a 0, dunque non cambierebbe niente.
un esempio banale:
101101
vuoi solo il bit 2 e 3
101101 >> 2 = 001011
ora devi annullare il contributo di tutti gli altri
001011 & 000011 = 000011
Ecco una cosa che non avevo capito:
11000011 & 0xf mi restituisce 00000011.
Pensavo di dover iniziare da sinistra, quindi che 0xf fosse incolonnato con i primi due 1 e dunque restituisse 11000000 ma effettivamente non ha troppo senso.
Ok adesso penso di aver capito, mi complica un po' la vita il fatto di dover convertire il rosso ed il blu in 5 bit mentre il verde in 6 però penso di potercela fare.
Tanto per la cronaca avevo 15 minuti per fare questo esercizio, ci sono dietro da stamattina!:mc:
I bitfield li studierò a parte, per ora questo esercizio è meglio che lo finisca così perchè nei test che mi hanno messo davanti ultimamente gli shift e gli and vengono usati moltissimo quindi è bene che mi abitui a gestirli.
Grazie dell'aiuto, poi se finisco posto il codice.
Oceans11
11-11-2013, 16:37
A me è venuta in mente una cosa del genere, non so quanto sia portabile però, per via dell'endianess.
#include <stdio.h>
#include <inttypes.h>
union {
uint32_t value;
struct {
uint8_t blue;
uint8_t green;
uint8_t red;
uint8_t pad;
} rgb;
} color;
int main(int argc, char *argv[]) {
color.value = 0x00AABBCC;
printf("rgb: %08x\n", color.value);
printf("pad: %02x\n", color.rgb.pad);
printf("red: %02x\n", color.rgb.red);
printf("green: %02x\n", color.rgb.green);
printf("blue: %02x\n", color.rgb.blue);
return 0;
}
con i bitfields come sarebbe?
Ok, ho finito. L'ho testata un po' e sembra dare il risultato corretto.
Ci saranno probabilmente modi più prestanti ed ora mi toccherà cercarli ma almeno mi sono studiato un po' di masking perchè ero veramente a secco.
//0x (padding) (red) (green) (blue)
unsigned int colorRGB = 0x00533498;
cout << "Color: " << bitset<32>(colorRGB) << endl << endl;
//shinfting left of 8 positions to discard the first 8 bits used for padding
colorRGB <<= 8;
//storing the components of the RGB color
_Uint32t red = colorRGB >> 24;
_Uint32t green = (colorRGB >> 16) & 0xff;
_Uint32t blue = (colorRGB >> 8) & 0x0ff;
unsigned short convertedRGB = 0;
//converting to 5 and 6 bits
red >>= 3; //red will be stored in 5 bits
green >>= 2; //green will be stored in 6 bits
blue >>= 3; //blue will be stored in 5 bits
cout << "------ 16 bit converted color components: " << endl << endl;
cout << "Red: " << bitset<32>(red) << endl;
cout << "Green: " << bitset<32>(green) << endl;
cout << "Blue: " << bitset<32>(blue) << endl;
cout << "-------------------- " << endl << endl;
//positioning correctly the significant bits of r, g, b to operate a OR
red <<= 11; //red must occupy positions 16-20 in the 32 bits long number
green <<= 5; //green must occupy positions 21-26 in the 32 bits long number
//blue = blue; //blue must occupy positions 27-31 in the 32 bits long number
cout << "------ Shifted color components: " << endl << endl;
cout << "Red: " << bitset<32>(red) << endl;
cout << "Green: " << bitset<32>(green) << endl;
cout << "Blue: " << bitset<32>(blue) << endl;
cout << "-------------------- " << endl << endl;
//obtaining the final converted RGB
convertedRGB = red | green | blue;
cout << "Color converted: " << bitset<16>(convertedRGB) << endl << endl;
Oceans11
11-11-2013, 17:06
I bitfield sono una generalizzazione della union che hai creato tu, dove puoi usare campi di dimensione arbitraria,e.g.
union
{
uint32 value;
struct
{
uint32 padding:8;
uint32 blue:8;
uint32 green:8;
uint32 red:8;
}
} color32;
union
{
uint16 value;
struct
{
uint16 blue:6;
uint16 green:5;
uint16 red:5;
}
} color16;
int main()
{
color32 x;
color16 y;
x.red = ...;
x.blue = ...;
x.green = ...;
y.red = x.red/8;
<...>
return 0;
}
Questa e' l'idea di base, non so se compila o se ho preso tutti i campi giusti come da testo dell'esercizio. La cosa importante e' che maschera tutti i dettagli di basso livello ed e' molto piu' facile da seguire.
c'è ancora qualcosa che mi sfugge, me la devo studiare meglio sta cosa.
grazie comunque
Sto guardando la versione con i bitfields, sembra interessante l'idea.
Cosa significa?
uint32 padding:8;
A me mentre scrivevo il codice con le Union era venuto da usare degli uint8 invece che 32.
http://www.tutorialspoint.com/cprogramming/c_bit_fields.htm
significa che padding e' un field di 8 bit all'interno di un tipo uint32.
Adesso me lo studio insieme a un po' di union che ho usato molto poco.
Ho scritto il codice usando i bitfields ma il risultato è sbagliato anche se di poco (c'è un bit a 0 di troppo).
union color32
{
UINT32 value;
struct
{
UINT32 padding:8;
UINT32 red:8;
UINT32 green:8;
UINT32 blue:8;
};
};
union color16
{
UINT16 value;
struct
{
UINT16 red:5;
UINT16 green:6;
UINT16 blue:5;
};
};
int _tmain(int argc, _TCHAR* argv[])
{
//0x (padding) (red) (green) (blue)
//int (padding) (83) (52) (152)
//expected result: 01010 001101 10011
unsigned int RGB32 = 0x00533498;
cout << "Color: " << bitset<32>(RGB32) << endl << endl;
//shift the color left to discard the padding
RGB32 <<= 8;
color32 color;
color.value = RGB32;
cout << "Color: " << bitset<32>(color.value) << endl << endl;
color16 converted;
converted.red = color.red/8;
converted.green = color.green/8;
converted.blue = color.blue/8;
cout << "Color converted: " << bitset<16>(converted.value) << endl << endl;
system("pause");
return 0;
}
Ragionandoci un attimo mi pare che eseguire lo shift a sinistra per scartare il padding non abbia senso usando i bitfields perchè poi posso accedere direttamente ai campi. Però in effetti se non lo metto la soluzione è troncata, mancano le ultime cifre mentre eseguendo lo shift è quasi corretta, c'è solo uno 0 di troppo che non capisco da dove venga fuori.
Ok, così è corretta:
//0x (padding) (red) (green) (blue)
//int (padding) (83) (52) (152)
//expected result: 01010 001101 10011
unsigned int RGB32 = 0x00533498;
cout << "Color: " << bitset<32>(RGB32) << endl << endl;
//shift the color left to discard the padding
RGB32 <<= 8;
color32 color;
color.value = RGB32;
color16 converted;
converted.red = color.red >> 3;
converted.green = color.green >> 2;
converted.blue = color.blue >> 3;
cout << "Color converted: " << bitset<16>(converted.value) << endl << endl;
Non capisco perchè sia necessario eseguire lo shift di 8 a sinistra per eliminare il padding.
Per il resto non posso rispondere nel dettaglio, dovrei provare il tuo codice per capire gli errori. quello che ti posso dire e' che se sono usait correttamente, coi bitfield non necessiti di fare nessuno shift.
Infatti è questo che mi pare strano, senza lo shift la soluzione è sbagliata, con lo shift è corretta.
Stasera farò altri esercizi con le union e magari capisco se ho commesso qualche errore quando avrò più chiaro il meccanismo.
Grazie.
l'errore è in come hai definito il tuo color32:
union color32
{
UINT32 value;
struct
{
UINT32 padding:8;
UINT32 red:8;
UINT32 green:8;
UINT32 blue:8;
};
};
questo vuol dire che verso il LSB ci sarà padding, mentre verso il MSB ci sarà blue.
poi fai questo:
unsigned int RGB32 = 0x00533498;
RGB32 <<= 8; // RGB32 contiene 0x53349800
che significa dare a blue 0x53, a green 0x34, a red 0x98, a padding 0x00
Ah :D , comprendo.
Ora ho modificato e funziona.
Dunque, giusto per conferma se io volessi usare le union per poter avere un campo long potendo accedere anche alle sue parti considerandolo spezzato in 2 sarebbe:
union
{
long value;
struct
{
int low;
int high;
}
};
Cioè li ordino a partire dall'lsb verso il msb.
Grazie.
Oceans11
12-11-2013, 13:51
Ah :D , comprendo.
Ora ho modificato e funziona.
Dunque, giusto per conferma se io volessi usare le union per poter avere un campo long potendo accedere anche alle sue parti considerandolo spezzato in 2 sarebbe:
union
{
long value;
struct
{
int low;
int high;
}
};
Cioè li ordino a partire dall'lsb verso il msb.
Grazie.
Sì, ma dipende dal tipo di architettura della macchina.
bitfields (http://c-faq.com/struct/bitfield0.html)
cdimauro
12-11-2013, 21:56
Non usate mai i bitfield, a meno che non sappiate come vengono gestiti dal compilatore, conoscete l'endianess del processore, ma soprattutto esattamente dove dovrà girare l'applicazione.
cdimauro
13-11-2013, 17:31
A te. La realtà è ben diversa. Vedi il tuo commento #21. ;)
cdimauro
14-11-2013, 22:09
Gli errori capitano normalmente: a maggior ragione con cose il cui comportamento dipende da fattori esterni (alla rigorosa definizione del linguaggio).
I bitfield rientrano pienamente in questa categoria: evitateli.
cdimauro
15-11-2013, 19:45
E infatti si vede come si comporta bene coi colori...
Davvero, faccio fatica a credere che una delle caratteristiche meno portabili in assoluto sia fatta passare come l'esatto opposto, quando:
- non è noto quanti bit verranno effettivamente riservati per un campo;
- non è noto se il compilatore aggiungerà padding all'inizio e/o alla fine di ogni campo;
- non è noto se il compilatore allocherà i campi di bit partendo dall'MSB o dall'LSB;
- non è noto se l'int sarà con o senza segno;
- dulcis in fundo, il compilatore potrebbe benissimo ignorare tutto e utilizzare una struct per implementare i campi di bit.
A maggior ragione se vengono utilizzati per manipolare la memoria (ad esempio registri hardware mappati in memoria), bisognerebbe evitarli.
Comunque visto che l'hai tirato in ballo, riporto cosa ne pensa lo standard ISO C.
Pag.101:
A bit-field shall have a type that is a qualified or unqualified version of _Bool, signed int, unsigned int, or some other implementation-defined type.
Pag. 102:
A bit-field is interpreted as a signed or unsigned integer type consisting of the specified number of bits.
* As specified in 6.7.2 above, if the actual type specifier used is int or a typedef-name defined as int, then it is implementation-defined whether the bit-field is signed or unsigned.
An implementation may allocate any addressable storage unit large enough to hold a bit-field. If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit. If insufficient space remains, whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined. The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the
addressable storage unit is unspecified.
Pag. 103:
Each non-bit-field member of a structure or union object is aligned in an implementation-defined manner appropriate to its type.
Riporto anche cosa dice l'annesso J, intitolato "Portability issues" (ma tu guarda un po' :D):
Pag. 488:
J.1 Unspecified behavior
The following are unspecified:
Pag. 489:
The alignment of the addressable storage unit allocated to hold a bit-field (6.7.2.1).
Penso sia abbastanza chiaro, no?
vBulletin® v3.6.4, Copyright ©2000-2026, Jelsoft Enterprises Ltd.