Obsah
1. Grafické metaformáty WMF a EMF
2. Podpora WMF a EMF v Linuxu
3. Hlavička souborů typu WMF
4. Typy grafických entit, které se vyskytují v souboru typu WMF
5. Bitmapy a bitmapové operace
6. Čísla jednotlivých příkazů
7. Formát WMF a funkce dostupné z Windows API
8. Odkazy na další informační zdroje
9. Obsah dalšího pokračování tohoto seriálu
1. Grafické metaformáty WMF a EMF
Grafické metaformáty WMF (Windows Metafile) a EMF (Enhanced Windows Metafile) byly navrženy firmou Microsoft pro její známé operační systémy Microsoft Windows. Metaformát WMF existuje už od šestnáctibitových verzí Windows, zatímco EMF odráží nové možnosti grafického subsystému třicetidvoubitových verzí (počínaje Windows NT a Windows 95). Jedná se o formáty, ve kterých mohou být uloženy jak vektorové objekty (úsečky, oblouky, elipsy, výplně, texty), tak i bitmapy a dokonce vybrané příkazy určené pro změnu stavu takzvaného kontextu zařízení (device context), přes který je vykreslování obsahu souboru prováděno. Na formátech WMF a EMF je z programátorského hlediska zajímavá především skutečnost, že se jedná o součást grafického subsystému Microsoft Windows (GDI), což mj. znamená, že se vytváření těchto souborů provádí stejným způsobem, jako vykresování grafiky na obrazovku nebo její tisk na tiskárnu. Liší se pouze kontext zařízení, přes který se vykresluje.
Jedna uživatelem naprogramovaná funkce v nějaké aplikaci tedy může vykreslovat dokumenty na obrazovce, provádět tisk na libovolné tiskárně, renderovat dokumenty do bitmapy či vytvářet soubory WMF či EMF – jedná se vlastně o základ systému WYSIWYG (What You See Is What You Get). Integrace obou grafických metaformátů se také odráží v možnosti přenosu (převážně) vektorových obrázků mezi různými aplikacemi přes schránku (clipboard), přičemž interním formátem schránky může být právě WMF či EMF s upravenou hlavičkou (konkrétně se jedná o formáty představované konstantami CF_ENHMETAFILE a CF_METAFILEPICT). Vektorový obrázek je možné použít i v těch nejzákladnějších aplikacích, například WordPadu či PaintBrushi (Malování). V prvním případě zůstane vektorový formát výkresů zachován, ve druhém případě systém provede jejich rasterizaci, tj. převod na bitmapu (tím pádem samozřejmě dochází ke ztrátě důležitých informací a původní výkres už nebude možné zcela zrekonstruovat).
Obrázek 2: Tentýž výkres po otevření v textovém editoru WordPadVelká závislost struktury metaformátů WMF a EMF na aplikačním rozhraní Microsoft Windows je však také nebezpečná – soubory tohoto typu mohou obsahovat volání funkcí Windows API, které neprovádí pouze vykreslení, ale i jiné činnosti, typicky spuštění programového kódu. V minulosti se už podobné útoky odehrály, proto byly funkce pracující s WMF/EMF upraveny tak, aby se možnost těchto útoků zminimalizovala – jedná se například o potenciálně nebezpečnou funkci PlayMetaFile() a PlayMetaFileRecord(). Aplikace, které soubory typu WMF/EMF zpracovávají svými vlastními metodami (a kontrolují tak, které funkce Windows API se ve skutečnosti volají), jsou proti těmto útokům do značné míry odolné.
Obrázek 3: Po vložení vektorového obrázku ze schránky do programu PaintBrush (Malování) dojde ke vložení jeho rastrové podoby
2. Podpora WMF a EMF v Linuxu
Po přečtení předchozích třech odstavců by se mohlo zdát, že formáty WMF a EMF jsou používány pouze na operačních systémech Microsoft Windows. Ve skutečnosti jsou však tyto formáty poměrně často využívány i na dalších operačních systémech, už jenom proto, že existuje velké množství hotových vektorových výkresů (klipartů), které je samozřejmě vhodné použít i na jiných platformách. Kromě toho jsou obrázky uložené v těchto formátech zakomponovány i do souborů EPS (Encapsulated PostScript) pro náhledy a samozřejmě také v dokumentech tvořených balíkem Microsoft Office (vložené vektorové obrázky, export z programu PowerPoint) či OpenOffice.org.
Obrázek 4: Tentýž vektorový obrázek zobrazený v prohlížeči Eye of Gnome (všimněte si nekorektního nastavení poměru stran)V Linuxu jsou grafické formáty WMF a EMF podporovány například skupinou filtrů z balíku Image Magick; jedná se především o filtry wmf2eps, wmf2fig, wmf2gd, wmf2svg a wmf2×. Mezi další aplikace, které s WMF a EMF pracují, patří OpenOffice.org, GhostView, Gnumeric a velké množství prohlížečů grafických souborů (Eye of GNOME, gThumb apod.). Z dalších aplikací jmenujme například yFiles (aplikace psaná v Javě) či knihovnu Batik (opět určeno primárně pro Javu) nebo původní Linuxovou knihovnu libwmf, kterou lze samostatně použít i když není nainstalovaný Image Magick.
Obrázek 5: I prohlížeč gThumb umí pracovat s formátem WMF
3. Hlavička souborů typu WMF
Jako většina grafických formátů a metaformátů, které jsou uloženy v samostatných souborech, obsahují soubory typu WMF a EMF binárně zakódovanou hlavičku, za kterou již následují grafická data. Vzhledem k tomu, že WMF a dnes především EMF prochází postupným vývojem, který sleduje rozšiřování funkčností samotného Windows API, můžeme se v praxi setkat s několika typy hlaviček. Standardní hlavička grafického souboru uloženého ve formátu WMF, která má délku 18 bytů, což odpovídá devíti dvoubytovým slovům, má následující strukturu (všechny vícebytové položky jsou uloženy ve formátu little endian a termínem slovo mám v tomto kontextu na mysli dvoubytovou hodnotu bez znaménka, tj. uloženou v přímém kódu):
Offset (dec) | Délka (byty) | Název | Význam datové položky |
---|---|---|---|
0 | 2 | FileTypeStorage | způsob uložení souboru: 2-na disku, 1-v paměti |
2 | 2 | HeaderSize | velikost hlavičky ve slovech (prakticky vždy 9) |
4 | 2 | Version | verze OS, ve které byl soubor vytvořen, většinou hodnota 0×0010 (bez bitmap) či 0×0030 (s bitmapami nezávislými na zařízení) |
6 | 4 | FileSize | velikost celého souboru ve slovech |
10 | 2 | NumOfObjects | celkový počet objektů uložených v souboru |
12 | 4 | MaxRecordSize | velikost největšího záznamu ve slovech (pravděpodobně pro potřeby bufferingu) |
16 | 2 | ParametersNumb | rezervováno, nastavené na nulu |
V ojedinělých případech, typicky při exportu grafických dat z aplikace OpenOffice.org, se můžeme setkat i se soubory typu WMF, které používají takzvanou rozšířenou výměnnou hlavičku, ve které jsou uvedeny informace o geometrii celého výkresu, tj. jeho rozměry a měřítko, přesněji řečeno počet délkových jednotek na jeden palec (i v případě této hlavičky se stále jedná o soubory typu WMF a ne EMF!). WMF soubory s touto hlavičkou se nazývají umístitelné metasoubory (placeable metafiles), protože je lze v rámci vykreslovací plochy jednoduše posouvat a měnit jejich velikost bez ohledu na to, jak složité jsou grafické objekty v nich umístěné – jedná se tedy o zjednodušenou analogii formátu EPS. Tyto soubory nejsou přímo zpracovatelné pomocí Windows API, ale jejich úprava na standardní WMF je velmi jednoduchá: postačuje odříznout prvních 22 bytů (například posunem ukazatele v paměti) a výsledkem je standardní (základní) soubor typu WMF.
Existenci rozšířené výměnné hlavičky je možné zjistit z „magického čísla“ (magic number), které je rovno hodnotě 0×9ac6cdd7, ovšem uloženého ve formátu little-endian, tj. jako sekvence bytů 0×d7 0×cd 0×c6 0×9a. Vzhledem k tomu, že normální hlavička začíná bytem s hodnotou 0×01 či 0×02, je existence rozšířené hlavičky vždy zjištěna jednoznačně. Formát výměnné hlavičky, za níž následuje obvyklá hlavička WMF souborů, je následující:
Offset (dec) | Délka (byty) | Název | Význam datové položky |
---|---|---|---|
0 | 4 | Password | magická hodnota 0×9ac6cdd7 |
4 | 2 | Handle | číslo souboru, většinou je zde uložena nula |
6 | 2 | LeftCorn | souřadnice levého okraje (délková jednotka twip) |
8 | 2 | TopCorn | souřadnice horního okraje (délková jednotka twip) |
10 | 2 | RightCorn | souřadnice pravého okraje (délková jednotka twip) |
12 | 2 | BottomCorn | souřadnice dolního okraje (délková jednotka twip) |
14 | 2 | Twip_p_Inch | počet délkových jednotek (twip) na palec, implicitně je použita hodnota 1440 |
16 | 4 | Reserved | tato čtveřice bytů musí být nulová |
20 | 2 | Checksum | kontrolní součet hlavičky |
Kontrolní součet hlavičky se počítá po jednotlivých slovech (dvojici bytů) a může se provádět například následujícím C-čkovým kódem, ve kterém je celá výměnná hlavička uložena ve struktuře nazvané phm:
pmh.Checksum= 0;
pmh.Checksum^=(pmh.Key & 0x0000FFFFUL);
pmh.Checksum^=((pmh.Key & 0xFFFF0000UL) >> 16);
pmh.Checksum^=pmh.Handle;
pmh.Checksum^=pmh.Left;
pmh.Checksum^=pmh.Top;
pmh.Checksum^=pmh.Right;
pmh.Checksum^=pmh.Bottom;
pmh.Checksum^=pmh.Inch;
pmh.Checksum^=(pmh.Reserved & 0x0000FFFFUL);
pmh.Checksum^=((pmh.Reserved & 0xFFFF0000UL) >> 16);
4. Typy grafických entit, které se vyskytují v souboru typu WMF
Po hlavičce či obou hlavičkách (výměnné a základní) jsou v souborech typu WMF uloženy informace o jednotlivých grafických entitách. Kromě samotných grafických entit zde mohou být uloženy příkazy pro změnu barvy, zarovnání textu, úpravu barvové palety, nastavení operací prováděných při vykreslování bitmap (ROP – Raster Operation) apod. Každá grafická entita či příkaz jsou uloženy v samostatných blocích, které mají jednotnou hlavičku se základními údaji, za kterou následují jejich parametry. Počet a význam parametrů se samozřejmě v jednotlivých příkazech liší:
Offset | Délka (byty) | Název | Význam |
---|---|---|---|
0 | 4 | Size | velikost celého bloku zadaná ve slovech (2 byty) |
4 | 2 | Function | Číslo GDI funkce (lze získat z referenční příručky nebo souboru wingdi.h) |
6 | 2×? | Parameters[] | Pole parametrů uložené v obráceném pořadí |
Některé složitější příkazy používají místo pole parametrů odlišnou datovou strukturu. Jedná se například o příkaz Polyline sloužící k vykreslení polyčáry s libovolným počtem vrcholů či příkaz BitBlt využívaná pro zobrazení bitmapy. Strukturu parametrů lze opět získat z referenční příručky WinAPI nebo přímo z hlavičkových souborů (ty jsou dodávány i ke GNU překladači gcc, který je na platformě Microsoft Windows šířen například v populárním balíku djgpp nebo MinGW).
Mezi podporované grafické entity patří:
- úsečka (kreslená dvojicí příkazů MoveTo a LineTo)
- kruhová výseč (vyplněná i nevyplněná)
- elipsa (specielně kružnice)
- polyčára (lomená čára)
- polygon (vyplněný mnohoúhelník)
- obdélník (i vyplněný vzorkem)
- jednořádkový text
- text omezený obdélníkem (může se jednat i o víceřádkový text)
- obecná výplň omezená libovolnou hranicí (flood fill)
5. Bitmapy a bitmapové operace
V grafických souborech typu WMF se nemusí nacházet pouze vektorové objekty (jejichž seznam byl uveden v předchozí kapitole), ale také bitmapy, tj. rastrové obrázky. WMF, podobně jako grafický subsystém Windows (GDI), podporuje při vykreslování bitmap aplikaci takzvaných rastrových operací (ROP), což je nastavení způsobu kombinace barev vykreslovaných pixelů s barvou pozadí. Vhodně nastavenou rastrovou operací je například možné zabránit překreslování pozadí v těch místech, kde bitmapa obsahuje zcela průhledné pixely. Pomocí operace STRETCHBLT a DIBSTRETCHBLT lze měnit velikost (měřítko) vykreslovaných bitmap, nebo tyto bitmapy zrcadlit.
Obrázek 6: WMF soubor vložený do tabulky v aplikaci Gnumeric
6. Čísla jednotlivých příkazů
Následující seznam příkazů GDI spolu s jejich čísly (ty jsou uloženy v hlavičce každého bloku, viz čtvrtou kapitolu) byl získán z hlavičkových souborů volně dostupných C-čkových překladačů pro Microsoft Windows; ovšem tento seznam se může hodit i lidem, kteří například implementují export WMF v jiném operačním systému (ale i v tomto případě je vhodné si obstarat soubor wingdi.h, který níže uvedený seznam obsahuje ve formě symbolických konstant). Při načítání WMF souborů je obecně nutné respektovat délku každého bloku (ta je uložena v jeho hlavičce) a neznámé bloky či bloky se špatnými parametry prostě ignorovat. Zápis, tj. vytváření WMF souborů, je většinou mnohem jednodušší, protože se používá pouze omezený počet příkazů. Například některé systémy typu CAD si vystačí s příkazy LINETO, MOVETO, ARC, POLYGON a POLYLINE.
META_SETBKCOLOR 0x0201
META_SETBKMODE 0x0102
META_SETMAPMODE 0x0103
META_SETROP2 0x0104
META_SETRELABS 0x0105
META_SETPOLYFILLMODE 0x0106
META_SETSTRETCHBLTMODE 0x0107
META_SETTEXTCHAREXTRA 0x0108
META_SETTEXTCOLOR 0x0209
META_SETTEXTJUSTIFICATION 0x020A
META_SETWINDOWORG 0x020B
META_SETWINDOWEXT 0x020C
META_SETVIEWPORTORG 0x020D
META_SETVIEWPORTEXT 0x020E
META_OFFSETWINDOWORG 0x020F
META_SCALEWINDOWEXT 0x0410
META_OFFSETVIEWPORTORG 0x0211
META_SCALEVIEWPORTEXT 0x0412
META_LINETO 0x0213
META_MOVETO 0x0214
META_EXCLUDECLIPRECT 0x0415
META_INTERSECTCLIPRECT 0x0416
META_ARC 0x0817
META_ELLIPSE 0x0418
META_FLOODFILL 0x0419
META_PIE 0x081A
META_RECTANGLE 0x041B
META_ROUNDRECT 0x061C
META_PATBLT 0x061D
META_SAVEDC 0x001E
META_SETPIXEL 0x041F
META_OFFSETCLIPRGN 0x0220
META_TEXTOUT 0x0521
META_BITBLT 0x0922
META_STRETCHBLT 0x0B23
META_POLYGON 0x0324
META_POLYLINE 0x0325
META_ESCAPE 0x0626
META_RESTOREDC 0x0127
META_FILLREGION 0x0228
META_FRAMEREGION 0x0429
META_INVERTREGION 0x012A
META_PAINTREGION 0x012B
META_SELECTCLIPREGION 0x012C
META_SELECTOBJECT 0x012D
META_SETTEXTALIGN 0x012E
META_CHORD 0x0830
META_SETMAPPERFLAGS 0x0231
META_EXTTEXTOUT 0x0a32
META_SETDIBTODEV 0x0d33
META_SELECTPALETTE 0x0234
META_REALIZEPALETTE 0x0035
META_ANIMATEPALETTE 0x0436
META_SETPALENTRIES 0x0037
META_POLYPOLYGON 0x0538
META_RESIZEPALETTE 0x0139
META_DIBBITBLT 0x0940
META_DIBSTRETCHBLT 0x0b41
META_DIBCREATEPATTERNBRUSH 0x0142
META_STRETCHDIB 0x0f43
META_EXTFLOODFILL 0x0548
META_SETLAYOUT 0x0149
META_DELETEOBJECT 0x01f0
META_CREATEPALETTE 0x00f7
META_CREATEPATTERNBRUSH 0x01F9
META_CREATEPENINDIRECT 0x02FA
META_CREATEFONTINDIRECT 0x02FB
META_CREATEBRUSHINDIRECT 0x02FC
META_CREATEREGION 0x06FF
7. Formát WMF a funkce dostupné z Windows API
Vzhledem k tomu, že grafické metaformáty WMF a EMF byly vyvinuty primárně pro použití v operačních systémech Microsoft Windows, není divu, že jsou nativně podporovány i v aplikačním rozhraní (API) těchto operačních systémů. Vytváření grafických metaformátů probíhá naprosto stejným způsobem jako kreslení na obrazovku nebo tisk na plotteru či tiskárně – stále se používají shodné funkce dostupné z grafického subsystému Microsoft Windows (GDI – Graphics Device Interface), liší se pouze rozměry kreslicího plátna, maximální počet barev a některé rozšiřující možnosti (plottery například nedokážou vyplňovat obecné plochy či kreslit barevné bitmapy). Ve většině aplikací je však jedinou změnou mezi vykreslením obrázku na obrazovku, jeho výstupem na tiskárnu či „tiskem“ do metasouboru použití odlišného kontextu zařízení (DC – device context), což je ukázáno na následujícím fragmentu kódu, ve kterém je nejdříve vytvořen kontext zařízení a posléze je do něj vykreslen jednoduchý vektorový obrázek:
// vytvoření kontextu zařízení grafického metasouboru vytvořeného v paměti
// při předání skutečného jména souboru se metasoubor vytvoří na disku
HDC hdcMeta=CreateMetaFile(NULL);
// vytvoření štětce, kterým se bude vyplňovat kružnice
// RGB je makro pro snazší výběr barvového odstínu
HBRSUH hBrush=CreateSolidBrush(RGB(0, 0, 255));
// vykreslení čtverce o velikosti 100x100
Rectangle(hdcMeta, 0, 0, 100, 100);
// vykreslení úhlopříček obdélníka
// MoveToEx vrací v posledním parametru původní
// souřadnice grafického kurzoru, ty však nepotřebujeme
MoveToEx(hdcMeta, 0, 0, NULL);
LineTo (hdcMeta, 100, 100);
MoveToEx(hdcMeta, 0, 100, NULL);
LineTo (hdcMeta, 100, 0);
// vykreslení vyplněné kružnice
SelectObject(hdcMeta, hBrush);
Ellipse(hdcMeta, 20, 20, 80, 80);
// nyní již nepotřebný štětec je vhodné smazat
// aby se zbytečně neplýtvalo prostředky GDI
DeleteObject(hBrush);
// uzavření kontextu zařízení a vrácení handlu
// metasouboru vytvořeného v operační paměti
HMETAFILE hmf=CloseMetaFile(hdcMeta);
Po uzavření metasouboru funkcí CloseMetaFile() je metasoubor buď uložen do operační paměti pro další použití (například rychlejší vykreslení složitějších obrázků či jejich přenos přes schránku) nebo je trvale uložen na disk – záleží na tom, s jakým parametrem byla zavolána funkce CreateMetaFile(). Pokud je metasoubor uložen v operační paměti, je možné provést jeho vykreslení pomocí funkce PlayMetaFile(), která opravdu „přehrává“ seznam grafických příkazů uložených v metasouboru na předaném kontextu zařízení. V následující ukázce je metasoubor vykreslen celkem stokrát ve mřížce o velikosti 10×10 buněk:
// začátek kreslení - získání kontextu zařízení
HDC hdc=BeginPaint(hwnd, &ps);
// po systému, resp. GDI nebudeme chtít, aby
// přepočítával předávané souřadnice
SetMapMode(hdc, MM_ANISOTROPIC);
// rozměry kreslicí plochy
SetWindowExtEx(hdc, 1000, 1000, NULL);
// rozměry viewportu podle aktuální velikosti okna
SetViewportExtEx(hdc, cxClient, cyClient, NULL);
// vykreslení obsahu metasouboru ve mřížce 10x10 buněk
for (x=0; x<10; x++) {
for (y=0; y<10; y++) {
// původní rozměr obrázku je 100x100 jednotek
SetWindowOrgEx(hdc, -100*x, -100*y, NULL);
// "přehrání" metasouboru a vykreslení jeho obsahu
PlayMetaFile (hdc, hmf);
}
}
// konec kreslení (popř. lze provést i double-buffering)
EndPaint(hwnd, &ps);
Pro práci s metaformátem WMF slouží ve Windows API sada jedenácti základních funkcí:
CloseMetaFile() |
CopyMetaFile() |
CreateMetaFile() |
DeleteMetaFile() |
EnumMetaFile() |
EnumMetaFileProc() |
GetMetaFile() |
GetMetaFileBitsEx() |
PlayMetaFile() |
PlayMetaFileRecord() |
SetMetaFileBitsEx() |
Rozšiřující EMF je v API podporován odpovídajícím větším množstvím funkcí, kterých je celkem 17 (pro GDI+ existuje ještě větší množství funkcí, ty si zde však nebudeme popisovat):
CloseEnhMetaFile() |
CopyEnhMetaFile() |
CreateEnhMetaFile() |
DeleteEnhMetaFile() |
EnhMetaFileProc() |
EnumEnhMetaFile() |
GdiComment() |
GetEnhMetaFile() |
GetEnhMetaFileBits() |
GetEnhMetaFileDescription() |
GetEnhMetaFileHeader() |
GetEnhMetaFilePaletteEntries() |
GetWinMetaFileBits() |
PlayEnhMetaFile() |
PlayEnhMetaFileRecord() |
SetEnhMetaFileBits() |
SetWinMetaFileBits() |
8. Odkazy na další informační zdroje
- Charles Petzold: Programming Windows,
Microsoft Press, 1998 - Microsoft: Platform SDK,
Microsoft, 1995–2007 - Wikipedia EN: Windows Metafile,
http://en.wikipedia.org/wiki/Windows_Metafile - Wikipedia EN: Windows Media Format,
http://en.wikipedia.org/wiki/Windows_Media_Format - Microsoft Windows Metafile,
http://wvware.sourceforge.net/caolan/ora-wmf.html - Windows Metafiles, a guide for non-windows programmers,
http://wvware.sourceforge.net/caolan/index.html - Metafile Reference,
http://msdn2.microsoft.com/en-us/library/ms534300.aspx - yFiles 2.3.x graph visualization library and WMF,
http://www.yworks.com/en/products_yfiles_ep_ywmf.htm - yFiles gallery,
http://www.yworks.com/en/products_yfiles_practicalinfo_gallery.htm - libwmf – library for converting WMF files,
http://wvware.sourceforge.net/libwmf.html
9. Obsah dalšího pokračování tohoto seriálu
V následujícím pokračování seriálu o grafických formátech a metaformátech si popíšeme ideového nástupce formátu WMF, kterým je grafický metaformát EMF.