Před několika dny se na diskusním fóru na Builder.cz objevil dotaz z kterého vyplynula odpověď jak převést barevnou (true-color) bitmapu na obrázek v 256 stupních šedé. Po uvedeném kódu ve Visual Basicu a mnohem rychlejším způsobu napsaném v C# a .NET jsem také přispěl algoritmem, který byl při mém testování více než 4x rychlejší ve srovnání s podobným kódem v NET. Podotýkám že si vůbec nemyslím že by tento rozdíl byl způsoben pouze volbou nástroje (C++ vs C#) a platformy (win32 vs. NET) ale spíše tím, že v cyklu while (který pochopitelně proběhne v závislosti na velikosti bitmapy mnohokrát) je při každém průběhu vytvářena pomocná instance třídy Color a proměnná typu int pro mezivýsledek.
Když jsem tedy byl u testování mého algoritmu, napadlo mě vytvořit malou "oddechovou" aplikaci, ve které jsou aplikovány také další občasný předměty dotazů na diskusním fóru - sejmutí opisu obrazovky do bitmapy a dále vytvoření okna které se zobrazí přes celou obrazovky (včetně pruhu úloh) které může být navíc v poloze "vždy na vrchu". Výsledkem je následující ptákovina - předem se zříkám veškeré odpovědnosti za následky způsobené jejím použitím. Co program umí? Po svém spuštění nebo po časovém intervalu zadaném v prvním parametru příkazové řádky (v sekundách) sejme opis obrazovky, převede jej do šedé stupnice a ihned zobrazí přes celou obrazovku v okně, které je v poloze "vždy na vrchu". Uživatel tedy má tedy jednak pocit že se mu zbláznil monitor (jehož obraz se najednou změní na černobílý) a zasekl systém (okno samozřejmě nereaguje na klávesnici ani pohyb či klikání myší. Lze ho samozřejmě zavřít pomocí Alt+F4 nebo stisknutím Ctrl+Alt+Del vyvolat správce úloh a naší aplikaci "sestřelit". Také stisknutí klávesy <Win> samozřejmě zobrazí nabídku "Start" a uživatel tedy pravděpodobně pozná že se stal obětí nějakého vtípku. Pokud navíc ve druhém parametru příkazové řádky "/!", okno nereaguje na zprávu WM_CLOSE a nelze jej tedy zavřít standardně pomocí Alt+f4, ale jediný způsob ukončení aplikace je její odstřelení Správcem úloh, takže používat pouze na vlastní odpovědnost:-). Ještě doplním, že časová prodleva po spuštění aplikace se zadává v 1. parametru příkazové řádky za lomítkem v sekundách.
Aplikace je napsána jako Win32 aplikace s použitím knihovny ATL. Je použita třída (resp. šablona) CAtlExeModuleT a pro zjednodušení práce s oknem třída CWindowImpl. Vzhledem k tomu že knihovna ATL je (na rozdíl např. od MFC) velmi malá a je realizována několika hlavičkovými soubory a nepotřebuje při použití její malé části natáhnout celý kód do paměti (jako to zjednodušeně řečeno dělá MFC). Důkazem budiž to že diskutovaná aplikace má bez závislosti na dalších externích DLL knihovnách 48 kB a pokud nastavíme dynamické použití runtimové knihovny jazyka C++ (msvcrt) dokonce 22.5 kB.
Nyní se podívejme na části zdrojového kódu. Aplikace používá třídu Bitmap knihovny GDI+. Třída hlavního okna má jako členskou proměnnou ukazatel na tuto třídu:
Bitmap* m_pBitmap;
Okno je vytvořené tak, že jeho styl obsahuje pouze WM_POPUP, tedy okno bez jakýchkoli rámečků. Poloha "vždy na vrchu" je oknu nastavena dynamicky před jeho zobrazením nastavení rozšířeného stylu WS_EX_TOPMOST, jak můžete sami najít v přiloženém zdrojovém kódu.
Po vytvoření okna nebo po uplynutí nastavené časové prodlevy je zavolána následující funkce, která sejme aktuální opis obrazovky a na jeho základě vytvoří instanci výše uvedené třídy Bitmap a zobarzí okno
BOOL COknoHlavni::Zobrazit()
{
POINT pt;
GetCursorPos(&pt);
m_hCursor = LoadCursor(NULL, IDC_ARROW);
if ( m_pBitmap )
AtlThrowLastWin32();
int sirka = GetSystemMetrics(SM_CXSCREEN);
int vyska = GetSystemMetrics(SM_CYSCREEN);
HDC hdcScreen = ::GetDC(NULL);
if ( !hdcScreen )
AtlThrowLastWin32();
HDC hdcBmp = CreateCompatibleDC(hdcScreen);
if ( !hdcBmp )
AtlThrowLastWin32();
HBITMAP hBitmap = NULL;
hBitmap = CreateCompatibleBitmap(hdcScreen, sirka, vyska);
if ( !hBitmap )
AtlThrowLastWin32();
SelectObject(hdcBmp, hBitmap);
::BitBlt(hdcBmp, 0, 0, sirka, vyska, hdcScreen, 0, 0, SRCCOPY);
DrawIconEx(hdcBmp, pt.x, pt.y,
m_hCursor, DI_DEFAULTSIZE, DI_DEFAULTSIZE,
0, 0, DI_NORMAL | DI_DEFAULTSIZE);
m_pBitmap = new Bitmap(hBitmap, NULL);
if ( hdcScreen )
::ReleaseDC(NULL, hdcScreen);
if ( hdcBmp )
::DeleteDC(hdcBmp);
if ( hBitmap )
::DeleteObject(hBitmap);
if ( m_pBitmap )
if ( m_pBitmap->GetLastStatus()!= Ok )
AtlThrowLastWin32();
RECT rect = { 0, 0, sirka, vyska };
PrevodGrayScale();
DWORD dwStylEx = GetWindowLongPtr(GWL_EXSTYLE);
SetWindowLongPtr(GWL_EXSTYLE, dwStylEx | WS_EX_TOPMOST);
SetWindowPos(HWND_TOPMOST, 0, 0, sirka, vyska, 0);
::ShowCursor(FALSE);
ShowWindow(SW_SHOW);
return TRUE;
}
Následující funkce převede vytvořenou bitmapu (opis obrazovky) do šedé stupnice 256 stupňů šedé.
void COknoHlavni::PrevodGrayScale()
{
if ( !m_pBitmap )
return;
BitmapData bData;
m_pBitmap->LockBits(&Rect(0, 0, m_pBitmap->GetWidth(),
m_pBitmap->GetHeight()),
ImageLockModeWrite | ImageLockModeRead,
PixelFormat32bppARGB, &bData);
BYTE* pData = (BYTE*)bData.Scan0;
BYTE* pKonec = (BYTE*)((BYTE*)bData.Scan0 +
( bData.Height * bData.Width * 4));
BYTE b;
while (pData < pKonec)
{
b = (( 11* (*(pData+2)) + 59 * (*(pData+1))+ 30 * (*(pData)))) / 100;
*(pData+3) = 255;
memset(pData, b, 3);
pData+=4;
}
m_pBitmap->UnlockBits(&bData);
}
Zbývá zobrazit tuto bitmapu v okně v obsluze zprávy WM_PAINT:
LRESULT COknoHlavni::OnPaint(UINT uMsg,
WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
if ( !m_pBitmap )
return 0;
PAINTSTRUCT ps;
HDC hDC = BeginPaint(&ps);
Graphics gr(hDC);
gr.DrawImage(m_pBitmap, 0, 0);
EndPaint(&ps);
return 0;
}
Zde si můžete stáhnout diskutovaný projekt včetně spustitelného exe v release verze
Copyright © 2010 Radek Chalupa || tel. 739 219 991 | Kontakt | Poslat e-mail | Úvod