Anweisung Vereinfachen bzw. Verständnisfrage hins. Array?



  • Moin allerseits,

    warum kann ich in folgender Schleife (die wie folgt angegeben korrekt funktoniert) in einem Array an der Adresse pBits[offset] nicht einfach ein 0x00bbggrr (RGB-Bytes) schreiben?

    for (int i = 0; i < st->npoints; i++) {
    
        offset = (cxScanLine * st->pointbuf[i].y) + (st->pointbuf[i].x * (sizeof(BYTE)) * 4);
    
        pBits[offset]   = 0x00;                    // 0x00bbggrr (!)
        pBits[offset++] = st->b[st->ccolour];      // bb schreiben
        pBits[offset++] = st->g[st->ccolour];      // gg schreiben
        pBits[offset++] = st->r[st->ccolour];      // rr schreiben
    }
    

    Ich denke die letzten vier Zeilen der Schleife müssten sich auf eine Anweisung die vier Bytes in einem Rutsch schreibt reduzieren lassen. Wobei dann natürlich die Elemente r, g und b der Struktur als DWORD nach dem Muster 0x00bbggrr vorliegen müssen. Das wäre kein Problem, das kann ich anpassen.

    Also, was habe ich hier noch nicht verstanden?



  • x++ ist post-increment. das hast du nicht verstanden.



  • c.rackwitz schrieb:

    x++ ist post-increment. das hast du nicht verstanden.

    Dachte eigentlich schon! Was könnte man am zuvor genannten Code denn Besser machen? Ich würde es ja gern begreifen(!).

    In genannter Schleife funktioniert es nicht richtig, wenn ich 0x00bbrrgg an die Adresse pBits[offset] schreibe. Dann werden die Farben falsch dargestellt (bzw. es ist immer BLAU). Schreibe ich die Bytes jedoch einzeln rein (wie im Codebeispiel) ist alles gut. Ich verstehe das "Warum" nicht?

    Kann ich nicht in einem Feld aus Bytes an einer Adresse ein DWORD schreiben? Mir ist klar, dass ich dann aufpassen muss, dass die Adresse auch an DWORD-Grenzen ausgerichtet sein muss.



  • timmix schrieb:

    Kann ich nicht in einem Feld aus Bytes an einer Adresse ein DWORD schreiben? Mir ist klar, dass ich dann aufpassen muss, dass die Adresse auch an DWORD-Grenzen ausgerichtet sein muss.

    Nein, kannst du nicht. Das DWORD wird dann zu einem Byte gekürzt (d.h. es wird nur 1 Byte statt 4 geschrieben).
    Du musst dein Array erst zu einem DWORD-Array casten.



  • Nanyuki schrieb:

    Du musst dein Array erst zu einem DWORD-Array casten.

    Aha, danke! Jetzt verstehe ich auch, warum immer nur blau gezeichnet wird!

    Typen casten verstehe ich sofort, aber ein Array? Ob es reicht, wenn ich folgendes mache?

    (DWORD) pBits[offset] = 0x00BBGGRR;
    

    Ich glaube ja fast, es ist sowas wie folgendes:

    (DWORD *) pBits[offset] = 0x00BBGGRR;
    

    Mmh, gleich mal probieren ...



  • timmix schrieb:

    Typen casten verstehe ich sofort, aber ein Array? Ob es reicht, wenn ich folgendes mache?

    (DWORD) pBits[offset] = 0x00BBGGRR;
    

    Ich glaube ja fast, es ist sowas wie folgendes:

    (DWORD *) pBits[offset] = 0x00BBGGRR;
    

    Mmh, gleich mal probieren ...

    Nein, reicht wohl nicht! Mmmh ... folgendes wird anstandslos kompiliert, macht aber gleich eine Speicherverletzung.

    DWORD *tmp  = (DWORD *) &pBits;                            
        tmp[offset] = 0x00AABBCC;
    

    Das scheint auch nicht der richtige Weg zu sein. Hat, jemand noch eine gute Idee für mich? 😉



  • die adresse an der der zeiger gespeichert ist interessiert dich nicht.
    caste den zeiger selbst:

    DWORD *tmp= (DWORD*)pBits;
    

    beachte bitte, dass dieser code 24bpp schreibt:

    pBits[offset]   = 0x00;                    // 0x00bbggrr (!)
        pBits[offset++] = st->b[st->ccolour];      // bb schreiben
        pBits[offset++] = st->g[st->ccolour];      // gg schreiben
        pBits[offset++] = st->r[st->ccolour];      // rr schreiben
    

    die 0x00 wird mit der naechsten anweisung wieder ueberschrieben.



  • hellihjb schrieb:

    caste den zeiger selbst:

    DWORD *tmp= (DWORD*)pBits;
    

    Das funktioniert leider auch nicht, da bekomme ich direkt eine Schutzverletzung, das hängt vermutlich mit der Berechnung des Offsets zusammen. Da setze ich mich später nochmal ran.

    hellihjb schrieb:

    die 0x00 wird mit der naechsten anweisung wieder ueberschrieben.

    Jo, da hast du recht gehabt! Ich mach es jetzt erstmal so:

    int szByte = sizeof(BYTE) * (bmih.biBitCount / 8); // funktioniert so mit 24/32bpp
    for (int i = 0; i < st->npoints; i++) {
        offset = (st->scanline * st->pointbuf[i].y) + (st->pointbuf[i].x * szByte);
        pBits[  offset] = st->b[st->ccolour];
        pBits[++offset] = st->g[st->ccolour];
        pBits[++offset] = st->r[st->ccolour];
        // nicht mehr nötig, da "bmih.biBitCount" auf 24bpp gesetzt 
        // pBits[++offset] = 0x00;
    }
    

    Die Farben werden jetzt fehlerfrei angezeigt, das ist schonmal was.

    Im übrigen geht es um animierte IFS-Fraktale! Try it: http://ticore.de/share/ifs3.zip (WIN)

    Vielen Dank erstmal.



  • hellihjb schrieb:

    die adresse an der der zeiger gespeichert ist interessiert dich nicht.
    caste den zeiger selbst:

    DWORD *tmp= (DWORD*)pBits;
    

    (... schnipp ...)

    Yo, funktioniert jetzt:

    static void drawpoints(struct state *st) {
        int offset;
        DWORD *tmp= (DWORD*)pBits;
        int szByte = sizeof(BYTE) * (bmih.biBitCount / 8);
        for (int i = 0; i < st->npoints; i++) {
            offset = ((st->scanline * st->pointbuf[i].y) + (st->pointbuf[i].x * szByte)) / sizeof(DWORD);
            tmp[offset] = st->colours[st->ccolour]; // ist ein 0xBBGGRR00
        }
        // Pointbuffer zurücksetzen
        st->npoints = 0;
    }
    

    Ich muss natürlich st->scanline und den daraus resultierenden Offset anders berechnen, wenn ich an DWORDs adressiere anstatt auf Bytes. 😉

    Klasse, danke!



  • Im übrigen geht es um animierte IFS-Fraktale!
    Try it: http://ticore.de/share/ifs3.zip

    ifs3.scr hat ein Problem festgestellt und muss beendet werden.

    falls du den aufbau deiner datenstrukturen mal erklaeren moechtest,
    findet man fuer diese konstruktion bestimmt eine bessere loesung:

    offset = ((st->scanline * st->pointbuf[i].y) + (st->pointbuf[i].x * szByte)) / sizeof(DWORD);
    

    divisionen pro pixel sind keine gute idee...



  • Möchte ich! Also:

    Ich habe eine DIBSection, ziemlich genau so groß wie mein Bildschirm, erzeugt in WM_CREATE, wie folgt:

    GetClientRect(hWnd, &myScreenSize);
    
    bmih.biSize          =  sizeof(BITMAPINFOHEADER);
    bmih.biWidth         =  myScreenSize.right;
    bmih.biHeight        = -myScreenSize.bottom;  // DIB ist bottom/left (minus).
    bmih.biPlanes        = 1;
    bmih.biBitCount      = 32;
    bmih.biCompression   = BI_RGB;
    bmih.biSizeImage     = 0;
    bmih.biXPelsPerMeter = 0;
    bmih.biYPelsPerMeter = 0;
    bmih.biClrUsed       = 0;
    bmih.biClrImportant  = 0;
    
    myDevCon     = GetDC(hWnd);
    myBitmap     = CreateDIBSection(NULL, (BITMAPINFO *) &bmih, 0, (void**)&pBits, NULL, 0); // <- hier
    myBackBuffer = CreateCompatibleDC(myDevCon);
    

    Die Größe in Bytes einer Bildschirmzeile (bzw. Scanline der DIBSection) berechne ich so:

    //Scanline = (Breite in Pixeln) x (32bpp / 8)
    st->scanline = bmih.biWidth * (bmih.biBitCount / 8);
    

    Und in Meiner Funktion berechne ich den tatsächlichen Offset an dem ich ein DWORD (mit den Farbwerten für das Pixel, nach dem Schema 0xBBGGRR00) schreiben möchte etwa so:

    static void drawpoints(struct state *st) {
        int offset;
        int szByte = sizeof(BYTE) * (bmih.biBitCount / 8);
    
        DWORD *tmp= (DWORD*)pBits; // Zeiger casten
    
        for (int i = 0; i < st->npoints; i++) {
            offset = ((st->scanline * st->pointbuf[i].y) + (st->pointbuf[i].x * szByte)) / sizeof(DWORD);
            tmp[offset] = st->colours[st->ccolour];
        }
        // Pointbuffer zurücksetzen
        st->npoints = 0;
    }
    

    (scanline * y[i]) ergibt doch die Größe in Bytes aller Zeilen des aktuellen Punktes[i], dazu muss ich doch die Position von x[i] in der Zeile hinzurechnen, oder? Da ich für jedes Pixel 4 Bytes verbrauche ergibt sich oben genannte Rechnung, oder etwa nicht?



  • du setzt ja explizit ein 32bit-pixelformat und schreibst diese pixel dann als dword.
    man darf also eigentlich auch davon ausgehen, dass "dword" 32bit und "byte" 8bit gross ist.
    ausserdem wird der anfang jeder scanline sowieso auf dword-grenzen aligned.
    du kannst auf deine bit-rechnerei also eigentlich verzichten:

    DWORD *tmp= (DWORD*)pBits;
        int scanlen= st->scanline >> 2;
        dword color= st->colours[st->ccolour]; // scheint ja eh 'ne konstante zu sein
        for (int i = 0; i < st->npoints; i++)
        {
            tmp[st->pointbuf[i].y*scanlen + st->pointbuf[i].x]= color;
        }
    


  • hellihjb schrieb:

    du kannst auf deine bit-rechnerei also eigentlich verzichten:

    Also, wenn ich die Sache mit dem ">>" verstehen würde, hätte ich heute was gelernt!

    DWORD *tmp= (DWORD*)pBits;
    
        int scanlen = st->scanline >> 2;    // <--------------------------- was genau macht das??
    
        COLORREF color = st->colours[st->ccolour]; // scheint ja eh 'ne konstante zu sein
    
        // das ist nicht so konstant wie man meinen könnte! Ein weiterer Timer setzt den
        // Zähler auf und ab - eine Art Palettenanimation für Arme. Aber du hast recht,
        // das muss nicht unbedingt in den Schleifenrumpf (und möglicherweise fünf bis
        // zehntausend mal dereferenziert werde). :)
    
        for (int i = 0; i < st->npoints; i++)
        {
            tmp[st->pointbuf[i].y * scanlen + st->pointbuf[i].x] = color;
            // genial, danke!
        }
    

    Läuft super!



  • das ist der shift operator.
    ">>" verschiebt alle bits um die angegebene anzahl von stellen nach rechts.
    da im dualsystem die jeweils naechste (linke) stelle doppelte wertigkeit (1 2 4 8 16 usw) hat, wird durch rechts-verschiebung um 1 jedem gesetztem bit die halbe wertigkeit zugewiesen, also der wert durch zwei geteilt.
    "<<" macht das gegenteil.


Anmelden zum Antworten