Helyesen keverje össze a színeket, vagy optimalizálja az írásjegyet
Az alfa keverés elmélete
Valószínűleg ismeri ezt az elméletet, ezért nem részletezem részletesen, csak a főbb pontokat fogom feljegyezni.
Így van 2 képpontunk - a forráském és a cél pixel. Meg kell keverni őket, és új rendeltetési képpontot kell kapnia. Minden pixel képviseli 4 bájt A, R, G, B, ahol A - érték (nem) átlátszó pixel (0 - teljesen átlátszó, 255 - teljesen átlátszatlan), RGB - színes komponenseket. A klasszikus keverési képlet a következő:
Fontos pont! Az egység a képletben van. Az életben az egység értéke 255. alkalmazni a képlet, először is meg kell osztani az érték az egyes byte a 255. Nem nehéz észrevenni, 255 és 256 - meglehetősen hasonló értékeket, és osszuk el 256 - ez csak egy jobbra léptetés 8 bit. Ezért gyakran van ilyen egyszerűsítés: a művelet helyett
Ez jól működik (és ami a legfontosabb, drámaian gyorsabb igazságos megosztása), de abban az esetben, alpha blending eredmény nem egészen helyes, azaz a keletkező pixel fordul kissé sötétebb. Ezután megmutatom neked, hogyan kell elvégezni a számításokat pontosan és a sebesség elvesztése nélkül.
Egy másik fontos pont! Nézd meg a képletet. A második rész SRC_COLOR * SRC_ALPHA. Az ilyen sokszorosítású 3D-s gyorsítók több millióban, sőt milliárdban is teljesítenek, szemhéj nélkül. De a CPU használatával próbáljuk megoldani a problémát, és a pixelenkénti szükségtelen szorzás (pontosabban 4 extra szorzás) nem túl jó. Miért szükségtelen? Igen, mert ez a sokszorosítás az eredeti kép átalakításával előre végrehajtható. Az ilyen képeknek is van címe: premultiplied. Nem ismerem a kifejezést oroszul, de az "előfinanszírozott" szó szót fordítom. És biztos, hogy az AlphaBlend GDI funkciója szigorúan premultiplied, mint a source image. Ésszerű.
Nos, az elmélet vége. A gyakorlatban 32 bites színnel dolgozunk. Egy képpont képviseli 32-bites szám, amelynek 4 bájtot, kezdve a legkevésbé átlagos: B (Lue), G (reen), R (ED), A (LPHa). Menjünk.
Az első megvalósítás
Az első megvalósításom ez volt:
Egyetértek azzal, hogy nem tűnik nagyon jónak. 4 valós (pontosabban 5) szorzás és 4 kerekítés pixelenként - ez túl sok. Nem meglepő, hogy a szörny gyorsasága elvesztette az AlphaBlend'u-t körülbelül 7-szer.
Próbáljunk javítani. Megszabadulunk az anyagi szorzástól.
Itt működik a BLUEx256, GREENx256 stb. visszaadja a megfelelő komponenst balra 8 bittel, azaz szorozva 256-mal.
Ez a funkció figyelemre méltó, hiszen jobbra van beállítva a 255 bázisú eltolás korrekciója 8 bitrel. Észre? Ha nem, légy türelmes, az alábbiakban részletesen leírom ezt a pontot.
A sebesség szempontjából ez a végrehajtás 3-szor alacsonyabb az AlphaBlendnél. Már jobb, de még mindig nagyon messze van az ideálisaktól.
Váratlan eredmény
Hogyan javíthatom az előző funkciót? Úgy tűnik, mindent megtettünk, amit tudtunk. Azonban sikerült ezt a funkciót olyan módon megnövelnem, ami meglepetés volt számomra. Próbáltam csak meggyőződni arról, hogy semmi sem történik. Azonban kiderült.
Mi a teendő, ha egy bájt egy byte-vel történő szorzásának műveletét teszem egy asztalhoz. Nem jön ki sokat - csak 65536 bájt. Penny.
Olyan jelet állítottunk fel:
Hát akkor. Nincs többé optimalizálni. Semmi sem jut eszembe. De AlphaBlend még mindig gyorsabb, minden második. Hogyan sikerült ezt elérni? Úgy tűnik, itt az ideje nyugdíjba vonulni?
A szétválás 255 váltással
Sok lehetőség van arra, hogy gyorsan feloszthassam 255-öt. Találkoztam ezzel:
Nem rossz. A 255-nél gyorsabb, mint a becsületes felosztás. De még mindig túl nehézkes. Régóta gondoltam, milyen gyorsan osztozzanak 255-tel, és nem veszítenek sem minőségben, sem sebességben. Hogyan lehet kompenzálni a színpusztulást a műszak használata során?
Tegyük fel, hogy egy 0xff (255) -es színkomponensnek van egy másik összetevője, amely szintén 0xff (255). Összeszorítva őket, kapunk:
0xff * 0xff = 0xfe01. 8 bit jobbra mozgatása esetén 0xfe-t kapunk - a komponens fényereje csökken. Ez rossz.
És mi van, ha növeljük az egyik komponenst 1-el, mielőtt megszorozzuk?
0xff * 0x100 = 0xff00. Hm, úgy tűnik. Ellenőrizzük az esetet, ha az egyik komponens 0:
0xff * 1 = 0x00ff. 8 bitet jobbra tolva, kapunk 0. Voila! Más összetevők esetén az eredmény is helyes.
Most könnyű megtalálni a kompenzációs teret a második függvényben: uint not_a = 256 - ALPHA (src);
Nem 255 - A, hanem 256 - A, azaz. +1 az összetevőre, mielőtt megszorozna. A táblázatos szorzási módszer esetében nem szükséges kompenzáció; a táblázatban minden értéket kiszámítunk, és szükség szerint kiszámítjuk.
Nehéz tüzérség - SSSE3 utasítások
Ideje gondolkodni a SIMD optimalizálásával kapcsolatban. Azt mondják, hogy az Intel fordító tudja, hogyan kell ezt tenni egy személy részvétele nélkül. Talán. De kétlem, hogy az Intel küzdi az AlphaBlend-et. Nos, a maximális - egyenlő vele. De gyorsabbnak kell lennem. Nyissa meg a könyvtárat és menjen előre.
Az első kérdés, melyet meg kell kérdezni, melyik utasításokat kell optimalizálni? Gyanítom, hogy az AlphaBlend optimalizálva van az MMX-hez, különben nem tudom megmagyarázni a fölényét a tiszta x86-os implementációnál. Az MMX jó, de ez a múlt század. Most nehéz megtalálni azt a számítógépet, ahol az SSE4 nem támogatható. És SSE általában lehet optimalizálni, nem is törődve azzal, hogy ellenőrizze, hogy a rendelkezésre álló támogatás ezen nyilatkozatok - annak valószínűsége, hogy a program futni fog valami alábbi Pentium 3 közel van a nullához. Persze, az asztali alkalmazásokról beszélek. Az exotikumok nem tartoznak e cikk hatálya alá.
Az SSSE3-ot választottam. Ez az utasításrendszer meglehetősen gyakori, hogy az optimalizálás megkérdőjelezhetővé válik, mivel nagyon kényelmes utasításokat tartalmaz.
A leghasznosabb utasítás, amely az összes optimalizálás alapját képezi, a pshufb (belső _mm_shuffle_epi8). Az ő kedvéért az SSSE3-ot választották. Mi a hatalma? Az a tény, hogy ez az utasítás lehetővé teszi, hogy az eredeti 16 bájtos regiszter bájtjait tetszőleges sorrendben szétoszthassa, vagy akár ezeket a bájtokat is feleslegessé teheti. Ie Ennek az utasításnak a segítségével elkészíthetem a szükséges számításokhoz szükséges dolgokat egy mozdulattal. Egy másik fontos utasítás a pmulhuw (belső _mm_mulhi_epu16) - ez 8 szorzás és 8 váltás jobbra 16 bitrel. Mint az alfa-keverési művelethez. Ie Ezzel a paranccsal egyedülállóan 2 képpontot számolok egyszerre.
Nos Nos, menjünk:
Az ASM kódlap
Amint láthatja, a simd implementációja 4 eredeti képpontot tartalmaz, amelyek egyszerre 4 képpontot tartalmaznak. Nos, akkor simd. Ennek a cikknek a keretében leírást adok a probléma megoldására, ha nem 4 pixeles többszörözni szeretnél. Személy szerint én "egy képpontos" c ++ megvalósítási kéréseket használok erre.
A cikk kódja csak az ssse3 optimalizálás elvének ismerete. Nem vagyok itt, hogy megadjam a használt állandók értékét. Ha az optimalizált AlphaBlend-et szeretné használni a projektben, akkor a munkakódot közvetlenül az isotoxin forráskódjából kell megkapnia (ez a fejlesztésem neve).
Repository Isotoxin'a a githubon.
Közvetlenül a fájl, amelyben a kívánt funkció található itt.
Elnézést kérek, hogy nem dolgoztam fel dolgozó példákat, és nem mindent külön könyvtárba helyeztem. Ha valóban szüksége van erre a szolgáltatásra, és Önnek problémái vannak, hogy saját forrásaimból származik, írj nekem egy privát üzenetet, és részletesen megmondom, hogyan kell csinálni.