[C++] Kopiowanie danych - jak?

0

Zatem tak... jest format pliku z obrazkiem stworzony przeze mnie, o budowie:

[2 bajty - szerokosc][2 bajty - wysokosc][B][G][R][B][G][R][B][G][R]...[ITD]

I właśnie w tym moim formacie bitmapa jest odwrócona, DO GÓRY NOGAMI, względem osi x. Ładuję go sobie... i chcę go wrzucić do bufora z pamięcią graficzną, z tym, że o formacie [R][G][B][A][R][G][B][A]...[ITD]. No i piksele nie są odwrócone upside down, są od góry do dołu, normalnie :). Pominę fakt, że RGB jest zamienione na BGR.

I teraz muszę z jednego bufora (24 bitowego koloru) wrzucić do tego drugiego bufora (32 bitowego koloru) z odwróceniem, żeby to było szybko, no i co 3 bajty wstawiało ten jeden bajt A, który może być i zerowy :)

Kto zaproponuje jakiś algorytm? Na razie mam taki całkowicie prymitywny:

ubuf - wskaznik na tablicę (ciąg) bufora 24bitowego odwróconego, BGR zamiast RGB;
lpVideoMemory - wskaźnik na tablicę docelową, 32bitowy normalny;

w = 1024;
h = 768;

for(int i = 0; i < w*h; i++) {
unsigned char b;
unsigned char g;
unsigned char r;
unsigned char a = 0;
memcpy(&b, &(ubuf[4+(i*3)]), 1);
memcpy(&g, &(ubuf[4+(i*3)+1]), 1);
memcpy(&r, &(ubuf[4+(i*3)+2]), 1);
memcpy(&(lpVideoMemory[(i*4)]), &r, 1);
memcpy(&(lpVideoMemory[(i*4)+1]), &g, 1);
memcpy(&(lpVideoMemory[(i*4)+2]), &b, 1);
memcpy(&(lpVideoMemory[(i*4)+3]), &a, 1);
}

Tylko nie dość, że algorytm do d**y, to jeszcze nie odwraca xD!
Niech mi ktoś pomoże :P

0
char BGRBuff[w*h*3]; //bufor BGR
char RGBABuff[w*h*4]; //bufor RGBA
char *BGRcounter = BGRBuff + w*h*3; //wskaznik po buforze BGR (koniec + 1)
char *RGBAcounter = RGBABuff; //wskaznik po buforze RGBA (poczatek)
int rem = (int) BGRBuff % 3; //reszta z dzielenia przez 3, pomoze w liczeniu pelnych 3-jek bajtow

while(BGRcounter != BGRBuff)
{
   *(RGBAcounter++) = *(--BGRcounter);
   if((int) BGRcounter % 3 == rem)  *RGBAcounter++ = 0;
}
0

Dwie pieczenie na jednym ogniu - unrolling i brak dzielenia (modulo) :P

char BGRBuff[w*h*3]; 
char RGBABuff[w*h*4];
char *BGRcounter = BGRBuff + w*h*3; 
char *RGBAcounter = RGBABuff; 

while(BGRcounter != BGRBuff)
{
   *(RGBAcounter++) = *(--BGRcounter);
   *(RGBAcounter++) = *(--BGRcounter);
   *(RGBAcounter++) = *(--BGRcounter);
   *(RGBAcounter++) = 0;
}

PS. powinny być 3 pieczenie - brak if'a (pośrednio skoku warunkowego) ;)

0

Nono konkretnie, tylko jeden problem... piksele są odwrócone w sposób taki, że nie są po prostu zapisane odwrotnym ciągiem (odbicie względem dwóch osi), tylko są jak gdyby lustrzanym odbiciem względem osi poziomej... powyższy algorytm odwraca:

X Y
Z Q

na

Q Z
Y X

No, a przydałoby się:

Z Q
X Y

Czyli tak, jak to w bmp'ach jest zapisane ;). Właśnie z tym problem, żeby te "wiersze" pikseli odwrócić w szybki sposób... Any ideas?

0
typedef struct { char B, G, R; } BGR;
typedef struct { char R, G, B, A; } RGBA;

inline void BGRtoRGBA(BGR *lpBGR, RGBA *lpRGBA)
{
   lpRGBA->R = lpBGR->R;
   lpRGBA->G = lpBGR->G;
   lpRGBA->B = lpBGR->B;
   lpRGBA->A = 0;
}

(...)

BGR BGRBuff[w*h];
RGBA RGBABuff[w*h];
BGR *BGRcounter = BGRBuff;
RGBA *RGBAcounter = RGBABuff;

for(BGR *lineend = BGRBuff + w*h; lineend != BGRBuff; lineend -= w)
{
   BGRcounter = lineend - w;
   while(BGRcounter != lineend) BGRtoRGBA(BGRcounter++, RGBAcounter++);
}

Nie jestem pewien co do inline.

Jeszcze szybciej będzie Assemblerem

0

W sumie... dzięki bardzo :). Nie potrafię(-iłem) myśleć w ten sposób :P

Korzystając z waszych wskazówek programik do konwersji zapisuje odwrócone linie:

unsigned char *row;
row = new unsigned char[w*3];
for (int i = h-1; i>=0; i--) {
fin.seekg(b.bfOffBits+(i*(w*3)), std::ios::beg);
fin.read((char*)row, w*3);
fout.write((char*)row, w*3);
} 

I sobie je wrzuca do LPDIRECTDRAWSURFACE'a bezpośrednim dostępem do pamięci... wszystko jest niby OK, a jednak są jaja przy wyświetlaniu. Gdy ma do surface'a załadować plik o rozmiarze 102476824bity, wszystko jest jak najbardziej ok. Lecz gdy jakiś malutki 636124 ładuję, wtedy "kosi" obrazek, nie dociąga pikseli do końca wiersza itd... a wysokość i szerokość obrazka odczytuje z pliku dobrze. Wtf?

0

A tak w ogóle po co ten własny format ??? Nie lepiej zapisać bitmape normalnie i nie martwić się o konwersje ?

0

Nieeee, bo mi BITMAPINFOHEADER i BITMAPFILEHEADER cały system rozwalają :P

0

To przynajmniej zapisz je tak, aby odczytywać je liniow bajt po bajcie, a nie przy pomocy algorytmu.

0

Orajt, coś wymyśliłem... jak się jeszcze nie znużyliście, to tutaj mam kolejny, aczkolwiek tylko z pozoru duży problem. Kod [spory, ale naprawdę krótki]:

bool Bitmap::Load(LPDIRECTDRAW lpDD, IODatFile* df, std::string oname){
    
    DDSURFACEDESC nddsd; // definicja powierzchni
    
    objectEntry image = *(df->GetObjectByName(oname)); // tutaj mozecie nie wiedziec, jest to
    std::ifstream ifile(df->fname.c_str(), std::ios::binary);   // ladowanie bufora z obrazkiem z pliku *.dat
    if (!ifile) return false;
    int w, h;
    ifile.seekg(image.offset);         // porusza sie po pliku w poszukiwaniu interesujacego nas obrazka...
    
    unsigned char *cbuf, *ubuf;        // bufory: do danych skompresowanych i rozkompresowanych
    cbuf = new unsigned char[image.CLength];
    ifile.read((char*)cbuf, image.CLength);    // czyta dane skompresowane...
    
    ubuf = new unsigned char[image.RLength];
    unsigned long real_length = image.RLength;
    uncompress(ubuf, &real_length, cbuf, image.CLength);    // ... i je rozkompresowuje

    delete cbuf;
    
    w = ubuf[0] << ubuf[1];                              // w rozkompresowanym buforze 4 pierwsze bajty 
    h = ubuf[2] << ubuf[3];                              // to szerokosc i dlugosc

    ZeroMemory(&nddsd, sizeof(nddsd));
    nddsd.dwSize = sizeof(nddsd);
    nddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
    nddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
    nddsd.dwWidth = w;                          // tworzymy surface o danej szerokosci i dlugosci...
    nddsd.dwHeight = h;

    HRESULT ret = lpDD->CreateSurface(&nddsd, &s, NULL);
    if (ret != DD_OK) return false;
    
    ubuf += 4;                       // ... przesuwamy wskaznik na bufor o 4 bajty do przodu,
                                          //     jestesmy juz przy wlasciwych danych
    
    HDC DirectDC=CreateCompatibleDC(NULL);        // tworzymy dc z bitmapa
	  if (!DirectDC) return false; else
	  {
                BITMAPINFO RGB32BitsBITMAPINFO;                 // bitmapinfoheader :P
		ZeroMemory(&RGB32BitsBITMAPINFO,sizeof(BITMAPINFO));

		RGB32BitsBITMAPINFO.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
		RGB32BitsBITMAPINFO.bmiHeader.biWidth=w;
		RGB32BitsBITMAPINFO.bmiHeader.biHeight=h;
		RGB32BitsBITMAPINFO.bmiHeader.biPlanes=1;
		RGB32BitsBITMAPINFO.bmiHeader.biBitCount=32;            // tworzymy bitmape...
		
		UINT * ptPixels;	

		HBITMAP DirectBitmap = CreateDIBSection(DirectDC,     // ...za pomoca CreateDIBSection
                                       (BITMAPINFO *)&RGB32BitsBITMAPINFO, 
                                       DIB_RGB_COLORS,
                                       (void **)&ptPixels, 
                                       NULL, 0);
                                       
  
            if (!DirectBitmap) return false; else
            {
                SelectObject(DirectDC, DirectBitmap);
		        BYTE r,g,b;
		        for (int i=0;i<(w*h);i++)             // tutaj kopiujemy piksele z bufora do bitmapy
		        {
			        r = *ubuf++;
			        g = *ubuf++;
			        b = *ubuf++;
			        ptPixels[i] = (0 | (b << 16) | (g << 8) | (r));
		        }
                
                HDC SDC;
                s->GetDC(&SDC);
                BitBlt(SDC, 0, 0, w, h, DirectDC, 0, 0, SRCCOPY); // wstawiamy dc z bitmapa do dc directdraw'a
                s->ReleaseDC(SDC);
            }
        }
    
    return true;
}

Troche dziwnie obkomentowalem, piszcie jak cos niezrozumiałe. Otóż wykorzystuję pliki *.dat, które zawieraja zindeksowane stringami dane, skompresowane zlibem. Metoda GetObjectByName daje nam strukturę z offsetem danych przypisanych do danego stringowego indeksu, ponadto ich dwie długości: skompresowaną i rozkompresowaną, które są potrzebne w dekompresji.

No i rozkompresowuje te dane, kopiuje je do dc, wyświetla je na directdraw... tylko zawsze jakoś na skos, z przemieszczeniem, czasami w ogóle... a bajty chyba policzyłem dobrze :S. Próbowałem wcześniej z zrzucaniem bajtów bezpośrednio do pamięci karty graficznej, wskazanej przez surface, ale też coś się kisiło :/

0

To działa jak należy???

    w = ubuf[0] << ubuf[1];                          
    h = ubuf[2] << ubuf[3];         

bo jeżeli wysokośc i szerość to 16 bitowe wartości to powinno wyglądać to tak:

    w = *((WORD*)&ubuf[0]);
    h = *((WORD*)&ubuf[2]);
BYTE r,g,b;
for (int i=0;i<(w*h);i++) 
{
   r = *ubuf++;
   g = *ubuf++;
   b = *ubuf++;
   ptPixels[i] = (0 | (b << 16) | (g << 8) | (r));
}

W zasadzie to ptPixels mogłoby być LPBYTE i wtedy nie musiałbyś robić

(0 | (b << 16) | (g << 8) | (r))

co ma wpływ na wydajność, a o to Ci wcześniej chyba chodziło ;) Wracając do problemu, to z tego co pamiętam to scanline'y muszą być wyrownane do (D)WORD'a. Może to jest problemem... no chyba, że bitmapa źródlowa jest inaczej zapisana (nie tak jak w pliku BMP).

0

W źródłowym pliku są RGBRGBRGBRGB[...]RGB bez żadnych dopełnień, ale nie wiem, czy przypadkiem podczas przerzucania do nowej bitmapy nie trzeba dopełniać o.0. Wie ktoś?

0

Jeżeli RGB32BitsBITMAPINFO.bmiHeader.biBitCount=32 to nie ma czego dopełniać bo:

Each DWORD in the bitmap array represents the relative intensities of blue, green, and red, respectively, for a pixel. The high byte in each DWORD is not used.

0

Reaktywacja problemu - póki co, mam tak, jak to jest w tym dużym kodzie na poprzedniej stronie. Więc... bitmapy WWHHRGBRGBRGB[...]RGB ładuje dobrze wówczas, gdy ich szerokość jest podzielna przez 4 o.0. Jeśli nie, wtedy właśnie "kosi" ów obrazek. A przecież nie powinno tak być, chociażby z powodu opiasnego powyżej. Obecnie kod po niewielkich zmianach:

if (DirectBitmap)
		{
		    SelectObject(DirectDC, DirectBitmap);
		    BYTE r,g,b;
                    for (int i=0;i<h;i++)
		    {
            		for (int j=0;j<w;j++) {
                        r = *ubuf++;
                        g = *ubuf++;
                        b = *ubuf++;
                        ptPixels[w*i+j] = RGB(r,g,b);
                        }
                       
                    
                    }
                   HDC sdc;
                   s->GetDC(&sdc);
                   BitBlt(sdc, 0, 0, w, h, DirectDC, 0, 0, SRCCOPY);
                   s->ReleaseDC(sdc);
                   DeleteDC(bdc);
		}

"s" to mój DIRECTDRAWSURFACE*. No i... kurczę, powinno działać normalnie! Przecież biBitCount=32, ja wrzucam do ptPixels dane tylko po cztery bajty (RGBA), więc ilość bajtów w wierszu % 4 zawsze będzie 0... A tutaj przy szerokościach obrazka z ubuf niepodzielnych przez 4 przestawia mi piksele :(

Cholera, wie ktoś, czemu tak się dzieje?

0

W nagłówku bitmapu ustaw biBitCount na 24, DX utwórz również 24-bitowego i użyj formatu WWHHBGRBGRBGR..., wtedy nie będziesz musiał odwracać bitów ani dopisywać zer, po prostu ptPixels = ubuf;

A tutaj przy szerokościach obrazka z ubuf niepodzielnych przez 4 przestawia mi piksele

Możliwe że każna linia musi być dopełniona do 4 piksli. Spróbuj dopełnić je zerami. A może błędnie zapisujesz/kompresujesz/dekompresujesz obrazek ?

Dalej cie nakłaniam abyś do zapisu bitmap użył formatu BMP, albo np. PNG jeśli ci zależy na kompresji.

0

przeczytaj sobie jeszcze w dokumentacji DD o pitch vs width. po zablokowaniu powierzchni dostajesz strukturę, w której pole dwPitch wskazuje na długość kazdej linii (w bajtach). tak więc jeśli masz surfaca 100x100 wcale nie oznacza to, że odległość adresów pomiedzy kolejnymi liniami wynosi 4bajty * 100 pixeli.

0

Adf: no raczej kompresja, dekompresja itd jest dobrze, bo identyczne algorytmy przy wejściowych bmpach z szerokością 320, 1024 itd. działają orajt.

@up: obadam ten pitch, dzięki.

1 użytkowników online, w tym zalogowanych: 0, gości: 1