Občas potřebujeme sejmout obrazovku v OpenGL a poté s ní pracovat jako s obyčejnou bitmapou. V tomto článku vám ukáži získání obsahu OpenGL okna a jeho uložení do DIBu ve formě nekomprimované bitmapy. Jediný omezením může být 24 bitová barevná hloubka obrazovky.
Celý kód článku pojmeme jako jedinou funkci, která bude zajišťovat všechny potřebné operace.
HANDLE COpenGLView::CreateDIB()// Sejme OpenGL obrazovku a vytvoří z ní DIB, jehož handle vrátí
{
BeginWaitCursor();// Přepne kurzor na přesýpací hodiny
V první fázi potřebujeme získat rozměry klientské oblasti okna. Tj. velikost zobrazované scény.
CRect rect;// Obdélník
GetClientRect(&rect);// Grabování velikosti klientské oblasti
CSize size(rect.Width(), rect.Height());// Šířka a výška okna
TRACE("Klientská plocha: (%d; %d)\n", size.cx, size.cy);// Pro ladění
Řádky musejí být zarovnávány na 32 bytů, za předpokladu 24 bitů na pixel, takže co je navíc, odstřihneme.
size.cx -= size.cx % 4;
TRACE("Konečná klientská plocha: (%d; %d)\n", size.cx, size.cy);// Pro ladění
Nyní potřebujeme alokovat paměť pro jednotlivé pixely. Počet pixelů získáme velice jednoduše. Vynásobíme výšku obrázku se šířkou. Abychom dostali potřebnou paměť musíme ještě násobit třemi, protože má každý pixel 3 byty (RGB).
int NbBytes = 3 * size.cx * size.cy;// Velikost paměti
unsigned char *pPixelData = new unsigned char[NbBytes];// Alokace paměti
Pomocí funkce glReadPixels() zkopírujeme pixely z obrazovky do právě alokované paměti. Předávané parametry vyjadřují x, y souřadnice levého dolního rohu, šířku a výšku kopírované oblasti, barevnou hloubku (GL_RGB = 24 bitů, GL_RGBA = 32 bitů), typ pixelových dat a ukazatel do paměti, kam se mají data nakopírovat.
::glReadPixels(0, 0, size.cx, size.cy, GL_RGB, GL_UNSIGNED_BYTE, pPixelData);// Kopírovaní pixelů
Deklarujeme a vyplníme hlavičku DIBu.
BITMAPINFOHEADER header;// Hlavička DIBu
header.biWidth = size.cx;// Šířka
header.biHeight = size.cy;// Výška
header.biSizeImage = NbBytes;// Počet bytů obrázku
header.biSize = sizeof(BITMAPINFOHEADER);// Velikost této struktury
header.biPlanes = 1;// Vždy jedna
header.biBitCount = 24;// Barevná hloubka
header.biCompression = BI_RGB;// Typ komprese -> nekomprimovaná
header.biXPelsPerMeter = 0;
header.biYPelsPerMeter = 0;
header.biClrUsed = 0;
header.biClrImportant = 0;
Vygenerujeme handle globální paměti pro DIB o velikosti hlavičky sečtené s počtem bytů.
HANDLE handle = (HANDLE)::GlobalAlloc(GHND, sizeof(BITMAPINFOHEADER) + NbBytes);
Pokud se ukazatel nerovná NULL byla alokace úspěšná. V takovém případě uzamkneme handle a tím na něj zároveň získáme ukazatel. Potom zkopírujeme hlavičku i data a odemkneme handle.
if(handle != NULL)// OK
{
char *pData = (char *) ::GlobalLock((HGLOBAL) handle);// Uzamkne handle
memcpy(pData, &header, sizeof(BITMAPINFOHEADER));// Zkopíruje hlavičku
memcpy(pData + sizeof(BITMAPINFOHEADER), pPixelData, NbBytes);// Zkopíruje data
::GlobalUnlock((HGLOBAL)handle);// Odemkne handle
delete [] pPixelData;// Uvolnění paměti pro data
Nastavíme původní tvar kurzoru a vrátíme právě vytvořené handle DIBu.
EndWaitCursor();// Původní tvar kurzoru
return handle;// Vrátí handle DIBu
}
Při neúspěchu vrátíme NULL
delete [] pPixelData;// Uvolnění paměti pro data
EndWaitCursor();// Původní tvar kurzoru
return NULL;// DIB nebyl nahrán
}
Až přestaneme DIB používat musíme jej smazat pomocí funkce GlobalFree(). Jedinou výjimkou by bylo, kdybychom tento DIB předali do schránky, která by se o smazání postarala sama.
A nakonec ukázka použití této funkce (uvnitř nějaké funkce třídy okna).
HANDLE hDib = CreateDIB();// Vytvoří DIB
if (hDib)// Byl vytvořen?
{
// Práce s DIBem
::GlobalFree(hDib);// Nakonec ho smažeme
}
Pokud nepotřebujete získat handle DIBu, ale stačí vám mít jen DC zobrazované OpenGL scény, je to velmi jednoduché. Získejte DC okna ve kterém se OpenGL scéna vykresluje pomocí funkce GetDC().
napsal: Milan Turek - Woq <nalim.kerut (zavináč) email.cz>