Obsah
1. Práce s rastrovými obrázky v knihovně OpenVG (pokračování)
2. Typy objektů, které lze použít pro čtení či zápis barev pixelů
3. Souhrn všech funkcí určených pro přenos barev pixelů mezi různými objekty
4. Kopie pixelů mezi dvojicí obrázků
6. Přenosy pixelů mezi obrázkem a kreslicí plochou
9. Přenosy pixelů v rámci aktuálně vybrané kreslicí plochy
11. Nízkoúrovňové operace pro přímý zápis či čtení pixelů do kreslicí plochy
1. Práce s rastrovými obrázky v knihovně OpenVG (pokračování)
V dnešním článku o grafické knihovně OpenVG se budeme téměř výhradně zabývat grafickou operací nazývanou BitBLT (Bit Block Transfer). Tato operace umožňuje provádět, jak ostatně její název naznačuje, blokové přenosy bitmap nebo jejich výřezů, popř. v rámci přenosu nad bitmapami provádět i různé další operace, například negaci barev zdrojové či cílové bitmapy, provedení bitové operace AND, XOR atd. (posléze se přidalo i zpracování alfa kanálu). První implementace operace BitBLT byla použita již v roce 1975 ve Smalltalku-72 a od té doby ji najdeme prakticky v každé implementaci tohoto programovacího jazyka, která obsahuje i knihovny pro práci s grafikou (mj. se jedná i o Squeak).
Pro Smalltalk-74 vytvořil Daniel Ingalls optimalizovanou variantu operace BitBLT implementovanou v mikrokódu. Operace BitBLT se tak stala součástí operačního systému a bylo ji možné volat jak z assembleru, tak i z programů napsaných v jazyce BCPL a samozřejmě i ze Smalltalku (právě tuto implementaci můžeme považovat za vůbec první grafickou akceleraci). Posléze se díky své univerzalitě tato funkce rozšířila i do mnoha dalších operačních systémů a grafických knihoven.
2. Typy objektů, které lze použít pro čtení či zápis barev pixelů
Vzhledem k tomu, že vykreslování rastrových obrázků do vytvářené dvoudimenzionální scény je velmi často používaná operace, není příliš překvapující, že se s touto operací můžeme setkat v API mnoha grafických knihoven či dokonce v API operačních systémů (asi nejznámějším příkladem je WinAPI). Tyto operace se většinou nazývají BitBlt, BitBLT, Blit či méně často PIXT (Pixel Transfer) a PIXBLT. V grafické knihovně OpenVG se s funkcemi určenými pro přenos pixelů samozřejmě setkáme taktéž. Navíc – což je minimálně zpočátku poměrně matoucí – se v této knihovně nachází hned osm (!) různých funkcí, které se od sebe odlišují především tím, jakého typu je zdrojový objekt a jakého typu je objekt cílový:
# | Objekt | Stručný popis |
---|---|---|
1 | void* | blok operační paměti považovaný za bitmapu |
2 | VGImage | objekt reprezentující rastrový obrázek a jeho metadata (formát atd.) |
3 | EGLSurface | vykreslovací plocha nastavená funkcí eglMakeCurrent() |
3. Souhrn všech funkcí určených pro přenos barev pixelů mezi různými objekty
Vzhledem k tomu, že existují tři typy objektů, z nichž lze pixely číst či do kterých je možné pixely naopak zapisovat, je možných devět různých kombinací zdroj+cíl. Z těchto devíti kombinací je osm kombinací pokryto funkcemi OpenVG a zbývající operaci lze realizovat například s využitím standardní funkce memcpy(), jak ostatně ukazuje následující tabulka:
Zdroj/Cíl | void* | VGImage | EGLSurface |
---|---|---|---|
void* | memcpy() | vgImageSubData() | vgWritePixels() |
VGImage | vgGetImageSubData() | vgCopyImage() | vgSetPixels() |
EGLSurface | vgReadPixels() | vgGetPixels() | vgCopyPixels() |
Poznámka: odkazy v tabulce vedou na příslušný článek a kapitolu, v níž je daná funkce popsána.
Poznámka2: funkce vgImageSubData() a vgGetImageSubData() byly popsány v předchozí části seriálu:
void vgImageSubData( VGImage image, const void *data, VGint dataStride, VGImageFormat dataFormat, VGint x, VGint y, VGint width, VGint height);
void vgGetImageSubData( VGImage image, void *data, VGint dataStride, VGImageFormat dataFormat, VGint x, VGint y, VGint width, VGint height);
4. Kopie pixelů mezi dvojicí obrázků
Poměrně často se setkáme se situací, kdy je zapotřebí zkopírovat oblast z jednoho obrázku (typu VGImage) do jiného obrázku. Může se například jednat o operaci typu copy&paste atd. Přenášená oblast je vždy tvořena osově orientovaným obdélníkem, u něhož se nastavují jeho rozměry a taktéž pozice jak ve zdrojovém, tak i v cílovém obrázku. Samozřejmě, že se při kopírování oblasti provedou případné převody barev pixelů v závislosti na tom, jaký formát je nastaven u zdrojového a cílového obrázku. V případě, že bitová hloubka cílového obrázku je menší, než hloubka obrázku zdrojového, může se při přenosu pixelů aplikovat operace ditheringu v závislosti na tom, jaké nastavení programátor použije.
Při kopírování pixelů se taktéž správně detekuje situace, kdy je zdrojový a cílový obrázek totožný. V tomto případě se testuje, zda se oblasti zdroje a cíle překrývají. Pokud k takové situaci dojde, použije se při přenosu buffer či se operace provede takovým způsobem, jako by byl buffer použit.
5. Funkce vgCopyImage()
Funkce, která se používá pro kopii obdélníkové oblasti pixelů mezi dvěma objekty typu VGImage (či mezi jedním a tím samým objektem) se jmenuje vgCopyImage a má následující hlavičku:
void vgCopyImage( VGImage dst, VGint dx, VGint dy, VGImage src, VGint sx, VGint sy, VGint width, VGint height, VGboolean dither);
Této funkci se předává devět parametrů:
# | Parametr | Význam |
---|---|---|
1 | dst | objekt představující cílový obrázek |
2 | dx | x-ová souřadnice levého horního rohu obdélníkové oblasti v rámci cílového obrázku |
3 | dy | y-ová souřadnice levého horního rohu obdélníkové oblasti v rámci cílového obrázku |
4 | src | objekt představující zdrojový obrázek |
5 | sx | x-ová souřadnice levého horního rohu obdélníkové oblasti v rámci zdrojového obrázku |
6 | sy | y-ová souřadnice levého horního rohu obdélníkové oblasti v rámci zdrojového obrázku |
7 | width | šířka kopírované obdélníkové oblasti |
8 | height | výška kopírované obdélníkové oblasti |
9 | dither | povoluje či zakazuje dithering, pokud má cílový obrázek menší bitovou hloubku |
6. Přenosy pixelů mezi obrázkem a kreslicí plochou
Celý subsystém pro práci s rastrovými obrázky v knihovně OpenVG má prakticky pouze dva cíle – dosáhnout zobrazení dané bitmapy ve vytvářené 2D scéně či naopak přečíst již vykreslenou scénu a vytvořit z ní screenshot. Tím se dostáváme k dalším dvěma funkcím sloužícím pro přenosy pixelů mezi obrázky typu VGImage a kreslicí plochou, tj. objektem typu EGLSurface. Jednu z těchto operací již známe – jedná se o vysokoúrovňovou funkci nazvanou vgDrawImage(). Pokud se tato funkce zavolá, aplikují se automaticky i další operace, například ořezávání, lineární transformace, volba výstupního bufferu (color buffer, stencil buffer atd.). To má současně i svoje nevýhody, protože operace vgDrawImage() může být poměrně pomalá. Při požadavcích na co nejvyšší rychlost přenosu pixelů (BitBLT) mezi objektem typu VGImage a EGLSurface lze použít nízkoúrovňové funkce nazvané vgSetPixels() a vgGetPixels().
7. Funkce vgSetPixels()
Pro přenos pixelů z vybraného obrázku typu VGImage na kreslicí plochu (tedy většinou přímo na obrazovku) slouží funkce nazvaná vgSetPixels() s následující hlavičkou:
void vgSetPixels( VGint dx, VGint dy, VGImage src, VGint sx, VGint sy, VGint width, VGint height);
# | Parametr | Význam |
---|---|---|
1 | dx | x-ová souřadnice levého horního rohu obdélníkové oblasti v rámci kreslicí plochy |
2 | dy | y-ová souřadnice levého horního rohu obdélníkové oblasti v rámci kreslicí plochy |
3 | src | objekt představující zdrojový obrázek |
4 | sx | x-ová souřadnice levého horního rohu obdélníkové oblasti v rámci zdrojového obrázku |
5 | sy | y-ová souřadnice levého horního rohu obdélníkové oblasti v rámci zdrojového obrázku |
6 | width | šířka kopírované obdélníkové oblasti |
7 | height | výška kopírované obdélníkové oblasti |
Vidíme, že se parametry funkce vgSetPixels() do značné míry podobají parametrům výše popsané funkce vgCopyImage(), chybí však určení cíle (ten je implicitní) a taktéž zde nenajdeme volbu ditheringu (opět implicitně nastaveno mimo tuto funkci).
8. Funkce vgGetPixels()
Opakem funkce vgSetPixels() je podle očekávání funkce nazvaná vgGetPixels(). Tu je možné použít například při vytváření screenshotů popř. při podobných operacích. Hlavička této funkce vypadá následovně:
void vgGetPixels( VGImage dst, VGint dx, VGint dy, VGint sx, VGint sy, VGint width, VGint height);
Parametry, které se této funkci předávají, opět slouží především pro specifikaci přesného umístění a rozměru obdélníkové oblasti, jež se má touto funkcí zkopírovat:
# | Parametr | Význam |
---|---|---|
1 | dst | objekt představující cílový obrázek |
2 | dx | x-ová souřadnice levého horního rohu obdélníkové oblasti v rámci cílového obrázku |
3 | dy | y-ová souřadnice levého horního rohu obdélníkové oblasti v rámci cílového obrázku |
4 | sx | x-ová souřadnice levého horního rohu obdélníkové oblasti v rámci kreslicí plochy |
5 | sy | y-ová souřadnice levého horního rohu obdélníkové oblasti v rámci kreslicí plochy |
6 | width | šířka kopírované obdélníkové oblasti |
7 | height | výška kopírované obdélníkové oblasti |
Povšimněte si chybějící explicitní specifikace zdrojového objektu (kreslicí plochy). Ta je vždy určena při inicializaci aplikace funkcí eglMakeCurrent.
9. Přenosy pixelů v rámci aktuálně vybrané kreslicí plochy
Pokud je zapotřebí zkopírovat nějakou oblast z jednoho místa kreslicí plochy do jiného místa (představme si například scrolling v určitém regionu), nemá smysl pro tuto operaci používat pomocný obrázek typu VGImage a volat dvojici funkcí vgGetPixels() a vgSetPixels(). To by bylo zbytečně zdlouhavé a taktéž zde existuje určité riziko: pokud nemá GPU dostatek paměti pro uložení rastrového obrázku, je VGImage uložen do operační paměti, takže funkce vgGetPixels() a vgSetPixels() přenáší data po sběrnici počítače, což je relativně pomalé. Ovšem knihovna OpenVG nabízí řešení i tohoto problému – je jím funkce nazvaná vgCopyPixels() umožňující kopii dat v rámci vybrané kreslicí plochy. Tato funkce, podobně jako již výše popsaná funkce vgCopyImage(), pracuje korektně i ve chvíli, kdy se zdrojový a cílový region překrývají.
10. Funkce vgCopyPixels()
Hlavička funkce vgCopyPixels() je poměrně jednoduchá, neboť se v ní pouze specifikuje velikost obdélníkové oblasti i její umístění v rámci jedné kreslicí plochy:
void vgCopyPixels( VGint dx, VGint dy, VGint sx, VGint sy, VGint width, VGint height);
Význam parametrů této funkce:
# | Parametr | Význam |
---|---|---|
1 | dx | x-ová souřadnice levého horního rohu obdélníkové oblasti v rámci kreslicí plochy |
2 | dy | y-ová souřadnice levého horního rohu obdélníkové oblasti v rámci kreslicí plochy |
3 | sx | x-ová souřadnice levého horního rohu obdélníkové oblasti v rámci kreslicí plochy |
4 | sy | y-ová souřadnice levého horního rohu obdélníkové oblasti v rámci kreslicí plochy |
5 | width | šířka kopírované obdélníkové oblasti |
6 | height | výška kopírované obdélníkové oblasti |
11. Nízkoúrovňové operace pro přímý zápis či čtení pixelů do kreslicí plochy
Většina aplikací používá při tvorbě 2D scén již předem připravené (či vypočtené) rastrové obrázky, které se nějakým způsobem do aplikace načtou (například ze souborů typu PNG) a následně je je možné zobrazit. V takovém případě má význam ze všech rastrových dat nejprve (jedenkrát) vytvořit objekty typu VGImage a následně tyto objekty vykreslit funkcí typu vgDrawImage(). Ovšem existují i situace a aplikace, v nichž se bitmapy teprve vytváří a postupně mění. Pravděpodobně nejtypičtějším příkladem jsou rastrové grafické editory (Photoshop, GIMP, můj oblíbený mtPaint apod.). U takových aplikací, u nichž se často přistupuje k jednotlivým pixelům, může být výhodnější přeskočit krok vytváření objektů typu VGImage a namísto toho přímo přenášet pixely z operační paměti na kreslicí plochu – ve skutečnosti je právě tato operace asi nejbližší v úvodu zmíněné původní operaci typu BitBLT.
12. Funkce vgWritePixels()
V případě, že má programátor v operační paměti již přichystanou oblast s hodnotami (barvami) pixelů a tyto hodnoty odpovídají některému podporovanému formátu, je možné tuto oblast ihned vykreslit funkcí nazvanou vgWritePixels(). Této funkci se musí předat ukazatel na datovou oblast v paměti a formát uložení pixelů. Dalším důležitým parametrem je parametr dataStride, kterým lze specifikovat mezery mezi jednotlivými obrazovými řádky (stride). Pozor na to, že některé jiné knihovny používají namísto hodnoty stride hodnotu pitch, což je ve skutečnosti offset mezi obrazovými řádky: pitch=width+stride. Kromě toho se specifikuje i velikost a umístění obdélníkové oblasti kreslicí plochy, do níž se bude obrázek přenášet:
void vgWritePixels( const void *data, VGint dataStride, VGImageFormat dataFormat, VGint dx, VGint dy, VGint width, VGint height);
Parametry této funkce:
# | Parametr | Význam |
---|---|---|
1 | *data, | region v operační paměti, o jehož alokaci a naplnění se musí postarat programátor |
2 | dataStride | mezery mezi obrazovými řádky (na vstupu), hodnota je v bajtech |
3 | dataFormat | vstupní formát pixelů, jedna ze symbolických konstant popsaných minule |
4 | dx | x-ová souřadnice levého horního rohu obdélníkové oblasti v rámci kreslicí plochy |
5 | dy | y-ová souřadnice levého horního rohu obdélníkové oblasti v rámci kreslicí plochy |
6 | width | šířka kopírované obdélníkové oblasti |
7 | height | výška kopírované obdélníkové oblasti |
Volání:
vgWritePixels(data, dataStride, dataFormat, dx, dy, width, height);
by mělo být – alespoň co se týká funkcionality – ekvivalentní:
VGImage image = vgCreateImage(dataFormat, width, height, 0); vgImageSubData(image, data, dataStride, dataFormat, 0, 0, width, height); vgSetPixels(dx, dy, image, width, height); vgDestroyImage(image);
13. Funkce vgReadPixels()
Při vytváření screenshotů apod. se může hodit funkce vgReadPixels(), která dokáže získat obsah části kreslicí plochy (či samozřejmě celou plochu, pokud je tak specifikováno) a uložit ji do již alokovaného regionu operační paměti. Tato funkce je opakem funkce vgWritePixels(), takže nás její parametry s velkou pravděpodobností nepřekvapí:
void vgReadPixels( void *data, VGint dataStride, VGImageFormat dataFormat, VGint sx, VGint sy, VGint width, VGint height);
Parametry této funkce:
# | Parametr | Význam |
---|---|---|
1 | *data, | region v operační paměti, o jehož alokaci se musí postarat programátor |
2 | dataStride | mezery mezi obrazovými řádky (na výstupu), hodnota je v bajtech |
3 | dataFormat | vstupní formát pixelů, jedna ze symbolických konstant popsaných minule |
4 | sx | x-ová souřadnice levého horního rohu obdélníkové oblasti v rámci kreslicí plochy |
5 | sy | y-ová souřadnice levého horního rohu obdélníkové oblasti v rámci kreslicí plochy |
6 | width | šířka kopírované obdélníkové oblasti |
7 | height | výška kopírované obdélníkové oblasti |
Opět platí, že volání:
vgReadPixels(data, dataStride, dataFormat, sx, sy, width, height);
je funkčně ekvivalentní kódu:
VGImage image = vgCreateImage(dataFormat, width, height, 0); vgGetPixels(image, 0, 0, sx, sy, width, height); vgGetImageSubData(image, data, dataStride, dataFormat, width, height); vgDestroyImage(image);
14. Odkazy na Internetu
- So What's the Big Deal with Horizontal and Vertical Bezier Handles Anyway? (pro grafiky)
http://theagsc.com/blog/tutorials/so-whats-the-big-deal-with-horizontal-vertical-bezier-handles-anyway/ - EGL quick reference card
https://www.khronos.org/files/egl-1–4-quick-reference-card.pdf - EGL Reference Pages Index
https://www.khronos.org/registry/egl/sdk/docs/man/html/indexflat.php - Funkce eglInitialize
https://www.khronos.org/registry/egl/sdk/docs/man/html/eglInitialize.xhtml - Funkce eglGetDisplay
https://www.khronos.org/registry/egl/sdk/docs/man/html/eglGetDisplay.xhtml - Funkce eglGetConfigs
https://www.khronos.org/registry/egl/sdk/docs/man/html/eglGetConfigs.xhtml - Funkce eglGetConfigAttrib
https://www.khronos.org/registry/egl/sdk/docs/man/html/eglGetConfigAttrib.xhtml - Funkce eglDestroySurface
https://www.khronos.org/registry/egl/sdk/docs/man/html/eglDestroySurface.xhtml - Funkce eglDestroyContext
https://www.khronos.org/registry/egl/sdk/docs/man/html/eglDestroyContext.xhtml - Funkce eglTerminate
https://www.khronos.org/registry/egl/sdk/docs/man/html/eglTerminate.xhtml - Khronos Native Platform Graphics Interface
https://www.khronos.org/registry/egl/specs/eglspec.1.4.pdf - Khronos Group
https://www.khronos.org/ - Khronos Group (Wikipedia)
https://en.wikipedia.org/wiki/Khronos_Group - Raspberry Pi VideoCore APIs
http://elinux.org/Raspberry_Pi_VideoCore_APIs - Programming AudioVideo on the Raspberry Pi GPU
https://jan.newmarch.name/RPi/index.html - The Standard for Vector Graphics Acceleration
https://www.khronos.org/openvg/ - OpenVG (Wikipedia)
https://en.wikipedia.org/wiki/OpenVG - OpenVG Quick Reference Card
https://www.khronos.org/files/openvg-quick-reference-card.pdf - OpenVG on the Raspberry Pi
http://mindchunk.blogspot.cz/2012/09/openvg-on-raspberry-pi.html - ShivaVG: open-source ANSI C OpenVG
http://ivanleben.blogspot.cz/2007/07/shivavg-open-source-ansi-c-openvg.html - Testbed for exploring OpenVG on the Raspberry Pi
https://github.com/ajstarks/openvg - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: knihovna Pygame
http://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-knihovna-pygame/ - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: knihovna Pygame prakticky
http://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-knihovna-pygame-prakticky/ - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: práce s bitmapami a TrueType fonty
http://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-prace-s-bitmapami-a-truetype-fonty/ - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: sprity v knihovně Pygame
http://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-sprity-v-knihovne-pygame/ - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: detekce kolize spritů
http://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-detekce-kolize-spritu/ - Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky: transformace rastrových obrázků
http://mojefedora.cz/programovaci-jazyky-a-knihovny-urcene-pro-vyuku-zakladu-pocitacove-grafiky-transformace-rastrovych-obrazku/ - Seriál Grafické karty a grafické akcelerátory
http://www.root.cz/serialy/graficke-karty-a-graficke-akceleratory/ - Grafika na osmibitových počítačích firmy Sinclair II
http://www.root.cz/clanky/grafika-na-osmibitovych-pocitacich-firmy-sinclair-ii/ - Xiaolin_Wu's Line Algorithm
https://en.wikipedia.org/wiki/Xiaolin_Wu's_line_algorithm - Grafické čipy v osmibitových počítačích Atari
http://www.root.cz/clanky/graficke-cipy-v-osmibitovych-pocitacich-atari/ - Osmibitové počítače Commodore a čip VIC-II
http://www.root.cz/clanky/osmibitove-pocitace-commodore-a-cip-vic-ii/ - Grafika na osmibitových počítačích firmy Apple
http://www.root.cz/clanky/grafika-na-osmibitovych-pocitacich-firmy-apple/ - Počátky grafiky na PC: grafické karty CGA a Hercules
http://www.root.cz/clanky/pocatky-grafiky-na-pc-graficke-karty-cga-a-hercules/ - Karta EGA: první použitelná barevná grafika na PC
http://www.root.cz/clanky/karta-ega-prvni-pouzitelna-barevna-grafika-na-pc/ - Grafické karty MCGA a VGA
http://www.root.cz/clanky/graficke-karty-mcga-a-vga/ - Grafický subsystém počítačů Amiga
http://www.root.cz/clanky/graficky-subsystem-pocitacu-amiga/ - Grafický subsystém počítačů Amiga II
http://www.root.cz/clanky/graficky-subsystem-pocitacu-amiga-ii/ - Raspberry Pi pages
https://www.raspberrypi.org/ - BCM2835 registers
http://elinux.org/BCM2835_registers - VideoCore (archiv stránek společnosti Alphamosaic)
http://web.archive.org/web/20030209213838/www.alphamosaic.com/videocore/ - VideoCore (Wikipedia)
https://en.wikipedia.org/wiki/Videocore - RPi lessons: Lesson 6 Screen01
http://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/screen01.html - Raspberry Pi forum: Bare metal
https://www.raspberrypi.org/forums/viewforum.php?f=72 - C library for Broadcom BCM 2835 as used in Raspberry Pi
http://www.airspayce.com/mikem/bcm2835/ - Raspberry Pi Hardware Components
http://elinux.org/RPi_Hardware#Components - (Linux) Framebuffer
http://wiki.linuxquestions.org/wiki/Framebuffer - (Linux) Framebuffer HOWTO
http://tldp.org/HOWTO/Framebuffer-HOWTO/ - Linux framebuffer (Wikipedia)
https://en.wikipedia.org/wiki/Linux_framebuffer - RPi Framebuffer
http://elinux.org/RPi_Framebuffer - HOWTO: Boot your Raspberry Pi into a fullscreen browser kiosk
http://blogs.wcode.org/2013/09/howto-boot-your-raspberry-pi-into-a-fullscreen-browser-kiosk/ - Zdrojový kód fb.c pro RPI
https://github.com/jncronin/rpi-boot/blob/master/fb.c - RPiconfig
http://elinux.org/RPi_config.txt - Mailbox framebuffer interface
https://github.com/raspberrypi/firmware/wiki/Mailbox-framebuffer-interface - Seriál Grafické formáty
http://www.root.cz/serialy/graficke-formaty/ - Vykreslovací pipeline OpenVG (schéma)
https://www.khronos.org/assets/uploads/apis/openvg_pipeline1.jpg - sRGB
https://cs.wikipedia.org/wiki/SRGB