[c++] 24bits waarde uit array lezen

Pagina: 1
Acties:

  • Arjan
  • Registratie: Juni 2001
  • Niet online

Arjan

copyright is wrong

Topicstarter
goedesmiddags,

Ik was wat aan het expirimenteren met de ffmpeg library in combinatie met SDL. Nu heb ik de volgende situatie.

een SDL_Surface bewaart de pixels in een Uint32 pointer array. (of in een Uint8 voor 8bits kleur)
24bits waardes worden gewoon in 32bits integers gestopt. Als ik SDL een surface laat maken uit een 24bits array, dan gaat dat op een of andere manier prima. Als ik vervolgens echter per-pixel door deze verzameling heen wil lopen dan springt ie logischerwijs met stappen van 32bits, omdat ik 'm cast naar een Uint32 type.
C++:
1
2
3
for(int i=0; i<frame->h*frame->pitch/4; i++) {
    ( (Uint32_t*)frame->pixels )[i] = 0xFF0000;
}

Voor zover ik weet is er echter geen 24bits datatype.
Ik ben nu dan ook opzoek naar een simpele manier om toch de pixels te kunnen lezen. Ik neem aan dat het wel mogelijk is om dmv een datastream steeds 3bytes te lezen en deze te shiften naar hun juiste plek binnen de integer, maar ivm performance hoop ik dat er een simpelere manier is :)

oprecht vertrouwen wordt nooit geschaad


  • DroogKloot
  • Registratie: Februari 2001
  • Niet online

DroogKloot

depenisvanjezus

Gewoon direct elke uint32 in de array 8 bits naar rechts shiften? Of bedoel je dat de array-elementen allemaal 24 bits van de ene pixel en 8 van de andere representeren? :?

[ Voor 115% gewijzigd door DroogKloot op 10-09-2006 15:38 ]


  • Arjan
  • Registratie: Juni 2001
  • Niet online

Arjan

copyright is wrong

Topicstarter
DroogKloot schreef op zondag 10 september 2006 @ 15:30:
Gewoon een masker over elke uint32 in de array leggen dat de 8 LSB's negeert en de rest niet (wat niet eens nodig is, 8 bits naar rechts shiften is genoeg)? :?
het probleem is dus dat de array uit 24bits waardes bestaat.

code:
1
2
24b: 0xRRGGBB 0xRRGGBB 0xRRGGBB
32b: 0xRRGGBBAA 0xRRGGBBAA 0xRRGGBBAA

omdat ik de void pointer van frame->pixels niet naar een 24bits waarde kan casten, loopt de code erdoorheen alsof het 32 bits waardes zijn wat erin zit.
code:
1
0xRRGGBBRR 0xGGBBRRGG 0xBBRRGGBB

wat jij bedoelt werkt prima voor een 32bits type met daarin een 24bits waarde, maar daar voorziet SDL standaard al in, of begrijp ik je uitleg nu verkeerd?

oprecht vertrouwen wordt nooit geschaad


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 19:13
Afhankelijk van je architectuur zou dit kunnen werken:

C:
1
2
3
char *data;
// ...
int value = (*(int*)(data + 3*n))&((1<<24) - 1)
Dit is niet portable, want op sommige architecturen mag je geen unaligned pointers gebruiken.

De portable manier is om ze handmatig te reconstrueren:
C:
1
2
3
char *data;
// ..
int value = (data[3*n] << 16) | (data[3*n + 1] << 8) | (data[3*n + 2] << 0);


In een lusje kan dat natuurlijk wel wat efficienter:
C:
1
2
3
4
5
6
7
8
char *p = data;
for(int n = 0; n < pixel_count; ++n)
{
    int value = *(data++) << 16;
    value |= *(data++) << 8;
    value |= *(data++);
    // doe iets met value...
}


Dit alles onder aanname van 8-bit bytes (of tenminste dat elke component van elke pixel in een aparte byte staat) anders wordt het allemaal nog veel ingewikkelder, en theoretischer. ;)

[ Voor 53% gewijzigd door Soultaker op 10-09-2006 15:55 ]


  • DroogKloot
  • Registratie: Februari 2001
  • Niet online

DroogKloot

depenisvanjezus

Ah, nu begrijp ik je probleem. ;) Wat je zou kunnen doen is in stappen van 3 uint32s tegelijk door de array wandelen en uit die drie uint32s vier uint24s lospeuteren, maar dat is natuurlijk niet echt een structurele oplossing...

[ Voor 3% gewijzigd door DroogKloot op 10-09-2006 15:57 ]


  • Arjan
  • Registratie: Juni 2001
  • Niet online

Arjan

copyright is wrong

Topicstarter
Soultaker schreef op zondag 10 september 2006 @ 15:44:
Afhankelijk van je architectuur zou dit kunnen werken:

C:
1
2
3
char *data;
// ...
int value = (*(int*)(data + 3*n))&((1<<24) - 1)
Dit is niet portable, want op sommige architecturen mag je geen unaligned pointers gebruiken.

De portable manier is om ze handmatig te reconstrueren:
C:
1
2
3
char *data;
// ..
int value = (data[3*n] << 16) | (data[3*n + 1] << 8) | (data[3*n + 2] << 0);


In een lusje kan dat natuurlijk wel wat efficienter:
C:
1
2
3
4
5
6
7
8
char *p = data;
for(int n = 0; n < pixel_count; ++n)
{
    int value = *(data++) << 16;
    value |= *(data++) << 8;
    value |= *(data++);
    // doe iets met value...
}


Dit alles onder aanname van 8-bit bytes (of tenminste dat elke component van elke pixel in een aparte byte staat) anders wordt het allemaal nog veel ingewikkelder, en theoretischer. ;)
Ja, zo heb ik het in principe ook opgelost, maar dan heb ik het resultaat opgeslagen in een 32bits buffer.
Als blijkt dat deze conversie een te grote performance hit is zal ik toch wat anders moeten verzinnen.
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Uint32* convert8to32(uint8_t* buf, int width, int height) {
    Uint32* temp = new Uint32[width*height];

    for(int i=0; i<width*height; i++) {
        temp[i] = 0;
        for(int a=0; a<3; a++) {
            temp[i] |= buf[(i*3)+a] << (a*8);
        }
                // alpha op max zetten //
        temp[i] |= 0xFF000000;
    }

    return temp;
}

Ook deze code is big/little endian gevoelig, is er geen mogelijkheid zelf een 24 bits datatype te defineren? dat zou natuurlijk ideaal zijn. Ohja, portabiliteit is overigens wel een issue.

[ Voor 5% gewijzigd door Arjan op 10-09-2006 16:21 ]

oprecht vertrouwen wordt nooit geschaad


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 19:13
Ik begrijp niet helemaal wat het probleem met endianness is, hier. De indeling van ARGB in een unsigned long (met A blijkbaar als hoogste 8 bits, en B als laagste 8 bits) is niet platformafhankelijk.

Verder is alleen de vraag of de manier waarop SDL pixels indeelt in z'n surface memory platform afhankelijk is. Dat kan ik niet beantwoorden (ik ken SDL verder niet), maar het lijkt mij redelijk om aan te nemen dat als je een RGB surface formaat hebt, R op alle platforms eerst komt. (Veel media libraries ondersteunen ook aparte functies voor BGR bijvoorbeeld.)

Zelf een 3-bytes datatype maken zit er in ieder geval niet in. Dat kan ook niet op portable wijze -- zoals ik al zei ondersteunen veel platforms unaligned pointer dereferencing gewoon niet.

edit:
(Klopt trouwens niet helemaal wat ik zeg -- als je een 3-bytes datatype kan maken is 'ie natuurlijk niet meer unaligned. Maar goed, de conclusie klopt wel: het kan gewoon niet portable. ;))

[ Voor 27% gewijzigd door Soultaker op 10-09-2006 16:57 ]


  • Arjan
  • Registratie: Juni 2001
  • Niet online

Arjan

copyright is wrong

Topicstarter
Soultaker schreef op zondag 10 september 2006 @ 16:33:
[...]
Verder is alleen de vraag of de manier waarop SDL pixels indeelt in z'n surface memory platform afhankelijk is. Dat kan ik niet beantwoorden (ik ken SDL verder niet), maar het lijkt mij redelijk om aan te nemen dat als je een RGB surface formaat hebt, R op alle platforms eerst komt. (Veel media libraries ondersteunen ook aparte functies voor BGR bijvoorbeeld.)
SDL past zich aan, aan het platform, je moet zelf aangeven wat de betreffende RGB maskers zijn.
Zelf een 3-bytes datatype maken zit er in ieder geval niet in. Dat kan ook niet op portable wijze -- zoals ik al zei ondersteunen veel platforms unaligned pointer dereferencing gewoon niet.
Ok, dat is duidelijk :)

oprecht vertrouwen wordt nooit geschaad


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 10-12-2025
Simpel: gewoon met een char* er doorheen stappen, byte voor byte lezen. Je snelheid wordt simpelweg door je main memory bepaald, misschien L2 cache. Als je dan in je CPU wat bitshifts doet dan maakt dat echt niets uit - profile maar. De enige truc die misschien de moeite waard ism is een peek-ahead (een dummy-read van een adres wat je gaat gebruiken).

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13-02 18:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

Atgast schreef op zondag 10 september 2006 @ 16:16:
is er geen mogelijkheid zelf een 24 bits datatype te defineren?
Duh, natuurlijk wel
C++:
1
2
3
4
5
struct pixel
{
    unsigned char b, g, r;
    unsigned value() const { return r << 16 | g << 8 | b; }
};

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Count
  • Registratie: Augustus 2000
  • Laatst online: 10-08-2023
Daar moet je overigens waarschijnlijk wel

#pragma pack(push)
#pragma pack(1)

voorzetten en

#pragma pack(pop)

achterzetten om padding te voorkomen.

Zoals gezegd is dit niet portable.

[ Voor 18% gewijzigd door Count op 11-09-2006 17:10 ]

Great minds think in parallel gutters.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13-02 18:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

Onzin. Een struct krijgt doorgaans de alignment van z'n inhoud, en daar het allemaal chars zijn heeft de struct zelf ook een char alignment. :)

[ Voor 45% gewijzigd door .oisyn op 11-09-2006 17:15 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • JeromeB
  • Registratie: September 2003
  • Laatst online: 29-12-2025

JeromeB

woei

.oisyn schreef op maandag 11 september 2006 @ 17:11:
Onzin. Een struct krijgt doorgaans de alignment van z'n inhoud, en daar het allemaal chars zijn heeft de struct zelf ook een char alignment. :)
Heb je de garantie dat een unsigend char altijd 8 bits is? Dat lijkt me niet. Je kan dan wel gebruik maken van bitfields, maar dan nog kan de struct groter zijn dan de alignment van z'n inhoud.

Volgensmij bieden bieden sommige compilers de mogelijkheid om via #pragma pack te bepalen dat een struct dezelfde grootte heeft als de alignment van z'n inhoud.

[ Voor 3% gewijzigd door JeromeB op 11-09-2006 18:37 ]

PC load letter? What the fuck does that mean?


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13-02 18:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

JeromeB schreef op maandag 11 september 2006 @ 18:36:
[...]


Heb je de garantie dat een unsigend char altijd 8 bits is? Dat lijkt me niet. Je kan dan wel gebruik maken van bitfields, maar dan nog kan de struct groter zijn dan de alignment van z'n inhoud.
Een char is CHAR_BIT bits en is altijd het kleinst adresseerbare type. Als die niet 8 is zit je sowieso in de shit :). Op system waarmee je met een fullcolor framebuffer werkt zul je sowieso al niets anders dan 8 bits zien. Bitfields zijn trouwens alleen syntactische suiker binnen het type waarmee je ze definieert. Een bitfield met als type int krijgt nog altijd de alignment van een int.
Volgensmij bieden bieden sommige compilers de mogelijkheid om via #pragma pack te bepalen dat een struct dezelfde grootte heeft als de alignment van z'n inhoud.
Dat is als je bijvoorbeeld dit hebt:
C++:
1
2
3
4
5
struct S
{
    char a, b;
    int i;
};

Op een 32 bits systeem en een reguliere compiler is sizeof(S) gelijk aan 8, omdat die i op 4 bytes gealigned wil zijn. Met #pragma pack(1) zet je de maximale alignment op 1 byte zodat ie op offset 2 in de struct komt. Als alle elementen in de struct dezelfde alignment hebben dan boeit dat sowieso niet en kun je die pragma dus net zo goed achterwege laten.
Padding in een struct treedt op:
- tussen 2 members a en b, als b een bepaalde alignment wilt en hij anders niet op een veelvoud daarvan begint
- aan het eind van de struct, om de size van de struct een veelvoud te maken van het element met de grootste alignment (dit ivm arrays).

De volgende structs zijn dus doorgaans equivalent wat memory layout betreft:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct A
{
    char a;
    int b;
    double c;
    char d;
};

struct B
{
    char a;
    char _padd1[3];
    int b;
    double c;
    char d;
    char _padd2[7];
};


Overigens is dit allemaal nogal implementatie-specifiek, in principe zegt de standaard alleen dat nonstatic member b dat is gevolgd door een nonstatic member a zonder tussenliggende access specifier een hoger offset krijgt binnen de struct/class. Daartussen komt evt. implementation-defined padding, en de volgorde van members met tussenliggende access specifiers is al helemaal niet gedefinieerd.

[ Voor 78% gewijzigd door .oisyn op 11-09-2006 19:23 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Je moet ook erg uitkijken dat je op sommige systemen geen "int" kan uitlezen over een alignment grens. Stel op een 32bit systeem, en je krijgt het voor elkaar om 3 RGB bytes na elkaar op te slaan, in sequence. Dan lees je het als optimalisatie uit door eerst een unsigned char te lezen, en dan een int, etc. Dat gaat goed fout op veel systemen.

De standaard C++ oplossing voor zoiets is overigens een "evaluator" proxy class.

[ Voor 10% gewijzigd door Zoijar op 11-09-2006 21:12 ]


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 10-12-2025
.oisyn schreef op maandag 11 september 2006 @ 19:01:
Bitfields zijn trouwens alleen syntactische suiker binnen het type waarmee je ze definieert. Een bitfield met als type int krijgt nog altijd de alignment van een int.
Nou nee. Aangezien je geen pointer ernaar kunt hebben, is de hele notie van een alignment bij bitfields afwezig. Alignment is normaal gesproken geassocieerd met lvalues, en bitfields zijn wat dat betreft rare dingen.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

.oisyn schreef op maandag 11 september 2006 @ 19:01:
Overigens is dit allemaal nogal implementatie-specifiek, in principe zegt de standaard alleen dat nonstatic member b dat is gevolgd door een nonstatic member a zonder tussenliggende access specifier een hoger offset krijgt binnen de struct/class. Daartussen komt evt. implementation-defined padding, en de volgorde van members met tussenliggende access specifiers is al helemaal niet gedefinieerd.
Plus dat ze layout compatible zijn met elkaar. Het zijn PODs dit, die hebben apparte constraints. Voor classes/structs gaat er idd geen enkele regel op...
- Layout
The bytes constituting a POD object are contiguous [§1.8, ¶5].
"POD-struct ... types are layout-compatible if they have the same number of members, and corresponding members (in order) have layout-compatible types" [§9.2, ¶14].
POD-union ... types are layout-compatible if they have the same number of members, and corresponding members (in any order) have layout-compatible types" [§9.2, ¶15].

- Initialization
A non-const POD object declared with no initializer has an "indeterminate initial value" [§8.5, ¶9].
Default initialization of a POD object is zero initialization [§8.5, ¶5].
A static POD object declared with an initializer is given its initial value:
if local, "before its block is first entered" [§6.7, ¶4]; else
if non-local, "before any dynamic initialization takes place" [§3.6.2, ¶1].

- Copying
The bytes constituting a POD object can be copied (e.g., via memcpy())
to a sufficiently large array of char or unsigned char and back again without changing the object's value [§3.9, ¶2], or
to another object of the same POD-type, in which case the second object's value will be the same as that of the first [§3.9, ¶3].
Any POD type may be used as the "character" type in the standard's templated string classes [§21, ¶1].

- Addressing
The address of a POD object can be an address constant expression (or part of one) [§5.19, ¶4], while a reference to a POD member can be a reference constant expression [§5.19, ¶5].
"A pointer to a POD-struct object, suitably converted using a reinterpret_cast, points to its initial member ... and vice versa" [§9.2, ¶17].

  • Arjan
  • Registratie: Juni 2001
  • Niet online

Arjan

copyright is wrong

Topicstarter
.oisyn schreef op maandag 11 september 2006 @ 11:54:
[...]

Duh, natuurlijk wel
C++:
1
2
3
4
5
struct pixel
{
    unsigned char b, g, r;
    unsigned value() const { return r << 16 | g << 8 | b; }
};
inmiddels is gebleken dat ik sowieso een 32bits array nodig heb in het vervolg van de code, maar de omzetting is nu wel lekker makkelijk.
C++:
1
2
3
4
5
pixel* buf = data;

for(int i=0; i<bmp->w*bmp->h; i++) {
    ((Uint32*)bmp->pixels)[i] = 0xFF << 24 | buf[i].b << 16 | buf[i].g << 8 | buf[i].r;
}


Is er eigenlijk een manier om een dergelijke struct te laten reageren als een 'gewone' int?
Ik had zelf nog een = operator toegevoegd:
C++:
1
2
3
4
5
pixel operator=(int rhs) {
    b = (rhs & 0xFF0000) >> 16;
    g = (rhs & 0x00FF00) >> 8;
    r = (rhs & 0x0000FF);
}

Ik vroeg me af of je de boel zo kan opzetten dat je ook het volgende kon doen:
C++:
1
2
pixel foo = 10;
int bar = foo;

maar dan dus zonder de = operator van de int uit te breiden. Dus dat 'foo' zonder argumenten een int waarde returnd. Kan me haast niet voorstellen dat het mogelijk is zonder het geheel stuque te maken, maar het zou wel erg handig zijn ;)

oprecht vertrouwen wordt nooit geschaad


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 19:13
Jawel, een conversie-operator implementeren:
C++:
1
2
3
4
struct Foo {
  unsigned char r, g, b;
  operator int () { return r | (g << 8) | (b << 16); }
};


edit:
.oisyn: grappig, ik zat me dat ook al af te vragen. Ik ga er maar vanuit dat Atgast zelf de members wel kan permuteren tot ze in de juiste volgorde staan. :+

[ Voor 35% gewijzigd door Soultaker op 12-09-2006 02:33 ]


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13-02 18:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

I'm confused, in je 2e post in deze draad laat je zien dat je een bgr buffer gebruikt (ik ga er even vanuit dat je op een little-endian platform werkt aangezien je het in een van je eerdere topics over windows had, die meestal ook bgr buffers gebruikt). Nu hanteer je echter een rgb scheme.

.edit: oh ik zie het al, je notatie in je 2e post was gewoon verkeerd :)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.

Pagina: 1