17. Átlátszóság (Transparency)
2014.06.21. 19:56
Példa: bmp_two
Átlátszó bitképek
Elég egyszerűen tudunk a képek kinézetének átlátszóságot adni. Ehhez a következő feltételek szükségesek: Először is a színes képen minden átlátszónak szánt képpont legyen fekete. Továbbá a maszk képen mindenhol ahol átlátszóságot szeretnénk, a szín legyen fehér, egyébként fekete. A szín és maszk képeket mutatja be a példán látható kép bal oldala.
A BitBlt() műveletek
Hogyan működik az átlátszóság? Kell a BitBlt(), aminek átadjuk egyszer a maszk képet SRCAND művelettel, majd a színes képet SRCPAINT művelettel. Ahol átlászóságot akartunk, akkor nem változik a HDC, ahol viszont nem, ott meg rendesen megrajzolta a képpontokat.
SelectObject(hdcMem, g_hbmMask); BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCAND); SelectObject(hdcMem, g_hbmBall); BitBlt(hdc, 0, bm.bmHeight, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCPAINT);
Egyszerűnek tűnik? Szerencsére igen. Azért egy kérdés így is marad. Honnan származik a maszk? Erre van több módszer is. Az egyik, ha magunk elkészítjük egy rajzoló programmal. Ez akkor ajánlatos, ha kis számú képet használunk, mert csak felvesszük erőforrásnak, és a LoadBitmap()-el betöltjük.
Azt is lehet, hogy kiválasztunk futás közben egy színt a képen, ezt nevezzük ki "átlátszó színnek", és generálunk hozzá egy maszkot, hogy ahol ez a szín szerepelt, ott fehér, mindenhol máshol meg fekete legyen.
Ha az első módot választjuk, akkor ennyi, végeztünk. Ha viszont a másodikat, akkor most még jön hozzá pár dolog.
Maszk létrehozása
A legegyszerűbb az lenne, ha a kép minden képpontján végigmennénk, és az aktuális szín alapján állítanánk be a maszkot fehérre, vagy feketére. Ez jól hangzik, de a SetPixel() nagyon lassú, így nem választhatjuk ezt az utat. Kicsit jobbak vagyunk, ha a BitBlt() használatával átkonvertáljuk a színes képet fekete-fehérré.
TODO: Ez nagyjából megvan, de elég kusza:
If you BitBlt() (using SRCCOPY) from an HDC holding a colour image into an HDC holding a black and white image, it will check what colour is set as the Background Colour on the colour image, and set all of those pixels to White, any pixel that is not the background colour will end up Black.
Ez teljesen jól működik egészen addig, amíg a maszk egy bites fekete/fehér színű. Ha viszont a bitmélység több mint egy bit (pl. 16, 24 bit), akkor már nem működik.
Emlékszünk még a sikeres maszkolás első feltételére? Úgy szólt, hogy a színes képen legyen minden fekete, ahol átlátszóságot akarunk. A példánkban ez már teljesült, ezért nincs több tennivalónk. De ha másik képet használnánk másik átlátszósági színnel (megegyezés szerint az élénk pink szokott ez a szín lenni), akkor szükségessé válik a második lépés, amikor az átlátszósági színt megváltoztatjuk feketére.
HBITMAP CreateBitmapMask(HBITMAP hbmColour, COLORREF crTransparent) { HDC hdcMem, hdcMem2; HBITMAP hbmMask; BITMAP bm; // Create monochrome (1 bit) mask bitmap. GetObject(hbmColour, sizeof(BITMAP), &bm); hbmMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL); // Get some HDCs that are compatible with the display driver hdcMem = CreateCompatibleDC(0); hdcMem2 = CreateCompatibleDC(0); SelectBitmap(hdcMem, hbmColour); SelectBitmap(hdcMem2, hbmMask); // Set the background colour of the colour image to the colour // you want to be transparent. SetBkColor(hdcMem, crTransparent); // Copy the bits from the colour image to the B+W mask... everything // with the background colour ends up white while everythig else ends up // black...Just what we wanted. BitBlt(hdcMem2, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY); // Take our new mask and use it to turn the transparent colour in our // original colour image to black so the transparency effect will // work right. BitBlt(hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem2, 0, 0, SRCINVERT); // Clean up. DeleteDC(hdcMem); DeleteDC(hdcMem2); return hbmMask; }
Megjegyzés: Ez a függvény a SelectObject()-et hívja meg, hogy átmenetileg kiválassza a színes képet, és átadja azt a HDC-nek. Egy bitképet nem használhat több HDC egyszerre, ezért győződjünk meg róla, hogy a függvény hívásakor éppen nem használja másik HDC! Ellenkező esetben a hívás sikertelen lesz. Most már létrehozhatjuk a maszkot az eredeti képből:
case WM_CREATE: g_hbmBall = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_BALL)); if(g_hbmBall == NULL) MessageBox(hwnd, "Could not load IDB_BALL!", "Error", MB_OK | MB_ICONEXCLAMATION); g_hbmMask = CreateBitmapMask(g_hbmBall, RGB(0, 0, 0)); if(g_hbmMask == NULL) MessageBox(hwnd, "Could not create mask!", "Error", MB_OK | MB_ICONEXCLAMATION); break;
A második paraméter természetesen az eredeti képben átlátszóságra jelölt szín. Jelenleg a fekete.
Hogyan működik ez az egész?
Remélhetőleg már kenjük-vágjuk a C, C++ -t, tehát értjük mi az az OR, XOR, AND és NOT ésatöbbi bináris műveletek. Ugyanis nem rágjuk bele magunkat ebbe, de megnézünk egy példát, hogy hogyan használjuk. Ha végül mégsem tiszta az ügy, akkor olvassunk utána a bináris műveleteknek! Persze nem feltétlenül kell megérteni, mert attól még működik!
SRCAND
Az SRCAND raszter művelet vagy ROP kód a BitBlt()-hez a biteket ÉS (AND) kapcsolatba hozza egymással.Tehát csak azok a bitek lesznek 1-be állítva, ahol a forrás és a cél is 1 volt. Ahol a maszk fekete volt (ez a nulla bitnek felel meg) ott az eredmény fekete lesz, és ahol a maszk fehér (1-es bit) ott pedig átlátszó lesz. A 0 értéket bármivel ÉS-eljük, az eredmény 0 (fekete) lesz. Az 1 (fehér) értéket maszknak használva az eredmény átlátszó, mert visszakapjuk az eredeti színt. (Tudom, az "ÉS-elés" nagyon furán hangzik :)) Az ÉS művelet ereményét látjuk a példa kép jobb felső negyedében.
SRCPAINT
Az SRCPAINT gyakorlatilag a VAGY (OR) műveletnek felel meg. Ha az egyik (vagy mindkét) bit értéke beállított (beállított, tehát 1-es), akkor az eredmény bit is 1-es lesz. Ha az átlátszó részt (fekete) kombináljuk a színes képpel, akkor az eredmény az érintetlen színes kép lesz. Ugyanakkor, ha nem fekete résszel kombináljuk a színes képet, akkor az eredmény egy olyan színes valami lesz, ahol a színek a forrás és cél színek kombinációja lesz. Erre a jelenségre mutat példát a jobb alsó példakép. Igazából ez az egész maszkolásos játék erre megy ki. Előbb feketére cseréljük az alap színt, és erre tesszük a színeset. Így nem keveredik össze a színes kép az alatta lévő réteg színeivel.
SRCINVERT
Ez a KIZÁRÓ VAGY (XOR) műveletet jelenti. Ezzel beállíthatjuk az eredeti átlátszó színt feketére (ha még nem volt az). Ahol fekete volt a maszk és nem háttér színű a pixel, ott érintetlenül hagyja, míg a maszk fehér színét a háttérszínnel feketére festi.
Ezzel az egész GDI színezéssel túl sokat ne foglalkozzunk, mert csak fejfájást okoz!
A bejegyzés trackback címe:
Kommentek:
A hozzászólások a vonatkozó jogszabályok értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a Felhasználási feltételekben és az adatvédelmi tájékoztatóban.