Hlavní navigace

Gradientní výplně a textové objekty v SVG

6. 9. 2007
Doba čtení: 14 minut

Sdílet

Gradientní výplně a grafické objekty reprezentující text tvoří důležitou a nenahraditelnou součást mnoha vektorových obrázků. Proto si v dnešním článku ukážeme, jak je možné v souborech typu SVG použít lineární i radiální gradientní výplně a jak se do SVG obrázků vkládají grafické objekty s textem.

Obsah

1. Vyplňování uzavřených objektů pomocí gradientních výplní
2. Typy gradientních výplní a jejich různé modifikace
3. První demonstrační příklad – použití lineární gradientní výplně
4. Druhý demonstrační příklad – použití radiální gradientní výplně
5. Základy vytváření textových objektů
6. Třetí demonstrační příklad – vložení jednořádkových textů do výkresu
7. Obsah následující části tohoto seriálu

1. Vyplňování uzavřených objektů pomocí gradientních výplní

V mnoha vyspělých vektorových grafických editorech, například Adobe Illustratoru, Inkscape, CorelDraw, ale třeba i v grafickém editoru integrovaném do OpenOffice.org, se setkáme s možností vyplňování uzavřených geometrických objektů takzvanou gradientní výplní čili přechodem. Použití tohoto typu výplně vychází ze samotného principu vektorové grafiky, pomocí které se dá velmi dobře popsat obrys (hranice) objektu, ale styl vykreslení jeho vnitřní části se již popisuje poměrně složitým způsobem. I z tohoto důvodu většina vektorových grafických editorů podporuje použití textur, tj. vyplnění objektů pomocí rastrového obrázku – tímto způsobem je možné spojit přednosti vektorové grafiky (přesný a bezeztrátový popis hranice objektů) a grafiky rastrové (relativně úsporný popis barevně složitých obrazců).

Gradientní výplně/přechody nám v některých případech dovolují vytváření zdánlivě stínovaných či nasvícených objektů, přidávají do obrázku hloubku, simulují použití některých pokročilejších technik, jakou je například blending apod. V mnoha případech je také umožněno zkombinovat možnosti gradientních výplní se změnou průhlednosti (α-kanál lze považovat za čtvrtou složku barvy) a vytvářet objekty typu zvětšovací sklo, vypouklý ciferník hodin, sklenice atd. Teoreticky je sice možné gradientní výplně nahradit vhodnou texturou, avšak za cenu ztráty největších předností vektorové grafiky – nezávislosti na měřítku zobrazení, malé ztráty informace o ukládaných objektech (výkresy lze snadno editovat), a malému objemu souboru s obrázkem při jeho uložení na disk či jeho přenosu po síti.

5401
Obrázek 1: Gradientní přechody v OpenOffice.org

2. Typy gradientních výplní a jejich různé modifikace

V grafickém formátu SVG (Scalable Vector Graphics) je možné použít dva typy gradientních výplní. Jedná se o lineární přechod a radiální přechod. Nemusí se nutně jednat o přechod mezi pouhou dvojicí barev, ale lze definovat libovolné množství „zastávek“ mezi oběma limity, přičemž každé „zastávce“ je přiřazena libovolná barva z celého RGB spektra. Kromě barev lze pro každou „zastávku“ specifikovat i průhlednost, což si ukážeme na dvou demonstračních příkladech uvedených ve třetí a čtvrté kapitole. Na tomto místě je také vhodné říci, že lineární přechody je poměrně jednoduché vykreslit s využitím grafických akcelerátorů, především při jejich použití spolu s objekty typu obdélník či mnohoúhelník.

V souborech typu SVG se definice přechodů ukládají uvnitř párové značky <defs>, jejíž použití jsme si ukázali i v předchozí části tohoto seriálu při vysvětlování použití symbolů (markers). Lineární přechod je reprezentován značkou <linearGradient>, radiální přechod pak značkou <radialGradient>. Opět se jedná o párové značky, to znamená, že se mezi počátečním a koncovým tagem mohou vyskytovat i další typy značek. V tomto případě se jedná o značku <stop>, pomocí které se specifikují výše zmíněné „zastávky“. Každá zastávka může mít uvedenu svoji lokalizaci (atribut offset), barvu (atribut stop-color) a popř. i průhlednost (atribut stop-opacity). Atributy uvedené ve značkách <linearGradient> či <radialGradient> budou vysvětleny přímo u demonstračních příkladů, zde si pouze řekneme, že každý přechod by měl být jednoznačně pojmenován pomocí atributu id.

Vlastní aplikace gradientní výplně při vykreslování některého objektu je poměrně snadná. V předchozích částech tohoto seriálu jsme si řekli, že hodnotou atributu fill je u uzavřeného objektu barva výplně. Pokud se však místo jména barvy či jejího hexadecimálního kódu uvede řetězec url(#idGradien­tu), znamená to, že SVG prohlížeč nebo grafický editor vyhledá gradient se jménem idGradientu a použije ho při vyplňování daného objektu. Tu stejnou gradientní výplň je samozřejmě možné použít i pro větší množství objektů. Konkrétní způsob použití bude opět uveden v obou navazujících kapitolách.

3. První demonstrační příklad – použití lineární gradientní výplně

V dnešním prvním demonstračním příkladu, jehož zdrojový kód je uložen zde, je ukázáno použití lineárního barevného přechodu. Nejprve je nadefinováno pět gradientních přechodů, které dostaly názvy „Gradient1“ až „Gradient5“. První přechod je velmi jednoduchý – zleva doprava se postupně mění barva od červené po žlutou. Postačí tedy nadefinovat dvě „zastávky“, z nichž první je umístěna na začátku vektoru specifikujícího přechod a druhá zastávka je umístěna na konci tohoto vektoru (vektor je automaticky vytvořen pro každý objekt, který daný gradient používá). Druhý přechod se liší pouze v umístění zastávek. Ty již neleží na začátku a konci vektoru, ale zhruba na třetině a dvou třetinách jeho délky. Výsledkem je, že první a poslední třetina výplně obrázku je vykreslena konstantní barvou a vlastní přechod se nachází pouze mezi 30 % – 70 % plochy celého obrazce.

U třetího přechodu je definována transformace, která způsobí, že je celá oblast přechodu posunuta doprava o 20 % jeho šířky. I čtvrtý přechod používá transformaci, tentokrát se však jedná o rotaci o 30° doprava okolo počátku lokálního souřadného systému přechodu. Kromě transformací lze změnit i počáteční a koncový bod vektoru, kterým je gradient specifikován – postačuje nastavit hodnoty atributů x1, y1, x2 a y2. Pátý přechod, který byl použit pro poměrně primitivní vytvoření zvětšovacího skla, se od předchozích čtyř přechodů odlišuje v tom, že se nemění barva, ale průhlednost. Současně je provedena rotace celého přechodu o 90°, takže horní část obrazce je zcela průhledná, zatímco spodní část naopak zcela neprůhledná. Průhlednost je možné zadávat buď ve formě číselné hodnoty z rozsahu 0..1, nebo pomocí procentuálního vyjádření 0%..100%. Hodnoty menší než 0 (či 0 %) a větší než 1 (či 100 %) jsou automaticky ořezány, nejde tedy použít zápornou průhlednost.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg width="400"
     height="400"
     viewBox="0 0 200 200"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink">

     <!-- definice gradientů -->
     <defs>
         <!-- jednoduchý lineární gradient od čistě červené barvy ke žluté barvě -->
         <linearGradient id="Gradient1">
             <stop offset="0%" stop-color="red" />
             <stop offset="100%" stop-color="yellow" />
         </linearGradient>

         <!-- lineární gradient, ve kterém je přechod omezen na rozsah 30-70% šířky objektu -->
         <linearGradient id="Gradient2">
             <stop offset="30%" stop-color="red" />
             <stop offset="70%" stop-color="yellow" />
         </linearGradient>

         <!-- posun původně vycentrovaného přechodu o 20% -->
         <linearGradient id="Gradient3" gradientTransform="translate(0.2)">
             <stop offset="30%" stop-color="red" />
             <stop offset="70%" stop-color="yellow" />
         </linearGradient>

         <!-- rotace původně horizontálního přechodu na 30 stupňů -->
         <linearGradient id="Gradient4" gradientTransform="rotate(30)">
             <stop offset="30%" stop-color="red" />
             <stop offset="70%" stop-color="yellow" />
         </linearGradient>

         <!-- přechod mezi stejnými barvami, avšak rozdílnou průhledností -->
         <linearGradient id="Gradient5" gradientTransform="rotate(90)">
             <stop offset="0%" stop-color="black" stop-opacity="0"/>
             <stop offset="100%" stop-color="black" stop-opacity="1"/>
         </linearGradient>
     </defs>

     <!-- úsečky vykreslené implicitním stylem -->
     <line x1="0" y1="0"   x2="0"   y2="199" />
     <line x1="0" y1="20"  x2="20"  y2="199" />
     <line x1="0" y1="40"  x2="40"  y2="199" />
     <line x1="0" y1="60"  x2="60"  y2="199" />
     <line x1="0" y1="80"  x2="80"  y2="199" />
     <line x1="0" y1="100" x2="100" y2="199" />
     <line x1="0" y1="120" x2="120" y2="199" />
     <line x1="0" y1="140" x2="140" y2="199" />
     <line x1="0" y1="160" x2="160" y2="199" />
     <line x1="0" y1="180" x2="180" y2="199" />
     <line x1="0" y1="199" x2="200" y2="199" />

     <!-- úsečky vykreslené s různou tloušťkou -->
     <line stroke="#8080ff" stroke-width="2"  x1="20"  y1="80"  x2="20" y2="180" />
     <line stroke="#8080ff" stroke-width="4"  x1="40"  y1="80"  x2="40" y2="180" />
     <line stroke="#8080ff" stroke-width="6"  x1="60"  y1="80"  x2="60" y2="180" />
     <line stroke="#8080ff" stroke-width="8"  x1="80"  y1="80"  x2="80" y2="180" />
     <line stroke="#8080ff" stroke-width="10" x1="100" y1="80" x2="100" y2="180" />
     <line stroke="#8080ff" stroke-width="12" x1="120" y1="80" x2="120" y2="180" />
     <line stroke="#8080ff" stroke-width="14" x1="140" y1="80" x2="140" y2="180" />

     <!-- kružnice s nastaveným stylem výplně a barvou obrysu -->
     <circle cx="40" cy="40" r="30" fill="url(#Gradient1)" stroke="black" stroke-width="4" />

     <!-- kružnice s nastaveným stylem výplně a barvou obrysu -->
     <circle cx="160" cy="40" r="30" fill="url(#Gradient2)" stroke="black" stroke-width="4" />

     <!-- kružnice s nastaveným stylem výplně a barvou obrysu -->
     <circle cx="40" cy="160" r="30" fill="url(#Gradient3)" stroke="black" stroke-width="4" />

     <!-- kružnice s nastaveným stylem výplně a barvou obrysu -->
     <circle cx="160" cy="160" r="30" fill="url(#Gradient4)" stroke="black" stroke-width="4" />

     <!-- kružnice s nastaveným stylem výplně a barvou obrysu -->
     <circle cx="100" cy="100" r="50" fill="url(#Gradient5)" stroke="black" stroke-width="4" />

</svg> 

5402
Obrázek 2: První demonstrační příklad po zobrazení v prohlížeči SVG souborů

4. Druhý demonstrační příklad – použití radiální gradientní výplně

Ve druhém demonstračním příkladu, jehož zdrojový kód je opět k dispozici, je vykresleno pět objektů vyplněných radiální výplní. U této výplně je nutné specifikovat její střed (většinou se jedná o první „zastávku“) a poloměr (zde typicky leží poslední „zastávka“). Aby se snadno zadávaly všechny rozměry pomocí procentuálního vyjádření, je nutné nastavit délkové jednotky na hodnotu „objectBoundin­gBox“, což znamená, že se pro každý objekt, na který má být aplikována výplň, vypočítá obalový obdélník z jehož rozměrů se vychází při specifikaci jakýchkoli vzdáleností uvedených v definici radiální výplně.

Pomocí hodnot atributů cx, cy a r se specifikuje kružnice, uvnitř které celý přechod leží – současně se tím určuje také pozice středu radiální výplně. Kromě toho je však možné vytvořit i přechod, jehož střed neleží přesně ve středu obalové kružnice; k tomuto účelu slouží atributy fx a fy. Pokud tyto atributy nejsou nastaveny, je jejich hodnota rovná hodnotám atributů cx a cy. První radiální gradient je jednoduchý: počáteční „zastávka“ se nachází přesně ve středu obalové kružnice, druhá „zastávka“ na jejím obvodu. Ve druhém gradientu se pozice zastávek posunula ze středu obalové kružnice i z jejího obvodu, čímž se oblast vlastního přechodu zúžila. V definici třetího přechodu došlo k přesunu středu kružnice do pravého dolního rohu objektu.

Čtvrtý přechod se od ostatních přechodů odlišuje v tom, že střed přechodu je oproti středu obalové kružnice posunutý, zatímco střed obalové kružnice stále zůstává umístěn uprostřed celého objektu. Pátý přechod je (opět) vytvořen tím způsobem, že se nemění vlastní barva přechodu, ale jeho průhlednost. Efekt „lupy“ by byl viditelnější v případě, že by došlo i ke změně středu přechodu, například do levého horního rohu obrazce.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg width="400"
     height="400"
     viewBox="0 0 200 200"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink">

     <!-- definice gradientů -->
     <defs>

         <!-- jednoduchý radiální gradient od čistě červené barvy ke žluté barvě -->
         <radialGradient id="Gradient1"
             gradientUnits="objectBoundingBox"
             cx="50%" cy="50%" r="50%">
             <stop offset="0%" stop-color="red" />
             <stop offset="100%" stop-color="yellow" />
         </radialGradient>

         <!-- jednoduchý radiální gradient se zkrácenou délkou přechodu -->
         <radialGradient id="Gradient2"
             gradientUnits="objectBoundingBox"
             cx="50%" cy="50%" r="50%">
             <stop offset="30%" stop-color="red" />
             <stop offset="70%" stop-color="yellow" />
         </radialGradient>

         <!-- posun středu výplně -->
         <radialGradient id="Gradient3"
             gradientUnits="objectBoundingBox"
             cx="100%" cy="100%" r="100%">
             <stop offset="20%" stop-color="red" />
             <stop offset="80%" stop-color="yellow" />
         </radialGradient>

         <radialGradient id="Gradient4"
             gradientUnits="objectBoundingBox"
             cx="50%" cy="50%" r="50%" fx="0%" fy="0%">
             <stop offset="20%" stop-color="red" />
             <stop offset="80%" stop-color="yellow" />
         </radialGradient>

         <!-- přechod mezi stejnými barvami, avšak rozdílnou průhledností -->
         <radialGradient id="Gradient5"
             gradientUnits="objectBoundingBox"
             cx="50%" cy="50%" r="50%">
             <stop offset="0%" stop-color="black" stop-opacity="0"/>
             <stop offset="100%" stop-color="black" stop-opacity="1"/>
         </radialGradient>
     </defs>

     <!-- úsečky vykreslené implicitním stylem -->
     <line x1="0" y1="0"   x2="0"   y2="199" />
     <line x1="0" y1="20"  x2="20"  y2="199" />
     <line x1="0" y1="40"  x2="40"  y2="199" />
     <line x1="0" y1="60"  x2="60"  y2="199" />
     <line x1="0" y1="80"  x2="80"  y2="199" />
     <line x1="0" y1="100" x2="100" y2="199" />
     <line x1="0" y1="120" x2="120" y2="199" />
     <line x1="0" y1="140" x2="140" y2="199" />
     <line x1="0" y1="160" x2="160" y2="199" />
     <line x1="0" y1="180" x2="180" y2="199" />
     <line x1="0" y1="199" x2="200" y2="199" />

     <!-- úsečky vykreslené s různou tloušťkou -->
     <line stroke="#8080ff" stroke-width="2"  x1="20"  y1="80"  x2="20" y2="180" />
     <line stroke="#8080ff" stroke-width="4"  x1="40"  y1="80"  x2="40" y2="180" />
     <line stroke="#8080ff" stroke-width="6"  x1="60"  y1="80"  x2="60" y2="180" />
     <line stroke="#8080ff" stroke-width="8"  x1="80"  y1="80"  x2="80" y2="180" />
     <line stroke="#8080ff" stroke-width="10" x1="100" y1="80" x2="100" y2="180" />
     <line stroke="#8080ff" stroke-width="12" x1="120" y1="80" x2="120" y2="180" />
     <line stroke="#8080ff" stroke-width="14" x1="140" y1="80" x2="140" y2="180" />

     <!-- kružnice s nastaveným stylem výplně a barvou obrysu -->
     <circle cx="40" cy="40" r="30" fill="url(#Gradient1)" stroke="black" stroke-width="4" />

     <!-- kružnice s nastaveným stylem výplně a barvou obrysu -->
     <circle cx="160" cy="40" r="30" fill="url(#Gradient2)" stroke="black" stroke-width="4" />

     <!-- kružnice s nastaveným stylem výplně a barvou obrysu -->
     <circle cx="40" cy="160" r="30" fill="url(#Gradient3)" stroke="black" stroke-width="4" />

     <!-- kružnice s nastaveným stylem výplně a barvou obrysu -->
     <circle cx="160" cy="160" r="30" fill="url(#Gradient4)" stroke="black" stroke-width="4" />

     <!-- kružnice s nastaveným stylem výplně a barvou obrysu -->
     <circle cx="100" cy="100" r="50" fill="url(#Gradient5)" stroke="black" stroke-width="4" />

</svg> 

5403
Obrázek 3: Druhý demonstrační příklad po zobrazení v prohlížeči SVG souborů

5. Základy vytváření textových objektů

V SVG lze pracovat i s textovými objekty. Jedná se o plnohodnotné uzavřené objekty, takže je lze vyplňovat jak jednobarevnou výplní, tak i gradientním přechodem, je možné měnit barvu, styl a šířku obrysu, provádět s nimi transformace apod. Co je však nejdůležitější – při všech těchto modifikacích zůstává zachována celistvost textových objektů (nejsou tedy „rozsekány“ na jednotlivé znaky či jejich části), proto je umožněno celý textový objekt či jeho část vybrat a následně uložit do schránky. Tuto operaci lze dokonce provádět i u textů, které jsou vykresleny podél nějaké křivky (samotný výběr je potom poněkud krkolomný, ale stále ho lze provést).

Při specifikaci fontu se používá výběrový mechanismus, který je do značné míry podobný CSS (Cascading Style Sheets), je plně podporováno kódování UTF-8 a pokud daný font obsahuje ligatury (slitky), měly by být v textu použity. V původním SVG bylo umožněno vytváření jednořádkového textu, to se však dnes změnilo a tak je podporován i text odstavcový. Text se do SVG souborů zapisuje značkou <text>, která je párová, protože všechny znaky zapsané uvnitř této značky jsou považovány za řetězec, který se má vykreslit. To mimo jiného znamená, že pokud nějakým filtrem odstraníme ze SVG souboru všechny značky i jejich atributy, dostaneme čitelný textový soubor (samozřejmě záleží na pořadí značek).

5404
Obrázek 4: Obrázek vytvořený pomocí textového objektu a lineárního gradientního přechodu

6. Třetí demonstrační příklad – vložení jednořádkových textů do výkresu

Ve třetím demonstračním příkladu jsou použity čtyři textové objekty. První objekt je vykreslen známým fontem DejaVu Sans (bezpatkové písmo) s implicitním duktem vyplněným červenou barvou. Druhý objekt využívá font DejaVu Serif (patkové písmo) a je zde předvedena bezchybná podpora češtiny ve formátu SVG – není se ostatně čemu divit, když je použito kódování UTF-8. U třetího textového objektu bylo zakázáno vyplňování a naopak byla nastavena barva okraje (hranice) a její šířka. Výsledkem je obrysové písmo (typografové doufám prominou). Čtvrtý textový objekt má definovanou průhlednost a tmavší duktus, takže je viditelné i pozadí.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg width="400"
     height="400"
     viewBox="0 0 200 200"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink">

     <!-- úsečky vykreslené implicitním stylem -->
     <line x1="0" y1="0"   x2="0"   y2="199" />
     <line x1="0" y1="20"  x2="20"  y2="199" />
     <line x1="0" y1="40"  x2="40"  y2="199" />
     <line x1="0" y1="60"  x2="60"  y2="199" />
     <line x1="0" y1="80"  x2="80"  y2="199" />
     <line x1="0" y1="100" x2="100" y2="199" />
     <line x1="0" y1="120" x2="120" y2="199" />
     <line x1="0" y1="140" x2="140" y2="199" />
     <line x1="0" y1="160" x2="160" y2="199" />
     <line x1="0" y1="180" x2="180" y2="199" />
     <line x1="0" y1="199" x2="200" y2="199" />

     <!-- textový objekt -->
     <text x="20" y="30" font-family="DejaVu Sans" font-size="30" fill="red">
     SVG
     </text>

     <!-- kontrola češtiny -->
     <text x="20" y="60" font-family="DejaVu Serif" font-size="30" fill="blue">
     ěščřžýáíéúů
     </text>

     <!-- jen obrys -->
     <text x="20" y="90" font-family="DejaVu Serif" font-size="30" fill="none" stroke="black">
     ěščřžýáíéúů
     </text>

     <!-- poloprůhledný textový objekt -->
     <text x="20" y="180" font-family="DejaVu Sans" font-size="50" font-weight="bold" fill="green" fill-opacity="0.5">
     SVG
     </text>
</svg> 

CS24_early

5405
Obrázek 5: Třetí demonstrační příklad po zobrazení v prohlížeči SVG souborů

7. Obsah následující části tohoto seriálu

V navazující části seriálu o grafických formátech a metaformátech si – kromě dokončení kapitoly o vykreslování textových objektů a o způsobu jejich lineární i nelineární transformace – ukážeme tvorbu animací v souborech typu SVG. Animace a s ní související podpora skriptování, například pomocí populárního a rozšířeného JavaScriptu (přes DOM), dělají ze SVG velmi flexibilní grafický formát, který se některými svými schopnostmi vyrovná i známému Flashi.

Byl pro vás článek přínosný?

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.